
/* ==== css/theme.css ==== */
/* Theme Variables - Simplified Color System */
/* Colors are loaded dynamically from /api/theme.css */

html {
    scrollbar-gutter: stable;
    --dashboard-bg-opacity: 1;

    /*
     * Two tokens the stylesheets ask for and no theme supplies.
     *
     * A var() with no fallback that resolves to nothing makes the whole
     * declaration invalid, so these did not degrade -- they vanished. The
     * transitions in status.css simply never ran, and the finder search mode,
     * which is coloured to tell it apart from the other modes, inherited the
     * colour of whatever it sat in and so looked like no mode at all.
     *
     * accent-secondary is mixed from the accent the theme does define rather
     * than picked: it has to differ from --accent-primary to do its job, and
     * it must not borrow --accent-warning or --accent-success, whose colours
     * already mean something. Pulling the accent towards the text colour
     * gives a second tone of the same family on every theme.
     */
    --accent-secondary: color-mix(in srgb, var(--accent-primary) 55%, var(--text-primary));
    --transition-normal: 0.2s ease;
}

/* Background Patterns */
html[data-theme] body {
    background-color: var(--background-primary);
    color: var(--text-primary);
    isolation: isolate;
}

html[data-theme] body:not(.no-background-dots)::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background-image: radial-gradient(var(--background-dots) 1px, transparent 1px);
    background-size: 15px 15px;
}

body.no-background-dots::before {
    content: none;
}

/* Custom Background (auto / gradient / image) — ::after keeps opacity on the backdrop only */
body.has-custom-background::after {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background-image: var(--custom-background-image);
    background-size: cover;
    opacity: var(--dashboard-bg-opacity, 1);
}

body.has-custom-background.bg-image::after {
    background-position: center center;
    background-repeat: no-repeat;
}

/* Preview icons in config */
.theme-icon-preview .preview-icon {
    width: 28px;
    height: 28px;
    display: inline-block;
    margin-right: 8px;
    border-radius: var(--radius-4, 6px);
    background: linear-gradient(135deg, rgba(255,255,255,0.06), rgba(0,0,0,0.06));
}

/* Themed icon wrappers and variants */
.icon-themed, .icon-themed img, .icon-themed .preview-icon {
    transition: filter .15s ease, opacity .15s ease, background-color .15s ease;
    border-radius: var(--radius-4, 6px);
}
.icon-themed--muted img, .icon-themed--muted .preview-icon {
    filter: grayscale(calc(var(--icon-theme-intensity, 0.6))); 
    opacity: calc(1 - (var(--icon-theme-intensity, 0.25) * 0.8));
}
.icon-themed--tinted img, .icon-themed--tinted .preview-icon {
    filter: saturate(calc(0.6 + var(--icon-theme-intensity, 0.4)));
    mix-blend-mode: multiply;
}
.icon-themed--tinted {
    background-color: color-mix(in srgb, var(--accent-primary) var(--icon-theme-intensity, 0.12), transparent);
}
/* A glyph is its own host — there is no <img> inside a category header's emoji
   — so the variants above, which reach for img/.preview-icon, would leave it at
   full colour beside harmonised favicons. Tinted deliberately skips the multiply
   blend the image rule uses: on text it eats the glyph rather than colouring it. */
.icon-themed--muted.icon-themed-glyph {
    filter: grayscale(calc(var(--icon-theme-intensity, 0.6)));
    opacity: calc(1 - (var(--icon-theme-intensity, 0.25) * 0.8));
}
.icon-themed--tinted.icon-themed-glyph {
    filter: saturate(calc(0.6 + var(--icon-theme-intensity, 0.4)));
}

.icon-themed--overlay {
    padding: 3px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background-color: color-mix(in srgb, var(--accent-primary) var(--icon-theme-intensity, 0.12), transparent);
}


/* ── Focus styles (keyboard navigation) ── */

*:focus {
    outline: none;
}

input:focus-visible,
textarea:focus-visible,
select:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 0;
}

button:focus-visible,
a:focus-visible,
[role="button"]:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}

.category-title:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
    border-radius: var(--radius-1, 2px);
}

.search-command-group-header:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: -2px;
}

/* No Animations Option */
body.no-animations * {
    animation: none !important;
    transition: none !important;
    transform: none !important;
}

/* ==== css/spacing-tokens.css ==== */
/* Spacing tokens — the single scale for padding/margin/gap across the
   dashboard.

   Unlike layout-modern-tokens.css, this is NOT scoped to
   body[data-layout-version="modern"]: classic is the default layout every
   fresh install starts on (LayoutVersion defaults to "classic" — see
   models.go), so a token layer that only modern can reach would help almost
   nobody. These apply everywhere.

   Two groups:

   1. The macro scale — steps on a 0.25rem (4px) rhythm, for spacing where no
      other value nearby carries a different meaning. Loaded ahead of every
      view stylesheet so every declaration that adopts var(--space-N, …)
      resolves to the token, with the existing literal kept as the fallback.

      --space-1-5 breaks the strict 0.25rem rhythm on purpose: 0.35rem is not
      an outlier, it is the single most common spacing value in the app —
      roughly 120 uses across dashboard.css, config-view.css, health-view.css
      and dashboard-inbox.css combined, more than any other value including
      0.25rem and 0.5rem. Rounding it onto the strict rhythm would have been
      the single largest visual change in this whole pass, applied at the
      widest scale, to the value the app already leans on hardest. Naming it
      instead of erasing it is the more honest read of what the CSS is
      already doing.

   2. A separate, deliberately finer density sub-scale for the three bookmark
      row densities (comfortable/compact/dense). These do NOT collapse onto
      the macro scale: the three-way distinction between them is the entire
      point of the setting, and 0.25rem steps are too coarse to hold three
      readable levels apart (0.35rem and 0.2rem, the current compact/dense
      values, are both closer to the same macro step than to each other).
      Named here so the relationship between the six values is visible in one
      place instead of scattered across three separate rules, without
      pretending they are three cases of one number. */

:root {
    /* ── Macro scale ─────────────────────────────────────────────────── */
    --space-hairline: 0.15rem;
    --space-1: 0.25rem;
    --space-1-5: 0.35rem; /* deliberate exception — see note above */
    --space-2: 0.5rem;
    --space-3: 0.75rem;
    --space-4: 1rem;
    --space-5: 1.5rem;
    --space-6: 2rem;
    --space-7: 3rem;

    /* ── Bookmark row density sub-scale ──────────────────────────────── */
    /* Vertical padding inside a row. */
    --density-row-pad-comfortable: 0.5rem;
    --density-row-pad-compact: 0.35rem;
    --density-row-pad-dense: 0.2rem;

    /* Gap between rows in a list. */
    --density-row-list-gap-comfortable: 0.42rem;
    --density-row-list-gap-compact: 0.3rem;
    --density-row-list-gap-dense: 0.18rem;

    /* Gap between the elements inside one row (favicon / name / shortcut /
       response time). Only "dense" narrows this today. */
    --density-row-inner-gap-dense: 0.45rem;
}

/* ==== css/typography-tokens.css ==== */
/* Typography tokens — the scale for font-size across the dashboard.

   Unscoped, same reasoning as spacing-tokens.css: classic is the default
   layout, so a token layer classic cannot reach helps almost nobody.

   Anchored on the actual distribution rather than a clean textbook ratio.
   Measured across every static/css/*.css file: 365 font-size declarations
   using rem, spread over 40 distinct values between 0.46rem and 1.65rem —
   most of them within a couple hundredths of a rem of their neighbour.
   0.72rem alone accounts for 65 of the 365 — more than any other single
   value — with 0.68rem (45) and 0.7rem (30) immediately next to it.

   Seven steps, each placed at the densest point of a real cluster in that
   distribution, not at a round mathematical interval:

     --text-2xs   0.65rem   the smallest meta text (absorbs 0.58–0.68rem)
     --text-xs    0.72rem   labels, shortcuts, status text (absorbs 0.7–0.76rem)
     --text-sm    0.8rem    secondary body text (absorbs 0.78–0.85rem)
     --text-base  0.9rem    primary small body text (absorbs 0.86–0.95rem)
     --text-md    1rem      the app's base size (absorbs 0.98–1.05rem)
     --text-lg    1.1rem    small headings, emphasised values (absorbs 1.1–1.15rem)
     --text-xl    1.25rem   section headings (absorbs 1.25–1.32rem)

   A handful of values fell outside every cluster (0.46–0.54rem and
   1.35–1.65rem, 14 declarations total) and were left as literals rather than
   forced onto the nearest step — those are few enough, and far enough from
   their neighbours, to read as deliberate rather than accidental. */

:root {
    --text-2xs: 0.65rem;
    --text-xs: 0.72rem;
    --text-sm: 0.8rem;
    --text-base: 0.9rem;
    --text-md: 1rem;
    --text-lg: 1.1rem;
    --text-xl: 1.25rem;
}

/* ==== css/radius-tokens.css ==== */
/* Radius tokens — the scale for border-radius across the dashboard.

   Unscoped, same reasoning as spacing-tokens.css and typography-tokens.css.

   layout-modern-tokens.css already defines --layout-radius-sm/md/lg/pill
   (8/12/16px/pill), but only for body[data-layout-version="modern"] — and
   only four steps, which is coarser than what classic actually uses today.
   Measured across every static/css/*.css file: 352 border-radius
   declarations, mixing px and rem for the same handful of intended sizes
   (3px sitting next to 0.2rem, 4px next to 0.25rem, and so on), clustering
   around eight real steps rather than four:

     --radius-1     2px    barely-rounded corners (chips, thin borders)
     --radius-2     3px
     --radius-3     4px    the single largest cluster in the app
     --radius-4     6px    the second-largest cluster
     --radius-5     8px    matches --layout-radius-sm in the modern layer
     --radius-6     10px
     --radius-7     12px   matches --layout-radius-md in the modern layer
     --radius-pill  999px  fully round — already used consistently (82
                           uses, already all 999px); named for the same
                           reason the others are, not because it needed
                           consolidating

   Deliberately in px, not rem: border-radius does not need to scale with
   the user's font-size setting the way spacing and type do, and every
   existing literal in the codebase already mixes both units for the same
   intended size — a single unit removes that mismatch outright.

   One declaration (20px, in responsive.css) sat 8px from the nearest step
   and was left as a literal rather than forced onto --radius-7. */

:root {
    --radius-1: 2px;
    --radius-2: 3px;
    --radius-3: 4px;
    --radius-4: 6px;
    --radius-5: 8px;
    --radius-6: 10px;
    --radius-7: 12px;
    --radius-pill: 999px;
}

/* ==== css/theme-depth.css ==== */
/* Depth tokens — a second layer, derived from the thirteen a theme declares.

   A theme is thirteen colours (see ThemeColors in models.go), of which two are
   surfaces: background-primary and background-secondary. Two surfaces cannot
   express depth, which is why every view ends up flat — a card, a panel and a
   control are all painted the same, and only a border says where one stops and
   the next begins.

   Rather than ask every theme for more colours, this derives them. Three
   reasons that is the right trade:

     - There are 148 built-in themes. Adding a field means editing all of them,
       plus every custom theme somebody already made, plus a migration for
       colors.json.
     - A theme's colours cannot hold a gradient anyway: sanitizeCSSColor
       (security.go) accepts hex, rgb(), hsl() and colour names, so a
       linear-gradient() in a theme field arrives as "transparent".
     - layout-modern-tokens.css already works this way and has proven it holds
       up across the whole set.

   Nothing here paints anything on its own. These are tokens for view CSS to
   consume, so this file can land without changing a single pixel, and each
   surface can be moved over one at a time. */

/* ── Depth scale ────────────────────────────────────────────────────────────
   The two scalars every token below is built from, set per depth choice.

   On <body> rather than <html> because that is where data-depth lands, and a
   variable cannot read an attribute from an element below it. Everything
   derived from them lives on body too, so a descendant inherits both. */

body[data-depth="flat"] body {
    --theme-tint: 0%;
    --theme-depth: 0;
}

body,
body[data-depth="soft"] body {
    --theme-tint: 3%;
    --theme-depth: 1;
}

body[data-depth="rich"] body {
    --theme-tint: 5%;
    --theme-depth: 1.5;
}

html[data-theme] body {
    /*
     * How much of the theme's own colour bleeds into its greys.
     *
     * This is what separates a palette that looks designed from one that looks
     * like grey with coloured text on it: in Catppuccin, Tokyo Night and Night
     * Owl the backgrounds are not neutral, they are heavily desaturated
     * versions of the accent. Three percent is enough to read as intent and
     * little enough to keep contrast where it was.
     *
     * Set above per depth choice, and overridden per theme at the bottom of
     * this file: a paper theme wants 0, a neon one can carry more.
     */

    /*
     * The surface ladder.
     *
     * Mixed with text-primary rather than with white or black, because
     * text-primary is the one colour that moves the right way in both light
     * and dark themes: toward the reader, away from the page. A hard-coded
     * rgba(255,255,255,…) would lighten a light theme into nothing.
     *
     * surface-0 is the page. Each step above it is a card on a card.
     */
    --surface-0: var(--background-primary);
    --surface-1: color-mix(in srgb,
        var(--accent-primary) var(--theme-tint),
        color-mix(in srgb, var(--text-primary) calc(3% * var(--theme-depth)), var(--background-primary)));
    --surface-2: color-mix(in srgb,
        var(--accent-primary) var(--theme-tint),
        color-mix(in srgb, var(--text-primary) calc(6% * var(--theme-depth)), var(--background-primary)));
    --surface-3: color-mix(in srgb,
        var(--accent-primary) var(--theme-tint),
        color-mix(in srgb, var(--text-primary) calc(9% * var(--theme-depth)), var(--background-primary)));

    /*
     * Hairlines.
     *
     * border-primary is used at full opacity today, which draws a line around
     * everything and makes a dashboard read as a form. At 45% it separates
     * without announcing itself; the strong variant is for what should be
     * announced.
     */
    --hairline: color-mix(in srgb, var(--border-primary) 45%, transparent);
    --hairline-strong: color-mix(in srgb, var(--border-primary) 70%, transparent);

    /*
     * The lit top edge of a raised surface. One inset line, and a flat
     * rectangle starts reading as something sitting on the page rather than
     * printed on it. This is the cheapest depth cue there is.
     */
    --edge-light: inset 0 1px 0 color-mix(in srgb,
        var(--text-primary) calc(6% * var(--theme-depth)), transparent);

    /*
     * A very slight vertical wash for a card to carry. Two and a half percent:
     * invisible when you look for it, and the reason a large surface stops
     * looking like a swatch.
     */
    --surface-sheen: linear-gradient(
        180deg,
        color-mix(in srgb, var(--text-primary) calc(2.5% * var(--theme-depth)), transparent) 0%,
        transparent 40%
    );

    /*
     * Accent washes, for what is selected, hovered or currently true. Named by
     * strength rather than by use, so a badge and a row can share one scale.
     */
    --accent-wash-weak: color-mix(in srgb, var(--accent-primary) 8%, transparent);
    --accent-wash: color-mix(in srgb, var(--accent-primary) 16%, transparent);
    --accent-wash-strong: color-mix(in srgb, var(--accent-primary) 28%, transparent);
}

/*
 * Contrast comes first.
 *
 * Every mix above moves a text background, and the tint moves it toward a
 * colour rather than along the grey axis — which is exactly the direction that
 * costs contrast on a light theme. A reader who has asked for more contrast is
 * asking for the flat version.
 */
@media (prefers-contrast: more) {
    html[data-theme] body {
        --theme-tint: 0%;
        --theme-depth: 0.5;
        --hairline: var(--border-primary);
        --hairline-strong: var(--border-primary);
    }
}

/* Per-theme character ------------------------------------------------------
   Only the themes whose whole point is a texture the default would flatten or
   overstate. Everything else inherits the values above. */

/* Paper is paper: no tint, and barely any lift. */
html[data-theme="paper-ink-light"] body,
html[data-theme="paper-ink-dark"] body,
html[data-theme="bone-china-light"] body,
html[data-theme="bone-china-dark"] body,
html[data-theme="salt-flat-light"] body,
html[data-theme="salt-flat-dark"] body {
    --theme-tint: 0%;
    --theme-depth: 0.4;
}

/* A CRT and a neon grid are the one place where more is the point. */
html[data-theme="neon-grid-dark"] body,
html[data-theme="midnight-neon-dark"] body,
html[data-theme="retro-crt-dark"] body,
html[data-theme="cold-cathode-dark"] body {
    --theme-tint: 6%;
    --theme-depth: 1.4;
}

/* Monochrome themes have no hue to bleed; tinting them would invent one. */
html[data-theme="monochrome-mist-dark"] body,
html[data-theme="monochrome-mist-light"] body,
html[data-theme="graphite-prism-dark"] body,
html[data-theme="graphite-prism-light"] body {
    --theme-tint: 0%;
}

/* ── Painting ──────────────────────────────────────────────────────────────
   Everything above is definitions. Below is the first set of surfaces moved
   over to them.

   Kept in this file rather than spread through the view stylesheets, so the
   whole visual change is one <link> in dashboard.html: remove it and the
   dashboard is byte-for-byte what it was. When this settles it belongs behind
   an Appearance setting (Flat / Soft / Rich) rather than behind a link tag. */

/*
 * Two accent washes behind the page.
 *
 * On the body itself rather than on a pseudo-element: ::before already draws
 * the dot grid and switches off with it, and ::after belongs to the custom
 * background. Painting here means the glow survives both, and costs no extra
 * layer to composite.
 *
 * Large, soft and low: at 7% and 5% this is not a visible gradient, it is the
 * page no longer being one flat rectangle. The warning colour in the second
 * one is deliberate — a single hue looks like a mistake, two related hues look
 * like light.
 */
html[data-theme] body:not([data-depth="flat"]) {
    background-image:
        radial-gradient(65rem 45rem at 12% -12%,
            color-mix(in srgb, var(--accent-primary) calc(7% * var(--theme-depth)), transparent),
            transparent 70%),
        radial-gradient(55rem 38rem at 96% 6%,
            color-mix(in srgb, var(--accent-warning) calc(5% * var(--theme-depth)), transparent),
            transparent 70%);
    background-attachment: fixed;
    background-repeat: no-repeat;
}

/*
 * A reader who chose their own background chose it instead of this, not on top
 * of it. Two sets of colour fighting over the same pixels is worse than either.
 */
html[data-theme] body.has-custom-background {
    background-image: none;
}

/*
 * The card presets, on the ladder.
 *
 * Both already mixed a surface by hand — 92% and 94% of the page colour with a
 * little text mixed in — which is the same idea as surface-2 and surface-1
 * without the tint or a name. Swapping them keeps the two presets distinct and
 * gives them the theme's own colour, the lit top edge, and a border that stops
 * outlining every card at full strength.
 */
body:not([data-depth="flat"]) .dashboard-grid.layout-cards .category {
    background-color: var(--surface-2);
    background-image: var(--surface-sheen);
    border: 1px solid var(--hairline);
    box-shadow: var(--edge-light);
}

body:not([data-depth="flat"]) .dashboard-grid.layout-widgets .category {
    background-color: var(--surface-1);
    background-image: var(--surface-sheen);
    border: 1px solid var(--hairline);
    box-shadow: var(--edge-light);
}

/*
 * The widget body.
 *
 * A tile is the one block on the dashboard that is nothing but a surface with
 * figures on it, so it is where a flat palette shows most. It mixed 5% of the
 * secondary text colour into nothing, which is a surface without being on the
 * ladder — surface-2 puts it a step above the card it sits in.
 */
body:not([data-depth="flat"]) .dashboard-widget .dashboard-widget-body {
    background: var(--surface-2);
    background-image: var(--surface-sheen);
    border: 1px solid var(--hairline);
    box-shadow: var(--edge-light);
}

/*
 * A modal keeps its own background.
 *
 * background-modal is deliberately translucent and the modern layout blurs what
 * is behind it; replacing that with an opaque surface would trade a good effect
 * for a smaller one. What it gets from the ladder is the edge — the lit top
 * line and a border that has stopped being a full-strength outline.
 */
body:not([data-depth="flat"]) .modal {
    border: 1px solid var(--hairline-strong);
    box-shadow:
        var(--edge-light),
        0 24px 64px rgba(0, 0, 0, 0.28),
        0 2px 8px rgba(0, 0, 0, 0.12);
}

/* ── Selection, scrollbars ─────────────────────────────────────────────────
   Two things no theme has ever touched, both visible on every screen. */

/*
 * Selected text was browser blue on all 148 themes.
 *
 * Nothing in the stylesheets ever set ::selection, so a reader dragging over a
 * bookmark name got the operating system's idea of a highlight on top of a
 * carefully chosen palette. This is one rule and it applies everywhere,
 * including at flat: it is not depth, it is a colour the theme already owns.
 */
html[data-theme] ::selection {
    background: color-mix(in srgb, var(--accent-primary) 30%, transparent);
    color: var(--text-primary);
}

/*
 * The same argument for scrollbars. A few view stylesheets styled their own;
 * the page itself did not, so the one piece of chrome that is on screen the
 * whole time stayed the browser default.
 *
 * scrollbar-color is the standard property and covers Firefox and current
 * Chromium; the -webkit rules below carry the ones that do not have it yet.
 */
html[data-theme] {
    scrollbar-color: var(--hairline-strong) transparent;
    scrollbar-width: thin;
}

html[data-theme] ::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}

html[data-theme] ::-webkit-scrollbar-track {
    background: transparent;
}

html[data-theme] ::-webkit-scrollbar-thumb {
    background: var(--hairline-strong);
    border-radius: var(--radius-pill, 999px);
    /* Inset by a border rather than by width, so the thumb keeps the full
       track's hit area while looking narrower than it is. */
    border: 3px solid transparent;
    background-clip: padding-box;
}

html[data-theme] ::-webkit-scrollbar-thumb:hover {
    background: color-mix(in srgb, var(--accent-primary) 45%, var(--border-primary));
    background-clip: padding-box;
}

/* ── The backdrop pattern ──────────────────────────────────────────────────
   The dot grid was the only texture in the app, and it was the same texture in
   all of them. It is two things now: what a theme asks for, and what the reader
   asks for instead.

   Left alone (the "auto" default), a theme picks the texture that suits it — a
   blueprint gets a grid, a CRT gets rules, paper gets nothing. Choose anything
   else in Appearance and that wins everywhere, on every theme.

   The line patterns are drawn weaker than the dots deliberately: see
   --theme-pattern-line below. */

html[data-theme] body {
    --theme-pattern-image: radial-gradient(var(--background-dots) 1px, transparent 1px);
    --theme-pattern-size: 15px 15px;
    /*
     * Line patterns are drawn from a weakened copy of the dot colour.
     *
     * background-dots was picked to read as a sparse speckle. A rule or a hatch
     * at that same value puts twenty times as much ink on the page and starts
     * competing with the text — legible on a dark theme, heavy on a light one.
     * Forty-five percent is where a texture stays a texture.
     */
    --theme-pattern-line: color-mix(in srgb, var(--background-dots) 45%, transparent);
}

html[data-theme] body:not(.no-background-dots)::before {
    background-image: var(--theme-pattern-image);
    background-size: var(--theme-pattern-size);
}

/* ── What a theme asks for ─────────────────────────────────────────────────
   Only where the texture is part of what the theme is. Everything not named
   here keeps the dots. These lose to an explicit choice, which carries one more
   attribute and therefore more specificity — order in this file is not what
   decides it. */

/* A ruled grid, for the themes that are drawings. */
html[data-theme="blueprint-dark"] body,
html[data-theme="blueprint-light"] body,
html[data-theme="graphite-prism-dark"] body,
html[data-theme="graphite-prism-light"] body {
    --theme-pattern-image:
        linear-gradient(var(--theme-pattern-line) 1px, transparent 1px),
        linear-gradient(90deg, var(--theme-pattern-line) 1px, transparent 1px);
    --theme-pattern-size: 32px 32px, 32px 32px;
}

/* Horizontal rules, for the ones pretending to be a screen. Eight pixels, not
   four: at four it moirés on a HiDPI display and the page shimmers as it
   scrolls. */
/* Both halves of each family: these four named only their dark variant, so
   switching one of them to light quietly dropped the scanlines and left the
   theme looking like every other. A family is one idea whichever way round it
   is printed. */
html[data-theme="retro-crt-dark"] body,
html[data-theme="retro-crt-light"] body,
html[data-theme="neon-grid-dark"] body,
html[data-theme="neon-grid-light"] body,
html[data-theme="cold-cathode-dark"] body,
html[data-theme="cold-cathode-light"] body,
html[data-theme="static-noise-dark"] body,
html[data-theme="static-noise-light"] body {
    --theme-pattern-image: linear-gradient(var(--theme-pattern-line) 1px, transparent 1px);
    --theme-pattern-size: 100% 8px;
}

/* Diagonal hatch, widely spaced, for the paper and fabric ones. */
html[data-theme="sumi-ink-dark"] body,
html[data-theme="sumi-ink-light"] body,
html[data-theme="moss-stone-dark"] body,
html[data-theme="moss-stone-light"] body,
html[data-theme="desert-rose-dark"] body,
html[data-theme="desert-rose-light"] body {
    --theme-pattern-image: repeating-linear-gradient(
        45deg,
        var(--theme-pattern-line) 0 1px,
        transparent 1px 14px
    );
    --theme-pattern-size: auto;
}

/* Dots, for the misty, watery and cool ones: fog, glass, ice, deep water.
   Named rather than left to the default above, so the intent is on the page
   for every family instead of a quarter of them getting it by omission. */
html[data-theme="arctic-cyan-dark"] body,
html[data-theme="arctic-cyan-light"] body,
html[data-theme="cerulean-skylark-dark"] body,
html[data-theme="cerulean-skylark-light"] body,
html[data-theme="coral-reef-dark"] body,
html[data-theme="coral-reef-light"] body,
html[data-theme="deep-lagoon-dark"] body,
html[data-theme="deep-lagoon-light"] body,
html[data-theme="dusk-horizon-dark"] body,
html[data-theme="dusk-horizon-light"] body,
html[data-theme="forest-everglade-dark"] body,
html[data-theme="forest-everglade-light"] body,
html[data-theme="forest-moss-dark"] body,
html[data-theme="forest-moss-light"] body,
html[data-theme="frosted-juniper-dark"] body,
html[data-theme="frosted-juniper-light"] body,
html[data-theme="glacier-mint-dark"] body,
html[data-theme="glacier-mint-light"] body,
html[data-theme="harbour-fog-dark"] body,
html[data-theme="harbour-fog-light"] body,
html[data-theme="hermetic-teal-dark"] body,
html[data-theme="hermetic-teal-light"] body,
html[data-theme="iceberg-drift-dark"] body,
html[data-theme="iceberg-drift-light"] body,
html[data-theme="iris-meadow-dark"] body,
html[data-theme="iris-meadow-light"] body,
html[data-theme="kelp-drift-dark"] body,
html[data-theme="kelp-drift-light"] body,
html[data-theme="lavender-mist-dark"] body,
html[data-theme="lavender-mist-light"] body,
html[data-theme="monochrome-mist-dark"] body,
html[data-theme="monochrome-mist-light"] body,
html[data-theme="nordic-frost-dark"] body,
html[data-theme="nordic-frost-light"] body,
html[data-theme="ocean-depth-dark"] body,
html[data-theme="ocean-depth-light"] body,
html[data-theme="owl-hours-dark"] body,
html[data-theme="owl-hours-light"] body,
html[data-theme="pale-night-dark"] body,
html[data-theme="pale-night-light"] body,
html[data-theme="pastel-mountain-dark"] body,
html[data-theme="pastel-mountain-light"] body,
html[data-theme="porcelain-blue-dark"] body,
html[data-theme="porcelain-blue-light"] body,
html[data-theme="rose-pine-dark"] body,
html[data-theme="rose-pine-light"] body,
html[data-theme="sea-glass-dark"] body,
html[data-theme="sea-glass-light"] body,
html[data-theme="storm-petrel-dark"] body,
html[data-theme="storm-petrel-light"] body {
    --theme-pattern-image: radial-gradient(var(--background-dots) 1px, transparent 1px);
    --theme-pattern-size: 15px 15px;
}

/* Grid, for the technical ones: circuitry, editors, steel and machinery. */
html[data-theme="andromeda-drift-dark"] body,
html[data-theme="andromeda-drift-light"] body,
html[data-theme="cherry-graphite-dark"] body,
html[data-theme="cherry-graphite-light"] body,
html[data-theme="cobalt-ink-dark"] body,
html[data-theme="cobalt-ink-light"] body,
html[data-theme="commit-grey-dark"] body,
html[data-theme="commit-grey-light"] body,
html[data-theme="copper-circuit-dark"] body,
html[data-theme="copper-circuit-light"] body,
html[data-theme="cosmic-editor-dark"] body,
html[data-theme="cosmic-editor-light"] body,
html[data-theme="denim-fade-dark"] body,
html[data-theme="denim-fade-light"] body,
html[data-theme="editor-default-dark"] body,
html[data-theme="editor-default-light"] body,
html[data-theme="emerald-matrix-dark"] body,
html[data-theme="emerald-matrix-light"] body,
html[data-theme="foundry-iron-dark"] body,
html[data-theme="foundry-iron-light"] body,
html[data-theme="licorice-layer-dark"] body,
html[data-theme="licorice-layer-light"] body,
html[data-theme="midnight-ink-dark"] body,
html[data-theme="midnight-ink-light"] body,
html[data-theme="moonlit-steel-dark"] body,
html[data-theme="moonlit-steel-light"] body,
html[data-theme="obsidian-gold-dark"] body,
html[data-theme="obsidian-gold-light"] body,
html[data-theme="oceanic-steel-dark"] body,
html[data-theme="oceanic-steel-light"] body,
html[data-theme="polar-night-dark"] body,
html[data-theme="polar-night-light"] body,
html[data-theme="slate-one-dark"] body,
html[data-theme="slate-one-light"] body,
html[data-theme="smoked-plum-dark"] body,
html[data-theme="smoked-plum-light"] body,
html[data-theme="steel-dawn-dark"] body,
html[data-theme="steel-dawn-light"] body,
html[data-theme="thunderhead-dark"] body,
html[data-theme="thunderhead-light"] body,
html[data-theme="tidal-slate-dark"] body,
html[data-theme="tidal-slate-light"] body,
html[data-theme="tomorrow-dusk-dark"] body,
html[data-theme="tomorrow-dusk-light"] body,
html[data-theme="vampire-castle-dark"] body,
html[data-theme="vampire-castle-light"] body,
html[data-theme="velocity-ink-dark"] body,
html[data-theme="velocity-ink-light"] body,
html[data-theme="volcanic-ash-dark"] body,
html[data-theme="volcanic-ash-light"] body {
    --theme-pattern-image:
        linear-gradient(var(--theme-pattern-line) 1px, transparent 1px),
        linear-gradient(90deg, var(--theme-pattern-line) 1px, transparent 1px);
    --theme-pattern-size: 32px 32px, 32px 32px;
}

/* Scanlines, for the ones that glow: neon, signal, phosphor, electric. */
html[data-theme="absinthe-dark"] body,
html[data-theme="absinthe-light"] body,
html[data-theme="bio-abyss-dark"] body,
html[data-theme="bio-abyss-light"] body,
html[data-theme="candy-pop-dark"] body,
html[data-theme="candy-pop-light"] body,
html[data-theme="chartreuse-static-dark"] body,
html[data-theme="chartreuse-static-light"] body,
html[data-theme="city-lights-dark"] body,
html[data-theme="city-lights-light"] body,
html[data-theme="electric-orchid-dark"] body,
html[data-theme="electric-orchid-light"] body,
html[data-theme="fox-night-dark"] body,
html[data-theme="fox-night-light"] body,
html[data-theme="horizon-glow-dark"] body,
html[data-theme="horizon-glow-light"] body,
html[data-theme="jungle-neon-dark"] body,
html[data-theme="jungle-neon-light"] body,
html[data-theme="midnight-firefly-dark"] body,
html[data-theme="midnight-firefly-light"] body,
html[data-theme="midnight-neon-dark"] body,
html[data-theme="midnight-neon-light"] body,
html[data-theme="peacock-dark"] body,
html[data-theme="peacock-light"] body,
html[data-theme="retro-groove-dark"] body,
html[data-theme="retro-groove-light"] body,
html[data-theme="royal-amethyst-dark"] body,
html[data-theme="royal-amethyst-light"] body,
html[data-theme="signal-flare-dark"] body,
html[data-theme="signal-flare-light"] body,
html[data-theme="solar-ember-dark"] body,
html[data-theme="solar-ember-light"] body,
html[data-theme="sunflower-ink-dark"] body,
html[data-theme="sunflower-ink-light"] body,
html[data-theme="synth-sunset-dark"] body,
html[data-theme="synth-sunset-light"] body,
html[data-theme="terminal-amber-dark"] body,
html[data-theme="terminal-amber-light"] body,
html[data-theme="tyrian-dark"] body,
html[data-theme="tyrian-light"] body,
html[data-theme="ultraviolet-dark"] body,
html[data-theme="ultraviolet-light"] body,
html[data-theme="violet-shades-dark"] body,
html[data-theme="violet-shades-light"] body,
html[data-theme="vivid-hyper-dark"] body,
html[data-theme="vivid-hyper-light"] body {
    --theme-pattern-image: linear-gradient(var(--theme-pattern-line) 1px, transparent 1px);
    --theme-pattern-size: 100% 8px;
}

/* Hatch, for paper, cloth, earth and anything made by hand. */
html[data-theme="bamboo-panda-dark"] body,
html[data-theme="bamboo-panda-light"] body,
html[data-theme="bone-china-dark"] body,
html[data-theme="bone-china-light"] body,
html[data-theme="candlelit-study-dark"] body,
html[data-theme="candlelit-study-light"] body,
html[data-theme="desert-sand-dark"] body,
html[data-theme="desert-sand-light"] body,
html[data-theme="ember-ash-dark"] body,
html[data-theme="ember-ash-light"] body,
html[data-theme="great-wave-dark"] body,
html[data-theme="great-wave-light"] body,
html[data-theme="library-mahogany-dark"] body,
html[data-theme="library-mahogany-light"] body,
html[data-theme="marigold-dusk-dark"] body,
html[data-theme="marigold-dusk-light"] body,
html[data-theme="mirage-sand-dark"] body,
html[data-theme="mirage-sand-light"] body,
html[data-theme="mulberry-silk-dark"] body,
html[data-theme="mulberry-silk-light"] body,
html[data-theme="olive-drab-dark"] body,
html[data-theme="olive-drab-light"] body,
html[data-theme="oxblood-leather-dark"] body,
html[data-theme="oxblood-leather-light"] body,
html[data-theme="paper-ink-dark"] body,
html[data-theme="paper-ink-light"] body,
html[data-theme="patina-verdigris-dark"] body,
html[data-theme="patina-verdigris-light"] body,
html[data-theme="pistachio-cream-dark"] body,
html[data-theme="pistachio-cream-light"] body,
html[data-theme="rhubarb-tart-dark"] body,
html[data-theme="rhubarb-tart-light"] body,
html[data-theme="rusted-rail-dark"] body,
html[data-theme="rusted-rail-light"] body,
html[data-theme="saffron-robe-dark"] body,
html[data-theme="saffron-robe-light"] body,
html[data-theme="sakura-night-dark"] body,
html[data-theme="sakura-night-light"] body,
html[data-theme="salt-flat-dark"] body,
html[data-theme="salt-flat-light"] body,
html[data-theme="solar-flats-dark"] body,
html[data-theme="solar-flats-light"] body,
html[data-theme="tarnished-brass-dark"] body,
html[data-theme="tarnished-brass-light"] body,
html[data-theme="terracotta-studio-dark"] body,
html[data-theme="terracotta-studio-light"] body,
html[data-theme="wheat-field-dark"] body,
html[data-theme="wheat-field-light"] body,
html[data-theme="zen-ember-dark"] body,
html[data-theme="zen-ember-light"] body {
    --theme-pattern-image: repeating-linear-gradient(
        45deg,
        var(--theme-pattern-line) 0 1px,
        transparent 1px 14px
    );
    --theme-pattern-size: auto;
}

/* ── What the reader asks for ──────────────────────────────────────────────
   Anything but "auto" overrides whatever the theme wanted, on every theme. */

html[data-theme] body[data-pattern="dots"] {
    --theme-pattern-image: radial-gradient(var(--background-dots) 1px, transparent 1px);
    --theme-pattern-size: 15px 15px;
}

html[data-theme] body[data-pattern="grid"] {
    --theme-pattern-image:
        linear-gradient(var(--theme-pattern-line) 1px, transparent 1px),
        linear-gradient(90deg, var(--theme-pattern-line) 1px, transparent 1px);
    --theme-pattern-size: 32px 32px, 32px 32px;
}

html[data-theme] body[data-pattern="lines"] {
    --theme-pattern-image: linear-gradient(var(--theme-pattern-line) 1px, transparent 1px);
    --theme-pattern-size: 100% 8px;
}

html[data-theme] body[data-pattern="hatch"] {
    --theme-pattern-image: repeating-linear-gradient(
        45deg,
        var(--theme-pattern-line) 0 1px,
        transparent 1px 14px
    );
    --theme-pattern-size: auto;
}

/* Nothing at all, without touching the dots toggle's other behaviour. */
html[data-theme] body[data-pattern="none"]::before {
    background-image: none;
}

/* ── Status colours as a family ────────────────────────────────────────────
   accent-success, -warning and -error are used at full saturation for every
   badge and pill, which is right for a dot and loud for a filled shape. These
   are the quiet halves of the same three colours, for backgrounds and borders,
   so the health view can carry a hundred rows without shouting a hundred
   times. */

html[data-theme] body {
    --success-wash: color-mix(in srgb, var(--accent-success) 14%, transparent);
    --success-edge: color-mix(in srgb, var(--accent-success) 45%, transparent);
    --warning-wash: color-mix(in srgb, var(--accent-warning) 14%, transparent);
    --warning-edge: color-mix(in srgb, var(--accent-warning) 45%, transparent);
    --error-wash: color-mix(in srgb, var(--accent-error) 14%, transparent);
    --error-edge: color-mix(in srgb, var(--accent-error) 45%, transparent);
}

/* ── Grain ─────────────────────────────────────────────────────────────────
   Large soft gradients band on 8-bit displays: the wash behind the page shows
   as rings rather than as a wash. Two percent of noise over the top breaks the
   steps up, and gives every surface a texture that a flat fill cannot have.

   On body::after, which is free unless a custom background is set — that rule
   in theme.css owns the same pseudo-element, and its backdrop is the reader's
   own choice rather than something to sprinkle over. */

html[data-theme] body:not(.has-custom-background):not([data-depth="flat"])::after {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    opacity: calc(0.022 * var(--theme-depth));
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ── The category header ───────────────────────────────────────────────────
   On the default preset a category has no surface at all — it is a title and a
   list, and the glow behind the page is the only thing that changed for the
   readers who use it. This gives the title a start without turning the list
   into a card: a rule under it that carries the theme's colour and fades. */

body:not([data-depth="flat"]) .dashboard-grid:not(.layout-cards):not(.layout-widgets) .category-title {
    border-bottom: 1px solid transparent;
    border-image: linear-gradient(
        90deg,
        color-mix(in srgb, var(--accent-primary) 55%, transparent) 0%,
        var(--hairline) 35%,
        transparent 85%
    ) 1;
    padding-bottom: var(--space-1-5, 0.35rem);
}

/* A category spanning more than one column says so with its rule.
 *
 * The rule above fades out at 85%, which is right for a single column: the
 * title names the category and the line trails off under it. Across two
 * columns that same fade lands well short of the edge, so a wide category
 * looked like a narrow one whose line happened to be long -- the width was
 * only legible from the badge in the header, or by counting the bookmarks.
 *
 * Here it carries further and holds its colour longer, so the eye follows it
 * to where the category actually ends. Still a hairline, still fading rather
 * than stopping: the point is to trace the span, not to draw a box around it.
 */
body:not([data-depth="flat"]) .dashboard-grid:not(.layout-cards):not(.layout-widgets) .category.category--wide .category-title {
    border-image: linear-gradient(
        90deg,
        color-mix(in srgb, var(--accent-primary) 70%, transparent) 0%,
        color-mix(in srgb, var(--accent-primary) 30%, var(--hairline)) 45%,
        var(--hairline) 80%,
        transparent 100%
    ) 1;
}

/* ── Typographic character ─────────────────────────────────────────────────
   A theme is a palette today. These two variables let it also be a voice.

   Deliberately narrow: the font stack stays whatever the reader chose in
   Appearance, because a theme silently replacing a chosen typeface is a theme
   overstepping. What a theme may do is the spacing and the case of the one
   element that names it — the category title — which is where a terminal reads
   as a terminal and a library reads as a library. */

html[data-theme] body {
    --theme-title-spacing: normal;
    --theme-title-transform: none;
}

body:not([data-depth="flat"]) .category-title-label {
    letter-spacing: var(--theme-title-spacing);
    text-transform: var(--theme-title-transform);
}

/* The terminals: wide, upper, like a status line. */
html[data-theme="retro-crt-dark"] body,
html[data-theme="retro-crt-light"] body,
html[data-theme="terminal-amber-dark"] body,
html[data-theme="terminal-amber-light"] body,
html[data-theme="neon-grid-dark"] body,
html[data-theme="cold-cathode-dark"] body,
html[data-theme="static-noise-dark"] body,
html[data-theme="static-noise-light"] body {
    --theme-title-spacing: 0.09em;
    --theme-title-transform: uppercase;
}

/* The printed ones: a little air, no shouting. */
html[data-theme="paper-ink-dark"] body,
html[data-theme="paper-ink-light"] body,
html[data-theme="library-mahogany-dark"] body,
html[data-theme="library-mahogany-light"] body,
html[data-theme="sumi-ink-dark"] body,
html[data-theme="sumi-ink-light"] body,
html[data-theme="bone-china-dark"] body,
html[data-theme="bone-china-light"] body {
    --theme-title-spacing: 0.02em;
}

/* ==== css/theme-browser.css ==== */
/* The theme browser.

   Built on the tokens theme-depth.css derives, so the modal is painted in the
   theme being browsed and every card sits on the same surface ladder as the
   dashboard behind it. */

.modal--theme-browser .modal-text {
    /* The grid brings its own spacing; the modal's prose padding would only
       narrow it. */
    text-align: left;
}

.theme-browser {
    display: flex;
    flex-direction: column;
    gap: var(--space-3, 0.75rem);
    max-height: min(70vh, 40rem);
}

.theme-browser-bar {
    display: flex;
    gap: var(--space-2, 0.6rem);
    align-items: center;
    flex-wrap: wrap;
}

.theme-browser-search {
    flex: 1 1 14rem;
    min-width: 0;
    padding: var(--space-2, 0.45rem) var(--space-2, 0.6rem);
    border-radius: var(--layout-radius-sm, 8px);
    border: 1px solid var(--hairline-strong, var(--border-primary));
    background: var(--surface-1, var(--background-secondary));
    color: var(--text-primary);
    font: inherit;
}

.theme-browser-search:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 1px;
}

.theme-browser-segments {
    display: inline-flex;
    gap: var(--space-1, 0.25rem);
}

.theme-browser-segment,
.theme-browser-variant {
    padding: var(--space-1, 0.3rem) var(--space-2, 0.6rem);
    border-radius: var(--layout-radius-pill, 999px);
    border: 1px solid var(--hairline, var(--border-secondary));
    background: transparent;
    color: var(--text-secondary);
    font: inherit;
    font-size: 0.85em;
    cursor: pointer;
}

.theme-browser-segment.is-on,
.theme-browser-variant.is-on {
    background: var(--accent-wash, color-mix(in srgb, var(--accent-primary) 16%, transparent));
    border-color: var(--accent-primary);
    color: var(--text-primary);
}

.theme-browser-count {
    margin: 0;
    color: var(--text-tertiary);
    font-size: 0.85em;
}

.theme-browser-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
    gap: var(--space-2, 0.6rem);
    overflow-y: auto;
    /* Room for the focus ring, which would otherwise be clipped by the scroll
       container on the first and last row. */
    padding: 2px;
}

.theme-browser-card {
    display: flex;
    flex-direction: column;
    gap: var(--space-1-5, 0.4rem);
    padding: var(--space-2, 0.55rem);
    border-radius: var(--layout-radius-md, 12px);
    border: 1px solid var(--hairline, var(--border-secondary));
    background: var(--surface-1, var(--background-secondary));
    box-shadow: var(--edge-light, none);
    cursor: pointer;
    transition: border-color var(--layout-transition, 0.2s ease),
                transform var(--layout-transition, 0.2s ease);
}

.theme-browser-card:hover,
.theme-browser-card:focus-visible {
    border-color: var(--accent-primary);
    transform: translateY(-1px);
    outline: none;
}

.theme-browser-card.is-current {
    border-color: var(--accent-primary);
    box-shadow: var(--edge-light, none), 0 0 0 1px var(--accent-primary) inset;
}

/* The swatch bar is the whole point of a grid over a list: at this many themes
   nobody chooses by name. Six stops, in the order the eye needs them — the two
   surfaces first, then the text that has to sit on them, then the accents. */
.theme-browser-swatches {
    display: flex;
    height: 1.6rem;
    border-radius: var(--layout-radius-sm, 8px);
    overflow: hidden;
    border: 1px solid var(--hairline, var(--border-secondary));
}

.theme-browser-swatch {
    flex: 1 1 0;
}

.theme-browser-card-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-1-5, 0.4rem);
}

.theme-browser-card-name {
    font-weight: 600;
    color: var(--text-primary);
    overflow-wrap: anywhere;
}

.theme-browser-star {
    border: none;
    background: none;
    padding: 0 var(--space-hairline, 0.1rem);
    cursor: pointer;
    color: var(--text-tertiary);
    font-size: 1em;
    line-height: 1;
}

.theme-browser-star.is-on {
    color: var(--accent-warning);
}

.theme-browser-card-traits {
    margin: 0;
    color: var(--text-tertiary);
    font-size: 0.8em;
}

.theme-browser-card-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-1-5, 0.4rem);
    margin-top: auto;
}

.theme-browser-variants {
    display: inline-flex;
    gap: var(--space-1, 0.2rem);
}

.theme-browser-single,
.theme-browser-current {
    color: var(--text-tertiary);
    font-size: 0.78em;
}

.theme-browser-current {
    color: var(--accent-primary);
}

.theme-browser-empty {
    margin: var(--space-4, 1rem) 0;
    color: var(--text-tertiary);
    grid-column: 1 / -1;
}

@media (max-width: 40rem) {
    .theme-browser-grid {
        grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
    }
    .theme-browser {
        max-height: 75vh;
    }
}

/* The picker and its Browse button, side by side.
   The picker keeps the width it had; the button takes only what it needs, so
   the field still reads as one control with a way out of it. */
.config-theme-row {
    display: flex;
    gap: var(--space-2, 0.5rem);
    align-items: stretch;
}

.config-theme-row .config-theme-picker {
    flex: 1 1 auto;
    min-width: 0;
}

.config-theme-browse {
    flex: 0 0 auto;
    white-space: nowrap;
}

/* ==== css/layout-modern-tokens.css ==== */
/* Modern layout design tokens — the single definition of the modern visual
   language (radii, shadows, surfaces, motion).

   Every modern override file consumes these; none of them redefine a token.
   Load this BEFORE any stylesheet that reads a --layout-* value, so view CSS
   that ships modern rules of its own (health-view.css, config-view.css) can
   use the tokens without repeating them or guessing at fallbacks.

   Scoped to body[data-layout-version="modern"], so classic inherits nothing
   from this file and stays byte-for-byte the layout it always was. */

body[data-layout-version="modern"] {
    --layout-radius-sm: 8px;
    --layout-radius-md: 12px;
    --layout-radius-lg: 16px;
    --layout-radius-pill: 999px;
    --layout-shadow-sm: 0 1px 3px color-mix(in srgb, var(--text-primary) 8%, transparent);
    --layout-shadow-md: 0 4px 16px color-mix(in srgb, var(--text-primary) 12%, transparent);
    --layout-shadow-lg: 0 8px 28px color-mix(in srgb, var(--text-primary) 16%, transparent);
    --layout-gap-header: 1.25rem;
    --layout-transition: 0.2s cubic-bezier(0.4, 0, 0.2, 1);
    --layout-surface: color-mix(in srgb, var(--background-secondary) 78%, transparent);
    --layout-surface-border: color-mix(in srgb, var(--border-primary) 48%, transparent);
    --layout-focus-ring: 2px solid color-mix(in srgb, var(--accent-primary) 72%, transparent);
    --layout-overlay-bg: var(--background-modal, var(--background-secondary));
    --layout-modal-bg: var(--background-modal, var(--background-secondary));

    /* Accent wash used behind an active/hovered row. Kept as one gradient so a
       row reads as lit from the left edge rather than uniformly tinted. */
    --modern-bookmark-row-hover-bg: linear-gradient(
        90deg,
        color-mix(in srgb, var(--accent-primary) 58%, var(--background-secondary)) 0%,
        color-mix(in srgb, var(--accent-primary) 32%, var(--background-primary)) 48%,
        color-mix(in srgb, var(--accent-primary) 10%, var(--background-primary)) 100%
    );
    --modern-bookmark-row-selected-bg: linear-gradient(
        90deg,
        color-mix(in srgb, var(--accent-primary) 68%, var(--background-secondary)) 0%,
        color-mix(in srgb, var(--accent-primary) 38%, var(--background-primary)) 48%,
        color-mix(in srgb, var(--accent-primary) 16%, var(--background-primary)) 100%
    );
    --modern-bookmark-row-dimmed-bg: linear-gradient(
        90deg,
        color-mix(in srgb, var(--accent-primary) 12%, var(--background-secondary)) 0%,
        color-mix(in srgb, var(--accent-primary) 7%, var(--background-primary)) 48%,
        color-mix(in srgb, var(--accent-primary) 3%, var(--background-primary)) 100%
    );
    --bookmark-row-hover-bg: var(--modern-bookmark-row-hover-bg);
    --bookmark-row-selected-bg: var(--modern-bookmark-row-selected-bg);
    --bookmark-row-dimmed-bg: var(--modern-bookmark-row-dimmed-bg);

    /* The 100% stop of each gradient above, for the shortcut label that floats
       over the row's right edge in "hover" mode. Same three names as
       dashboard-bookmark-row.css defines for classic; only the percentages
       differ, because these gradients are stronger. */
    --bookmark-row-edge-hover: color-mix(in srgb, var(--accent-primary) 10%, var(--background-primary));
    --bookmark-row-edge-selected: color-mix(in srgb, var(--accent-primary) 16%, var(--background-primary));
    --bookmark-row-edge-dimmed: color-mix(in srgb, var(--accent-primary) 3%, var(--background-primary));
}

/* ==== css/dashboard.css ==== */
/* Dashboard Styles */
* {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

/* Remove tap highlight on mobile devices */
button,
a,
input,
select,
textarea,
[role="button"],
.clickable {
    -webkit-tap-highlight-color: transparent;
    -webkit-touch-callout: none;
}

/*
 * Reserve the scrollbar's width whether or not the page scrolls. Without this,
 * a short section (Pages & tags, a filtered list) has no scrollbar and a tall
 * one does, so switching between them changed the usable width and every
 * centred layout — config's nav column most visibly — jumped sideways. Safari
 * and Firefox on macOS reserve real width for a scrollbar; browsers with
 * overlay scrollbars never showed the shift, which is why it reads as a
 * platform bug rather than a layout one.
 */
html {
    scrollbar-gutter: stable;
}

body {
    min-height: 100vh;
    transition: background-color 0.3s ease, color 0.3s ease;
    font-size: var(--font-size-text);
    /* clip, not hidden: `overflow-x: hidden` promotes the other axis from
       visible to auto, which made body its own scroll container (computed
       "hidden auto") instead of letting the viewport scroll the page. That is
       why an overlay's body-level scroll lock could strand the page and why the
       scrollbar visibly belonged to body. clip suppresses horizontal overflow
       without creating a scroll container. layout-modern.css already uses it. */
    overflow-x: clip;
    width: 100%;
}

/* Dashboard Sections */
.dashboard-section {
    padding: 0;
}

.dashboard-section.section-controls {
    padding-top: var(--space-6, 2rem);
    padding-bottom: 0;
}

.dashboard-section.section-title {
    padding-top: 0;
    padding-bottom: 0;
}

.dashboard-section.section-content {
    padding-top: 0;
    padding-bottom: 8rem;
}

.container {
    /* Fixed clamp() side margin (not a percentage) so the whitespace shrinks
       toward the floor as the window narrows, giving the columns the reclaimed
       space before they have to get narrower. Roomy margins keep the columns
       closer together on wide screens.

       The clamp is the "balanced" value of the side-margin setting, so a
       dashboard that never touches the setting looks exactly as it always has;
       the fallback here repeats it for the case where no body attribute is set
       at all (a view rendered before setupDOM runs). */
    max-width: 1600px;
    width: 100%;
    margin: 0 auto;
    padding: 0 var(--dashboard-side-margin, clamp(2rem, 6vw, 8rem));
}

/* Left/right band beside the grid.
 *
 * Each keeps a clamp so the margin still collapses on a narrow window rather
 * than squeezing the columns — only the ceiling differs. "snug" hands roughly
 * 100px per side back to the grid on a wide screen; "airy" is for people who
 * want the columns pulled together. */
body[data-side-margin="snug"] {
    --dashboard-side-margin: clamp(var(--space-4, 1rem), 2.5vw, var(--space-7, 3rem));
}
body[data-side-margin="balanced"] {
    --dashboard-side-margin: clamp(var(--space-6, 2rem), 6vw, 8rem);
}
body[data-side-margin="airy"] {
    --dashboard-side-margin: clamp(var(--space-7, 3rem), 9vw, 12rem);
}

/* Header Top - Contains date, tabs, and config button */
.header-top {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.date {
    display: flex;
    flex-direction: column;
    gap: var(--space-1, 0.25rem);
    font-size: calc(var(--font-size-controls) * 1.2);
    font-family: var(--font-family-main);
    font-weight: var(--font-weight-semibold);
    letter-spacing: 0;
    text-transform: none;
    line-height: 1.2;
    color: var(--text-secondary);
}

.date-badge-mobile {
    display: none;
}

.date-time-line,
.date-weather-line {
    font-weight: var(--font-weight-semibold);
}

.date-weather-line--error {
    color: var(--text-secondary);
    font-size: calc(var(--font-size-controls) * 0.95);
    font-weight: var(--font-weight-normal, 400);
    opacity: 0.85;
}

.date-time-line {
    font-size: calc(var(--font-size-controls) * 1.3); /* slightly smaller */
    color: var(--text-secondary);
    cursor: pointer;
    user-select: none;
    border-radius: var(--layout-radius-sm, 8px);
    transition: opacity 0.12s ease;
}
.date-time-line:hover {
    opacity: 0.75;
}

/* ─── Date popover ───────────────────────────────────────────── */
.date-popover {
    position: fixed;
    z-index: 9000;
    background: var(--background-secondary);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-7, 12px);
    padding: var(--space-3, 0.85rem) var(--space-4, 1rem) var(--space-3, 0.7rem);
    min-width: 210px;
    color: var(--text-primary);
    font-family: var(--font-family-main);
    box-shadow: 0 4px 18px color-mix(in srgb, var(--text-primary) 14%, transparent);
    animation: datePopoverIn 0.15s ease forwards;
}

@keyframes datePopoverIn {
    from { opacity: 0; transform: translateY(-6px) scale(0.97); }
    to   { opacity: 1; transform: translateY(0)   scale(1);    }
}

.date-popover-week-label {
    font-size: var(--text-2xs, 0.65rem);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-secondary);
    margin-bottom: var(--space-2, 0.55rem);
}

.date-popover-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 2px;
    text-align: center;
}

.date-popover-col-label {
    font-size: var(--text-2xs, 0.62rem);
    color: var(--text-tertiary, var(--text-secondary));
    padding-bottom: var(--space-1, 0.25rem);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.date-popover-day {
    font-size: var(--text-sm, 0.78rem);
    font-weight: var(--font-weight-semibold, 600);
    padding: var(--space-1, 0.3rem) 0;
    border-radius: var(--radius-4, 6px);
    line-height: 1;
    color: var(--text-primary);
}

.date-popover-day.is-weekend {
    color: var(--text-tertiary, var(--text-secondary));
}

.date-popover-day.is-today {
    background: color-mix(in srgb, var(--accent-primary) 22%, transparent);
    color: var(--text-primary);
}

.date-popover-footer {
    margin-top: var(--space-3, 0.7rem);
    padding-top: var(--space-2, 0.55rem);
    border-top: 1px solid var(--border-primary);
    text-align: center;
}

.date-popover-cal-link {
    font-size: var(--text-2xs, 0.68rem);
    color: var(--text-secondary);
    text-decoration: none;
    transition: color 0.1s ease;
}
.date-popover-cal-link:hover {
    color: var(--accent-primary);
    text-decoration: underline;
}

/* ─── Move popover ───────────────────────────────────────────── */
/* One sentence about one control, only while it is asked for.
   Fixed rather than absolute: the bookmark form scrolls its own body, and an
   absolutely-placed bubble would be clipped by it. Above .bookmark-form-modal
   (10050) and its confirm dialogs, since it can be opened from either. */
.field-popover {
    position: fixed;
    z-index: 10160;
    max-width: min(19rem, calc(100vw - 1rem));
    padding: var(--space-1-5, 0.4rem) var(--space-2, 0.6rem);
    border-radius: var(--radius-5, 0.5rem);
    border: 1px solid var(--border-primary);
    background: var(--background-secondary);
    color: var(--text-primary);
    font-family: var(--font-family-main);
    font-size: calc(var(--font-size-controls) * 0.85);
    line-height: 1.4;
    box-shadow: 0 6px 20px color-mix(in srgb, var(--text-primary) 16%, transparent);
    pointer-events: none;
}

.field-popover[hidden] {
    display: none;
}

/* A warning is not a description: same bubble, the colour of the field's own
   error state, so a shortcut clash cannot be mistaken for a hint. */
.field-popover.field-popover-warning {
    border-color: var(--accent-error);
    color: var(--accent-error);
}

.move-popover {
    position: fixed;
    z-index: 9000;
    background: var(--background-secondary);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-6, 0.65rem);
    padding: var(--space-1-5, 0.35rem) 0 var(--space-2, 0.45rem);
    min-width: 12rem;
    max-width: min(16rem, calc(100vw - 1rem));
    max-height: min(70vh, 20rem);
    overflow-y: auto;
    color: var(--text-primary);
    font-family: var(--font-family-main);
    box-shadow: 0 4px 18px color-mix(in srgb, var(--text-primary) 14%, transparent);
    animation: datePopoverIn 0.13s ease forwards;
}

/* The category menu sizes to what it holds.
 *
 * .move-popover is capped at 16rem for the move, tag and delete pickers, which
 * list every category, page or tag you have and want to stay narrow. This menu
 * is four fixed rows, and each carries the key that does the same thing — at
 * the shared cap the widest row needed 274px and got 237, so the chip was
 * quietly cut off by the ellipsis. Longer translations make that worse: the
 * French label for spreading is half again as long as the English one. */
#category-context-menu {
    width: max-content;
    max-width: min(24rem, calc(100vw - 1rem));
}

/* The bookmark context menu is a fixed, short action list — unlike the move,
   tag and delete popovers, which share .move-popover and list every category,
   page or tag you have and genuinely need to scroll.
   The shared 20rem cap was already within one item of this menu's height, so
   adding the two multi-select entries pushed it over: the last entries fell
   below the fold and a scrollbar appeared on a menu that is meant to be read at
   a glance. It sizes to its content instead, and only falls back to scrolling
   when the viewport itself is too short to hold it. */
.bookmark-context-menu {
    max-height: none;
    overflow-y: visible;
}

@media (max-height: 34rem) {
    .bookmark-context-menu {
        max-height: calc(100vh - 2rem);
        overflow-y: auto;
    }
}

.move-popover-header {
    font-size: calc(var(--font-size-controls) * 0.72);
    font-weight: var(--font-weight-semibold);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-secondary);
    padding: var(--space-1-5, 0.4rem) var(--space-3, 0.85rem) var(--space-1, 0.25rem);
}

.move-popover-current-hint {
    padding: 0 var(--space-3, 0.85rem) var(--space-2, 0.55rem);
    font-size: calc(var(--font-size-controls) * 0.88);
    font-weight: var(--font-weight-medium);
    color: var(--text-primary);
    line-height: 1.35;
}

.move-popover-section-label {
    font-size: calc(var(--font-size-controls) * 0.68);
    font-weight: var(--font-weight-medium);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-tertiary, var(--text-secondary));
    padding: var(--space-2, 0.5rem) var(--space-3, 0.85rem) var(--space-1, 0.2rem);
}

.move-popover-item {
    display: flex;
    align-items: center;
    gap: var(--space-2, 0.5rem);
    padding: var(--space-2, 0.45rem) var(--space-3, 0.85rem);
    font-size: calc(var(--font-size-controls) * 0.92);
    font-weight: var(--font-weight-medium);
    color: var(--text-primary);
    cursor: pointer;
    border-radius: var(--radius-4, 0.4rem);
    margin: 0 var(--space-1, 0.3rem);
    transition:
        background-color 0.12s ease,
        color 0.12s ease;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.move-popover-item.is-focused {
    background: color-mix(in srgb, var(--accent-primary) 12%, var(--background-primary));
    color: var(--text-primary);
}

.move-popover-item.is-current {
    color: var(--text-primary);
    background: color-mix(in srgb, var(--accent-primary) 8%, var(--background-primary));
    cursor: default;
}

.move-popover-item.is-current.is-focused {
    background: color-mix(in srgb, var(--accent-primary) 12%, var(--background-primary));
}

.move-popover-item.is-danger {
    color: var(--accent-error, var(--accent-danger, #c0392b));
}

.move-popover-item.is-danger.is-focused {
    background: color-mix(in srgb, var(--accent-error, var(--accent-danger, #c0392b)) 14%, var(--background-primary));
    color: var(--accent-error, var(--accent-danger, #c0392b));
}

.move-popover-item.is-danger .move-popover-check {
    color: var(--accent-error, var(--accent-danger, #c0392b));
}

.move-popover-check {
    width: 0.9rem;
    flex-shrink: 0;
    font-size: var(--text-xs, 0.75rem);
    color: var(--accent-primary);
    text-align: center;
}

.move-popover-divider {
    height: 1px;
    margin: var(--space-1-5, 0.35rem) var(--space-2, 0.5rem);
    background: var(--border-primary);
}

/* Marks an entry that opens a submenu rather than acting immediately. */
.move-popover-submenu-caret {
    margin-left: auto;
    padding-left: var(--space-2, 0.5rem);
    font-size: var(--text-xs, 0.7rem);
    color: var(--text-tertiary);
}

/* The current value of an entry that opens a submenu — "3 columns" beside
   "Width", so the menu answers as well as asks. It takes over the trailing
   alignment from whatever follows it, or the row would grow three separate
   right-aligned blocks. */
.move-popover-item-detail {
    margin-left: auto;
    padding-left: var(--space-2, 0.5rem);
    font-size: var(--text-xs, 0.7rem);
    color: var(--text-tertiary);
    white-space: nowrap;
}

.move-popover-item-detail ~ .move-popover-submenu-caret,
.move-popover-item-detail ~ .move-popover-item-key {
    margin-left: 0;
}

/* An entry that is present but cannot act — it still says what it is and, in
   its detail, why it cannot. Removing it would leave the user looking for a
   feature they read about. */
.move-popover-item.is-disabled {
    opacity: 0.55;
    cursor: default;
}

/* ─── Check-mode submenu ─────────────────────────────────────
   Three named modes, each with the sentence that distinguishes it — the same
   copy the health view shows, so the choice reads identically wherever it is
   made. Two-line rows need the alignment and wrapping the flat menu forbids. */
.bookmark-check-mode-menu {
    min-width: 17rem;
    max-width: min(20rem, calc(100vw - 1rem));
}

.bookmark-check-mode-menu .move-popover-item {
    align-items: flex-start;
    white-space: normal;
    overflow: visible;
}

.bookmark-check-mode-menu .move-popover-check {
    margin-top: var(--space-hairline, 0.15rem);
}

.check-mode-option-text {
    display: flex;
    flex-direction: column;
    gap: var(--space-hairline, 0.1rem);
    min-width: 0;
}

.check-mode-option-label {
    display: flex;
    align-items: center;
    gap: var(--space-1-5, 0.4rem);
}

/* The accelerator is printed on the option rather than taught elsewhere, so the
   keyboard route is discoverable from the menu itself. */
/* Keyboard hint on a menu row: pushed to the trailing edge so the labels stay
   left-aligned and the keys line up as a column of their own. Same chip as
   .check-mode-option-key, which sits inside a label rather than beside one. */
.move-popover-item-key {
    margin-left: auto;
    padding: 0 var(--space-1, 0.25rem);
    border: 1px solid var(--border-secondary);
    border-radius: var(--radius-2, 0.2rem);
    font-family: inherit;
    font-size: var(--text-2xs, 0.65rem);
    line-height: 1.4;
    color: var(--text-tertiary);
    white-space: nowrap;
}

.check-mode-option-key {
    padding: 0 var(--space-1, 0.25rem);
    border: 1px solid var(--border-secondary);
    border-radius: var(--radius-2, 0.2rem);
    font-family: inherit;
    font-size: var(--text-2xs, 0.65rem);
    line-height: 1.4;
    color: var(--text-tertiary);
}

.check-mode-option-body {
    font-size: var(--text-xs, 0.72rem);
    font-weight: var(--font-weight-normal);
    color: var(--text-secondary);
    white-space: normal;
}

/* On hover the row tints, so the explanation follows rather than staying grey
   against a coloured background. */
.bookmark-check-mode-menu .move-popover-item.is-focused .check-mode-option-body {
    color: inherit;
}

/* ─── Tag popover (Shift+T) ─────────────────────────────────── */
.tag-popover {
    min-width: 13rem;
    max-width: min(18rem, calc(100vw - 1rem));
    outline: none;
}

.tag-popover:focus {
    outline: none;
}

.tag-popover .move-popover-item {
    outline: none;
}

.tag-popover-bookmark-name {
    padding-bottom: var(--space-1-5, 0.35rem);
}

.tag-popover-current-tags {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1-5, 0.35rem) var(--space-1-5, 0.4rem);
    padding: 0 var(--space-3, 0.85rem) var(--space-3, 0.65rem);
}

.tag-popover-current-tags-label {
    font-size: calc(var(--font-size-controls) * 0.72);
    font-weight: var(--font-weight-medium);
    color: var(--text-secondary);
    margin-right: var(--space-hairline, 0.15rem);
}

.tag-popover-current-tags-none {
    font-size: calc(var(--font-size-controls) * 0.88);
    color: var(--text-tertiary, var(--text-secondary));
    font-style: italic;
}

.tag-popover-current-chip {
    font-size: calc(var(--font-size-controls) * 0.78);
    padding: var(--space-hairline, 0.12rem) var(--space-2, 0.45rem);
}

.tag-popover-empty-hint {
    padding: var(--space-hairline, 0.15rem) var(--space-3, 0.85rem) var(--space-3, 0.65rem);
    font-size: calc(var(--font-size-controls) * 0.86);
    color: var(--text-secondary);
    line-height: 1.35;
}

.tag-popover-item-label {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

.tag-popover-item-meta {
    flex: 0 0 auto;
    font-size: calc(var(--font-size-controls) * 0.72);
    color: var(--text-tertiary, var(--text-secondary));
    margin-left: auto;
    padding-left: var(--space-1-5, 0.35rem);
}

.move-popover-item.is-current .tag-popover-item-meta {
    color: var(--text-secondary);
}

.date-weather-line {
    font-size: calc(var(--font-size-controls) * 1.6); /* larger for weather */
    color: var(--text-tertiary);
}

/* Add spacing below the date/weather block to separate from page title */
.dashboard-section.section-controls {
    padding-bottom: var(--space-5, 1.5rem);
}

.weather-icon {
    display: inline-flex;
    align-items: center;
    margin: 0 var(--space-1, 0.22rem) 0 var(--space-hairline, 0.18rem);
    vertical-align: -0.12em;
}

.weather-icon-svg {
    width: 1.05em;
    height: 1.05em;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.8;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.weather-text {
    vertical-align: baseline;
}

.title {
    font-size: var(--font-size-title);
    font-family: var(--font-family-main);
    font-weight: var(--font-weight-black);
    opacity: 0.8;
    user-select: none;
    color: var(--text-primary);
}

/* Section trail under the view name. Sits in the header's own type scale, not
   the title's, so it reads as orientation rather than a second heading. */
.title-breadcrumb {
    margin: var(--space-hairline, 0.15rem) 0 0;
    font-family: var(--font-family-main);
    font-size: var(--font-size-controls);
    color: var(--text-secondary);
    user-select: none;
}

.title-breadcrumb[hidden] {
    display: none;
}

.header-actions {
    --header-control-gap: var(--space-2, 0.5rem);
    display: flex;
    align-items: center;
    gap: var(--header-control-gap);
    margin-left: auto;
}

/* inbox · health · config — same gap as between page tabs and the pages icon. */
.header-destinations {
    display: flex;
    align-items: center;
    gap: var(--header-control-gap, 0.5rem);
    min-width: 0;
    flex-shrink: 0;
}

.bookmark-link.find-hidden {
    display: none;
}

/* Tag filter view — same dashboard-grid / packed-columns as normal categories */
#dashboard-layout.dashboard-grid.tag-filter-view {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    grid-template-columns: unset;
    row-gap: var(--space-3, 0.75rem);
    width: 100%;
    max-width: 100%;
}

.tag-filter-banner {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-2, 0.55rem);
    padding: var(--space-1-5, 0.35rem) var(--space-2, 0.5rem) var(--space-3, 0.65rem);
    flex-shrink: 0;
    width: 100%;
}

.tag-filter-banner-head {
    width: 100%;
}

.tag-filter-bulk-toolbar {
    width: 100%;
}

#dashboard-layout.tag-filter-view .tag-filter-bulk-toolbar {
    margin-bottom: 0;
    padding-bottom: 0;
    border-bottom: none;
}

#dashboard-layout.tag-filter-view .tag-filter-bulk-toolbar .recent-bookmarks-open-btn.modal-button {
    flex: 0 1 auto;
    min-width: min(100%, 10rem);
}

.tag-filter-indicator-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1, 0.28rem);
    max-width: 100%;
    min-width: 0;
    padding: var(--space-1, 0.22rem) var(--space-2, 0.55rem);
    border: 1px solid color-mix(in srgb, var(--accent-primary) 55%, var(--border-primary));
    border-radius: var(--radius-pill, 999px);
    background: color-mix(in srgb, var(--accent-primary) 12%, var(--background-secondary));
    color: var(--text-primary);
    font-family: var(--font-family-main);
    font-size: calc(var(--font-size-controls) * 0.92);
    font-weight: var(--font-weight-semibold);
    line-height: 1.25;
    box-shadow: 0 1px 4px color-mix(in srgb, var(--accent-primary) 18%, transparent);
}

.tag-filter-indicator-prefix {
    color: var(--accent-primary);
    font-weight: var(--font-weight-bold);
}

.tag-filter-indicator-tag {
    color: var(--accent-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.tag-filter-indicator-count {
    color: var(--text-secondary);
    font-weight: var(--font-weight-normal);
    font-size: calc(var(--font-size-controls) * 0.85);
    white-space: nowrap;
}

.tag-filter-indicator-count::before {
    content: '·';
    margin-right: var(--space-1, 0.28rem);
    opacity: 0.55;
}

/* Shown only while a tag filter is on — updateTagFilterIndicator sets `hidden`
   the rest of the time, and [hidden] would otherwise lose to this rule. */
.tag-filter-indicator {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1-5, 0.35rem);
    min-width: 0;
    max-width: 100%;
}

.tag-filter-indicator[hidden] {
    display: none;
}

.tag-filter-indicator-head {
    display: inline-flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-1-5, 0.35rem);
    max-width: 100%;
    min-width: 0;
}

.tag-filter-indicator-chips {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1, 0.3rem);
    min-width: 0;
    max-width: 100%;
}

.tag-filter-indicator-tag-item {
    display: inline-flex;
    align-items: center;
    gap: var(--space-hairline, 0.1rem);
    max-width: 100%;
    min-width: 0;
}

.tag-filter-indicator-tag-remove {
    flex-shrink: 0;
    border: 1px solid transparent;
    background: transparent;
    color: var(--text-tertiary);
    width: 1.25rem;
    height: 1.25rem;
    border-radius: var(--radius-pill, 999px);
    font-size: var(--text-base, 0.95rem);
    line-height: 1;
    cursor: pointer;
    transition:
        border-color 0.15s ease,
        color 0.15s ease,
        background-color 0.15s ease;
}

.tag-filter-indicator-tag-remove:hover,
.tag-filter-indicator-tag-remove:focus-visible {
    border-color: var(--accent-primary);
    color: var(--accent-primary);
    background: color-mix(in srgb, var(--background-primary) 94%, transparent);
    outline: none;
}

.tag-filter-indicator-summary {
    color: var(--text-secondary);
    font-family: var(--font-family-main);
    font-size: calc(var(--font-size-controls) * 0.85);
    font-weight: var(--font-weight-normal);
    white-space: nowrap;
}

.tag-filter-indicator-summary::before {
    content: '·';
    margin-right: var(--space-1, 0.28rem);
    opacity: 0.55;
}

.tag-filter-indicator-chip {
    cursor: pointer;
    transition:
        border-color 0.15s ease,
        background-color 0.15s ease,
        transform 0.15s ease;
}

.tag-filter-indicator-chip:hover,
.tag-filter-indicator-chip:focus-visible {
    border-color: var(--accent-primary);
    background: color-mix(in srgb, var(--accent-primary) 18%, var(--background-secondary));
    outline: none;
    transform: translateY(-1px);
}

.tag-filter-indicator-clear {
    flex-shrink: 0;
    border: 1px solid var(--border-primary);
    background: color-mix(in srgb, var(--background-secondary) 80%, transparent);
    color: var(--text-secondary);
    width: 1.55rem;
    height: 1.55rem;
    border-radius: var(--radius-pill, 999px);
    font-size: var(--text-md, 1rem);
    line-height: 1;
    cursor: pointer;
    transition:
        border-color 0.15s ease,
        color 0.15s ease,
        background-color 0.15s ease;
}

.tag-filter-indicator-clear:hover,
.tag-filter-indicator-clear:focus-visible {
    border-color: var(--accent-primary);
    color: var(--accent-primary);
    background: color-mix(in srgb, var(--background-primary) 94%, transparent);
    outline: none;
}

#dashboard-layout.tag-filter-view .tag-filter-view-body.dashboard-grid {
    width: 100%;
    max-width: 100%;
}

#dashboard-layout.tag-filter-view .tag-filter-view-body.dashboard-grid:not(.packed-columns) {
    width: fit-content;
    max-width: min(100%, var(--dashboard-grid-max-width, 100%));
    margin-inline: auto;
}

.category.tag-filter-chunk {
    gap: 0;
    width: 100%;
    max-width: 100%;
    min-width: 0;
    box-sizing: border-box;
}

/* Equal-width columns: one chunk (10 bookmarks) per dashboard-column */
#dashboard-layout.tag-filter-view .tag-filter-view-body.packed-columns {
    column-gap: var(--gap, 1.5rem);
}

#dashboard-layout.tag-filter-view .tag-filter-view-body.packed-columns .dashboard-column {
    flex: 1 1 0;
    flex-basis: min(
        var(--dashboard-column-max, 300px),
        calc((100% - (var(--packed-columns, 2) - 1) * var(--gap, 1.5rem)) / var(--packed-columns, 2))
    );
    max-width: min(
        var(--dashboard-column-max, 300px),
        calc((100% - (var(--packed-columns, 2) - 1) * var(--gap, 1.5rem)) / var(--packed-columns, 2))
    );
    width: min(
        var(--dashboard-column-max, 300px),
        calc((100% - (var(--packed-columns, 2) - 1) * var(--gap, 1.5rem)) / var(--packed-columns, 2))
    );
    min-width: 0;
}

#dashboard-layout.tag-filter-view .tag-filter-view-body:not(.packed-columns) .category.tag-filter-chunk {
    width: 100%;
    max-width: 100%;
}

#dashboard-layout.tag-filter-view .tag-filter-chunk .bookmarks-list {
    width: 100%;
    max-width: 100%;
}

/* Tag filter always uses vertical bookmark rows (overrides launcher tiles on parent grid) */
#dashboard-layout.tag-filter-view .tag-filter-chunk .bookmarks-list {
    display: grid;
    grid-template-columns: var(--bookmark-lead-col, minmax(1.25rem, max-content)) minmax(0, 1fr) max-content;
    row-gap: var(--space-2, 0.5rem);
    flex-direction: unset;
    flex-wrap: unset;
}

#dashboard-layout.tag-filter-view .tag-filter-chunk .bookmarks-list > * {
    grid-column: 1 / -1;
}

#dashboard-layout.tag-filter-view .tag-filter-chunk .bookmark-link {
    display: grid;
    grid-template-columns: subgrid;
    grid-column: 1 / -1;
    width: 100%;
    max-width: 100%;
    align-items: center;
    padding: 0.5rem var(--bookmark-row-end-pad, 3px) 0.5rem var(--bookmark-row-start-pad, 6px);
    min-height: 1.9rem;
    white-space: nowrap;
    border-radius: 0;
    gap: unset;
    flex-direction: unset;
}

#dashboard-layout.tag-filter-view .tag-filter-chunk .bookmark-link:hover {
    transform: none;
}

#dashboard-layout.tag-filter-view .tag-filter-chunk .bookmark-open::before {
    content: none;
}

#dashboard-layout.tag-filter-view .tag-filter-chunk .bookmark-reorder-handle {
    display: block !important;
}

#dashboard-layout.tag-filter-view .tag-filter-chunk .bookmark-shortcut {
    display: block !important;
    grid-column: 3;
    grid-row: 1;
    text-align: right;
    justify-self: end;
}

#dashboard-layout.tag-filter-view .tag-filter-chunk .bookmarks-list[data-show-ping="true"] {
    grid-template-columns:
        var(--bookmark-lead-col, minmax(1.25rem, max-content))
        minmax(0, 1fr)
        var(--bookmark-status-col, 3.35em)
        max-content;
}

#dashboard-layout.tag-filter-view .tag-filter-chunk .bookmarks-list[data-show-ping="true"] .bookmark-link > .status-text {
    grid-column: 3;
    grid-row: 1;
    justify-self: end;
    align-self: center;
}

#dashboard-layout.tag-filter-view .tag-filter-chunk .bookmarks-list[data-show-ping="true"] .bookmark-link > .bookmark-shortcut {
    grid-column: 4;
}

#dashboard-layout.tag-filter-view .tag-filter-chunk .bookmark-open-count {
    display: none !important;
}

#dashboard-layout.tag-filter-view .tag-filter-chunk .bookmark-pin-badge:not(.is-empty),
#dashboard-layout.tag-filter-view .tag-filter-chunk .bookmark-note-badge:not(.is-empty) {
    display: inline-flex !important;
}

#dashboard-layout.tag-filter-view .tag-filter-chunk .bookmark-lead {
    display: inline-flex;
    justify-content: flex-start;
    align-items: center;
    grid-column: 1;
    grid-row: 1;
    min-height: 1.15em;
    width: auto;
    min-width: 1.15em;
}

#dashboard-layout.tag-filter-view .tag-filter-chunk .bookmark-icon-slot {
    width: auto;
    height: auto;
}

#dashboard-layout.tag-filter-view .tag-filter-chunk .bookmark-icon {
    width: 1.15em;
    height: 1.15em;
}

#dashboard-layout.tag-filter-view .tag-filter-chunk a.bookmark-open {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    grid-column: 2;
    grid-row: 1;
    width: auto;
    min-width: 0;
    flex: unset;
    overflow: hidden;
}

#dashboard-layout.tag-filter-view .tag-filter-chunk .bookmark-text {
    font-size: var(--bookmark-font-size, 1rem);
    text-align: left;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    display: block;
    -webkit-line-clamp: unset;
    -webkit-box-orient: unset;
    line-height: inherit;
}

.empty-state--tag-filter {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-3, 0.75rem);
    padding: var(--space-5, 1.5rem) var(--space-3, 0.75rem);
    color: var(--text-tertiary);
    font-size: var(--font-size-text);
}

.empty-state--tag-filter-text {
    margin: 0;
    line-height: 1.5;
}

.empty-state--tag-filter-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2, 0.5rem);
}

.empty-state--tag-filter-btn {
    appearance: none;
    border: 1px solid var(--border-primary);
    border-radius: var(--header-chip-radius, 999px);
    background: color-mix(in srgb, var(--background-secondary) 55%, transparent);
    color: var(--text-secondary);
    padding: var(--space-1-5, 0.35rem) var(--space-3, 0.8rem);
    font: inherit;
    font-size: var(--font-size-controls);
    cursor: pointer;
    transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}

.empty-state--tag-filter-btn:hover,
.empty-state--tag-filter-btn:focus-visible {
    color: var(--text-primary);
    border-color: var(--text-primary);
    background: var(--background-secondary);
    outline: none;
}

/* ── Page navigation ─────────────────────────────────────────────────────── */
.page-navigation {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1, 0.25rem);
}

.page-swipe-hint {
    display: none;
}

.page-nav-inbox-host {
    display: flex;
    align-items: center;
}

.page-nav-inbox-host:empty {
    display: none;
}

/* On a touch device wide enough to keep the desktop layout — a tablet, or a
   phone in landscape — the page tabs stay on screen and are tapped with a
   finger. They measured 28x33 there, under the 44px both iOS and Android ask
   for. The phone layout is unaffected: it hides #page-navigation outright and
   has its own 44px rules for the controls it keeps.

   Scoped to coarse pointers rather than applied everywhere, because on a mouse
   these are a dense row of tabs and padding them out to 44px would push the
   later pages off the strip for no gain. */
@media (hover: none) and (pointer: coarse) {
    body:not([data-mobile-layout="true"]) .page-nav-btn {
        min-height: 44px;
        min-width: 44px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        touch-action: manipulation;
    }
}

.page-nav-btn {
    padding: var(--space-1-5, 0.4rem) var(--space-2, 0.6rem);
    font-size: var(--font-size-controls);
    font-family: var(--font-family-main);
    font-weight: var(--font-weight-semibold);
    background: transparent;
    border: none;
    border-bottom: 2px solid transparent;
    color: var(--text-secondary);
    cursor: pointer;
    transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
    text-align: center;
    position: relative;
}

.page-nav-btn::before,
.page-nav-btn::after {
    content: none;
}

.page-nav-btn:hover {
    color: var(--text-primary);
    background: color-mix(in srgb, var(--background-secondary) 60%, transparent 40%);
}

.page-nav-btn:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}

.page-nav-btn.active {
    background: var(--background-secondary);
    color: var(--text-primary);
    border-bottom-color: var(--accent-primary);
    font-weight: var(--font-weight-bold);
}

.page-nav-btn.active::before,
.page-nav-btn.active::after {
    content: none;
}

.page-tab-icon {
    margin-right: 0.2em;
    font-style: normal;
    line-height: 1;
}

.page-tab-dot {
    display: inline-block;
    width: 0.45em;
    height: 0.45em;
    border-radius: 50%;
    margin-right: 0.35em;
    vertical-align: middle;
    flex-shrink: 0;
}

/* Page-tab edit popover (top/left set in JS from anchor + viewport) */
.page-tab-popover {
    position: fixed;
    z-index: 1100;
    background: var(--background-secondary);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-6, 0.65rem);
    padding: var(--space-2, 0.6rem) var(--space-3, 0.7rem);
    box-shadow: 0 4px 18px rgba(0,0,0,0.22);
    display: flex;
    flex-direction: column;
    gap: var(--space-2, 0.5rem);
    min-width: 12rem;
    max-width: min(20rem, calc(100vw - 1rem));
}

.page-tab-popover-row {
    display: flex;
    gap: var(--space-1-5, 0.4rem);
    align-items: center;
}

.page-tab-popover-icon {
    width: 2.6rem;
    text-align: center;
    font-size: 1.1em;
    background: var(--background-primary);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-4, 0.4rem);
    color: var(--text-primary);
    padding: var(--space-1, 0.2rem) var(--space-1, 0.3rem);
    outline: none;
}
.page-tab-popover-icon:focus { border-color: var(--accent-primary); }

.page-tab-popover-name {
    flex: 1;
    background: var(--background-primary);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-4, 0.4rem);
    color: var(--text-primary);
    font: inherit;
    font-size: var(--font-size-controls);
    padding: var(--space-1, 0.2rem) var(--space-1-5, 0.4rem);
    outline: none;
}
.page-tab-popover-name:focus { border-color: var(--accent-primary); }

.page-tab-color-swatches {
    display: flex;
    gap: var(--space-1, 0.3rem);
    flex-wrap: wrap;
}

.page-tab-color-swatch {
    width: 1.1rem;
    height: 1.1rem;
    border-radius: 50%;
    border: 2px solid transparent;
    cursor: pointer;
    padding: 0;
    transition: border-color 0.12s, transform 0.12s;
}
.page-tab-color-swatch:hover { transform: scale(1.2); }
.page-tab-color-swatch.selected { border-color: var(--accent-primary); }
.page-tab-color-swatch.swatch-none {
    border: 2px solid var(--border-primary);
    background: var(--background-primary) !important;
    position: relative;
}
.page-tab-color-swatch.swatch-none::after {
    content: '✕';
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--text-2xs, 0.6rem);
    color: var(--text-secondary);
    line-height: 1;
    pointer-events: none;
}

.page-tab-rename-input {
    background: transparent;
    border: none;
    border-bottom: 1px solid var(--text-primary);
    color: var(--text-primary);
    font: inherit;
    font-size: var(--font-size-controls);
    font-weight: var(--font-weight-semibold);
    padding: 0;
    margin: 0;
    width: 8ch;
    min-width: 4ch;
    max-width: 20ch;
    outline: none;
}

@keyframes healthLinkShimmer {
    0% {
        text-shadow: 0 0 0 rgba(var(--accent-primary-rgb, 66, 135, 245), 0);
    }
    50% {
        text-shadow: 0 0 8px rgba(var(--accent-primary-rgb, 66, 135, 245), 0.6);
    }
    100% {
        text-shadow: 0 0 0 rgba(var(--accent-primary-rgb, 66, 135, 245), 0);
    }
}

.health-link a,
.config-link a {
    text-decoration: none;
    font-size: var(--font-size-controls);
    font-family: var(--font-family-main);
    font-weight: var(--font-weight-semibold);
    transition: color 0.3s ease;
    color: var(--text-secondary);
}

.health-link a {
    animation: healthLinkShimmer 3s ease-in-out infinite;
    position: relative;
    display: inline-flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.35em;
}

.health-link a:hover,
.config-link a:hover {
    color: var(--text-primary);
}

.health-link a:hover {
    animation: none;
    text-shadow: 0 0 12px rgba(var(--accent-primary-rgb, 66, 135, 245), 0.8);
}

.health-badge {
    position: absolute;
    top: -0.45em;
    right: -0.55em;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.15em;
    height: 1.15em;
    padding: 0 0.25em;
    font-size: 0.62em;
    font-weight: var(--font-weight-bold);
    font-variant-numeric: tabular-nums;
    line-height: 1;
    border-radius: var(--radius-pill, 999px);
    color: #fff;
    background: var(--accent-error);
    pointer-events: none;
    z-index: 1;
    box-sizing: border-box;
}

.health-badge-warn {
    background: var(--accent-warning);
    color: var(--text-on-warning, #000);
}

/* Icon variant: pages, health, config, and inbox (in the destination cluster)
   share one chrome; spacing is .header-actions / .header-destinations gap. */
.pages-link--icon,
.health-link--icon,
.config-link--icon {
    margin-left: 0;
}

.pages-link--icon .pages-link-anchor,
.health-link--icon a,
.config-link--icon a,
.header-destinations .page-nav-btn--inbox {
    animation: none;
    display: inline-flex;
    align-items: center;
    flex-wrap: nowrap;
    gap: var(--space-1, 0.3rem);
    padding: var(--space-1-5, 0.4rem) var(--space-2, 0.5rem);
    border-radius: var(--layout-radius-sm, 8px);
}

.pages-link--icon .pages-link-anchor {
    font-family: var(--font-family-main);
    font-size: var(--font-size-controls);
    font-weight: var(--font-weight-semibold);
    color: var(--text-secondary);
    background: transparent;
    border: none;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    text-decoration: none;
    transition: color 0.3s ease;
}

.pages-link--icon .pages-link-anchor:hover,
.health-link--icon a:hover,
.config-link--icon a:hover,
.header-destinations .page-nav-btn--inbox:hover {
    color: var(--text-primary);
    text-shadow: none;
}

.pages-link--icon .pages-link-anchor:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}

/* Active state matched to .page-nav-btn.active: header destinations are the
   same kind of tab as page tabs, so "you are here" must look the same. */
.pages-link--icon .pages-link-anchor,
.health-link--icon a,
.config-link--icon a,
.header-destinations .page-nav-btn--inbox {
    border-bottom: 2px solid transparent;
}

.pages-link--icon .pages-link-anchor.active,
.health-link--icon a.active,
.config-link--icon a.active,
.header-destinations .page-nav-btn--inbox.active {
    background: var(--background-secondary);
    color: var(--text-primary);
    border-bottom-color: var(--accent-primary);
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
}

.pages-link--icon .pages-link-icon,
.health-link--icon .health-link-icon,
.config-link--icon .config-link-icon,
.header-destinations .page-nav-btn--inbox .page-tab-icon--svg {
    width: 1.15rem;
    height: 1.15rem;
    display: inline-flex;
    align-items: center;
    color: currentColor;
    flex-shrink: 0;
}

.header-destinations .page-nav-btn--inbox {
    margin-left: 0;
}

/* Inline pill counter after the icon (overrides the absolute superscript dot). */
.health-link--icon .health-badge {
    position: static;
    top: auto;
    right: auto;
    min-width: 1.35rem;
    height: auto;
    padding: var(--space-hairline, 0.1rem) var(--space-1-5, 0.4rem);
    font-size: var(--text-xs, 0.72rem);
    font-weight: var(--font-weight-semibold);
    border-radius: var(--radius-pill, 999px);
    border: 1px solid color-mix(in srgb, var(--accent-error) 40%, transparent);
    background: color-mix(in srgb, var(--accent-error) 18%, transparent);
    color: var(--accent-error);
}

.health-link--icon .health-badge.health-badge-warn {
    border-color: color-mix(in srgb, var(--accent-warning) 45%, transparent);
    background: color-mix(in srgb, var(--accent-warning) 20%, transparent);
    color: var(--accent-warning);
}

/* The down badge shares broken's red — an outage is not a milder thing than a
   dead link. The two are told apart by movement, not hue: only the down state
   pulses the link (see .health-link.is-health-alert). Explicit here so the
   base .health-badge red still applies even beside the warn override above. */
.health-link--icon .health-badge.health-badge-down {
    border-color: color-mix(in srgb, var(--accent-error) 55%, transparent);
    background: color-mix(in srgb, var(--accent-error) 22%, transparent);
    color: var(--accent-error);
}

/* Fired once when the number of down monitors rises, then removed — a header
   ornament that pulsed forever would stop reading as an alert within a day.
   The cooldown and the rise check live in dashboard-visual.js; this is only the
   look of the pulse. */
@keyframes health-alert-pulse {
    0%   { transform: scale(1); }
    30%  { transform: scale(1.18); }
    60%  { transform: scale(0.96); }
    100% { transform: scale(1); }
}

.health-link.is-health-alert .health-link-anchor {
    animation: health-alert-pulse 0.5s cubic-bezier(0.22, 1, 0.36, 1) 3;
    transform-origin: center;
}

/* A quiet ring under the pulse, so a colour-blind or briefly-glanced view still
   catches it. Also removed with the class when the animation ends. */
.health-link.is-health-alert .health-badge {
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-error) 30%, transparent);
}

body.no-animations .health-link.is-health-alert .health-link-anchor {
    animation: none !important;
}

@media (prefers-reduced-motion: reduce) {
    .health-link.is-health-alert .health-link-anchor {
        animation: none !important;
    }
}

/* Dashboard Grid */
.dashboard-grid {
    --dashboard-column-min: 250px;
    /* Upper bound a column may grow to. Raised from 300px so that on wide
       screens the columns absorb the available width (fewer, wider, more
       readable columns) instead of staying narrow and leaving large side
       whitespace. Below this width the per-column calc() takes over and the
       columns shrink gracefully toward --dashboard-column-min. */
    --dashboard-column-max: 360px;
    /* The horizontal space between two categories. Named because a wide
       category has to reproduce it between its own inner columns, and packed
       mode sets a different one. */
    --dashboard-column-gap: var(--space-5, 1.5rem);
    display: grid;
    row-gap: var(--category-row-gap, 2rem);
    column-gap: var(--dashboard-column-gap);
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    width: 100%;
    max-width: 100%;
}

#dashboard-layout.page-transition {
    animation: dashboardPageFade 200ms ease-out;
}

@keyframes dashboardPageFade {
    from {
        opacity: 0;
        transform: translateY(10px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.dashboard-grid.columns-1 {
    grid-template-columns: 1fr;
}

.dashboard-grid.columns-2 {
    grid-template-columns: repeat(2, 1fr);
}

.dashboard-grid.columns-3 {
    grid-template-columns: repeat(3, 1fr);
}

.dashboard-grid.columns-4 {
    grid-template-columns: repeat(4, 1fr);
}

.dashboard-grid.columns-5 {
    grid-template-columns: repeat(5, 1fr);
}

.dashboard-grid.columns-6 {
    grid-template-columns: repeat(6, 1fr);
}

.dashboard-grid.columns-3:not(.packed-columns) .category,
.dashboard-grid.columns-4:not(.packed-columns) .category,
.dashboard-grid.columns-5:not(.packed-columns) .category,
.dashboard-grid.columns-6:not(.packed-columns) .category {
    width: fit-content;
    max-width: 100%;
}

/* A category set wider than one column.
 *
 * `width: fit-content` above shrinks a category to its content, which is right
 * for a one-column block and wrong for this one: the whole point is to occupy
 * the columns it was given. Same specificity as the rule above and deliberately
 * placed after it, so this wins for the categories that carry the class.
 *
 * Grid only: this is the rule that reaches across grid columns. In packed mode
 * the same width is carried by a band of its own — see the packed block below —
 * and everything from the inner columns down is shared by both.
 */
.dashboard-grid:not(.packed-columns) .category.category--wide {
    grid-column: span var(--category-span, 1);
    width: 100%;
    max-width: 100%;
}

/* Every width, inner columns included, is driven by this one property, so
   putting it back to 1 is enough to undo the whole thing.
 *
 * `!important` because the effective width is written as an inline style at
 * render time and the window can be resized without a re-render — the grid
 * would otherwise still be told to span three columns it no longer has, and
 * spanning past the last column creates implicit ones and scrolls the page
 * sideways. */
@media (max-width: 767px) {
    .dashboard-grid .category.category--wide {
        --category-span: 1 !important;
    }
}

.dashboard-grid.columns-1 .category.category--wide {
    --category-span: 1 !important;
}

/* One category, several columns — and no second rule to say so.
 *
 * A wide category used to draw its own line across the full span, under the
 * header. The header already carries one: the fading rule the depth presets
 * give every category title, which runs the same width because the title does.
 * Two lines a few pixels apart read as a mistake, so this one is gone and the
 * width is left to the spread badge and the layout itself.
 */

/* The marker in the header: how many columns this category is currently taking.
   Small and quiet — it is the one thing that says the width out loud, for the
   times the width alone is ambiguous (a narrow window, two columns of similar
   length). */
.category-spread-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.15em;
    padding: 0 0.3em;
    border: 1px solid var(--border-secondary);
    border-radius: var(--radius-3, 0.25rem);
    font-size: 0.7em;
    line-height: 1.5;
    color: var(--text-tertiary);
    white-space: nowrap;
}

/* Wider means more bookmarks side by side, not longer rows.
 *
 * The list is a three-track grid (four with ping times) whose rows span every
 * track, and each row is a subgrid of it — that is what keeps names and status
 * aligned down a column. Repeating the whole track pattern once per column and
 * letting a row span one pattern keeps all of that intact and fills row by row.
 *
 * The launcher and list presets replace the list's display entirely, so these
 * declarations are inert there — their own wrapping already handles the width.
 *
 * Not scoped to either layout: how a wide category arranges its own bookmarks
 * is the same question whether the width came from spanning grid columns or
 * from a packed band.
 */
.dashboard-grid .category.category--wide .bookmarks-list {
    grid-template-columns:
        repeat(var(--category-span, 1),
            var(--bookmark-lead-col, minmax(1.25rem, max-content))
            minmax(0, 1fr)
            var(--bookmark-shortcut-col, max-content));
    /* The grid gap plus the padding two neighbouring categories would each
       have contributed.
     *
     * A run of ordinary categories pads every column on both sides; a wide one
     * is a single box and pads only its outer edges, so its inner columns drift
     * left of the columns above and below by exactly that missing padding. Add
     * it to the gap and the tracks land where a category's content lands.
     *
     * Only the column gap: the row gap belongs to the density setting, whose
     * rule sits further down this file and would otherwise win both. */
    column-gap: calc(var(--dashboard-column-gap, 1.5rem) + 2 * var(--category-inline-pad, 0.5rem));
}

/* The shortcut track is the one intrinsic column in the pattern, and repeating
   the pattern sizes each copy to its own rows: odd rows land in the first
   column, even rows in the second, so the two ended up different widths and
   every column after the first drifted. It is measured across the whole list
   and pinned — see syncWideColumnTracks — which makes the repeats identical
   and the columns line up with the categories above and below. */
.dashboard-grid .category--wide .bookmarks-list[data-show-ping="true"] {
    grid-template-columns:
        repeat(var(--category-span, 1),
            var(--bookmark-lead-col, minmax(1.25rem, max-content))
            minmax(0, 1fr)
            var(--bookmark-status-col, 3.35em)
            var(--bookmark-shortcut-col, max-content));
}

.dashboard-grid .category--wide .bookmarks-list > * {
    grid-column: span 3;
}

.dashboard-grid .category--wide .bookmarks-list[data-show-ping="true"] > * {
    grid-column: span 4;
}

/* The "+ N more" toggle and the empty-category placeholders are not bookmarks:
   they belong under the whole list, not in the first column of it. */
.dashboard-grid .category--wide .bookmarks-list > .category-show-more,
.dashboard-grid .category--wide .bookmarks-list > .empty-state--category,
.dashboard-grid .category--wide .bookmarks-list > .smart-collection-empty {
    grid-column: 1 / -1;
}

/* Packed columns: vertical stacks per column (round-robin), less vertical gap than CSS grid rows.
 *
 * This is the layout packed mode has always had, and it is what runs while no
 * category is wider than one column. A category set wider switches the page to
 * the masonry grid further down — a block cannot reach across flex columns it
 * is sitting inside of.
 */
.dashboard-grid.packed-columns {
    /* The width of a single packed column, written once — the masonry tracks
       below are the same width, so the two shapes line up. */
    --dashboard-column-gap: var(--gap, 1.5rem);
    --packed-column-width: min(
        var(--dashboard-column-max, 300px),
        calc((100% - (var(--packed-columns, 3) - 1) * var(--dashboard-column-gap)) / var(--packed-columns, 3))
    );
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: flex-start;
    justify-content: center;
    column-gap: var(--dashboard-column-gap);
    grid-template-columns: unset;
    width: 100%;
}

.dashboard-grid.packed-columns .dashboard-column {
    display: flex;
    flex-direction: column;
    gap: var(--space-7, 3rem);
    flex: 0 0 var(--packed-column-width);
    width: var(--packed-column-width);
    max-width: var(--packed-column-width);
    min-width: 0;
    contain: layout style;
}

/* Packed, with something wider than one column in it.
 *
 * Fixed columns cannot carry a block that reaches across them, and breaking the
 * page into bands left a hole the height of the tallest column beside the wide
 * one. So the page becomes a grid of very short rows: every category spans as
 * many of them as it is tall (set from JS, which is the only place a height is
 * known), and dense flow fills what is left — a wide block takes its columns
 * wherever it fits and the rest carry on beside and beneath it.
 *
 * align-self is what keeps the measuring honest: stretched to its row span, a
 * category would report the height it was given rather than the height it has,
 * and every pass would confirm the previous one.
 */
.dashboard-grid.packed-columns.packed-masonry {
    display: grid;
    grid-template-columns: repeat(var(--packed-columns, 3), var(--packed-column-width));
    grid-auto-rows: 4px;
    grid-auto-flow: row dense;
    column-gap: var(--dashboard-column-gap);
    row-gap: 0;
    justify-content: center;
    align-items: start;
    width: 100%;
}

.dashboard-grid.packed-columns.packed-masonry > .category {
    grid-row: span var(--masonry-span, 1);
    grid-column: span var(--category-span, 1);
    align-self: start;
    min-width: 0;
}

.dashboard-grid.packed-columns .category {
    width: 100%;
    box-sizing: border-box;
}

/* Center column block when the viewport is wider than the configured column count needs */
@media (min-width: 768px) {
    .dashboard-grid:not(.packed-columns):not(.layout-launcher) {
        width: fit-content;
        max-width: min(100%, var(--dashboard-grid-max-width, 100%));
        margin-inline: auto;
        justify-content: center;
    }

    .layout-launcher.packed-columns {
        justify-content: flex-start;
    }
}

.dashboard-grid.layout-compact.packed-columns .dashboard-column {
    gap: var(--space-5, 1.5rem);
}

.dashboard-grid.layout-cards.packed-columns .dashboard-column {
    gap: var(--space-5, 1.5rem);
}

.dashboard-grid.layout-terminal.packed-columns .dashboard-column {
    gap: var(--space-6, 2rem);
}

/* Layout presets */
.dashboard-grid.layout-default {
    row-gap: var(--category-row-gap, 2rem);
    column-gap: var(--space-5, 1.5rem);
}

.dashboard-grid.layout-compact {
    row-gap: var(--space-5, 1.5rem);
    column-gap: var(--space-4, 1rem);
}

.dashboard-grid.layout-compact .category {
    gap: var(--space-3, 0.65rem);
}

.dashboard-grid.layout-compact .bookmarks-list {
    gap: var(--space-1, 0.25rem);
}

.dashboard-grid.layout-compact .bookmark-link {
    padding: var(--space-1, 0.25rem) var(--bookmark-row-end-pad, 3px) var(--space-1, 0.25rem) var(--bookmark-row-start-pad, 6px);
}

.dashboard-grid.density-comfortable .bookmark-link {
    min-height: 2.05rem;
    padding-block: var(--density-row-pad-comfortable, 0.5rem);
}

.dashboard-grid.density-compact .bookmark-link {
    min-height: 1.85rem;
    padding-block: var(--density-row-pad-compact, 0.35rem);
}

.dashboard-grid.density-dense .bookmark-link {
    min-height: 1.65rem;
    padding-block: var(--density-row-pad-dense, 0.2rem);
    gap: var(--density-row-inner-gap-dense, 0.45rem);
}

/* auto: grid class density-auto had no row rules → smart vs normal drifted; align with compact baseline */
.dashboard-grid.density-auto .bookmark-link {
    min-height: 1.85rem;
    padding-block: var(--density-row-pad-compact, 0.35rem);
}

/* Inter-row gap scales with density for every bookmarks list (smart + reorderable). */
.dashboard-grid.density-comfortable .bookmarks-list {
    gap: var(--density-row-list-gap-comfortable, 0.42rem);
}
.dashboard-grid.density-compact .bookmarks-list {
    gap: var(--density-row-list-gap-compact, 0.3rem);
}
.dashboard-grid.density-dense .bookmarks-list {
    gap: var(--density-row-list-gap-dense, 0.18rem);
}
.dashboard-grid.density-auto .bookmarks-list {
    gap: var(--density-row-list-gap-compact, 0.3rem);
}

.dashboard-grid.layout-cards {
    gap: var(--space-5, 1.5rem);
}

.dashboard-grid.layout-cards .category {
    background: color-mix(in srgb, var(--background-primary) 92%, var(--text-secondary) 8%);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-6, 10px);
    padding: var(--space-4, 1rem);
}

.dashboard-grid.layout-cards .bookmark-link {
    border: none;
    border-radius: var(--layout-radius-sm, 8px);
    background: transparent;
    padding: var(--cards-pad-block, 0.45rem) 0.55rem;
}

.dashboard-grid.layout-cards .bookmark-open {
    align-items: flex-start;
}

.dashboard-grid.layout-cards .bookmark-text {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    line-clamp: 2;
    white-space: normal;
}

.dashboard-grid.layout-terminal {
    row-gap: var(--space-6, 2rem);
    column-gap: var(--space-5, 1.5rem);
}

/* Note badge tooltip */
.bookmark-note-badge {
    position: relative;
    pointer-events: auto;
    cursor: default;
}

.bookmark-note-badge[data-note-tooltip]::after {
    content: attr(data-note-tooltip);
    position: absolute;
    bottom: calc(100% + 6px);
    right: 0;
    width: max-content;
    max-width: 20rem;
    padding: var(--space-1, 0.3rem) var(--space-2, 0.55rem);
    border: 1px solid var(--border-primary);
    background: var(--background-secondary);
    color: var(--text-primary);
    border-radius: var(--layout-radius-sm, 8px);
    font-size: var(--text-xs, 0.72rem);
    white-space: normal;
    word-break: break-word;
    text-align: left;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s ease;
    z-index: 30;
}

.bookmark-note-badge[data-note-tooltip]:hover::after {
    opacity: 1;
}

/* Inline editor textarea for note */
.bookmark-inline-textarea {
    width: 100%;
    min-height: 3.2rem;
    padding: var(--space-1-5, 0.4rem);
    font-size: var(--text-base, 0.9rem);
    resize: vertical;
    font-family: inherit;
    border: 1px solid var(--border-primary);
    border-radius: var(--layout-radius-sm, 8px);
    background: var(--background-primary);
    color: var(--text-primary);
}

/*
 * Your own note, which is the one line on the card nobody else could have
 * written — and which used to be the faintest thing on it, in the same grey as
 * the site's marketing description.
 */
.bookmark-preview-card-note {
    margin: var(--space-2, 0.6rem) var(--space-3, 0.8rem) 0;
    padding: var(--space-1-5, 0.38rem) var(--space-2, 0.55rem);
    border-left: 2px solid var(--accent-primary, var(--text-secondary));
    border-radius: 0 5px 5px 0;
    background: color-mix(in srgb, var(--accent-primary, var(--text-secondary)) 8%, transparent);
    color: var(--text-primary);
    font-size: calc(var(--font-size-controls) * 0.88);
    line-height: 1.4;
}

.bookmark-preview-card-note-label {
    display: block;
    margin-bottom: 3px;
    font-family: var(--font-family-mono, monospace);
    font-size: calc(var(--font-size-controls) * 0.66);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--accent-primary, var(--text-secondary));
}

.bookmark-preview-card-note-text {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    overflow: hidden;
}

/* Tag autocomplete dropdown (appended to body by tag-autocomplete.js) */
.tag-ac-dropdown {
    position: fixed;
    z-index: 9999;
    margin: 0;
    padding: 0;
    list-style: none;
    background: var(--background-secondary);
    border: 1px solid var(--border-primary);
    max-height: 14rem;
    overflow-y: auto;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.35);
}

.tag-ac-item {
    padding: var(--space-1-5, 0.35rem) var(--space-2, 0.6rem);
    font-family: inherit;
    font-size: var(--font-size-controls);
    color: var(--text-secondary);
    cursor: pointer;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.tag-ac-item strong {
    color: var(--text-primary);
    font-weight: var(--font-weight-semibold);
}

.tag-ac-item:hover,
.tag-ac-item-active {
    background: rgba(255, 255, 255, 0.06);
    color: var(--text-primary);
}

/* Preview card tags */
.bookmark-preview-card-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    padding: var(--space-2, 0.55rem) var(--space-3, 0.8rem) 0;
}

.dashboard-grid.layout-terminal .category-title-label::before {
    content: '$ ';
}

/* Terminal layout uses "$ " instead of "// "; hide the "//" prefix/handle. */
.dashboard-grid.layout-terminal .category-reorder-handle,
.dashboard-grid.layout-terminal .category-title-prefix {
    display: none;
}

.dashboard-grid.layout-terminal .bookmark-link {
    font-family: "Source Code Pro", monospace;
    letter-spacing: 0.01em;
    gap: var(--space-1-5, 0.35rem);
    padding-left: var(--space-4, 1rem);
    white-space: nowrap;
    overflow: hidden;
}

.dashboard-grid.layout-terminal .bookmark-link::before {
    content: '> ';
    opacity: 0.65;
    margin-right: 0;
    position: absolute;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    line-height: 1;
    pointer-events: none;
}

.dashboard-grid.layout-terminal .bookmark-open {
    min-width: 0;
    overflow: hidden;
}

.dashboard-grid.layout-terminal .bookmark-text {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.dashboard-grid.layout-terminal .bookmark-shortcut {
    width: auto;
}

/* New layout presets: masonry, list, widgets */
.dashboard-grid.layout-masonry {
    gap: var(--space-4, 1rem);
}
.dashboard-grid.layout-masonry .category {
    /* allow variable-height cards and denser packing */
    gap: var(--space-3, 0.75rem);
}
.dashboard-grid.layout-masonry .bookmark-open {
    align-items: stretch;
}

.dashboard-grid.layout-list {
    gap: var(--space-2, 0.6rem);
}
.dashboard-grid.layout-list .category {
    background: transparent;
    border: none;
    padding: 0;
}
.dashboard-grid.layout-list .bookmarks-list {
    display: block;
}
.dashboard-grid.layout-list .bookmark-link {
    grid-template-columns: var(--bookmark-lead-col) minmax(0, 1fr) max-content;
    padding: var(--space-2, 0.6rem) var(--space-1-5, 0.4rem);
    border-bottom: 1px solid var(--border-primary);
}
.dashboard-grid.layout-list .bookmark-text {
    white-space: normal;
}

.dashboard-grid.layout-widgets {
    gap: var(--space-5, 1.5rem);
}
.dashboard-grid.layout-widgets .category {
    background: color-mix(in srgb, var(--background-primary) 94%, var(--text-secondary) 6%);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-7, 12px);
    padding: var(--space-4, 1rem);
}
.dashboard-grid.layout-widgets .bookmark-link {
    padding: var(--space-2, 0.6rem);
    border: none;
    border-radius: var(--layout-radius-sm, 8px);
    background: transparent;
}

/* Preset-driven CSS variables applied via body attributes */
.dashboard-grid {
    gap: var(--gap, 1.5rem);
    font-size: var(--dashboard-font-size, 14px);
}
.dashboard-grid .bookmark-link {
    padding-block: var(--card-padding, 0.5rem);
}
.dashboard-grid .bookmark-text {
    font-size: var(--bookmark-font-size, 1rem);
}

/* Layout presets: set default variable values */
body[data-layout-preset="default"] .dashboard-grid {
    --gap: var(--space-7, 3rem);
    --card-padding: var(--space-4, 0.9rem);
    --dashboard-font-size: 14px;
    --bookmark-font-size: var(--text-md, 1rem);
}
body[data-layout-preset="compact"] .dashboard-grid {
    --gap: var(--space-5, 1.5rem);
    --card-padding: var(--space-2, 0.45rem);
    --dashboard-font-size: 13px;
    --bookmark-font-size: var(--text-md, 0.98rem);
}
body[data-layout-preset="cards"] .dashboard-grid {
    --gap: var(--space-5, 1.5rem);
    --card-padding: var(--space-4, 0.9rem);
    --dashboard-font-size: 14px;
}
body[data-layout-preset="terminal"] .dashboard-grid {
    --gap: var(--space-6, 2rem);
    --card-padding: var(--space-1-5, 0.35rem);
    --dashboard-font-size: 13px;
}
body[data-layout-preset="masonry"] .dashboard-grid {
    --gap: var(--space-4, 1rem);
    --card-padding: var(--space-2, 0.6rem);
    --dashboard-font-size: 14px;
}
body[data-layout-preset="list"] .dashboard-grid {
    --gap: var(--space-2, 0.6rem);
    --card-padding: var(--space-2, 0.6rem);
    --dashboard-font-size: 14px;
}
body[data-layout-preset="widgets"] .dashboard-grid {
    --gap: var(--space-5, 1.5rem);
    --card-padding: var(--space-4, 1rem);
    --dashboard-font-size: 15px;
}
body[data-layout-preset="launcher"] .dashboard-grid {
    --gap: var(--space-1-5, 0.4rem);
    --card-padding: var(--space-2, 0.5rem);
    --dashboard-font-size: 14px;
}

/* ─── Launcher layout preset ─────────────────────────────────── */
.dashboard-grid.layout-launcher,
.dashboard-grid.layout-launcher.packed-columns {
    display: flex;
    flex-direction: column;
    gap: var(--space-6, 2rem);
}

/* With packed-columns: dissolve .dashboard-column wrappers so     */
/* category-sections participate directly in the launcher flex     */
.layout-launcher.packed-columns .dashboard-column {
    display: contents;
}

.layout-launcher .category {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-1, 0.2rem);
    padding: 0;
}

.layout-launcher .category-title {
    text-align: left;
    white-space: nowrap;
    padding: 0;
    cursor: default;
}

.layout-launcher .bookmarks-list {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    gap: var(--space-2, 0.6rem);
    width: 100%;
}

.layout-launcher .bookmark-link {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    width: 88px;
    padding: var(--space-3, 0.65rem) var(--space-2, 0.5rem) var(--space-2, 0.5rem);
    border-radius: var(--radius-6, 10px);
    gap: var(--space-1-5, 0.4rem);
    cursor: pointer;
    transition: background 0.12s ease, transform 0.1s ease;
    /* prevent subgrid/grid-row from base styles bleeding in */
    grid-template-columns: none;
    min-height: 0;
    white-space: normal;
}
.layout-launcher .bookmark-link:hover {
    transform: translateY(-2px);
}

/* Stretched-link: transparent ::before overlay covers the whole tile so
   clicking the icon or anywhere on the tile triggers the <a> */
.layout-launcher .bookmark-open::before {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: var(--radius-6, 10px);
    z-index: 1;
}

.layout-launcher .bookmark-reorder-handle,
.layout-launcher .bookmark-shortcut,
.layout-launcher .bookmark-note-badge,
.layout-launcher .bookmark-pin-badge,
.layout-launcher .bookmark-open-count { display: none !important; }

.layout-launcher .bookmark-lead {
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 48px;
    width: 48px;
    flex-shrink: 0;
}
.layout-launcher .bookmark-icon-slot {
    display: flex;
    width: 48px;
    height: 48px;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.layout-launcher .bookmark-icon {
    width: 48px;
    height: 48px;
    object-fit: contain;
}

.layout-launcher .bookmark-open {
    display: flex;
    justify-content: center;
    width: 100%;
    overflow: visible;
    min-width: 0;
    flex: 0 0 auto;
}
.layout-launcher .bookmark-text {
    font-size: var(--text-xs, 0.7rem);
    text-align: center;
    white-space: normal;
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    line-height: 1.3;
}

/* ─── Launcher icon sizes ─────────────────────────────────────── */
body[data-launcher-icon-size="small"] .layout-launcher .bookmark-link { width: 68px; }
body[data-launcher-icon-size="small"] .layout-launcher .bookmark-lead { min-height: 32px; width: 32px; }
body[data-launcher-icon-size="small"] .layout-launcher .bookmark-icon-slot { width: 32px; height: 32px; }
body[data-launcher-icon-size="small"] .layout-launcher .bookmark-icon { width: 32px; height: 32px; }

body[data-launcher-icon-size="large"] .layout-launcher .bookmark-link { width: 108px; }
body[data-launcher-icon-size="large"] .layout-launcher .bookmark-lead { min-height: 64px; width: 64px; }
body[data-launcher-icon-size="large"] .layout-launcher .bookmark-icon-slot { width: 64px; height: 64px; }
body[data-launcher-icon-size="large"] .layout-launcher .bookmark-icon { width: 64px; height: 64px; }

/* ─── Launcher dim (search filter) ───────────────────────────── */
.layout-launcher .bookmark-link.launcher-dim {
    opacity: 0.15;
    filter: grayscale(1);
    transition: opacity 0.18s ease, filter 0.18s ease;
    pointer-events: none;
}
.layout-launcher .bookmark-link:not(.launcher-dim) {
    transition: opacity 0.18s ease, filter 0.18s ease;
}

/* ─── Corner FAB layout tokens ───────────────────────────────────────────── */
:root {
    --corner-fab-edge: 1.5rem;
    --corner-fab-height: 2.75rem;
    --corner-fab-gap: var(--space-1-5, 0.4rem);
}

@media (max-width: 768px), (hover: none) and (pointer: coarse) {
    :root {
        --corner-fab-height: 3.35rem;
    }
}

/* ─── What's new FAB ──────────────────────────────────────────────────────── */
.whats-new-btn {
    position: fixed;
    bottom: var(--corner-fab-edge);
    left: var(--corner-fab-edge);
    z-index: 1001;
    padding: var(--space-2, 0.62rem) var(--space-3, 0.72rem);
    border: 1px solid var(--border-primary);
    /* Opaque, and in both layout versions: this FAB floats over the grid with
       nothing behind it either way -- modern styles the dock and the / FAB but
       never this one -- so the page used to read straight through it. The wash
       is the same 74%, now composited over the page background instead of over
       transparent, which is the colour it already showed over empty page. */
    background: color-mix(in srgb, var(--background-secondary) 74%, var(--background-primary));
    color: var(--accent-primary);
    font-size: var(--font-size-controls);
    font-family: var(--font-family-main);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-6, 0.65rem);
    transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
    -webkit-tap-highlight-color: transparent;
    user-select: none;
}
.whats-new-btn-icon {
    display: block;
    flex-shrink: 0;
}
.whats-new-btn-update-dot {
    position: absolute;
    top: 0.35rem;
    right: 0.35rem;
    width: 0.45rem;
    height: 0.45rem;
    border-radius: 50%;
    background: var(--accent-primary);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--background-secondary) 80%, transparent);
    pointer-events: none;
    opacity: 0;
    transform: scale(0);
    transition: opacity 0.2s ease, transform 0.2s ease;
}
.whats-new-btn.has-update-available .whats-new-btn-update-dot {
    opacity: 1;
    transform: scale(1);
    animation: whats-new-update-pulse 1.8s ease-in-out infinite;
}
@keyframes whats-new-update-pulse {
    0%, 100% {
        transform: scale(1);
        box-shadow:
            0 0 0 2px color-mix(in srgb, var(--background-secondary) 80%, transparent),
            0 0 0 0 color-mix(in srgb, var(--accent-primary) 0%, transparent);
    }
    50% {
        transform: scale(1.2);
        box-shadow:
            0 0 0 2px color-mix(in srgb, var(--background-secondary) 80%, transparent),
            0 0 0 5px color-mix(in srgb, var(--accent-primary) 38%, transparent);
    }
}
body.no-animations .whats-new-btn-update-dot {
    animation: none;
    transform: scale(1);
}
.whats-new-btn:hover {
    /* 94% of the page background over the page background is the page
       background: same colour as before, minus the window. */
    background: var(--background-primary);
    border-color: var(--accent-primary);
    transform: translateY(-1px);
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.16);
}
.whats-new-btn:active {
    transform: translateY(0);
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
}
/* Tooltip */
.whats-new-btn[data-tooltip] {
    position: fixed;
}
.whats-new-btn[data-tooltip]::after {
    content: attr(data-tooltip);
    position: absolute;
    bottom: calc(100% + 6px);
    left: 0;
    white-space: nowrap;
    padding: var(--space-1, 0.2rem) var(--space-2, 0.5rem);
    background: var(--background-secondary);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-3, 0.3rem);
    color: var(--text-secondary);
    font-family: var(--font-family-main);
    font-size: calc(var(--font-size-controls) * 0.85);
    font-weight: var(--font-weight-normal);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
    pointer-events: none;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.15s ease, visibility 0.15s ease;
    z-index: 1001;
}
.whats-new-btn[data-tooltip]:hover::after {
    opacity: 1;
    visibility: visible;
}
@media (pointer: coarse) {
    .whats-new-btn[data-tooltip]::after {
        display: none;
    }
}
/* Linker-dock: what's new naar rechts (tegenover de knoppenbalk) */
body[data-button-position="bottom-left"] .whats-new-btn {
    left: auto;
    right: var(--corner-fab-edge);
}
body[data-button-position="bottom-left"] .whats-new-btn[data-tooltip]::after {
    left: auto;
    right: 0;
}

/* Rechter-dock: what's new expliciet links (tegenover de knoppenbalk) */
body[data-button-position="bottom-right"] .whats-new-btn {
    left: var(--corner-fab-edge);
    right: auto;
}

/* Density overrides (can be combined with layout presets) */
body[data-density-mode="comfortable"] .dashboard-grid {
    --card-padding: 1.2rem;
    --dashboard-font-size: 16px;
    --cards-pad-block: 0.6rem;
}
body[data-density-mode="compact"] .dashboard-grid {
    --card-padding: var(--space-3, 0.8rem);
    --dashboard-font-size: 14px;
    --cards-pad-block: 0.45rem;
}
body[data-density-mode="dense"] .dashboard-grid {
    --card-padding: var(--space-2, 0.45rem);
    --dashboard-font-size: 13px;
    --cards-pad-block: 0.3rem;
}
/* 'auto' leaves variables to layout + responsive CSS to decide */

/* Vertical space between category rows.
 *
 * Distinct from density above, which sizes the bookmark rows inside a category:
 * this is the gap between the rows of categories themselves, which is what
 * makes a wide page feel empty. The old fixed 3rem is still available as
 * "airy"; the default drops to 2rem, because on a five-column layout 3rem left
 * a visible band of nothing between every row.
 *
 * The variable is read by .dashboard-grid.layout-default and the auto-fit grid;
 * the packed and compact presets keep their own tighter gaps. */
body[data-category-spacing="snug"] {
    --category-row-gap: 1.25rem;
}
body[data-category-spacing="balanced"] {
    --category-row-gap: var(--space-6, 2rem);
}
body[data-category-spacing="airy"] {
    --category-row-gap: var(--space-7, 3rem);
}


/* Keep terminal rows single-line in dense layouts (e.g. 6 columns). */
.dashboard-grid.layout-terminal .status-text {
    display: none !important;
}

/* Category */
.category {
    display: flex;
    flex-direction: column;
    gap: var(--space-4, 1rem);
    min-width: 0; /* Allow flex items to shrink below content size */
    /* Named, because a wide category has to add it back between its inner
       columns — see the column-gap on its bookmarks list. */
    --category-inline-pad: var(--space-2, 0.5rem);
    padding-left: var(--category-inline-pad);
    padding-right: var(--category-inline-pad);
}

.category.animate-enter {
    opacity: 0;
    transform: translateY(8px);
    animation: categoryEnter 140ms ease-out forwards;
    animation-delay: calc(80ms + var(--stagger-index, 0) * 6ms);
}

@keyframes categoryEnter {
    from {
        opacity: 0;
        transform: translateY(8px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Centred rather than baseline-aligned, which is what keeps the controls in the
   header from making it taller. On a baseline the "+" sits by its own text
   baseline, and the padding and border below that baseline hang past the line
   the title occupies — 1.4px of extra header, per header, for a button that is
   shorter than the text beside it. Centring aligns boxes instead, so the header
   is simply as tall as its tallest child and a control that fits costs nothing.
   Titles that wrap keep flex-start (see .category-title--multiline): chips
   belong beside the first line there, not centred across both. */
.category-title {
    font-size: var(--font-size-category);
    font-weight: var(--font-weight-bold);
    letter-spacing: 0.05em;
    color: var(--text-tertiary);
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    min-width: 0;
}

.category-title-label {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
}

/* The "//" prefix is now a real element that doubles as the drag-reorder handle. */
.category-reorder-handle {
    cursor: grab;
    touch-action: none;
    user-select: none;
    -webkit-user-select: none;
    white-space: pre;
    opacity: 0.55;
    transition: opacity 0.15s ease;
}

.category-title:hover .category-reorder-handle {
    opacity: 0.85;
}

.category-reorder-handle:hover {
    opacity: 1;
}

.category-reorder-handle:active {
    cursor: grabbing;
}

/* Category being dragged across the grid. */
#dashboard-layout .category.is-draggable {
    opacity: 0.4;
    transform: scale(0.985);
    transition: opacity 0.1s, transform 0.1s;
}

.category-title-trailing {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: baseline;
    flex-shrink: 0;
    white-space: nowrap;
}

.category-title-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.category-title-name--multiline {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: normal;
    word-break: break-word;
}

.category-title--multiline {
    align-items: flex-start;
}

.category-chevron {
    flex-shrink: 0;
}

/* One button plus a ⋯, not the full set: four sort buttons on every category
   header — and the header repeats per category — took more width than the
   bookmark names beside them. In manual order there is no sort to report, so
   only the ⋯ is left. */
.category-sort-controls {
    display: inline-flex;
    align-items: center;
    gap: 0.2em;
    margin-left: 0.35em;
    flex-shrink: 0;
    opacity: 1;
    pointer-events: auto;
    position: relative;
}

.category-sort-menu-btn {
    font: inherit;
    font-size: 0.68em;
    line-height: 1;
    min-height: 1.45em;
    padding: 0.15em 0.4em;
    border: 1px solid transparent;
    border-radius: var(--radius-2, 3px);
    background: none;
    color: var(--text-tertiary);
    cursor: pointer;
    opacity: 0.7;
}

.category-sort-menu-btn:hover,
.category-sort-menu-btn:focus-visible,
.category-sort-menu-btn[aria-expanded="true"] {
    opacity: 1;
    color: var(--text-secondary);
    border-color: color-mix(in srgb, var(--text-tertiary) 28%, transparent);
    background: color-mix(in srgb, var(--background-secondary, transparent) 55%, transparent);
}

.category-sort-menu {
    position: absolute;
    top: calc(100% + 0.25em);
    right: 0;
    z-index: 60;
    display: flex;
    flex-direction: column;
    min-width: 7em;
    padding: 0.2em;
    border: 1px solid var(--border-primary);
    border-radius: var(--layout-radius-sm, 6px);
    background: var(--background-primary);
    box-shadow: 0 6px 18px color-mix(in srgb, var(--text-primary) 14%, transparent);
}

.category-sort-menu-item {
    font: inherit;
    font-size: 0.68em;
    text-align: left;
    padding: 0.3em 0.5em;
    border: 0;
    border-radius: var(--radius-2, 3px);
    background: none;
    color: var(--text-secondary);
    cursor: pointer;
    white-space: nowrap;
}

.category-sort-menu-item:hover,
.category-sort-menu-item:focus-visible {
    background: color-mix(in srgb, var(--text-primary) 8%, transparent);
    color: var(--text-primary);
}

.category-sort-menu-item.is-active {
    color: var(--text-primary);
    font-weight: var(--font-weight-bold);
}

.category-sort-menu-item.is-active::before {
    content: '· ';
}

.category-sort-btn {
    font: inherit;
    font-size: 0.68em;
    font-weight: var(--font-weight-semibold);
    letter-spacing: 0.02em;
    line-height: 1.2;
    min-height: 1.45em;
    padding: 0.15em 0.42em;
    border: 1px solid color-mix(in srgb, var(--text-tertiary) 28%, transparent);
    border-radius: var(--radius-2, 3px);
    background: color-mix(in srgb, var(--background-secondary, transparent) 55%, transparent);
    color: var(--text-tertiary);
    cursor: pointer;
    opacity: 0.9;
}

.category-sort-btn:hover {
    opacity: 1;
    color: var(--text-secondary);
    border-color: color-mix(in srgb, var(--text-secondary) 35%, transparent);
    background: color-mix(in srgb, var(--hover-bg, currentColor) 35%, transparent);
}

.category-sort-btn.is-active,
.category-sort-controls[data-sort-mode="az"] .category-sort-btn[data-sort-mode="az"],
.category-sort-controls[data-sort-mode="recent"] .category-sort-btn[data-sort-mode="recent"] {
    opacity: 1;
    color: var(--text-primary);
    font-weight: var(--font-weight-bold);
    border-color: color-mix(in srgb, var(--accent-color, currentColor) 65%, transparent);
    background: color-mix(in srgb, var(--accent-color, currentColor) 20%, var(--background-secondary, transparent));
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent-color, currentColor) 28%, transparent);
    text-decoration: none;
}

.category-sort-controls[data-sort-mode="order"] .category-sort-btn.is-active {
    opacity: 0.9;
    font-weight: var(--font-weight-semibold);
    color: var(--text-tertiary);
    border-color: color-mix(in srgb, var(--text-tertiary) 28%, transparent);
    background: color-mix(in srgb, var(--background-secondary, transparent) 55%, transparent);
    box-shadow: none;
}

.category-sort-btn + .category-sort-btn::before {
    content: none;
}

.bookmarks-list.bookmarks-list--sort-active .bookmark-reorder-handle {
    display: none;
}

/* Manual drag is off while A–Z / Recent sorting owns the order. Signal it: no grab
   affordance, and a not-allowed cursor on a press-drag. A plain click still opens
   the bookmark (the link keeps its own pointer cursor). */
.bookmarks-list.bookmarks-list--sort-active .bookmark-link.reorder-item:active {
    cursor: not-allowed;
}

/* ─── Category collapse — grid-template-rows transition ──────── */
.category-body {
    display: grid;
    grid-template-rows: 1fr;
    transition: grid-template-rows 260ms ease;
}

.category[data-collapsed="true"] .category-body {
    grid-template-rows: 0fr;
}

/* A widget's body collapses on the same terms: it sits in the same
   .category-body wrapper, so it needs the same two properties for the grid row
   to shrink past its content. */
.category-body > .bookmarks-list,
.category-body > .dashboard-widget-body {
    min-height: 0;          /* required: lets the grid row shrink below content height */
    overflow: hidden;
    opacity: 1;
    transition: opacity 200ms ease;
}

.category:not([data-collapsed="true"]) .category-body > .bookmarks-list {
    overflow-x: visible;
}

.category[data-collapsed="true"] .category-body > .bookmarks-list,
.category[data-collapsed="true"] .category-body > .dashboard-widget-body {
    opacity: 0;
}

/* The height a widget body reserves is there so the packed layout does not
   shift when the figures arrive — a question that does not exist while the
   block is collapsed, and 3.5rem of reserved space is exactly what would keep a
   collapsed widget from closing. Scoped tightly enough to beat the rule that
   sets it, which is later in the file and equally specific. */
.dashboard-widget[data-collapsed="true"] .category-body > .dashboard-widget-body {
    min-height: 0;
    padding-top: 0;
    padding-bottom: 0;
    border-width: 0;
}

body.no-animations .category-body {
    transition: none !important;
}
body.no-animations .category-body > .bookmarks-list,
body.no-animations .category-body > .dashboard-widget-body {
    transition: none !important;
}

.category-title {
    cursor: pointer;
    user-select: none;
}

.category-title--renaming .category-title-label::before {
    content: none;
}

.category-chevron {
    display: inline-block;
    margin-left: 0.4em;
    font-size: 0.7em;
    opacity: 0.35;
    transition: transform 200ms ease, opacity 150ms ease;
    transform: rotate(0deg);
    vertical-align: 0.05em;
    pointer-events: none;
}

.category-chevron::after {
    content: '▾';
}

.category-title:hover {
    background: color-mix(in srgb, var(--hover-bg) 65%, transparent);
    border-radius: var(--layout-radius-sm, 8px);
    padding-left: var(--space-1, 0.25rem);
    margin-left: -0.25rem;
}

.category-title:hover .category-chevron {
    opacity: 0.7;
}

.category[data-collapsed="true"] .category-chevron {
    transform: rotate(-90deg);
}

body.no-animations .category-chevron {
    transition: none;
}

.smart-collection-title {
    color: var(--accent-primary);
}

.smart-collection-title .category-chevron {
    color: var(--accent-primary);
}

.smart-collection-why-btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-left: 0.35em;
    padding: 0;
    width: 1.15em;
    height: 1.15em;
    border: 1px solid color-mix(in srgb, var(--accent-primary) 45%, var(--border-primary));
    background: color-mix(in srgb, var(--accent-primary) 10%, var(--background-primary));
    color: var(--accent-primary);
    font-size: 0.62em;
    font-weight: var(--font-weight-bold);
    font-family: var(--font-family-main);
    line-height: 1;
    cursor: help;
    vertical-align: 0.08em;
    opacity: 0.82;
    pointer-events: auto;
    flex-shrink: 0;
}

.smart-collection-why-btn:hover,
.smart-collection-why-btn:focus-visible {
    opacity: 1;
    background: color-mix(in srgb, var(--accent-primary) 16%, var(--background-primary));
    outline: none;
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-primary) 22%, transparent);
}

.smart-collection-why-popover {
    position: fixed;
    z-index: 200000;
    margin: 0;
    padding: var(--space-2, 0.5rem) var(--space-3, 0.65rem);
    max-width: min(22rem, calc(100vw - 1rem));
    border-radius: var(--radius-4, 6px);
    border: 1px solid color-mix(in srgb, var(--accent-primary) 40%, var(--border-primary));
    background: var(--background-secondary, #141414);
    color: var(--text-secondary);
    font-family: var(--font-family-main);
    font-size: var(--font-size-controls);
    font-weight: var(--font-weight-normal);
    line-height: 1.45;
    letter-spacing: normal;
    text-transform: none;
    box-shadow:
        0 10px 28px rgba(0, 0, 0, 0.55),
        0 0 0 1px rgba(0, 0, 0, 0.35);
    pointer-events: none;
}

.smart-collection-why-popover[hidden] {
    display: none !important;
}

.smart-collection-why-btn[data-tooltip]::after {
    content: none !important;
    display: none !important;
}

.smart-collection-empty {
    display: flex;
    align-items: center;
    gap: var(--space-2, 0.45rem);
    margin: var(--space-1-5, 0.35rem) 0 var(--space-hairline, 0.15rem);
    padding: var(--space-2, 0.55rem) var(--space-3, 0.75rem);
    font-size: var(--font-size-controls);
    font-style: normal;
    font-weight: var(--font-weight-semibold);
    line-height: 1.45;
    color: var(--text-secondary);
    opacity: 1;
    border: 1px dashed color-mix(in srgb, var(--border-primary) 80%, transparent);
    border-radius: var(--layout-radius-sm, 8px);
    background: color-mix(in srgb, var(--background-secondary) 50%, transparent);
}

.smart-collection-empty::before {
    content: '○';
    flex-shrink: 0;
    font-size: 0.72em;
    font-weight: var(--font-weight-bold);
    color: var(--text-tertiary);
    opacity: 0.85;
}

.category-rename-input {
    background: transparent;
    border: 1px solid var(--border-primary);
    border-radius: var(--layout-radius-sm, 8px);
    color: inherit;
    font: inherit;
    font-size: inherit;
    font-weight: inherit;
    letter-spacing: inherit;
    padding: 0 0.25em;
    margin: 0;
    width: 12ch;
    min-width: 4ch;
    max-width: 28ch;
    outline: none;
    opacity: 1;
}

.category-rename-input:focus {
    border-color: var(--accent-primary);
}

/* Bookmarks */
.bookmarks-list {
    display: grid;
    grid-template-columns: var(--bookmark-lead-col, minmax(1.25rem, max-content)) minmax(0, 1fr) max-content;
    row-gap: var(--space-2, 0.5rem);
    min-width: 0;
    min-height: 2rem;
    width: 100%;
    --bookmark-row-start-pad: 6px;
    --bookmark-row-end-pad: 3px;
    --bookmark-status-col: 3.35em;
}

.bookmarks-list[data-show-ping="true"] {
    grid-template-columns:
        var(--bookmark-lead-col, minmax(1.25rem, max-content))
        minmax(0, 1fr)
        var(--bookmark-status-col, 3.35em)
        max-content;
}

.bookmarks-list > * {
    grid-column: 1 / -1;
}

body.bookmark-dragging .bookmarks-list.reorder-container:not([data-smart-collection="true"]):not(:has(.bookmark-link)) {
    min-height: 3rem;
    position: relative;
}

body.bookmark-dragging .bookmarks-list.reorder-container:not([data-smart-collection="true"]):not(:has(.bookmark-link))::after {
    content: '';
    position: absolute;
    inset: 0;
    border: 1px dashed var(--text-secondary);
    border-radius: var(--layout-radius-sm, 8px);
    opacity: 0.45;
    pointer-events: none;
    transform-origin: top center;
    animation: placeholderIn 220ms cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
}

/* Subtle highlight on all reorder containers while dragging */
body.bookmark-dragging .bookmarks-list.reorder-container:not([data-smart-collection="true"]) {
    border-radius: var(--layout-radius-sm, 8px);
    outline: 1px dashed color-mix(in srgb, var(--text-secondary) 35%, transparent);
    outline-offset: 4px;
}

.bookmark-link {
    --bookmark-lead-col: minmax(1.25rem, max-content);
    display: grid;
    grid-template-columns: subgrid;
    grid-column: 1 / -1;
    grid-auto-rows: auto;
    align-items: center;
    column-gap: var(--space-2, 0.6rem);
    text-decoration: none;
    font-size: var(--font-size-bookmark);
    font-weight: var(--font-weight-semibold);
    transition: transform 140ms ease, opacity 120ms ease, color 140ms ease, background 140ms ease;
    position: relative;
    padding: var(--space-2, 0.5rem) var(--bookmark-row-end-pad, 3px) var(--space-2, 0.5rem) var(--bookmark-row-start-pad, 6px);
    color: var(--text-primary);
    max-width: 100%;
    min-height: 1.9rem;
}

.bookmark-link.animate-enter {
    opacity: 0;
    transform: translateY(6px);
    animation: bookmarkEnter 130ms ease-out forwards;
    animation-delay: calc(var(--item-index, 0) * 10ms);
}

/* Safety: status updates should never re-hide rows, even if animate-enter lingers. */
.bookmark-link.animate-enter.status-online,
.bookmark-link.animate-enter.status-offline,
.bookmark-link.animate-enter.status-checking {
    animation: none;
    opacity: 1;
    transform: none;
}

@keyframes bookmarkEnter {
    from {
        opacity: 0;
        transform: translateY(6px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Single-row bookmark chrome (cards/list use multiline title; see overrides below) */
.dashboard-grid:not(.layout-cards):not(.layout-list) .bookmark-link:not(.bookmark-inline-editing) {
    white-space: nowrap;
}

/* Keep all row controls on one grid row (ping/shortcut/pin/edit) */
.bookmark-link:not(.bookmark-inline-editing) > * {
    grid-row: 1;
}

a.bookmark-open {
    grid-column: 2;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: var(--space-1, 0.3rem);
    color: inherit;
    text-decoration: none;
    overflow: hidden;
    outline: none;
}

a.bookmark-open:hover {
    color: inherit;
}

.bookmark-lead {
    position: relative;
    grid-column: 1;
    display: inline-flex;
    align-items: center;
    justify-content: flex-start;
    gap: var(--space-1-5, 0.35rem);
    min-width: 1.15em;
    min-height: 1.15em;
    cursor: grab;
}

.bookmarks-list[data-smart-collection="true"] .bookmark-lead {
    cursor: default;
}

.bookmark-reorder-handle {
    position: absolute;
    inset: 0;
    border-radius: var(--radius-2, 3px);
    cursor: grab;
    touch-action: none;
    background: transparent;
    opacity: 0;
    box-sizing: border-box;
}

.bookmark-link:hover .bookmark-reorder-handle {
    opacity: 0.55;
}

.bookmark-reorder-handle:active {
    cursor: grabbing;
}

.bookmarks-list[data-smart-collection="true"] .bookmark-reorder-handle {
    display: none;
}

.bookmark-link.bookmark-longpress-armed {
    user-select: none;
}

.bookmark-link.bookmark-longpress-armed .bookmark-open {
    user-select: none;
}

.category-title.category-title-longpress-armed {
    user-select: none;
}

/* Inline badge — shared base for pin, note, status */
.bookmark-superscript-badge {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    gap: 0.15em;
    font-size: var(--text-2xs, 0.6rem);
    font-weight: var(--font-weight-semibold);
    line-height: 1;
    padding: 0.18em 0.38em;
    border-radius: var(--radius-2, 3px);
    background: var(--background-secondary);
    color: var(--text-tertiary);
    border: 1px solid var(--border-secondary);
    white-space: nowrap;
    pointer-events: none;
    user-select: none;
    transition: opacity 120ms ease;
}

.bookmark-pin-badge svg {
    width: 0.7rem;
    height: 0.7rem;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
    display: inline-block;
}

.bookmark-pin-badge.is-empty {
    display: none;
}

.bookmark-note-badge svg {
    width: 0.7rem;
    height: 0.7rem;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
    display: inline-block;
}

.bookmark-note-badge.is-empty {
    display: none;
}

.bookmark-open-count {
    display: none;
}

/* "3 new" — what this page published since the row was last opened. It borrows
   the superscript badge's shape and adds only a hint of the accent: it is worth
   noticing on a scan and never worth reading first. */
.bookmark-fresh-badge {
    margin-left: 0.3em;
    color: var(--accent-primary);
    border-color: color-mix(in srgb, var(--accent-primary) 35%, var(--border-secondary));
    background: color-mix(in srgb, var(--accent-primary) 8%, var(--background-secondary));
}

.bookmark-fresh-badge.is-empty {
    display: none;
}

.bookmark-tags {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1, 0.2rem);
    margin-left: var(--space-1-5, 0.35rem);
}

.bookmark-tag-chip {
    font-size: var(--text-2xs, 0.68rem);
    line-height: 1;
    padding: 0.15em 0.45em;
    border-radius: var(--radius-pill, 999px);
    background: var(--background-secondary);
    color: var(--text-tertiary);
    border: 1px solid var(--border-secondary);
    white-space: nowrap;
    pointer-events: none;
    user-select: none;
}

.bookmark-shortcut {
    grid-column: 3;
    text-align: right;
    justify-self: end;
    white-space: nowrap;
}

.bookmarks-list[data-show-ping="true"] .bookmark-link > .status-text {
    grid-column: 3;
    grid-row: 1;
    justify-self: end;
    align-self: center;
}

.bookmarks-list[data-show-ping="true"] .bookmark-link > .bookmark-shortcut {
    grid-column: 4;
}

.bookmark-shortcut.is-empty {
    display: none;
}


.bookmark-icon-slot {
    width: 1.15em;
    height: 1.15em;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

.empty-state--category {
    display: flex;
    align-items: center;
    gap: var(--space-2, 0.5rem);
    padding: var(--space-1-5, 0.35rem) var(--space-2, 0.5rem);
    color: var(--text-tertiary);
    font-size: var(--font-size-controls);
}

.empty-state--category-btn {
    background: color-mix(in srgb, var(--accent-primary) 10%, transparent);
    border: 1px solid color-mix(in srgb, var(--accent-primary) 40%, var(--border-primary));
    border-radius: var(--layout-radius-sm, 8px);
    padding: var(--space-hairline, 0.15rem) var(--space-2, 0.45rem);
    font-size: var(--font-size-controls);
    color: var(--accent-primary);
    cursor: pointer;
    font-family: inherit;
    transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.empty-state--category-btn:hover {
    background: color-mix(in srgb, var(--accent-primary) 18%, transparent);
    border-color: var(--accent-primary);
    color: var(--accent-primary);
}

/* Per-category item cap: rows past the limit are hidden until "show more". */
.bookmark-link.is-overflow-hidden {
    display: none;
}

/* "+ N more" / "show less" toggle — a quiet, secondary control so it doesn't
   compete with the primary "+ bookmark" action. */
.category-show-more {
    display: inline-flex;
    align-items: center;
    margin-top: var(--space-hairline, 0.15rem);
    padding: var(--space-hairline, 0.1rem) var(--space-1-5, 0.35rem);
    background: transparent;
    border: none;
    border-radius: var(--layout-radius-sm, 8px);
    font-family: inherit;
    font-size: calc(var(--font-size-controls) * 0.92);
    color: var(--text-tertiary);
    cursor: pointer;
    transition: color 0.15s ease, background 0.15s ease;
}

.category-show-more:hover,
.category-show-more:focus-visible,
.category-show-more.keyboard-selected {
    color: var(--accent-primary);
    background: color-mix(in srgb, var(--accent-primary) 10%, transparent);
    outline: none;
}

/* Keyboard selection needs to read as "selected" even without :focus-visible,
   which browsers withhold after a click. */
.category-show-more.keyboard-selected {
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-primary) 45%, transparent);
}

/* Terminal layout: monospace style with > prefix */
.dashboard-grid.layout-terminal .empty-state--category {
    padding-left: var(--space-4, 1rem);
    position: relative;
}

.dashboard-grid.layout-terminal .empty-state--category-btn {
    font-family: "Source Code Pro", monospace;
    letter-spacing: 0.01em;
    background: none;
    border-color: color-mix(in srgb, var(--accent-primary) 45%, transparent);
    font-size: inherit;
}

.dashboard-grid.layout-terminal .empty-state--category::before {
    content: '> ';
    position: absolute;
    left: 0;
    opacity: 0.65;
    font-family: "Source Code Pro", monospace;
    pointer-events: none;
}

/* Launcher layout: tile-style add button */
.layout-launcher .empty-state--category {
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: 88px;
    height: 80px;
    padding: var(--space-2, 0.5rem);
    gap: var(--space-1, 0.3rem);
}

.layout-launcher .empty-state--category-text {
    display: none;
}

.layout-launcher .empty-state--category-btn {
    width: 88px;
    height: 80px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-6, 10px);
    border-style: dashed;
    border-width: 1.5px;
    padding: 0;
    font-size: calc(var(--font-size-controls) * 0.85);
    opacity: 0.7;
    transition: opacity 0.15s ease, background 0.15s ease, border-color 0.15s ease;
}

.layout-launcher .empty-state--category-btn:hover {
    opacity: 1;
    background: color-mix(in srgb, var(--accent-primary) 15%, transparent);
    border-color: var(--accent-primary);
}

.bookmark-icon-letter {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--background-secondary, rgba(128, 128, 128, 0.15));
    color: var(--text-tertiary, currentColor);
    font-size: 0.6em;
    font-weight: 600;
    border-radius: var(--layout-radius-xs, 4px);
    text-transform: uppercase;
    line-height: 1;
    user-select: none;
    pointer-events: none;
    flex-shrink: 0;
}

.bookmark-link.bookmark-inline-editing {
    display: block;
    padding: var(--space-1-5, 0.35rem) 0;
    min-height: 0;
    min-width: 0;
}

/* Launcher tiles are narrow glass chips; inline edit needs a full-width panel row. */
.layout-launcher .bookmark-link.bookmark-inline-editing {
    width: auto;
    min-width: min(420px, 90vw);
    max-width: min(520px, 95vw);
    flex: 1 1 100%;
    align-items: stretch;
    padding: 0;
    background: transparent;
    border: none;
    box-shadow: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    transform: none;
}

.bookmark-link.bookmark-inline-editing.keyboard-selected {
    background: none;
}

/*
 * Inline edit focus: dim the chrome that is not being edited.
 *
 * Dimmed, not blurred. `filter: blur()` on the neighbouring rows gave each
 * column its own compositing layer, and WebKit paints those in an order that
 * ignores the z-index of unrelated elements — so a tall inline form opened from
 * Health had a blurred neighbouring column painted on top of it, eating every
 * click on its fields. `pointer-events: none` does not help: WebKit hit-tests
 * the composited layer regardless. The same class of bug was fixed the same way
 * in the check-mode popover, by taking the blur out.
 *
 * At 18% opacity the blur was doing almost nothing visually, which is what
 * makes this a straight removal rather than a trade.
 *
 * Do NOT use a body::before backdrop-filter overlay either — it stacks above
 * #dashboard-layout and frosts the form in Safari and Chrome.
 */
body.bookmark-inline-edit-active {
    --inline-edit-panel-bg: var(--background-primary);
    --inline-edit-field-bg: var(--background-secondary);
}

body.bookmark-inline-edit-active .dashboard-section.section-controls,
body.bookmark-inline-edit-active .dashboard-section.section-title,
body.bookmark-inline-edit-active .button-container,
body.bookmark-inline-edit-active #dashboard-layout .category-title,
body.bookmark-inline-edit-active #dashboard-layout .bookmark-link:not(.bookmark-inline-editing) {
    opacity: 0.18;
    pointer-events: none;
    transition: opacity 0.2s ease;
}

body.bookmark-inline-edit-active #dashboard-layout .bookmark-link:not(.bookmark-inline-editing) > * {
    /* No filter here: see above. The row is already at 18% opacity. */
    pointer-events: none;
}

body.bookmark-inline-edit-active #shortcut-search:not(.show) {
    opacity: 0.18;
    pointer-events: none;
    transition: opacity 0.2s ease;
}

body.bookmark-inline-edit-active .bookmark-link.bookmark-inline-editing {
    filter: none;
    opacity: 1;
    pointer-events: auto;
    position: relative;
    z-index: 20;
    isolation: isolate;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    touch-action: manipulation;
}

body.bookmark-inline-edit-active .bookmark-inline-form,
body.bookmark-inline-edit-active .bookmark-inline-form * {
    pointer-events: auto;
    -webkit-user-select: text;
    user-select: text;
}

body.bookmark-inline-edit-active .category .bookmarks-list,
body.bookmark-inline-edit-active .category-body > .bookmarks-list {
    overflow: visible;
    min-height: auto;
}

@media (prefers-reduced-motion: reduce) {
    body.bookmark-inline-edit-active .dashboard-section.section-controls,
    body.bookmark-inline-edit-active .dashboard-section.section-title,
    body.bookmark-inline-edit-active .button-container,
    body.bookmark-inline-edit-active #dashboard-layout .category-title,
    body.bookmark-inline-edit-active #dashboard-layout .bookmark-link:not(.bookmark-inline-editing),
    body.bookmark-inline-edit-active #shortcut-search:not(.show) {
        transition: none;
    }

    body.bookmark-inline-edit-active #dashboard-layout .bookmark-link:not(.bookmark-inline-editing) > * {
        filter: none;
    }
}

body.bookmark-inline-edit-active .bookmark-inline-form {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    filter: none;
    background-color: var(--inline-edit-panel-bg);
    background-image: none;
}

body.bookmark-inline-edit-active #app-modal.modal-overlay.show {
    z-index: 10050;
}

.bookmark-inline-form {
    width: 100%;
    min-width: min(420px, 90vw);
    max-width: none;
    display: flex;
    flex-direction: column;
    gap: var(--space-1-5, 0.35rem);
    padding: var(--space-3, 0.7rem) var(--space-3, 0.8rem);
    border-radius: var(--radius-5, 8px);
    /* Solid panel — never rgba modal (see --inline-edit-panel-bg). */
    background-color: var(--inline-edit-panel-bg);
    border: 2px solid var(--border-primary);
    box-shadow:
        0 0 0 1px color-mix(in srgb, var(--accent-primary) 22%, var(--border-primary)),
        0 12px 36px color-mix(in srgb, var(--text-primary) 18%, transparent);
    box-sizing: border-box;
    position: relative;
    z-index: 1;
    isolation: isolate;
    animation: inlineEditReveal 180ms ease-out;
}

.bookmark-inline-form .bookmark-inline-input,
.bookmark-inline-form .bookmark-inline-select,
.bookmark-inline-form .bookmark-inline-textarea,
.bookmark-inline-form .bookmark-inline-action-btn,
.bookmark-inline-form .bookmark-inline-icon-preview {
    background-color: var(--inline-edit-field-bg);
    pointer-events: auto;
    user-select: text;
    -webkit-user-select: text;
}

@keyframes inlineEditReveal {
    from {
        opacity: 0;
        transform: translateY(-4px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

@media (prefers-reduced-motion: reduce) {
    .bookmark-inline-form {
        animation: none;
    }
}

.bookmark-inline-field {
    display: flex;
    flex-direction: column;
    gap: var(--space-hairline, 0.15rem);
    min-width: 0;
}

.bookmark-inline-field.bookmark-inline-check {
    flex-direction: row;
    align-items: center;
    gap: var(--space-1-5, 0.4rem);
}

/* ─── Flag toggles (pinned / status check / monitor) ─────────────────────────
   One horizontal row of pills instead of three stacked checkbox rows: the flags
   read as a single group, sit near the top where they are easy to find, and cost
   one line instead of three. */

.bookmark-inline-toggles {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1, 0.3rem);
}

/* The real checkbox stays focusable and reachable by screen readers; the pill
   label is the visible control. */
.bookmark-inline-toggle-input {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
}

.bookmark-inline-toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1, 0.3rem);
    padding: var(--space-1, 0.25rem) var(--space-2, 0.55rem);
    border: 1px solid var(--border-secondary);
    border-radius: var(--radius-pill, 999px);
    background-color: var(--inline-edit-field-bg);
    color: var(--text-secondary);
    font-size: var(--font-size-controls);
    line-height: 1.2;
    cursor: pointer;
    user-select: none;
    transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
}

.bookmark-inline-toggle svg {
    width: 0.85em;
    height: 0.85em;
    fill: currentColor;
    flex: 0 0 auto;
}

.bookmark-inline-toggle:hover {
    border-color: color-mix(in srgb, var(--accent-primary) 45%, var(--border-secondary));
    color: var(--text-primary);
}

.bookmark-inline-toggle-input:checked + .bookmark-inline-toggle {
    border-color: var(--accent-primary);
    background-color: color-mix(in srgb, var(--accent-primary) 14%, var(--inline-edit-field-bg));
    color: var(--text-primary);
    font-weight: var(--font-weight-semibold);
}

/* Keyboard focus must stay visible even though the input itself is off-screen. */
.bookmark-inline-toggle-input:focus-visible + .bookmark-inline-toggle {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}

/* ─── Availability check mode ────────────────────────────────────────────────
   A segmented control, not two checkboxes: Monitor is a superset of Periodic, so
   only one can be true at a time and the UI should make that structural rather
   than rely on the user knowing it. */

.bookmark-inline-checkmode {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1, 0.25rem);
}

.bookmark-inline-checkmode-input {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
}

.bookmark-inline-checkmode-option {
    padding: var(--space-1, 0.25rem) var(--space-2, 0.6rem);
    border: 1px solid var(--border-secondary);
    border-radius: var(--radius-pill, 999px);
    background-color: var(--inline-edit-field-bg);
    color: var(--text-secondary);
    font-size: var(--font-size-controls);
    line-height: 1.2;
    cursor: pointer;
    user-select: none;
    transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
}

.bookmark-inline-checkmode-option:hover {
    border-color: color-mix(in srgb, var(--accent-primary) 45%, var(--border-secondary));
    color: var(--text-primary);
}

.bookmark-inline-checkmode-input:checked + .bookmark-inline-checkmode-option {
    border-color: var(--accent-primary);
    background-color: color-mix(in srgb, var(--accent-primary) 14%, var(--inline-edit-field-bg));
    color: var(--text-primary);
    font-weight: var(--font-weight-semibold);
}

.bookmark-inline-checkmode-input:focus-visible + .bookmark-inline-checkmode-option {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}

.bookmark-inline-checkmode-info {
    width: 1.15rem;
    height: 1.15rem;
    padding: 0;
    border: 1px solid var(--border-secondary);
    border-radius: 50%;
    background: transparent;
    color: var(--text-tertiary);
    font-size: var(--text-xs, 0.7rem);
    font-style: italic;
    line-height: 1;
    cursor: pointer;
    flex: 0 0 auto;
}

.bookmark-inline-checkmode-info:hover {
    border-color: var(--accent-primary);
    color: var(--text-primary);
}

/* Explainer modal */
.check-mode-explain-row + .check-mode-explain-row {
    margin-top: var(--space-4, 0.9rem);
}

.check-mode-explain-row h4 {
    margin: 0 0 var(--space-1, 0.2rem);
    font-size: var(--text-base, 0.95rem);
    color: var(--text-primary);
}

.check-mode-explain-row p {
    margin: 0;
    color: var(--text-secondary);
    line-height: 1.5;
}

@media (prefers-reduced-motion: reduce) {
    .bookmark-inline-checkmode-option {
        transition: none;
    }
}

/* Overrides the shared .bookmark-inline-select width:100% (defined later in this
   file) so the interval sits beside the pills instead of claiming its own line —
   the extra specificity is the point, not an accident. */
.bookmark-inline-toggles .bookmark-inline-select.bookmark-inline-toggle-select,
.bookmark-inline-checkmode .bookmark-inline-select.bookmark-inline-toggle-select {
    flex: 0 1 auto;
    width: auto;
    min-width: 0;
    max-width: 100%;
    padding: var(--space-1, 0.2rem) var(--space-1-5, 0.4rem);
    font-size: var(--font-size-controls);
}

@media (prefers-reduced-motion: reduce) {
    .bookmark-inline-toggle {
        transition: none;
    }
}

.bookmark-inline-label {
    font-size: var(--font-size-controls);
    color: var(--text-tertiary);
    font-weight: var(--font-weight-semibold);
}

.bookmark-inline-input,
.bookmark-inline-select {
    width: 100%;
    font-family: inherit;
    font-size: var(--font-size-controls);
    padding: var(--space-1, 0.25rem) var(--space-1-5, 0.4rem);
    border: 1px solid var(--border-primary);
    border-radius: var(--layout-radius-sm, 8px);
    background: var(--background-secondary);
    color: var(--text-primary);
    outline: none;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
    -webkit-appearance: none;
    appearance: none;
    pointer-events: auto;
    touch-action: manipulation;
    -webkit-user-select: text;
    user-select: text;
}

.bookmark-inline-input:focus,
.bookmark-inline-select:focus {
    border-color: var(--accent-primary);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-primary) 20%, transparent);
}

.bookmark-inline-input.input-error,
.bookmark-inline-input.field-conflict {
    border-color: var(--accent-error);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-error) 20%, transparent);
}

.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.bookmark-inline-action-btn:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

.bookmark-inline-action-btn.bookmark-inline-save--invalid,
.bookmark-inline-action-btn[aria-disabled="true"] {
    opacity: 0.4;
    cursor: not-allowed;
}

.bookmark-inline-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1-5, 0.35rem);
    margin-top: var(--space-1, 0.25rem);
    align-items: center;
    position: relative;
    z-index: 5;
}

.bookmark-inline-actions .bookmark-inline-action-btn {
    position: relative;
    z-index: 2;
}

.bookmark-inline-hint {
    flex: 1 1 auto;
    min-width: 0;
    margin-left: auto;
    font-size: var(--text-xs, 0.7rem);
    color: var(--text-tertiary);
    white-space: nowrap;
    pointer-events: none;
    text-align: right;
}

.bookmark-inline-hint kbd {
    font-family: var(--font-family-main);
    font-size: var(--text-2xs, 0.68rem);
    background: var(--background-secondary);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-2, 3px);
    padding: var(--space-hairline, 0.05rem) var(--space-1, 0.3rem);
    color: var(--text-secondary);
}

.bookmark-inline-conflict {
    display: block;
    font-size: var(--text-xs, 0.72rem);
    color: var(--accent-error);
    margin-top: var(--space-1, 0.2rem);
}

.bookmark-inline-conflict[hidden] {
    display: none;
}

/* ─── Inline create (new page / new category from the form's dropdowns) ───────
   The row takes the select's place inside the same field, so choosing "New …"
   never reflows the form around it. The name input takes the leftover width and
   the two buttons keep their intrinsic size. */

.bookmark-inline-create {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1-5, 0.35rem);
}

.bookmark-inline-create[hidden] {
    display: none;
}

.bookmark-inline-create .bookmark-inline-create-input {
    flex: 1 1 8rem;
    width: auto;
    min-width: 0;
}

.bookmark-inline-create .bookmark-inline-create-btn {
    flex: 0 0 auto;
}

/* The error spans the whole row rather than squeezing in beside the buttons. */
.bookmark-inline-create .bookmark-inline-conflict {
    flex: 1 0 100%;
    margin-top: 0;
}

/* Sets the create entry apart from the real pages/categories below it. */
.bookmark-inline-new-option {
    font-weight: var(--font-weight-semibold);
}

/* ─── "+ category" — the grid's own add affordance ───────────────────────────
   Sits after the last column rather than inside one: it is not a category, and
   the drag-reorder instances would treat it as one if it were. Dashed and muted
   so it reads as an invitation next to real category headers, not as content.
   `c` does the same thing without it, which is why it may be hidden entirely on
   touch. */

/* Sized and coloured to match .category-sort-btn beside it: this is one more
   control in the same trailing group, not a separate affordance. Squarer than
   the A–Z/Rec chips because it holds a single glyph. */
.category-add-inline-btn {
    font: inherit;
    font-size: 0.68em;
    font-weight: var(--font-weight-semibold);
    line-height: 1.2;
    min-height: 1.45em;
    min-width: 1.45em;
    padding: 0.15em 0.3em;
    margin-left: 0.25em;
    border: 1px solid color-mix(in srgb, var(--text-tertiary) 28%, transparent);
    border-radius: var(--radius-2, 3px);
    background: color-mix(in srgb, var(--background-secondary, transparent) 55%, transparent);
    color: var(--text-tertiary);
    cursor: pointer;
    opacity: 0.75;
    transition: opacity 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}

.category-add-inline-btn:hover,
.category-add-inline-btn:focus-visible {
    opacity: 1;
    color: var(--text-secondary);
    border-color: color-mix(in srgb, var(--text-secondary) 35%, transparent);
    outline: none;
}

.category-add-inline-btn[hidden] {
    display: none;
}

/* The name row is hosted by the grid rather than the header the "+" sits in: a
   text input inside a one-line header would overflow the column. It spans the
   grid so it is never squeezed into a single column's width, and only exists
   while naming, so the row it adds is not permanent. */
.bookmark-inline-create.category-add-create {
    grid-column: 1 / -1;
    max-width: 22rem;
    margin-top: var(--space-3, 0.75rem);
}

.bookmark-inline-icon-actions {
    display: flex;
    align-items: center;
    gap: var(--space-1-5, 0.35rem);
    margin-top: var(--space-1, 0.3rem);
}

.bookmark-inline-icon-preview {
    width: 2rem;
    height: 2rem;
    border: 1px solid var(--border-primary);
    border-radius: var(--layout-radius-sm, 8px);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--background-primary);
    color: var(--text-tertiary);
    margin-top: var(--space-1, 0.25rem);
    overflow: hidden;
}

.bookmark-inline-icon-preview img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
}

.bookmark-inline-icon-preview span {
    font-size: var(--text-2xs, 0.65rem);
    line-height: 1;
}

.bookmark-inline-icon-state {
    font-size: var(--font-size-controls);
    color: var(--text-tertiary);
}

.bookmark-inline-action-btn,
.bookmark-inline-create-btn {
    border: 1px solid var(--border-primary);
    background: var(--background-primary);
    color: var(--text-primary);
    font-size: var(--font-size-controls);
    padding: var(--space-1, 0.3rem) var(--space-2, 0.55rem);
    border-radius: var(--layout-radius-sm, 8px);
    cursor: pointer;
}

.bookmark-inline-save,
.bookmark-inline-create-ok {
    border-color: var(--text-secondary);
}

.bookmark-inline-create-btn:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

.bookmark-inline-delete {
    color: var(--text-secondary);
}

@keyframes bookmarkMoveOut {
    0%   { opacity: 1; transform: translateX(0); }
    60%  { opacity: 0.3; transform: translateX(18px); }
    100% { opacity: 0; transform: translateX(32px); max-height: 0; padding: 0; margin: 0; }
}

.bookmark-move-out {
    animation: bookmarkMoveOut 0.32s ease-in forwards;
    overflow: hidden;
    pointer-events: none;
}

.bookmark-inline-delete:hover {
    border-color: var(--text-primary);
    color: var(--text-primary);
}

.bookmarks-list.reorder-container .bookmark-link.is-draggable {
    opacity: 0.45;
    transform: translateX(2px) scale(0.985);
}

.bookmarks-list.reorder-container .bookmark-drop-placeholder {
    height: 1.6rem;
    border: 1px dashed var(--text-secondary);
    border-radius: var(--layout-radius-sm, 8px);
    opacity: 0.65;
    background: color-mix(in srgb, var(--background-primary) 85%, var(--text-secondary) 15%);
    transform-origin: top center;
    animation: placeholderIn 220ms cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
}

@keyframes placeholderIn {
    from {
        opacity: 0;
        transform: scaleY(0.3);
    }
    to {
        opacity: 0.65;
        transform: scaleY(1);
    }
}

.bookmarks-list.reorder-container .bookmark-link.bookmark-move-in {
    animation: bookmarkMoveIn 180ms ease-out;
}

@keyframes bookmarkMoveIn {
    0% {
        opacity: 0.25;
        transform: translateX(8px) scale(0.97);
    }
    100% {
        opacity: 1;
        transform: translateX(0) scale(1);
    }
}

body.no-animations .bookmarks-list.reorder-container .bookmark-link,
body.no-animations .bookmarks-list.reorder-container .bookmark-link.bookmark-move-in,
body.no-animations .bookmarks-list.reorder-container .bookmark-drop-placeholder,
body.no-animations .bookmarks-list.reorder-container::after {
    transition: none;
    animation: none;
}

.bookmark-icon {
    width: 100%;
    height: 100%;
    object-fit: contain;
    flex-shrink: 0;
    -webkit-user-drag: none;
    user-drag: none;
    pointer-events: none;
}

.bookmark-link .bookmark-icon {
    display: block;
}

.category-title .bookmark-icon {
    width: 0.85em;
    height: 0.85em;
    vertical-align: -0.05em;
}

.bookmark-link.smart-recent-promote {
    animation: smartRecentPromote 320ms ease-out;
}

@keyframes smartRecentPromote {
    0% {
        transform: translateY(10px);
        opacity: 0.65;
        box-shadow: 0 0 0 1px var(--accent-success);
    }
    100% {
        transform: translateY(0);
        opacity: 1;
        box-shadow: 0 0 0 0 transparent;
    }
}

.bookmark-text {
    flex: 1;
    min-width: 0; /* Allow text to shrink */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    font-size: 0.88em;
    line-height: 1.2;
}

/*
 * The link preview card.
 *
 * Three bands in a fixed order — what it is, what it says, what I know about it
 * — each one absent rather than blank when it has nothing to carry. No card-level
 * max-height: the blocks clamp themselves, so nothing disappears without the
 * card being visibly shorter for it.
 */
.bookmark-preview-card {
    position: fixed;
    z-index: 2000;
    display: none;
    width: min(360px, calc(100vw - 24px));
    border-radius: var(--radius-7, 12px);
    border: 1px solid var(--border-primary);
    background: var(--background-secondary);
    box-shadow: 0 10px 28px rgba(0, 0, 0, 0.28);
    /* Peek is something to read, not something to reach: it takes no pointer
       events, so it never has to move out of the way of its own cursor. */
    pointer-events: none;
    overflow: hidden;
}

/* `display: flex` and `display: grid` both beat the hidden attribute, and the
   card's bands are laid out with both — so a band that says it is absent has to
   be made absent here, or it draws as an empty stripe. */
.bookmark-preview-card [hidden] {
    display: none !important;
}

.bookmark-preview-card.is-visible {
    display: block;
}

.bookmark-preview-card.is-pinned {
    pointer-events: auto;
    border-color: var(--text-secondary);
}

.bookmark-preview-card:focus-visible {
    outline: 2px solid var(--accent-primary, var(--text-secondary));
    outline-offset: 2px;
}

.bookmark-preview-card-head {
    display: flex;
    gap: var(--space-2, 0.55rem);
    align-items: flex-start;
    padding: var(--space-3, 0.7rem) var(--space-3, 0.8rem) var(--space-2, 0.5rem);
}

.bookmark-preview-card-fav {
    flex: none;
    width: 17px;
    height: 17px;
    margin-top: 2px;
    border-radius: var(--radius-3, 4px);
    overflow: hidden;
    background: var(--background-primary);
}

.bookmark-preview-card-fav img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
}

.bookmark-preview-card-headtext {
    flex: 1;
    min-width: 0;
}

.bookmark-preview-card-title {
    font-size: var(--font-size-controls);
    font-weight: var(--font-weight-bold);
    color: var(--text-primary);
    line-height: 1.25;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
}

/* One address, not two: the old card printed the full URL in mono and then its
   hostname on the next line, and neither could be clicked or copied. */
.bookmark-preview-card-domain {
    margin-top: 2px;
    font-family: var(--font-family-mono, monospace);
    font-size: calc(var(--font-size-controls) * 0.8);
    color: var(--text-tertiary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The byline sits with the address, not in the facts list below: publisher,
   author and date read as one sentence, and three labelled rows would make a
   paragraph out of a glance. Set apart from the address by weight, not colour,
   so the mono address stays the anchor. */
.bookmark-preview-card-byline {
    margin-top: 3px;
    font-size: calc(var(--font-size-controls) * 0.78);
    color: var(--text-secondary);
    line-height: 1.35;
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}

/* The provider's player, in a sandboxed frame. 16:9 is what video oEmbed
   returns almost without exception, and letting the frame size itself would
   collapse it to nothing -- srcdoc content has no intrinsic height. */
.bookmark-preview-card-embed {
    margin-top: 10px;
    border-radius: var(--radius-md, 8px);
    overflow: hidden;
    background: #000;
    aspect-ratio: 16 / 9;
}

.bookmark-preview-card-embed-frame {
    display: block;
    width: 100%;
    height: 100%;
    border: 0;
}

/* "Is this link still alive" is the question the rest of the app exists to
   answer, so it is the first thing on the card. */
.bookmark-preview-card-pill {
    flex: none;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 4px 7px;
    border-radius: var(--radius-pill, 999px);
    border: 1px solid var(--border-secondary);
    background: rgba(255, 255, 255, 0.03);
    color: var(--text-secondary);
    font-size: calc(var(--font-size-controls) * 0.72);
    font-weight: var(--font-weight-semibold);
    white-space: nowrap;
}

.bookmark-preview-card-pill::before {
    content: '';
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: currentColor;
}

.bookmark-preview-card-pill.is-ok {
    color: var(--accent-success, #10B981);
    border-color: color-mix(in srgb, var(--accent-success, #10B981) 40%, transparent);
    background: color-mix(in srgb, var(--accent-success, #10B981) 12%, transparent);
}

.bookmark-preview-card-pill.is-warn {
    color: var(--accent-warning, #F59E0B);
    border-color: color-mix(in srgb, var(--accent-warning, #F59E0B) 40%, transparent);
    background: color-mix(in srgb, var(--accent-warning, #F59E0B) 12%, transparent);
}

.bookmark-preview-card-pill.is-bad {
    color: var(--accent-error, #EF4444);
    border-color: color-mix(in srgb, var(--accent-error, #EF4444) 40%, transparent);
    background: color-mix(in srgb, var(--accent-error, #EF4444) 12%, transparent);
}

.bookmark-preview-card-image-wrap {
    width: 100%;
    aspect-ratio: 16 / 9;
    max-height: 158px;
    border-top: 1px solid var(--border-secondary);
    border-bottom: 1px solid var(--border-secondary);
    background: var(--background-primary);
}

.bookmark-preview-card-image {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.bookmark-preview-card-description {
    padding: var(--space-2, 0.55rem) var(--space-3, 0.8rem) 0;
    font-size: calc(var(--font-size-controls) * 0.92);
    color: var(--text-secondary);
    line-height: 1.42;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    overflow: hidden;
}

.bookmark-preview-card-empty {
    margin: 0;
    padding: var(--space-2, 0.55rem) var(--space-3, 0.8rem) 0;
    font-size: calc(var(--font-size-controls) * 0.88);
    font-style: italic;
    color: var(--text-tertiary);
}

.bookmark-preview-card-facts {
    margin: var(--space-3, 0.7rem) 0 0;
    padding: var(--space-2, 0.5rem) var(--space-3, 0.8rem) var(--space-2, 0.6rem);
    border-top: 1px solid var(--border-secondary);
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 3px 12px;
    font-size: calc(var(--font-size-controls) * 0.86);
}

.bookmark-preview-card-facts dt {
    font-family: var(--font-family-mono, monospace);
    font-size: calc(var(--font-size-controls) * 0.74);
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--text-tertiary);
    white-space: nowrap;
    line-height: 1.6;
}

.bookmark-preview-card-facts dd {
    margin: 0;
    color: var(--text-secondary);
    line-height: 1.6;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

.bookmark-preview-card-facts dd b {
    color: var(--text-primary);
    font-weight: var(--font-weight-semibold);
}

.bookmark-preview-card-facts dd.is-good b { color: var(--accent-success, #10B981); }
.bookmark-preview-card-facts dd.is-warn b { color: var(--accent-warning, #F59E0B); }
.bookmark-preview-card-facts dd.is-bad b { color: var(--accent-error, #EF4444); }

.bookmark-preview-card-facts dd kbd {
    font-family: var(--font-family-mono, monospace);
    font-size: calc(var(--font-size-controls) * 0.78);
    border: 1px solid var(--border-primary);
    border-bottom-width: 2px;
    border-radius: var(--radius-3, 4px);
    padding: 1px 5px;
    color: var(--text-primary);
}

.bookmark-preview-card-muted {
    color: var(--text-tertiary);
}

/* Only a pinned card has actions, so there is nothing on a peek to aim at. */
.bookmark-preview-card-foot {
    display: flex;
    gap: 4px;
    align-items: center;
    padding: var(--space-2, 0.45rem) var(--space-2, 0.55rem);
    border-top: 1px solid var(--border-secondary);
    background: rgba(0, 0, 0, 0.18);
}

.bookmark-preview-card-action {
    border: 1px solid transparent;
    border-radius: var(--radius-4, 6px);
    padding: 5px 8px;
    background: none;
    color: var(--text-secondary);
    font-size: calc(var(--font-size-controls) * 0.8);
    cursor: pointer;
}

.bookmark-preview-card-action:hover,
.bookmark-preview-card-action:focus-visible {
    color: var(--text-primary);
    border-color: var(--border-secondary);
    background: rgba(255, 255, 255, 0.04);
    outline: none;
}

.bookmark-preview-card-action:disabled {
    opacity: 0.65;
    cursor: wait;
}

.bookmark-preview-card-esc {
    margin-left: auto;
    font-family: var(--font-family-mono, monospace);
    font-size: calc(var(--font-size-controls) * 0.72);
    color: var(--text-tertiary);
}

/* Bookmark open flash animation */
@keyframes bookmarkFlash {
    0%   { opacity: 0.55; }
    100% { opacity: 0; }
}

.bookmark-link {
    position: relative;
}

.bookmark-link.bookmark-flash::after {
    content: '';
    position: absolute;
    inset: 0;
    background: var(--accent-primary);
    opacity: 0;
    pointer-events: none;
    animation: bookmarkFlash 380ms ease-out forwards;
}

/* Copy-URL row flash */
@keyframes bookmarkCopyFlash {
    0%   { opacity: 0.45; }
    100% { opacity: 0; }
}

.bookmark-link.bookmark-copy-flash::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background: var(--color-success, #4caf7d);
    opacity: 0;
    pointer-events: none;
    animation: bookmarkCopyFlash 500ms ease-out forwards;
}

    body.no-animations .bookmark-link.bookmark-copy-flash::after {
    animation: none;
}

body.no-animations .bookmark-link.bookmark-flash::after {
    animation: none !important;
}

/* Launcher scale-pulse on open */
@keyframes bookmarkPulse {
    0%   { transform: scale(1); }
    30%  { transform: scale(0.95); }
    70%  { transform: scale(1.05); }
    100% { transform: scale(1); }
}

.layout-launcher .bookmark-link.bookmark-pulse {
    animation: bookmarkPulse 260ms ease-out forwards;
}

body.no-animations .layout-launcher .bookmark-link.bookmark-pulse {
    animation: none;
}

/* Keyboard navigation — row highlight lives in theme.css */
.bookmark-link.keyboard-selected {
    position: relative;
}

.layout-launcher .bookmark-link.keyboard-selected,
.layout-launcher .bookmark-link:focus-within {
    outline: none;
    box-shadow: none;
}

.bookmark-shortcut {
    flex-shrink: 0;
    font-family: var(--font-family-mono, monospace);
    font-size: var(--text-2xs, 0.68rem);
    font-weight: var(--font-weight-normal);
    color: var(--text-tertiary);
    background: color-mix(in srgb, var(--background-secondary) 80%, transparent);
    border: 1px solid color-mix(in srgb, var(--border-primary) 65%, transparent);
    border-radius: var(--layout-radius-sm, 8px);
    padding: 0.1em 0.4em;
    line-height: 1.6;
    letter-spacing: 0.03em;
    white-space: nowrap;
    opacity: 1;
}

/* Loading */
.loading {
    text-align: center;
    font-size: var(--font-size-category);
    opacity: 0.7;
    grid-column: 1 / -1;
}

/* Visibility Controls */
body[data-show-title="false"] .title-wrapper {
    display: none;
}

/* Hide config button when showConfigButton is false */
body[data-show-config-button="false"] .config-link {
    display: none;
}

/* Hide cheatsheet button when showCheatSheetButton is false */
body[data-show-cheatsheet-button="false"] .help-button {
    display: none;
}

/* Hide add bookmark button when showAddBookmarkButton is false */
body[data-show-add-bookmark-button="false"] #quick-add-toolbar-btn {
    display: none;
}

/* Hide search button when showSearchButton is false */
body[data-show-search-button="false"] #search-button {
    display: none;
}

/* Hide finders button when showFindersButton is false */
body[data-show-finders-button="false"] .finders-button {
    display: none;
}

/* Hide commands button when showCommandsButton is false */
body[data-show-commands-button="false"] .commands-button {
    display: none;
}

/* Hide recent button when showRecentButton is false */
body[data-show-recent-button="false"] .recent-button {
    display: none;
}

/* Hide fold-all button when showCollapseAllButton is false */
body[data-show-collapse-all-button="false"] #collapse-all-button {
    display: none;
}

/* Collapse the optional secondary toolbar only when all three of its buttons
   are off. Fold-all used to have no setting of its own and rode along with this
   rule, so turning off Recent and Help took it away too — and turning Help back
   on brought it back. Each button now answers only to its own toggle. */
body[data-show-recent-button="false"][data-show-cheatsheet-button="false"][data-show-collapse-all-button="false"] .btn-group-secondary {
    display: none !important;
}

/* Side rail: / tag cloud is a direct .button-container child (not inside secondary group) */
body[data-button-position="side-left"] .button-container > #tag-cloud-toggle-btn.is-eligible {
    display: flex !important;
}

/* The three shortcut-label modes live together in dashboard-bookmark-row.css,
   which loads after this file and owns the row's hover and selection chrome --
   and "hover" is a hover state. Nothing here. */

/* Hide pin icon when showPinIcon is false */
body[data-show-pin-icon="false"] .bookmark-pin-badge {
    display: none;
}

/* Hide note icon when showNoteIcon is false */
body[data-show-note-icon="false"] .bookmark-note-badge {
    display: none;
}

/* Terminal layout variants */
body[data-show-shortcuts="false"] .dashboard-grid.layout-terminal .bookmark-link,
body[data-show-pin-icon="false"] .dashboard-grid.layout-terminal .bookmark-link,
body[data-show-note-icon="false"] .dashboard-grid.layout-terminal .bookmark-link {
    gap: var(--space-1, 0.28rem);
}

body[data-show-shortcuts="false"][data-show-pin-icon="false"] .dashboard-grid.layout-terminal .bookmark-link {
    gap: var(--space-1, 0.24rem);
}

/* Recent bookmarks modal */
.modal.recent-bookmarks-modal {
    max-height: 88vh;
    overflow: hidden;
    max-width: min(52rem, calc(100vw - 2.5rem));
    width: min(52rem, calc(100vw - 2.5rem));
}

.modal.recent-bookmarks-modal .modal-body {
    margin-top: var(--space-4, 1rem);
    margin-bottom: var(--space-4, 1rem);
}

.recent-bookmarks-modal-toolbar {
    display: flex;
    flex-direction: column;
    gap: var(--space-2, 0.5rem);
    margin-bottom: var(--space-3, 0.85rem);
    padding-bottom: var(--space-3, 0.85rem);
    border-bottom: 1px solid var(--border-primary);
}

.recent-bookmarks-open-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2, 0.5rem);
}

.recent-bookmarks-open-btn.modal-button {
    flex: 1 1 auto;
    min-width: min(100%, 12rem);
    justify-content: center;
}

.recent-bookmarks-open-hint {
    margin: 0;
    color: var(--text-tertiary);
    font-size: var(--font-size-controls);
    line-height: 1.35;
}

.recent-bookmarks-modal-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-2, 0.5rem);
    max-height: 58vh;
    overflow-y: auto;
    padding-right: var(--space-1, 0.25rem);
}

.recent-bookmarks-modal-item {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    gap: var(--space-3, 0.75rem);
    border: 1px solid var(--border-primary);
    border-radius: var(--layout-radius-sm, 8px);
    background: var(--background-primary);
    padding: var(--space-2, 0.55rem) var(--space-3, 0.7rem);
    color: var(--text-primary);
    text-decoration: none;
    transition: background 0.1s ease, color 0.1s ease, border-color 0.1s ease;
}

.recent-bookmarks-modal-item:hover {
    transform: none;
    background: color-mix(in srgb, var(--accent-primary) 9%, transparent);
    border-color: color-mix(in srgb, var(--accent-primary) 24%, transparent);
}

.recent-bookmarks-modal-rank {
    flex-shrink: 0;
    min-width: 1.35rem;
    color: var(--text-tertiary);
    font-size: var(--font-size-controls);
    font-variant-numeric: tabular-nums;
    text-align: center;
}

.recent-bookmarks-modal-body {
    display: flex;
    flex-direction: column;
    gap: var(--space-hairline, 0.12rem);
    min-width: 0;
}

.recent-bookmarks-modal-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: var(--font-weight-medium);
}

.recent-bookmarks-modal-detail {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--text-tertiary);
    font-size: var(--font-size-controls);
}

.recent-bookmarks-modal-stats {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: var(--space-hairline, 0.18rem);
    flex-shrink: 0;
}

.recent-bookmarks-modal-recency {
    padding: var(--space-hairline, 0.12rem) var(--space-2, 0.45rem);
    border-radius: var(--layout-radius-pill, 999px);
    border: 1px solid color-mix(in srgb, var(--accent-primary) 22%, var(--border-primary));
    background: color-mix(in srgb, var(--accent-primary) 8%, var(--background-primary));
    color: var(--text-secondary);
    font-size: var(--font-size-controls);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.recent-bookmarks-modal-opens {
    color: var(--text-tertiary);
    font-size: var(--font-size-controls);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.recent-bookmarks-modal-meta {
    flex-shrink: 0;
    color: var(--text-tertiary);
    font-size: var(--font-size-controls);
}

.recent-bookmarks-empty {
    padding: var(--space-5, 1.5rem) var(--space-2, 0.5rem);
    color: var(--text-tertiary);
    font-size: var(--font-size-text);
    text-align: center;
}

/* Dashboard toolbar modals — same placement and chrome as search overlay */
body:has(#dashboard-layout) #app-modal.modal-overlay:has(.recent-bookmarks-modal),
body:has(#dashboard-layout) #app-modal.modal-overlay:has(.keyboard-cheat-sheet-modal),
body:has(#dashboard-layout) #app-modal.modal-overlay:has(.whats-new-modal) {
    justify-content: flex-end;
    align-items: center;
    padding: 0 max(1.25rem, 2vw) calc(var(--dashboard-toolbar-stack, 5.75rem) + env(safe-area-inset-bottom, 0px));
    box-sizing: border-box;
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
}

body:has(#dashboard-layout) #app-modal.modal-overlay:has(.recent-bookmarks-modal) .modal,
body:has(#dashboard-layout) #app-modal.modal-overlay:has(.keyboard-cheat-sheet-modal) .modal,
body:has(#dashboard-layout) #app-modal.modal-overlay:has(.whats-new-modal) .modal {
    width: min(52rem, calc(100vw - 2.5rem));
    max-width: 100%;
    margin: 0;
    max-height: min(70vh, calc(100vh - var(--dashboard-toolbar-stack, 5.75rem) - 2.5rem));
    transform: translateY(10px) scale(0.98);
    transition: transform 0.15s ease-in, opacity 0.15s ease-in;
}

/* What's new is narrower than the two it shares the shell with: it is a single
   column of prose, and 52rem ran its lines to ~84 characters. Split out rather
   than folded into the rule above, because the recent-bookmarks list and the
   cheat sheet's two-column key table both still want the width. This selector
   has to repeat the full :has() chain — the shorter .modal.whats-new-modal in
   modal.css loses to it on specificity. */
body:has(#dashboard-layout) #app-modal.modal-overlay:has(.whats-new-modal) .modal {
    width: min(36rem, calc(100vw - 2.5rem));
}

/*
 * Room above the modal as well as below it.
 *
 * The block above leaves 2rem under the modal and nothing over it, and caps the
 * height at 100vh - 2rem — so a modal tall enough to reach its own limit runs
 * into the top edge of the window while sitting a comfortable distance off the
 * bottom. Measured on a 900px window: 868 tall, 32px below, 0 above.
 *
 * The padding is symmetric and the height leaves room for both. Scoped to
 * what's new; recent bookmarks and the cheat sheet share the block above and
 * the same lopsidedness, but neither was what anybody was looking at.
 */
body:has(#dashboard-layout) #app-modal.modal-overlay:has(.whats-new-modal) {
    padding-top: max(var(--space-6, 2rem), env(safe-area-inset-top, 0px));
}

body:has(#dashboard-layout)[data-button-position="side-left"] #app-modal.modal-overlay:has(.whats-new-modal) .modal,
body:has(#dashboard-layout)[data-button-position="bottom-left"] #app-modal.modal-overlay:has(.whats-new-modal) .modal,
body:has(#dashboard-layout)[data-button-position="bottom-right"] #app-modal.modal-overlay:has(.whats-new-modal) .modal {
    max-height: calc(100vh - 4rem);
}

body:has(#dashboard-layout) #app-modal.modal-overlay.show:has(.recent-bookmarks-modal) .modal,
body:has(#dashboard-layout) #app-modal.modal-overlay.show:has(.keyboard-cheat-sheet-modal) .modal,
body:has(#dashboard-layout) #app-modal.modal-overlay.show:has(.whats-new-modal) .modal {
    transform: translateY(0) scale(1);
    transition: transform 0.38s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.22s ease-out;
}

body:has(#dashboard-layout)[data-button-position="side-left"] #app-modal.modal-overlay:has(.recent-bookmarks-modal),
body:has(#dashboard-layout)[data-button-position="side-left"] #app-modal.modal-overlay:has(.keyboard-cheat-sheet-modal),
body:has(#dashboard-layout)[data-button-position="side-left"] #app-modal.modal-overlay:has(.whats-new-modal),
body:has(#dashboard-layout)[data-button-position="bottom-left"] #app-modal.modal-overlay:has(.recent-bookmarks-modal),
body:has(#dashboard-layout)[data-button-position="bottom-left"] #app-modal.modal-overlay:has(.keyboard-cheat-sheet-modal),
body:has(#dashboard-layout)[data-button-position="bottom-left"] #app-modal.modal-overlay:has(.whats-new-modal),
body:has(#dashboard-layout)[data-button-position="bottom-right"] #app-modal.modal-overlay:has(.recent-bookmarks-modal),
body:has(#dashboard-layout)[data-button-position="bottom-right"] #app-modal.modal-overlay:has(.keyboard-cheat-sheet-modal),
body:has(#dashboard-layout)[data-button-position="bottom-right"] #app-modal.modal-overlay:has(.whats-new-modal) {
    justify-content: center;
    padding-bottom: max(var(--space-6, 2rem), env(safe-area-inset-bottom, 0px));
}

body:has(#dashboard-layout)[data-button-position="side-left"] #app-modal.modal-overlay:has(.recent-bookmarks-modal) .modal,
body:has(#dashboard-layout)[data-button-position="side-left"] #app-modal.modal-overlay:has(.keyboard-cheat-sheet-modal) .modal,
body:has(#dashboard-layout)[data-button-position="side-left"] #app-modal.modal-overlay:has(.whats-new-modal) .modal,
body:has(#dashboard-layout)[data-button-position="bottom-left"] #app-modal.modal-overlay:has(.recent-bookmarks-modal) .modal,
body:has(#dashboard-layout)[data-button-position="bottom-left"] #app-modal.modal-overlay:has(.keyboard-cheat-sheet-modal) .modal,
body:has(#dashboard-layout)[data-button-position="bottom-left"] #app-modal.modal-overlay:has(.whats-new-modal) .modal,
body:has(#dashboard-layout)[data-button-position="bottom-right"] #app-modal.modal-overlay:has(.recent-bookmarks-modal) .modal,
body:has(#dashboard-layout)[data-button-position="bottom-right"] #app-modal.modal-overlay:has(.keyboard-cheat-sheet-modal) .modal,
body:has(#dashboard-layout)[data-button-position="bottom-right"] #app-modal.modal-overlay:has(.whats-new-modal) .modal {
    max-height: calc(100vh - 2rem);
}

body.no-animations:has(#dashboard-layout) #app-modal.modal-overlay:has(.recent-bookmarks-modal) .modal,
body.no-animations:has(#dashboard-layout) #app-modal.modal-overlay:has(.keyboard-cheat-sheet-modal) .modal,
body.no-animations:has(#dashboard-layout) #app-modal.modal-overlay:has(.whats-new-modal) .modal {
    transform: none !important;
    transition: opacity 0.15s ease !important;
}

/* Inbox intro modal — centered above the inbox feed */
body:has(#dashboard-layout.inbox-layout) #app-modal.modal-overlay:has(.inbox-intro-modal) {
    z-index: 13000 !important;
    justify-content: center;
    align-items: center;
    padding: max(1.25rem, 2vw);
    box-sizing: border-box;
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
    opacity: 1 !important;
    visibility: visible !important;
}

body:has(#dashboard-layout.inbox-layout) #app-modal.modal-overlay.show:has(.inbox-intro-modal) {
    opacity: 1 !important;
    visibility: visible !important;
}

body:has(#dashboard-layout.inbox-layout) #app-modal.modal-overlay:has(.inbox-intro-modal) .modal {
    width: min(40rem, calc(100vw - 2.5rem));
    max-width: 100%;
    margin: 0;
    max-height: min(85vh, calc(100vh - 2.5rem));
    transform: translateY(0) scale(1);
    opacity: 1 !important;
    visibility: visible !important;
    transition: transform 0.38s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.22s ease-out;
}

body.no-animations:has(#dashboard-layout.inbox-layout) #app-modal.modal-overlay:has(.inbox-intro-modal) .modal {
    transform: none !important;
    transition: opacity 0.15s ease !important;
}

/* Page overview modal — centered compact shell */
body:has(#dashboard-layout) #app-modal.modal-overlay:has(.page-overview-modal) {
    justify-content: center;
    align-items: center;
    padding: max(1.25rem, 2vw);
    box-sizing: border-box;
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
}

body:has(#dashboard-layout) #app-modal.modal-overlay:has(.page-overview-modal) .modal {
    width: min(22rem, calc(100vw - 2.5rem));
    max-width: 100%;
    margin: 0;
    max-height: min(70vh, calc(100vh - 4rem));
    transform: translateY(10px) scale(0.98);
    transition: transform 0.15s ease-in, opacity 0.15s ease-in;
}

body:has(#dashboard-layout) #app-modal.modal-overlay.show:has(.page-overview-modal) .modal {
    transform: translateY(0) scale(1);
    transition: transform 0.38s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.22s ease-out;
}

body.no-animations:has(#dashboard-layout) #app-modal.modal-overlay:has(.page-overview-modal) .modal {
    transform: none !important;
    transition: opacity 0.15s ease !important;
}

.recent-bookmarks-skeleton {
    display: flex;
    flex-direction: column;
    gap: var(--space-2, 0.5rem);
}

.recent-bookmarks-skeleton-row {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    gap: var(--space-3, 0.75rem);
    border: 1px solid var(--border-primary);
    background: var(--background-primary);
    padding: var(--space-2, 0.55rem) var(--space-3, 0.7rem);
    border-radius: var(--layout-radius-sm, 8px);
}

.recent-bookmarks-skeleton-rank {
    width: 1.35rem;
    height: 0.75em;
    border-radius: var(--radius-2, 3px);
    background: var(--background-secondary);
}

.recent-bookmarks-skeleton-body {
    display: flex;
    flex-direction: column;
    gap: var(--space-1, 0.25rem);
    min-width: 0;
}

.recent-bookmarks-skeleton-stats {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: var(--space-1, 0.2rem);
}

.recent-bookmarks-skeleton-name,
.recent-bookmarks-skeleton-detail,
.recent-bookmarks-skeleton-recency,
.recent-bookmarks-skeleton-opens {
    display: block;
    border-radius: var(--radius-2, 3px);
    background: linear-gradient(
        90deg,
        var(--background-secondary) 25%,
        color-mix(in srgb, var(--background-secondary) 60%, var(--text-tertiary) 40%) 50%,
        var(--background-secondary) 75%
    );
    background-size: 200% 100%;
    animation: skeletonShimmer 1.4s ease-in-out infinite;
}

.recent-bookmarks-skeleton-name {
    height: 0.85em;
    width: 72%;
}

.recent-bookmarks-skeleton-detail {
    height: 0.72em;
    width: 42%;
}

.recent-bookmarks-skeleton-recency {
    height: 1.15rem;
    width: 3.25rem;
}

.recent-bookmarks-skeleton-opens {
    height: 0.72em;
    width: 1.5rem;
}

@keyframes skeletonShimmer {
    0%   { background-position: 200% 0; }
    100% { background-position: -200% 0; }
}

@keyframes nextdashStaleFlash {
    0% {
        box-shadow: 0 0 0 0 var(--accent-primary, rgba(100, 180, 255, 0.65));
    }
    40% {
        box-shadow: 0 0 0 6px rgba(100, 180, 255, 0.15);
    }
    100% {
        box-shadow: 0 0 0 0 transparent;
    }
}

.category.nextdash-stale-flash {
    animation: nextdashStaleFlash 1.1s ease-out 2;
    border-radius: var(--radius-4, 6px);
}

@keyframes bookmarkDeepLinkFocus {
    0% {
        box-shadow: inset 3px 0 0 var(--accent-primary), 0 0 0 0 color-mix(in srgb, var(--accent-primary) 35%, transparent);
    }
    40% {
        box-shadow: inset 3px 0 0 var(--accent-primary), 0 0 0 6px color-mix(in srgb, var(--accent-primary) 22%, transparent);
    }
    100% {
        box-shadow: inset 3px 0 0 transparent, 0 0 0 0 transparent;
    }
}

.bookmark-link.bookmark-deep-link-focus {
    animation: bookmarkDeepLinkFocus 1.1s ease-out 2;
    border-radius: var(--layout-radius-sm, 8px);
}

body.no-animations .bookmark-link.bookmark-deep-link-focus {
    animation: none;
    box-shadow: inset 3px 0 0 var(--accent-primary);
}

body.no-animations #dashboard-layout.page-transition,
body.no-animations .category.animate-enter,
body.no-animations .bookmark-link.animate-enter,
body.no-animations .bookmark-inline-form,
body.no-animations .bookmark-move-out {
    animation: none !important;
    transform: none !important;
    opacity: 1 !important;
}

/* ── Quick-add omnibox ──────────────────────────────────────────────────── */

.omnibox-overlay {
    position: fixed;
    inset: 0;
    z-index: 1200;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    padding: 0 max(1.25rem, 2vw) calc(var(--dashboard-toolbar-stack, 5.75rem) + env(safe-area-inset-bottom, 0px));
    box-sizing: border-box;
    pointer-events: none;
    opacity: 0;
    background: rgba(0, 0, 0, 0.3);
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
    transition: opacity 0.2s ease-out;
}

.omnibox-overlay.is-visible {
    opacity: 1;
    pointer-events: auto;
}

.omnibox-box {
    display: flex;
    align-items: center;
    gap: var(--space-2, 0.6rem);
    width: min(52rem, calc(100vw - 2.5rem));
    max-width: 100%;
    box-sizing: border-box;
    padding: var(--space-3, 0.75rem) 1.25rem;
    border: 1px solid color-mix(in srgb, var(--border-primary) 75%, transparent);
    border-radius: var(--layout-radius-md, 12px);
    background: color-mix(in srgb, var(--background-modal) 90%, transparent);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    box-shadow: 0 24px 64px rgba(0, 0, 0, 0.28), 0 2px 8px rgba(0, 0, 0, 0.12);
    /* exit: slide down toward toolbar */
    transform: translateY(10px) scale(0.98);
    opacity: 0;
    transition: transform 0.15s ease-in, opacity 0.15s ease-in;
}

.omnibox-overlay.is-visible .omnibox-box {
    transform: translateY(0) scale(1);
    opacity: 1;
    /* entrance: spring — vertraagt soepel aan het einde */
    transition: transform 0.38s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.22s ease-out;
}

.omnibox-hint {
    font-family: "Source Code Pro", monospace;
    font-size: var(--text-xs, 0.72rem);
    color: var(--text-tertiary);
    white-space: nowrap;
    flex-shrink: 0;
}

.omnibox-hint::before {
    content: '+';
    color: var(--accent-primary);
    margin-right: var(--space-1-5, 0.4rem);
    font-weight: var(--font-weight-bold);
}

.omnibox-input {
    flex: 1;
    background: transparent;
    border: none;
    outline: none;
    color: var(--text-primary);
    font-family: "Source Code Pro", monospace;
    font-size: var(--text-base, 0.9rem);
    padding: 0;
    caret-color: var(--accent-primary);
}

.omnibox-input::placeholder {
    color: var(--text-tertiary);
    opacity: 0.5;
}

.omnibox-status {
    font-size: var(--text-xs, 0.72rem);
    color: var(--text-tertiary);
    white-space: nowrap;
    flex-shrink: 0;
    min-width: 5rem;
    text-align: right;
}

.omnibox-status.is-error {
    color: var(--accent-error);
}

@media (prefers-reduced-motion: reduce) {
    /* Spring drag-and-drop placeholder → instant appear, no scale */
    .bookmark-drop-placeholder,
    .bookmarks-list.reorder-container .bookmark-drop-placeholder {
        animation: none !important;
        opacity: 0.65 !important;
        transform: none !important;
    }
    /* Drag reorder row transitions */
    .bookmarks-list.reorder-container .bookmark-link,
    .bookmarks-list.reorder-container .bookmark-link.bookmark-move-in,
    .bookmarks-list.reorder-container::after {
        transition: none !important;
        animation: none !important;
    }
    /* Skeleton shimmer → static bar */
    .recent-bookmarks-skeleton-name,
    .recent-bookmarks-skeleton-detail,
    .recent-bookmarks-skeleton-recency,
    .recent-bookmarks-skeleton-opens { animation: none !important; }
    /* Page / category / bookmark entrance animations */
    #dashboard-layout.page-transition,
    .category.animate-enter,
    .bookmark-link.animate-enter,
    .bookmark-inline-form { animation: none !important; transform: none !important; opacity: 1 !important; }

    /* Near-zero rather than `none`, matching reduced-motion.css: each of these
       classes is removed again by an `animationend` listener with { once: true }
       (bookmark-move-out in dashboard-inline-edit, bookmark-copy-flash in four
       copy paths, bookmark-pulse in dashboard-bookmark-rows, deep-link-focus in
       dashboard-page-nav). `animation: none` fires no event, so the listener
       never ran and the class stayed on the row for the rest of the session.
       There is nothing to see either way — the animation is over in a frame. */
    .bookmark-move-out { animation-duration: 0.01ms !important; transform: none !important; opacity: 1 !important; }
    .bookmark-link.bookmark-deep-link-focus,
    .bookmark-link.bookmark-copy-flash::after,
    .bookmark-link.bookmark-flash::after,
    .layout-launcher .bookmark-link.bookmark-pulse {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
    }
    /* Decorative looping, nothing waits on it */
    .health-link a { animation: none !important; }
    /* Promo balloons (entrance + float) */
    .dashboard-feature-promo-balloon,
    .dashboard-g-jump-promo-balloon,
    .dashboard-grid-kbd-promo-balloon,
    .dashboard-smart-collection-promo-balloon,
    .dashboard-search-promo-balloon,
    .dashboard-search-promo-badge { animation: none !important; }
    /* Date popover entrance */
    .date-popover { animation: none !important; }
}



/* ─── Row tag chips ──────────────────────────────────────────────────────── */

/* Inside the link, not a grid column: .bookmark-link is a subgrid whose columns
   line up across every category on the page, so a new column would move every
   row. Same chip as the inbox (.inbox-item-tag) and Config (.config-bm-tag-chip)
   — a tag should look like a tag wherever it is listed. */
.bookmark-tag-strip {
    display: inline-flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: var(--space-1, 0.25rem);
    margin-left: var(--space-1-5, 0.4rem);
    min-width: 0;
    /* Never at the cost of the name: the strip gives way first. */
    flex: 0 1 auto;
    overflow: hidden;
}

.bookmark-tag-chip {
    padding: var(--space-hairline, 0.05rem) var(--space-2, 0.45rem);
    border-radius: var(--radius-pill, 999px);
    border: 1px solid var(--border-primary);
    background: var(--background-secondary);
    color: var(--text-secondary);
    font-size: var(--text-2xs, 0.68rem);
    font-weight: var(--font-weight-normal, 400);
    line-height: 1.5;
    white-space: nowrap;
    max-width: 8rem;
    overflow: hidden;
    text-overflow: ellipsis;
}

.bookmark-tag-chip--more {
    font-variant-numeric: tabular-nums;
    opacity: 0.75;
}

/* Terminal and list presets are deliberately dense; a chip there costs more
   than it says, so it drops to text weight instead of a pill. */
.dashboard-grid.layout-terminal .bookmark-tag-chip,
.dashboard-grid.layout-list .bookmark-tag-chip {
    border: 0;
    background: none;
    padding: 0 var(--space-hairline, 0.15rem);
    opacity: 0.7;
}

@media (max-width: 720px) {
    /* On a phone the name is the only thing with room. */
    .bookmark-tag-strip { display: none; }
}

/* g is a chord: for three seconds after it, the next key means "top" (g),
   "first pinned" (p) or a category number. Nothing said so, so the only way to
   learn it was to press something and watch. The pill is the key itself, which
   needs no translation. */
body[data-g-chord="armed"]::after {
    content: "g…";
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    z-index: 60;
    padding: var(--space-1, 0.2rem) var(--space-2, 0.55rem);
    border: 1px solid color-mix(in srgb, var(--accent-primary) 40%, transparent);
    border-radius: var(--radius-pill, 999px);
    background: color-mix(in srgb, var(--accent-primary) 14%, var(--background-secondary));
    color: var(--accent-primary);
    font-family: var(--font-family-main);
    font-size: var(--text-xs, 0.75rem);
    letter-spacing: 0.04em;
    pointer-events: none;
}

/* The key legend under the grid, matching the one health and inbox carry under
   their feeds. Decorative: every key it lists is in the cheat sheet, and the
   rows announce what they do on their own. */
.dashboard-legend {
    display: none;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--space-1, 0.3rem) var(--space-4, 0.9rem);
    margin: var(--space-4, 1rem) 0 var(--space-1, 0.25rem);
    font-size: var(--text-xs, 0.72rem);
    color: var(--text-tertiary);
    opacity: 0.75;
}

/* Only once the keyboard is in use: KeyboardNavigation stamps this at the first
   cursor move and clears it on Enter. Without it the line sat under the grid
   all day — under seven bookmarks, as tall as the thing it explained. No rule
   above it either, which is what made it read as a section of its own. */
body[data-grid-keys="shown"] .dashboard-legend {
    display: flex;
}

.dashboard-legend kbd {
    font-size: var(--text-xs, 0.7rem);
    padding: var(--space-hairline, 0.05rem) var(--space-1-5, 0.32rem);
    border-radius: var(--radius-3, 0.25rem);
    border: 1px solid var(--border-secondary);
    background: var(--background-secondary);
    color: var(--text-secondary);
}

/* The category icon editor: one field beside the header it changes, the same
   shape the page-tab popover uses. Category.Icon has been stored and rendered
   for a long time with nowhere to set it — eight categories on a page meant
   eight identical ⊣ headers. */
.category-icon-popover {
    position: absolute;
    z-index: 70;
    display: flex;
    align-items: center;
    gap: var(--space-1-5, 0.35rem);
    padding: var(--space-1-5, 0.35rem) var(--space-2, 0.45rem);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-4, 0.4rem);
    background: var(--background-secondary);
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
}

.category-icon-input {
    width: 3.2rem;
    padding: var(--space-hairline, 0.15rem) var(--space-1-5, 0.35rem);
    border: 1px solid var(--border-secondary);
    border-radius: var(--radius-3, 0.3rem);
    background: var(--background-primary);
    color: var(--text-primary);
    font: inherit;
    text-align: center;
}

.category-icon-clear {
    padding: var(--space-hairline, 0.15rem) var(--space-2, 0.45rem);
    border: 1px solid var(--border-secondary);
    border-radius: var(--radius-3, 0.3rem);
    background: transparent;
    color: var(--text-secondary);
    font: inherit;
    font-size: var(--text-sm, 0.78rem);
    cursor: pointer;
}

.category-icon-clear:hover,
.category-icon-clear:focus-visible {
    color: var(--accent-primary);
    border-color: color-mix(in srgb, var(--accent-primary) 45%, transparent);
}

/* ── Filter this page (Shift+F) ─────────────────────────────────────────────
   A slim bar above the grid, not a panel: it narrows what is already on screen
   and disappears again with one Escape. Rows and empty categories are hidden
   with a class rather than re-rendered, so the layout, the keyboard cursor and
   any selection survive the filter. */
.grid-filter-bar {
    display: flex;
    align-items: center;
    gap: var(--space-2, 0.5rem);
    /* It sits above the grid rather than in it, so it takes the container's
       width whatever shape the grid itself is in. */
    width: 100%;
    box-sizing: border-box;
    margin: 0 0 var(--space-2, 0.6rem);
    padding: var(--space-1, 0.3rem) var(--space-2, 0.5rem);
    border: 1px solid var(--border-primary);
    border-radius: var(--layout-radius-sm, 8px);
    background: var(--background-secondary);
}

.grid-filter-input {
    flex: 1 1 auto;
    min-width: 0;
    border: 0;
    background: transparent;
    color: var(--text-primary);
    font-family: inherit;
    font-size: var(--text-sm, 0.85rem);
    padding: var(--space-hairline, 0.15rem) 0;
}

.grid-filter-input:focus {
    outline: none;
}

.grid-filter-count {
    color: var(--text-tertiary);
    font-size: var(--text-xs, 0.75rem);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.grid-filter-clear {
    border: 0;
    background: transparent;
    color: var(--text-secondary);
    font-size: var(--text-md, 1rem);
    line-height: 1;
    cursor: pointer;
    padding: 0 var(--space-1, 0.2rem);
}

.grid-filter-clear:hover {
    color: var(--text-primary);
}

.bookmark-link.grid-filter-hidden,
.category.grid-filter-empty {
    display: none !important;
}

/* "Publishes, but nothing new" — the quiet half of Fresh, off unless asked for.
   A dot rather than a number, and dimmer than a count: it says this row takes
   part, which is worth a glance and never worth reading. */
.bookmark-fresh-badge.is-quiet {
    color: color-mix(in srgb, var(--accent-primary) 55%, var(--text-tertiary));
    border-color: var(--border-secondary);
    background: transparent;
    letter-spacing: 0;
}

/*
 * A drawn menu icon sits on the text baseline of a row whose other icons are
 * glyphs, so it is centred as a box rather than laid out as a letter.
 */
.move-popover-check--drawn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.move-popover-check--drawn svg {
    display: block;
}

/* ─── Paste choice modal ─────────────────────────────────────────────────── */
/* Lived in dashboard-inbox.css, which rides in the views bundle and is only
   fetched when Inbox, Health or Config is opened. This modal appears on the
   dashboard itself, on paste, so none of its rules had loaded yet and it drew
   as unstyled markup: the URL overlapping the question, both options collapsed
   into one line. It belongs with the view that shows it. */
.paste-choice-overlay {
    z-index: 12050;
}

.paste-choice-modal {
    max-width: 34rem;
}

.paste-choice-url {
    font-size: var(--text-sm, 0.82rem);
    color: var(--text-secondary);
    word-break: break-all;
    margin-bottom: var(--space-3, 0.65rem);
    padding: var(--space-2, 0.45rem) var(--space-2, 0.55rem);
    border-radius: var(--radius-4, 0.35rem);
    background: var(--background-secondary);
    border: 1px solid var(--border-secondary);
}

.paste-choice-lead {
    margin: 0 0 var(--space-3, 0.75rem);
    color: var(--text-primary);
}

.paste-choice-options {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-3, 0.65rem);
}

.paste-choice-card {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-1, 0.2rem);
    padding: var(--space-3, 0.75rem) var(--space-3, 0.8rem) var(--space-3, 0.8rem);
    border: 1px solid var(--border-primary);
    border-radius: var(--layout-radius-sm, 8px);
    background: var(--background-secondary);
    cursor: pointer;
    text-align: left;
    transition: border-color 0.15s ease, transform 0.15s ease;
}

.paste-choice-card:hover {
    border-color: color-mix(in srgb, var(--accent-primary) 50%, transparent);
    transform: translateY(-1px);
}

.paste-choice-card-icon {
    font-size: var(--text-lg, 1.1rem);
}

.paste-choice-card-title {
    font-weight: var(--font-weight-semibold);
    color: var(--text-primary);
}

.paste-choice-card-hint {
    font-size: var(--text-sm, 0.78rem);
    color: var(--text-secondary);
    line-height: 1.35;
}

.paste-choice-kbd {
    position: absolute;
    top: 0.45rem;
    right: 0.45rem;
    font-size: var(--text-2xs, 0.68rem);
    padding: var(--space-hairline, 0.1rem) var(--space-1, 0.3rem);
    border-radius: var(--radius-3, 0.25rem);
    border: 1px solid var(--border-secondary);
    color: var(--text-tertiary);
}

.paste-choice-remember {
    display: flex;
    align-items: center;
    gap: var(--space-1-5, 0.4rem);
    margin-top: var(--space-3, 0.85rem);
    font-size: var(--text-sm, 0.82rem);
    color: var(--text-secondary);
    cursor: pointer;
}

@media (max-width: 520px) {
    .paste-choice-options {
        grid-template-columns: 1fr;
    }
}




/*
 * A widget's body: a card inside an otherwise frameless grid.
 *
 * Categories are deliberately unframed -- they are lists of links, and a box
 * around each would turn the page into a grid of boxes. A widget is the
 * exception that earns one: it is a panel of figures, not a list, and without
 * an edge it reads as loose text that happens to sit under a heading.
 *
 * The heading stays outside the card, so a widget's title lines up with every
 * category title beside it.
 */
/*
 * The two surfaces a readout is built from, and both are translucent on purpose.
 *
 * Mixed with `transparent` rather than with a named background, so they
 * composite over whatever the card turned out to be. Deriving them from
 * --background-primary was the first attempt and it does not survive contact
 * with the themes: measured across four, the cell came out lighter than the
 * card on two and darker on the other two, and on two of them the difference
 * was under 1% -- invisible. The card is not mixed from --background-primary,
 * so anything mixed from it lands wherever it lands.
 *
 * A tint of the text colour always moves away from the card in the same
 * direction the text does: lighter on a dark theme, darker on a light one.
 * That is one step towards the reader on every theme, which is the whole
 * relationship a readout needs.
 */
.dashboard-widget {
    --dashboard-widget-rule: color-mix(in srgb, var(--text-secondary) 13%, transparent);
    --dashboard-widget-cell: color-mix(in srgb, var(--text-secondary) 7%, transparent);
    --dashboard-widget-cell-hover: color-mix(in srgb, var(--text-secondary) 16%, transparent);
}

.dashboard-widget .dashboard-widget-body {
    padding: var(--space-3, 0.75rem) var(--space-3, 0.85rem);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-5, 9px);
    background: color-mix(in srgb, var(--text-secondary) 5%, transparent);
    /*
     * Reserved before the data arrives.
     *
     * The packed layout measures block heights to place columns; a widget that
     * grew once its figures loaded would shift every column after it.
     */
    min-height: 3.5rem;
    /* The grid's own text size, not the smaller one used for meta lines: these
       are the widget's content, not a note about it. */
    font-size: var(--font-size-text);
    color: var(--text-primary);
}

/*
 * Nothing to report is a result, and it is the quietest one.
 *
 * These used to be an italic sentence at the tile's own text size, which on a
 * dashboard of thirteen tiles meant the ones with nothing to say were as loud
 * as the ones with something. Smaller, dimmer, and centred in the height the
 * tile reserves anyway -- so an empty tile reads as calm rather than as broken.
 *
 * Not italic: the grid is set in a monospaced face, and a slanted monospace at
 * small sizes is measurably harder to read than an upright one.
 */
.dashboard-widget .dashboard-widget-body--empty,
.dashboard-widget-waiting,
.dashboard-widget-empty,
.dashboard-widget-health-waiting {
    display: flex;
    align-items: center;
    margin: 0;
    min-height: 2rem;
    color: var(--text-tertiary);
    font-size: var(--font-size-small);
    font-style: normal;
    line-height: 1.45;
    text-wrap: pretty;
}

/* The health widget: a figure and what it counts, per row. */
.dashboard-widget-health {
    display: flex;
    flex-direction: column;
    /* The hairline between rows is the gap showing through -- see
       .dashboard-widget-stats, which is bound the same way and for the same
       reason. */
    gap: 1px;
    padding: 1px;
    border-radius: var(--radius-4, 7px);
    overflow: hidden;
    background: var(--dashboard-widget-rule);
}

.dashboard-widget-health-row {
    display: flex;
    align-items: baseline;
    gap: var(--space-3, 0.65rem);
    width: 100%;
    padding: var(--space-1-5, 0.34rem) var(--space-2, 0.55rem);
    border: none;
    background: var(--dashboard-widget-cell);
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.dashboard-widget-health-row:hover,
.dashboard-widget-health-row:focus-visible {
    background: var(--dashboard-widget-cell-hover);
}

/*
 * The figure at the size the figures on every other tile are set.
 *
 * This is a list rather than a grid because each row is a filter to click, but
 * it is the same instrument: a number that carries, and a word that explains
 * it. Sized and tracked to match .dashboard-widget-stat-value so a dashboard
 * holding health beside archive reads as one product.
 */
.dashboard-widget-health-value {
    min-width: 2.4em;
    font-size: 1.35em;
    line-height: 1.05;
    font-weight: var(--font-weight-semibold);
    letter-spacing: -0.015em;
    /* Figures read as a column when their digits line up. */
    font-variant-numeric: tabular-nums;
}

/*
 * Tone by what the figure means, not by its size.
 *
 * A zero in the broken row is the best news on the widget and still reads as
 * the broken row -- colouring by value would make "0 broken" green and
 * indistinguishable from "12 healthy".
 */
.dashboard-widget-health-row--bad .dashboard-widget-health-value { color: var(--status-error, var(--accent-error, var(--text-primary))); }
.dashboard-widget-health-row--warn .dashboard-widget-health-value { color: var(--status-warning, var(--accent-warning, var(--text-primary))); }
.dashboard-widget-health-row--good .dashboard-widget-health-value { color: var(--status-success, var(--accent-success, var(--text-primary))); }

.dashboard-widget-health-label {
    color: var(--text-tertiary);
    font-size: var(--font-size-small);
    letter-spacing: 0.03em;
}
.dashboard-widget-health-waiting { margin: 0; color: var(--text-tertiary); font-style: italic; }

/* Config: the widgets tab lists categories too, greyed, to be moved past. */
.config-widget-row--category .config-widget-category-name { color: var(--text-tertiary); }
.config-widget-kind {
    font-size: var(--font-size-small);
    color: var(--text-tertiary);
    margin-left: 8px;
}

/* ─── Widget rows ────────────────────────────────────────────────────────── */
/* Shared by the widgets that list rows — sources, feeds, certificates. One set
   of rules rather than three near-copies: they differ in what they say, not in
   how a row looks. */
.dashboard-widget-rows {
    display: flex;
    flex-direction: column;
    gap: 1px;
}

.dashboard-widget-row {
    display: flex;
    align-items: baseline;
    /*
     * The name takes the slack, so the figure beside it does not.
     *
     * space-between put the gap wherever the name stopped, which left the
     * numbers down the column starting at four different places -- barely
     * visible on a narrow tile and plainly crooked on a wide one, since a
     * wider tile gives the names more room to differ. Measured at 640px the
     * spread was 63px, and the rule below this one already promised these were
     * "lined up down the column".
     */
    gap: 10px;
    width: 100%;
    /* Taller than it was: these rows sit under a block of figures now, and a
       three-pixel row against a 1.5em number reads as an afterthought. It is
       also a click target on most of the tiles that use it. */
    padding: var(--space-1, 0.28rem) var(--space-2, 0.45rem);
    border: 0;
    border-radius: var(--layout-radius-sm, 5px);
    background: transparent;
    color: inherit;
    font: inherit;
    text-align: left;
}

button.dashboard-widget-row {
    cursor: pointer;
}

button.dashboard-widget-row:hover,
button.dashboard-widget-row:focus-visible,
button.dashboard-widget-row.keyboard-selected {
    background: rgba(127, 127, 127, 0.09);
}

/* The cursor has to read as "here" even without :focus-visible, which browsers
   withhold after a click — the same reason the show-more toggle carries a ring
   of its own. Inside a widget it also has to stand apart from the hover above,
   since a reader arrowing through a tile is often hovering something else. */
.dashboard-widget-body button.keyboard-selected {
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-primary) 45%, transparent);
    outline: none;
}

.dashboard-widget-row-name {
    /* min-width:0 or the ellipsis never happens: a flex item will not shrink
       below the width of its own text without it. */
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* The number or the verdict, kept on one line and lined up down the column.
   The width is a floor rather than a fixed size, so "100.0%" and "down" end at
   the same edge without either being clipped. */
.dashboard-widget-row-detail {
    flex: none;
    min-width: 3.4rem;
    text-align: right;
    font-variant-numeric: tabular-nums;
    color: var(--text-tertiary);
}

.dashboard-widget-row--bad .dashboard-widget-row-detail {
    color: var(--status-error, var(--accent-error, var(--text-primary)));
}

.dashboard-widget-row--warn .dashboard-widget-row-detail {
    color: var(--status-warning, var(--accent-warning, var(--text-primary)));
}



/* A figure with its qualifier beside it — the inbox count and how old the
   oldest is, the neglected count and the threshold that produced it. The
   qualifier is load-bearing: "eleven neglected" is a different claim at thirty
   days than at a year. */
.dashboard-widget-headline {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 2px 9px;
    margin-bottom: 8px;
}

.dashboard-widget-headline-value {
    font-size: 1.75em;
    font-weight: var(--font-weight-semibold);
    letter-spacing: -0.02em;
    font-variant-numeric: tabular-nums;
    line-height: 1;
}

/*
 * The qualifier, and it is load-bearing rather than decorative: "eleven
 * neglected" is a different claim at thirty days than at a year. Allowed to
 * wrap under the number on a narrow tile instead of being clipped -- a
 * half-shown condition is worse than a second line.
 */
.dashboard-widget-headline-note {
    color: var(--text-tertiary);
    font-size: var(--font-size-small);
    line-height: 1.4;
    text-wrap: pretty;
}

/* The heartbeat, as a row of ticks rather than a chart: what it has to show is
   a pattern of good and bad, not a value per moment. */
.dashboard-widget-spark {
    display: flex;
    flex: none;
    gap: 1px;
    align-items: flex-end;
    margin-left: 8px;
}

.dashboard-widget-spark-tick {
    width: 2px;
    height: 10px;
    border-radius: var(--radius-1, 1px);
    background: var(--border-secondary);
}

.dashboard-widget-spark-tick--up {
    background: var(--status-success, var(--accent-success, var(--text-secondary)));
}

.dashboard-widget-spark-tick--down {
    background: var(--status-error, var(--accent-error, var(--text-primary)));
}

/* degraded, as health_uptime.go names it: a bucket where some samples answered
   and some did not. */
.dashboard-widget-spark-tick--degraded {
    background: var(--status-warning, var(--accent-warning, var(--text-secondary)));
}

.dashboard-widget-trend {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 8px;
}

.dashboard-widget-trend-value {
    font-size: 1.5em;
    font-variant-numeric: tabular-nums;
    line-height: 1;
}

.dashboard-widget-trend-change {
    font-size: var(--font-size-small);
    font-variant-numeric: tabular-nums;
}

/* Fewer broken links is good news even when the number is still high, so the
   direction carries the colour and the value stays neutral. */
.dashboard-widget-trend-change.is-better {
    color: var(--status-success, var(--accent-success, var(--text-secondary)));
}

.dashboard-widget-trend-change.is-worse {
    color: var(--status-error, var(--accent-error, var(--text-primary)));
}

.dashboard-widget-trend-change.is-level {
    color: var(--text-tertiary);
}

.dashboard-widget-trend-line {
    display: block;
    width: 100%;
    height: 40px;
    margin-top: 4px;
}

a.dashboard-widget-row {
    text-decoration: none;
}

a.dashboard-widget-row:hover,
a.dashboard-widget-row:focus-visible {
    background: rgba(127, 127, 127, 0.09);
}

/* The row that says what did not fit. Quieter than the rows above it — it is a
   pointer, not a finding — but present, because a list that cuts itself off in
   silence answers a different question than the one it appears to answer. */
.dashboard-widget-row--more .dashboard-widget-row-name {
    color: var(--text-tertiary);
    font-size: var(--font-size-small);
}

button.dashboard-widget-row--more {
    cursor: pointer;
}

/* The custom widget's figures: a value with its name under it, side by side.
   Two or three numbers is the common case, so they wrap rather than scroll. */
.dashboard-widget-figures {
    display: flex;
    flex-wrap: wrap;
    gap: 12px 20px;
}

.dashboard-widget-figure {
    display: flex;
    flex-direction: column;
    gap: 1px;
}

.dashboard-widget-figure-value {
    font-size: 1.4em;
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
}

.dashboard-widget-figure-label {
    color: var(--text-tertiary);
    font-size: var(--font-size-small);
}

/* A path that stopped matching is marked rather than blank — a blank reads as a
   zero, and zero is a fact. */
.dashboard-widget-figure.is-missing .dashboard-widget-figure-value {
    color: var(--text-tertiary);
}

/*
 * Four shapes, so a tile of figures is not a list the reader has to weigh.
 *
 * Size carries the ranking and nothing else does: no borders, no boxes, no
 * second colour. A figure that matters is bigger than one that gives it
 * context, which is the whole of what the reader needs to be told and is
 * legible on every theme, because it asks the palette for nothing.
 */
.dashboard-widget-figure--large .dashboard-widget-figure-value {
    font-size: 2.1em;
    font-weight: 600;
    letter-spacing: -0.01em;
}

.dashboard-widget-figure--small .dashboard-widget-figure-value {
    font-size: 1em;
    color: var(--text-secondary);
}

/* A meter needs its own column: the bar belongs under the number it measures,
   at the width of the figure rather than of the tile. */
.dashboard-widget-figure--meter {
    flex: 1 1 8rem;
    min-width: 6rem;
}

.dashboard-widget-figure--meter .dashboard-widget-meter {
    margin-top: 6px;
}

/*
 * Wide enough to put the headline figure on its own line.
 *
 * The same breakpoint the panel's other layouts use, and for the same reason:
 * it answers to the width the tile was given, not to the columns the dashboard
 * was asked for. A widget set to two columns that the grid narrowed back to one
 * never reaches this, which is the point -- the figures stay stacked rather
 * than being spread across a width that is not there.
 */
@container (min-width: 22rem) {
    .dashboard-widget-panel .dashboard-widget-figures {
        gap: 14px 26px;
    }

    .dashboard-widget-panel .dashboard-widget-figure--large {
        flex-basis: 100%;
    }
}

.dashboard-widget-asof {
    margin: 8px 0 0;
    color: var(--text-tertiary);
    font-size: calc(var(--font-size-small) * 0.92);
}

/* ─── Widgets that answer to their own width ─────────────────────────────── */
/*
 * A widget is drawn one or two columns wide, and `widgetColumnSpan` narrows a
 * two-column widget back to one whenever the dashboard is only showing one --
 * so config.columns records what was asked for and never what was given. A
 * container query reads the width that actually happened, which is also the
 * right answer on a phone, where the width setting cannot be reached at all.
 *
 * Scoped to this element rather than to .dashboard-widget-body: the nine tiles
 * that shipped before this were laid out in a plain block and should stay in
 * one.
 */
.dashboard-widget-panel {
    container-type: inline-size;
    display: flex;
    flex-direction: column;
    gap: var(--space-2, 0.5rem);
}

/*
 * The figures: two abreast when narrow, four when there is room.
 *
 * A grid rather than a wrapping flex row, so the labels line up in columns
 * instead of drifting with the width of the number above them -- four figures
 * that do not share a baseline read as four unrelated facts.
 */
.dashboard-widget-stats {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    /*
     * Bound as one readout rather than spaced as four facts.
     *
     * The figures sat in open space with a generous gap, and four numbers
     * floating on a card read as four unrelated things -- which on a tile whose
     * whole job is a comparison ("0 kept against 4 lost") is the one reading
     * that must not happen. An inset surface with hairlines between the cells
     * says they belong to each other, and it separates the figures from the
     * list of rows that several of these tiles put underneath.
     */
    gap: 1px;
    padding: 1px;
    border-radius: var(--radius-4, 7px);
    overflow: hidden;
    background: var(--dashboard-widget-rule);
}

/*
 * Four abreast once there is room for it.
 *
 * Answering to the container and not the viewport: a widget is drawn one or
 * two columns wide and narrowed back to one whenever the dashboard is showing
 * one, so the width that matters is the width this tile actually got.
 */
@container (min-width: 22rem) {
    .dashboard-widget-stats--wide {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }
}

/*
 * Each cell paints the tile's own ground, so the 1px grid gap behind them is
 * what draws the hairlines -- one background instead of eight borders that
 * would double up wherever two cells meet.
 */
.dashboard-widget-stat {
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
    padding: var(--space-2, 0.45rem) var(--space-2, 0.55rem);
    border: 0;
    background: var(--dashboard-widget-cell);
    color: inherit;
    font: inherit;
    text-align: left;
}

button.dashboard-widget-stat {
    cursor: pointer;
}

button.dashboard-widget-stat:hover,
button.dashboard-widget-stat:focus-visible,
button.dashboard-widget-stat.keyboard-selected {
    background: color-mix(in srgb, var(--accent-primary) 8%, var(--dashboard-widget-cell));
}

button.dashboard-widget-stat:hover,
button.dashboard-widget-stat:focus-visible {
    background: color-mix(in srgb, var(--text-secondary) 10%, var(--background-primary, #111));
}

/*
 * The figure, and the whole point of a tile.
 *
 * Large, tight and lined up: a dashboard is scanned rather than read, so the
 * number has to carry at a glance and the word under it only has to survive a
 * second look. These were within a hair of the same size before, which is what
 * made a tile read as a paragraph of digits.
 */
.dashboard-widget-stat-value {
    font-size: 1.5em;
    line-height: 1.05;
    font-weight: var(--font-weight-semibold);
    letter-spacing: -0.015em;
    font-variant-numeric: tabular-nums;
    /* Long values -- a size, a version, a date -- shrink rather than push the
       cell beside them out of the grid. */
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

.dashboard-widget-stat-label {
    color: var(--text-tertiary);
    font-size: var(--font-size-small);
    /* Tracked out a little: a short dim word under a large number needs the
       width to read as a caption rather than as a smudge. */
    letter-spacing: 0.03em;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* Tone by what the figure means, never by how large it is -- the same rule the
   health widget follows, and for the same reason: nought lost is the best news
   the tile can carry and still belongs in the lost cell. */
.dashboard-widget-stat--bad .dashboard-widget-stat-value { color: var(--status-error, var(--accent-error, var(--text-primary))); }
.dashboard-widget-stat--warn .dashboard-widget-stat-value { color: var(--status-warning, var(--accent-warning, var(--text-primary))); }
.dashboard-widget-stat--good .dashboard-widget-stat-value { color: var(--status-success, var(--accent-success, var(--text-primary))); }

/*
 * A nought is not an alarm.
 *
 * Tone says which figure this is; it should not also shout when the figure is
 * the good one. Every value used to be painted its row's colour, so a tile
 * reading "0 lost, 0 died, 4 broken" was three reds and the reader had to
 * check all three to find the one that wanted them. Dimming the noughts leaves
 * exactly the numbers that need someone coloured, and the label still says
 * which row it is -- so "0 broken" is still plainly the broken figure.
 *
 * Every tone, the good one included. Painting "0 healthy" in the success
 * colour was the worst case of all: green is the screen's word for "this is
 * fine", and no healthy links is not fine. A nought is dim whatever row it is
 * in, and the colours are left to mean what they say.
 */
.dashboard-widget-stat.is-quiet .dashboard-widget-stat-value,
.dashboard-widget-health-row.is-quiet .dashboard-widget-health-value {
    color: var(--text-tertiary);
    font-weight: var(--font-weight-normal, 400);
}

/*
 * A proportion as a bar, beside the figure and never instead of it.
 *
 * It says how full at a glance and cannot say of what; "42 of 118" is the part
 * a person repeats to themselves afterwards.
 */
.dashboard-widget-meter {
    height: 6px;
    border-radius: var(--radius-2, 3px);
    overflow: hidden;
    /* A track that can be seen when the fill is short: at four pixels on a
       16%-tint ground the empty part of the bar was invisible, so a bar at 5%
       looked like a stray mark rather than like a bar that is nearly empty. */
    background: color-mix(in srgb, var(--text-secondary) 22%, transparent);
}

.dashboard-widget-meter-fill {
    display: block;
    height: 100%;
    border-radius: inherit;
    transition: width 240ms ease;
}

.dashboard-widget-meter-fill--good { background: var(--status-success, var(--accent-success, var(--text-secondary))); }
.dashboard-widget-meter-fill--warn { background: var(--status-warning, var(--accent-warning, var(--text-secondary))); }
.dashboard-widget-meter-fill--bad { background: var(--status-error, var(--accent-error, var(--text-secondary))); }

/* What the tile assumed, or what happens next. Quieter than a row, because it
   is a condition on the figures rather than one of them. */
.dashboard-widget-footnote {
    margin: 2px 0 0;
    color: var(--text-tertiary);
    font-size: var(--font-size-small);
}

.dashboard-widget-footnote--bad { color: var(--status-error, var(--accent-error, var(--text-primary))); }
.dashboard-widget-footnote--warn { color: var(--status-warning, var(--accent-warning, var(--text-primary))); }
.dashboard-widget-footnote--good { color: var(--status-success, var(--accent-success, var(--text-secondary))); }

.dashboard-widget-panel .dashboard-widget-headline {
    margin-bottom: 0;
}

/*
 * Where the two widths actually fall, since the numbers are not arbitrary.
 *
 * A grid column is bounded at 250px..360px (--dashboard-column-min/max), and
 * the body takes 1.7rem of padding plus its border off that -- so a one-column
 * widget's panel is 221px..331px, and a two-column one is that doubled plus a
 * 1.5rem gap: 487px at its narrowest, on a wide screen carrying six columns.
 *
 * 24rem and 28rem therefore sit in the gap between the two, with room on both
 * sides: no one-column widget can reach them and no two-column widget can miss
 * them. rem is safe here because nothing in this app changes the root size --
 * the font-size setting scales its own variables against a fixed root.
 */
@container (min-width: 24rem) {
    .dashboard-widget-stats {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }
}

/*
 * The rows, paired when the tile is wide.
 *
 * The second column of a two-column widget was otherwise white space beside a
 * single file of short rows, and a row is a name with a figure at the end --
 * exactly the shape that pairs without becoming a table.
 */
@container (min-width: 28rem) {
    .dashboard-widget-rows--pairs {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 2px var(--space-5, 1.4rem);
        align-content: start;
    }

    /* The count of what did not fit belongs to the whole list, not to whichever
       column it happened to land in. */
    .dashboard-widget-rows--pairs .dashboard-widget-row--more {
        grid-column: 1 / -1;
    }
}

/* ==== css/dashboard-bookmark-row.css ==== */
/* Bookmark row hover / selection — loaded after dashboard.css so layout presets cannot override */

:root {
    --bookmark-row-hover-bg: linear-gradient(
        90deg,
        color-mix(in srgb, var(--accent-primary) 22%, var(--background-secondary)) 0%,
        color-mix(in srgb, var(--accent-primary) 14%, var(--background-primary)) 48%,
        color-mix(in srgb, var(--accent-primary) 6%, var(--background-primary)) 100%
    );
    --bookmark-row-selected-bg: linear-gradient(
        90deg,
        color-mix(in srgb, var(--accent-primary) 30%, var(--background-secondary)) 0%,
        color-mix(in srgb, var(--accent-primary) 18%, var(--background-primary)) 48%,
        color-mix(in srgb, var(--accent-primary) 8%, var(--background-primary)) 100%
    );
    --bookmark-row-dimmed-bg: linear-gradient(
        90deg,
        color-mix(in srgb, var(--accent-primary) 12%, var(--background-secondary)) 0%,
        color-mix(in srgb, var(--accent-primary) 7%, var(--background-primary)) 48%,
        color-mix(in srgb, var(--accent-primary) 3%, var(--background-primary)) 100%
    );

    /* The 100% stop of each gradient above, on its own.
     *
     * In "hover" mode the shortcut label floats in over the right end of the
     * row, which is where a long name has been truncated to -- so the label and
     * the fade under it have to be painted in the colour the row actually is at
     * that edge. A flat --background-primary would be close but visibly flat
     * against a tinted row, and these three keep the two in step by definition:
     * change a gradient's last stop and you change this with it.
     *
     * Not derived from the gradients themselves because CSS cannot sample one.
     * layout-modern-tokens.css overrides all three with the modern percentages,
     * the same way it already aliases the gradients. */
    --bookmark-row-edge-hover: color-mix(in srgb, var(--accent-primary) 6%, var(--background-primary));
    --bookmark-row-edge-selected: color-mix(in srgb, var(--accent-primary) 8%, var(--background-primary));
    --bookmark-row-edge-dimmed: color-mix(in srgb, var(--accent-primary) 3%, var(--background-primary));

    /* Whichever of the three the row is wearing right now. Plain page
       background until a state rule says otherwise. */
    --bookmark-row-edge: var(--background-primary);
}

.bookmark-link:not(.bookmark-inline-editing) {
    transition:
        background 0.16s ease,
        box-shadow 0.16s ease,
        border-color 0.16s ease,
        color 0.16s ease;
}

body.no-animations .bookmark-link:not(.bookmark-inline-editing) {
    transition: none;
}

/* Status rows: keep extra inset so the left status bar does not crowd favicons. */
.bookmarks-list[data-bookmarks-list="true"]:not([data-smart-collection="true"]) .bookmark-link:is(.status-online, .status-offline, .status-checking):not(.bookmark-inline-editing) {
    padding-left: calc(var(--bookmark-row-start-pad, 6px) + 4px);
    border-radius: var(--layout-radius-sm, 8px);
}

.bookmark-link:is(:hover, .keyboard-selected, :focus-within):not(.bookmark-inline-editing) {
    color: var(--text-primary);
    background: var(--bookmark-row-hover-bg);
    --bookmark-row-edge: var(--bookmark-row-edge-hover);
    border-radius: var(--layout-radius-sm, 8px);
}

.bookmark-link:is(.keyboard-selected, :focus-within):not(.bookmark-inline-editing) {
    background: var(--bookmark-row-selected-bg);
    --bookmark-row-edge: var(--bookmark-row-edge-selected);
}

.bookmark-link:is(:hover, .keyboard-selected, :focus-within):not(.bookmark-inline-editing) .bookmark-superscript-badge,
.bookmark-link:is(:hover, .keyboard-selected, :focus-within):not(.bookmark-inline-editing) .status-text,
.bookmark-link:is(:hover, .keyboard-selected, :focus-within):not(.bookmark-inline-editing) .bookmark-shortcut {
    background: transparent;
    border-color: color-mix(in srgb, var(--accent-primary) 35%, var(--border-secondary));
    color: var(--text-secondary);
}

a.bookmark-open:focus-visible {
    outline: none;
}

/* Status rows: tint idle rows only; hover/selection use default bookmark chrome */
body:not(.status-color-lock) .bookmarks-list[data-bookmarks-list="true"]:not([data-smart-collection="true"]) .bookmark-link.status-offline:not(:hover):not(.keyboard-selected):not(:focus-within):not(.bookmark-inline-editing) {
    color: var(--status-offline-fg, var(--accent-error, #ef4444));
    background: var(--status-offline-bg, color-mix(in srgb, var(--accent-error, #ef4444) 24%, var(--background-primary)));
    box-shadow: inset 3px 0 0 var(--status-offline-bar, var(--accent-error, #ef4444));
}

body:not(.status-color-lock) .bookmarks-list[data-bookmarks-list="true"]:not([data-smart-collection="true"]) .bookmark-link.status-checking:not(:hover):not(.keyboard-selected):not(:focus-within):not(.bookmark-inline-editing) {
    color: var(--status-checking-fg, var(--accent-warning, #f59e0b));
    background: var(--status-checking-bg, color-mix(in srgb, var(--accent-warning, #f59e0b) 16%, var(--background-primary)));
}

/* While the mouse explores bookmarks, soften the stale keyboard selection until the next keyboard move. */
body.bookmark-kbd-selection-dimmed .bookmark-link.keyboard-selected:not(:hover):not(.bookmark-inline-editing) {
    background: var(--bookmark-row-dimmed-bg);
    --bookmark-row-edge: var(--bookmark-row-edge-dimmed);
}

body.bookmark-kbd-selection-dimmed .bookmark-link.keyboard-selected:not(:hover):not(.bookmark-inline-editing) .bookmark-superscript-badge,
body.bookmark-kbd-selection-dimmed .bookmark-link.keyboard-selected:not(:hover):not(.bookmark-inline-editing) .status-text,
body.bookmark-kbd-selection-dimmed .bookmark-link.keyboard-selected:not(:hover):not(.bookmark-inline-editing) .bookmark-shortcut {
    background: color-mix(in srgb, var(--background-secondary) 80%, transparent);
    border-color: color-mix(in srgb, var(--border-primary) 65%, transparent);
    color: var(--text-tertiary);
}

/* Status rows — explicit interaction chrome (wins over idle status tint / color-lock) */
.bookmarks-list[data-bookmarks-list="true"]:not([data-smart-collection="true"]) .bookmark-link:is(.status-online, .status-offline, .status-checking):is(:hover, .keyboard-selected, :focus-within):not(.bookmark-inline-editing) {
    color: var(--text-primary);
    background: var(--bookmark-row-hover-bg);
    --bookmark-row-edge: var(--bookmark-row-edge-hover);
    box-shadow: none;
}

.bookmarks-list[data-bookmarks-list="true"]:not([data-smart-collection="true"]) .bookmark-link:is(.status-online, .status-offline, .status-checking):is(.keyboard-selected, :focus-within):not(.bookmark-inline-editing) {
    background: var(--bookmark-row-selected-bg);
    --bookmark-row-edge: var(--bookmark-row-edge-selected);
}

/* ─── Shortcut label: the three display modes ─────────────────────────────── */
/*
 * The row always builds the label when the bookmark has a shortcut; which of
 * the three modes is in force is body[data-shortcut-display], so switching is
 * a repaint. "always" is the base styling elsewhere and needs nothing here.
 */

body[data-shortcut-display="never"] .bookmark-shortcut {
    display: none;
}

/*
 * "hover" draws the label as a pseudo-element of the bookmark name, and takes
 * the real one out of the row entirely. Two things follow from that, and both
 * are the point of the mode:
 *
 * The row's shortcut track has no item left in it, so it collapses and the name
 * gets that width -- roughly five characters a row, which is where the
 * truncated names came from.
 *
 * And the label lands exactly where the name's column ends. With response times
 * switched on that is short of the number, which owns the next column; with
 * them off it is the row's right edge. Nothing here has to know which, because
 * the label is a child of the name and `right: 0` means the name's own edge.
 *
 * The alternative -- one absolutely positioned label offset from the row's
 * right by the padding, the gap and the width of the status column -- was tried
 * and is a trap twice over. The column token is 3.35em and em on the label is
 * its own 0.68rem, so the offset came out a third short and the label sat on
 * top of the milliseconds; and even once corrected, the sum quietly stops being
 * true the first time any of those three change.
 */
body[data-shortcut-display="hover"] .bookmarks-list .bookmark-link > .bookmark-shortcut {
    display: none;
}

body[data-shortcut-display="hover"] .bookmarks-list .bookmark-link > .bookmark-open {
    position: relative;
}

/*
 * The padding-left is the fade runway, not spacing: the name is truncated to
 * this column's edge, so the label sits on its last characters and the gradient
 * dissolves one into the other rather than butting them together. It is painted
 * in the row's own edge colour -- see --bookmark-row-edge above -- so a hovered
 * or selected row fades into what it actually is there.
 */
body[data-shortcut-display="hover"] .bookmarks-list .bookmark-link > .bookmark-open[data-shortcut]::after {
    content: attr(data-shortcut);
    position: absolute;
    right: 0;
    top: 50%;
    transform: translateY(-50%);
    padding: 0.1em 0.4em 0.1em 1.75rem;
    font-family: var(--font-family-mono, monospace);
    font-size: var(--text-2xs, 0.68rem);
    font-weight: var(--font-weight-normal);
    line-height: 1.6;
    letter-spacing: 0.03em;
    color: var(--text-tertiary);
    background: linear-gradient(90deg, transparent 0, var(--bookmark-row-edge) 1.75rem);
    white-space: nowrap;
    pointer-events: none;
    user-select: none;
    opacity: 0;
    transition: opacity 0.12s ease;
}

body[data-shortcut-display="hover"] .bookmarks-list .bookmark-link:is(:hover, .keyboard-selected, :focus-within):not(.bookmark-inline-editing) > .bookmark-open[data-shortcut]::after {
    opacity: 1;
    color: var(--text-secondary);
}

body.no-animations[data-shortcut-display="hover"] .bookmarks-list .bookmark-link > .bookmark-open[data-shortcut]::after {
    transition: none;
}

/* ==== css/modal.css ==== */
/* Modal Styles - EXACT copy of shortcut-search */
.modal-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    z-index: 2000;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.2s ease, visibility 0.2s ease;
    background: rgba(0, 0, 0, 0.3);
}

.modal-overlay.show {
    opacity: 1;
    visibility: visible;
}

.modal {
    padding: var(--space-5, 1.5rem) var(--space-6, 2rem);
    max-width: 400px;
    width: 90%;
    border: 1px solid color-mix(in srgb, var(--border-primary) 75%, transparent);
    border-radius: var(--layout-radius-md, 12px);
    background: var(--background-modal);
    box-shadow: 0 24px 64px rgba(0, 0, 0, 0.28), 0 2px 8px rgba(0, 0, 0, 0.12);
    overflow: hidden;
    transform: scale(0.98) translateY(8px);
    opacity: 0;
    transition: transform 0.2s ease, opacity 0.2s ease;
}

.modal-overlay.show .modal {
    transform: scale(1) translateY(0);
    opacity: 1;
}

/* 36rem, not the 52rem the cheat sheet uses: this modal is one column of prose,
   and at 52rem its lines ran to ~84 characters — well past the 45–75 where the
   eye still finds the next line reliably. It sat at 40rem (~61 characters) and
   came down again once the release stopped competing with three panels above
   it; 36rem puts a line at ~58. The cheat sheet keeps 52rem because it is a
   two-column key table that needs the width. */
.modal.whats-new-modal {
    max-height: 90vh;
    max-width: min(36rem, calc(100vw - 2.5rem));
    width: min(36rem, calc(100vw - 2.5rem));
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

.modal.whats-new-modal .modal-body {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding-right: var(--space-1, 0.25rem);
}

/* Config → Help: same What's new shell as dashboard toolbar modals (centered) */
body:has(#config-main) #app-modal.modal-overlay:has(.whats-new-modal) {
    justify-content: center;
    align-items: center;
    padding: max(1.25rem, 2vw);
    box-sizing: border-box;
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
}

body:has(#config-main) #app-modal.modal-overlay:has(.whats-new-modal) .modal {
    width: min(36rem, calc(100vw - 2.5rem));
    max-width: 100%;
    margin: 0;
    max-height: min(70vh, calc(100vh - 4rem));
    transform: translateY(10px) scale(0.98);
    transition: transform 0.15s ease-in, opacity 0.15s ease-in;
}

body:has(#config-main) #app-modal.modal-overlay.show:has(.whats-new-modal) .modal {
    transform: translateY(0) scale(1);
    transition: transform 0.38s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.22s ease-out;
}

body.no-animations:has(#config-main) #app-modal.modal-overlay:has(.whats-new-modal) .modal {
    transform: none !important;
    transition: opacity 0.15s ease !important;
}

.wn-skeleton-stack {
    display: flex;
    flex-direction: column;
    gap: var(--space-2, 0.55rem);
    margin-top: var(--space-2, 0.5rem);
}

.wn-skeleton-line {
    height: 0.72rem;
    border-radius: var(--radius-3, 0.25rem);
    background: linear-gradient(
        90deg,
        color-mix(in srgb, var(--border-primary) 55%, transparent) 0%,
        color-mix(in srgb, var(--border-primary) 25%, transparent) 50%,
        color-mix(in srgb, var(--border-primary) 55%, transparent) 100%
    );
    background-size: 200% 100%;
    animation: wnSkeletonShimmer 1.2s ease-in-out infinite;
}

.wn-skeleton-line--short {
    width: 62%;
}

.wn-empty {
    margin: var(--space-2, 0.5rem) 0;
    color: var(--text-secondary);
    line-height: 1.55;
}

.wn-release-loading {
    margin: var(--space-4, 1rem) 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-2, 0.45rem);
}

@keyframes wnSkeletonShimmer {
    0% { background-position: 100% 0; }
    100% { background-position: -100% 0; }
}

body.no-animations .wn-skeleton-line {
    animation: none;
}

/* ── What's New intro block ─────────────────────────────────────── */
/* One column at every width: the release summary reads as a paragraph, and
   side by side with the Ko-fi block it was squeezed into a narrow column while
   the shorter block left dead space beside it.

   The two blocks are fixed overhead on every open, sitting in the most valuable
   space in the modal, so the spacing around them is kept tight — 1rem + 1.5rem
   pushed the first actual release item below the fold. */
@media (max-width: 42rem) {
    .modal.whats-new-modal .modal-header {
        flex-wrap: wrap;
        align-items: flex-start;
    }

    /* The footer's two halves stop fitting side by side long before the modal
       runs out of room; stacked they still read in the same order. */
    .wn-foot {
        flex-direction: column;
        align-items: flex-start;
    }
}

.modal.whats-new-modal .modal-header {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3, 0.65rem) var(--space-4, 1rem);
    flex-wrap: nowrap;
}

.modal.whats-new-modal .modal-header .modal-title {
    flex: 0 0 auto;
    width: auto;
    margin-right: 0;
}

/* Lives in the footer now, so it reads from the left like the text above it
   rather than right-aligned across the header. */
.wn-update-check-header {
    width: 100%;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-start;
    gap: var(--space-2, 0.55rem);
    min-width: 0;
}

/* An available update is worth marking, but a tinted bordered box in the
   footer would put back exactly the panel this redesign removed from the top.
   A dot in the accent is enough to catch the eye of somebody who has finished
   reading. */
.wn-update-check-header--warn::before {
    content: "";
    flex: 0 0 auto;
    width: 0.42rem;
    height: 0.42rem;
    border-radius: 50%;
    background: var(--accent-primary);
}

.wn-update-check-meta {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-start;
    flex-wrap: wrap;
    gap: var(--space-1-5, 0.35rem) var(--space-2, 0.45rem);
    min-width: 0;
    flex: 1 1 auto;
}

.wn-update-check-dismiss {
    padding: var(--space-hairline, 0.15rem) var(--space-2, 0.45rem);
    border: none;
    border-radius: var(--radius-4, 0.35rem);
    background: transparent;
    color: var(--text-secondary);
    font: inherit;
    font-size: calc(var(--font-size-controls) * 0.92);
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 0.12em;
}

.wn-update-check-dismiss:hover {
    color: var(--text-primary);
}

/* Wraps rather than truncates.

   It was one nowrap line with an ellipsis, which fitted while it had the whole
   header to itself. Beside the Ko-fi button in a narrower modal it started
   cutting off mid-sentence — and "Could not reach GitHub. Try again lat…" is a
   message nobody can act on. */
.wn-update-check-status {
    margin: 0;
    font-size: calc(var(--font-size-controls) * 0.94);
    color: var(--text-secondary);
    line-height: 1.45;
    text-align: left;
    white-space: normal;
    max-width: 100%;
}

.wn-update-check-status--warn { color: var(--accent-primary); }
.wn-update-check-status--ok { color: var(--text-secondary); }
.wn-update-check-status--error { color: var(--accent-error, #d1493f); }
.wn-update-check-status--loading { color: var(--text-secondary); font-style: italic; }

.wn-update-check-link {
    display: inline-block;
    margin-top: 0;
    font-size: var(--font-size-controls);
    color: var(--accent-primary);
    text-decoration: none;
    white-space: nowrap;
    flex-shrink: 0;
}

.wn-update-check-link:hover { text-decoration: underline; }

.wn-kofi-btn {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1-5, 0.4rem);
    padding: var(--space-1, 0.3rem) var(--space-3, 0.75rem);
    background: color-mix(in srgb, var(--accent-primary) 13%, transparent);
    color: var(--accent-primary);
    border: 1px solid color-mix(in srgb, var(--accent-primary) 32%, transparent);
    border-radius: var(--radius-4, 0.4rem);
    text-decoration: none;
    font-size: var(--font-size-controls);
    font-family: var(--font-family-main);
    font-weight: var(--font-weight-normal);
    white-space: nowrap;
    transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
    flex-shrink: 0;
}

.wn-kofi-btn:hover {
    background: color-mix(in srgb, var(--accent-primary) 22%, transparent);
    border-color: color-mix(in srgb, var(--accent-primary) 55%, transparent);
    transform: translateY(-1px);
}

.wn-kofi-btn:active {
    transform: translateY(0);
}

.wn-kofi-icon,
.wn-kofi-label {
    position: relative;
    z-index: 1;
    flex-shrink: 0;
}

/* What's new modal — animated donate CTA with twinkling stars */
.wn-kofi-btn--animated {
    position: relative;
    overflow: visible;
    isolation: isolate;
    animation: wn-kofi-btn-glow 3.2s ease-in-out infinite;
}

.wn-kofi-btn--animated::before {
    content: '';
    position: absolute;
    inset: -1px;
    border-radius: inherit;
    background: linear-gradient(
        105deg,
        transparent 35%,
        color-mix(in srgb, var(--accent-primary) 28%, transparent) 50%,
        transparent 65%
    );
    background-size: 220% 100%;
    animation: wn-kofi-btn-shimmer 3.2s ease-in-out infinite;
    pointer-events: none;
    z-index: 0;
    opacity: 0.85;
}

.wn-kofi-stars {
    position: absolute;
    inset: -0.55rem -0.75rem;
    pointer-events: none;
    z-index: 2;
}

.wn-kofi-star {
    position: absolute;
    line-height: 1;
    color: color-mix(in srgb, var(--accent-primary) 88%, #fff 12%);
    text-shadow: 0 0 6px color-mix(in srgb, var(--accent-primary) 45%, transparent);
    opacity: 0;
    transform: scale(0.5) rotate(0deg);
    animation: wn-kofi-star-twinkle 2.6s ease-in-out infinite;
}

.wn-kofi-star::before {
    content: '★';
    display: block;
    font-size: var(--text-2xs, 0.62rem);
}

.wn-kofi-star:nth-child(1) {
    top: -0.1rem;
    left: 6%;
    animation-delay: 0s;
}

.wn-kofi-star:nth-child(2) {
    top: -0.35rem;
    right: 10%;
    animation-delay: 0.65s;
}

.wn-kofi-star:nth-child(2)::before {
    content: '✦';
    font-size: 0.5rem;
}

.wn-kofi-star:nth-child(3) {
    bottom: -0.15rem;
    left: 18%;
    animation-delay: 1.3s;
}

.wn-kofi-star:nth-child(3)::before {
    font-size: 0.48rem;
}

.wn-kofi-star:nth-child(4) {
    bottom: 0;
    right: 4%;
    animation-delay: 1.95s;
}

.wn-kofi-star:nth-child(4)::before {
    content: '✦';
    font-size: 0.54rem;
}

@keyframes wn-kofi-star-twinkle {
    0%, 100% {
        opacity: 0;
        transform: scale(0.45) rotate(-12deg);
    }
    45%, 55% {
        opacity: 1;
        transform: scale(1.15) rotate(10deg);
    }
}

@keyframes wn-kofi-btn-glow {
    0%, 100% {
        box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent-primary) 0%, transparent);
    }
    50% {
        box-shadow: 0 0 0.65rem 0.12rem color-mix(in srgb, var(--accent-primary) 32%, transparent);
    }
}

@keyframes wn-kofi-btn-shimmer {
    0%, 100% {
        background-position: 120% 0;
    }
    50% {
        background-position: -20% 0;
    }
}

.wn-kofi-btn--animated:hover {
    animation: none;
    box-shadow: 0 0 0.75rem 0.15rem color-mix(in srgb, var(--accent-primary) 38%, transparent);
}

.wn-kofi-btn--animated:hover .wn-kofi-star {
    animation-duration: 1.4s;
}

body.no-animations .wn-kofi-btn--animated,
body.no-animations .wn-kofi-btn--animated::before,
body.no-animations .wn-kofi-star {
    animation: none;
}

body.no-animations .wn-kofi-btn--animated {
    box-shadow: 0 0 0.5rem 0.1rem color-mix(in srgb, var(--accent-primary) 28%, transparent);
}

body.no-animations .wn-kofi-star {
    opacity: 0.75;
    transform: scale(1);
}

/* ── What's New release blocks ─────────────────────────────────── */
/* ===========================================================================
   The release itself.

   Rewritten from a run of near-identical rows into three levels the eye can
   tell apart without reading: one release headline, a group per area, and an
   entry that is a title with an explanation under it.

   The measurement that drove it: 283px of the 918px modal was spent before the
   first release, on three bordered panels that were not about the release —
   and six separate elements shared the accent colour, so nothing it marked
   stood out. Panels are gone; the accent now marks two things.
   =========================================================================== */

.wn-hero {
    padding-bottom: var(--space-4, 1.1rem);
    margin-bottom: var(--space-1-5, 0.4rem);
    border-bottom: 1px solid var(--border-primary);
}

/* The version, set as the headline it is. Nothing else in the modal is this
   size, which is what makes "which release am I reading" answerable at a
   glance rather than by finding a small chip. */
.wn-hero-version {
    margin: 0;
    font-size: calc(var(--font-size-text) * 1.75);
    font-weight: var(--font-weight-semibold);
    line-height: 1.1;
    letter-spacing: -0.01em;
    color: var(--text-primary);
}

.wn-hero-meta {
    margin: var(--space-1, 0.2rem) 0 0;
    font-size: var(--font-size-controls);
    color: var(--text-tertiary);
    letter-spacing: 0.02em;
}

/* modalLead, written per release and previously boxed. A subtitle is what it
   always was. */
.wn-hero-lead {
    margin: var(--space-3, 0.75rem) 0 0;
    font-size: var(--font-size-text);
    line-height: 1.55;
    color: var(--text-primary);
    max-width: 46ch;
}

.wn-group { margin-top: var(--space-5, 1.5rem); }

/* An eyebrow rather than a heading: the group name is a label on what follows,
   and at heading weight it competed with the entry titles it introduces. */
.wn-group-title {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-2, 0.6rem);
    margin: 0 0 var(--space-2, 0.6rem);
    padding-bottom: var(--space-1-5, 0.35rem);
    border-bottom: 1px solid var(--border-primary);
    font-size: calc(var(--font-size-controls) * 0.86);
    font-weight: var(--font-weight-semibold);
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--text-tertiary);
}
.wn-group-count {
    letter-spacing: 0.04em;
    opacity: 0.75;
    font-variant-numeric: tabular-nums;
}

.wn-entries {
    list-style: none;
    padding: 0;
    margin: 0;
}

.wn-entry {
    display: grid;
    grid-template-columns: 2.4rem 1fr;
    gap: 0 var(--space-2, 0.6rem);
    padding: var(--space-2, 0.5rem) 0;
    align-items: start;
}
.wn-entry + .wn-entry {
    border-top: 1px solid color-mix(in srgb, var(--border-primary) 55%, transparent);
}

/* The word, not a dot.

   A filled or hollow dot stood here and was read by nobody: it says "these two
   entries differ" without saying how, and the reader has to be told once what
   the difference is before it means anything. It replaced a chip for two
   reasons, and both are answered rather than reverted.

   Width: the chip was uppercase at body size and cost about seven characters
   of a column deliberately kept to ~63. This one is 0.62rem, tabular-width, in
   a fixed 2.4rem track — a hair over three characters at the column's own size,
   and the text beside it starts at the same place on every row.

   Colour: "new" drew on --accent-success, which on a green accent is the same
   value as --accent-primary — so the badge and the version tag beside it were
   literally the same colour. --accent-info is no escape: on the terminal theme
   it resolves to the same value again. So neither badge takes an accent at
   all. New is the page's own foreground on a solid fill, fix is muted text in
   a hairline outline. Filled against outlined reads at a glance on any theme,
   and neither can collide with a hue it does not use. */
.wn-badge {
    justify-self: start;
    margin-top: var(--space-1-5, 0.34rem);
    padding: var(--space-hairline, 0.08rem) var(--space-1, 0.3rem);
    border-radius: var(--radius-3, 0.25rem);
    font-size: var(--text-2xs, 0.62rem);
    font-weight: var(--font-weight-semibold);
    line-height: 1.5;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.wn-badge--new {
    color: var(--background-primary);
    background: var(--text-primary);
    border: 1px solid var(--text-primary);
}
.wn-badge--fix {
    color: var(--text-tertiary);
    background: transparent;
    border: 1px solid var(--border-primary);
}

.wn-sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

.wn-entry-main { min-width: 0; }

/* The lead sentence, on its own line. It was bold-inside-a-paragraph before —
   ten to twenty bold words, which is long enough that bold stops reading as
   emphasis and the explanation behind it ran straight on. */
.wn-entry-title {
    font-size: var(--font-size-text);
    font-weight: var(--font-weight-semibold);
    line-height: 1.45;
    color: var(--text-primary);
}

.wn-entry-body {
    margin-top: var(--space-hairline, 0.15rem);
    font-size: calc(var(--font-size-text) * 0.95);
    line-height: 1.55;
    color: var(--text-secondary);
    opacity: 0.92;
}
/* Folded at three lines. A release of 110 items is one long scroll however it
   is set; this makes the scroll a list of headlines with detail on request. */
.wn-entry-body.is-folded {
    display: -webkit-box;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.wn-entry-body strong { color: var(--text-primary); }
.wn-entry-body code,
.wn-entry-title code {
    font-family: var(--font-family-main);
    font-size: 0.88em;
    background: var(--background-secondary);
    border: 1px solid var(--border-primary);
    padding: var(--space-hairline, 0.05rem) var(--space-1, 0.28rem);
    color: var(--text-primary);
}

.wn-entry-more {
    margin-top: var(--space-1, 0.2rem);
    padding: 0;
    background: none;
    border: 0;
    border-bottom: 1px dotted currentColor;
    font-family: inherit;
    font-size: calc(var(--font-size-text) * 0.88);
    color: var(--text-tertiary);
    cursor: pointer;
}
.wn-entry-more:hover { color: var(--accent-primary); }

/* Shortcuts keep the warning accent, which is the one place a second colour
   still earns its keep: a key you can press is a different kind of thing from
   a change you can read about. */
.wn-group--keys .wn-group-title { color: var(--accent-warning); }
.wn-entry--keys {
    grid-template-columns: auto 1fr;
    align-items: baseline;
    gap: 0 var(--space-2, 0.55rem);
}
.wn-keycap {
    flex-shrink: 0;
    font-family: var(--font-family-main);
    font-size: 0.82em;
    font-weight: var(--font-weight-semibold);
    color: var(--accent-warning);
    background: color-mix(in srgb, var(--accent-warning) 12%, var(--background-primary));
    border: 1px solid color-mix(in srgb, var(--accent-warning) 45%, transparent);
    border-radius: var(--layout-radius-sm, 5px);
    padding: var(--space-hairline, 0.1rem) var(--space-1-5, 0.4rem);
    white-space: nowrap;
    min-width: 2.2rem;
    text-align: center;
}
.wn-entry--keys .wn-entry-title { font-weight: var(--font-weight-normal, 400); }

/* ---- Everything older, one row each ------------------------------------- */

.wn-earlier {
    margin-top: var(--space-6, 2rem);
    padding-top: var(--space-4, 1.1rem);
    border-top: 1px solid var(--border-primary);
}

.wn-earlier-title {
    margin: 0 0 var(--space-1-5, 0.4rem);
    font-size: calc(var(--font-size-controls) * 0.86);
    font-weight: var(--font-weight-semibold);
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--text-tertiary);
}

.wn-earlier-list {
    list-style: none;
    padding: 0;
    margin: 0;
}

.wn-earlier-row {
    display: flex;
    align-items: baseline;
    gap: var(--space-2, 0.6rem);
    width: 100%;
    padding: var(--space-2, 0.45rem) 0;
    background: none;
    border: 0;
    border-bottom: 1px solid color-mix(in srgb, var(--border-primary) 55%, transparent);
    font-family: inherit;
    font-size: var(--font-size-controls);
    color: var(--text-secondary);
    text-align: left;
    cursor: pointer;
}
.wn-earlier-row:hover { color: var(--text-primary); }
.wn-earlier-tag {
    font-weight: var(--font-weight-semibold);
    color: var(--text-primary);
    white-space: nowrap;
}
.wn-earlier-date {
    flex: 1;
    color: var(--text-tertiary);
    min-width: 0;
}
.wn-earlier-chevron {
    color: var(--text-tertiary);
    transition: transform 0.15s ease;
}
.wn-earlier-row[aria-expanded="true"] .wn-earlier-chevron {
    transform: rotate(90deg);
}
body.no-animations .wn-earlier-chevron { transition: none; }

/* An opened release is indented behind a rule, so where it starts and where
   the next row begins is a matter of looking rather than counting. This is the
   one place the old left-hand accent rule still earns its keep: it marks a
   containment that is really there. */
.wn-earlier-body {
    padding: var(--space-hairline, 0.1rem) 0 var(--space-4, 1rem) var(--space-4, 0.9rem);
    margin-left: var(--space-hairline, 0.15rem);
    border-left: 2px solid color-mix(in srgb, var(--accent-primary) 35%, transparent);
}
.wn-earlier-body .wn-group:first-child { margin-top: var(--space-3, 0.8rem); }
.wn-earlier-body .wn-group-title { border-bottom-color: color-mix(in srgb, var(--border-primary) 70%, transparent); }

/* ---- The footer, where the asking happens ------------------------------- */

/* Sticky so it is always reachable without ever being the first thing read.
   Both halves used to be bordered panels above the first release: the update
   status across the header in the accent colour, and the support request
   directly under it. */
.wn-foot {
    position: sticky;
    bottom: 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2, 0.6rem) var(--space-4, 1rem);
    flex-wrap: wrap;
    margin-top: var(--space-5, 1.6rem);
    padding: var(--space-2, 0.6rem) 0 var(--space-1, 0.2rem);
    border-top: 1px solid var(--border-primary);
    background: var(--background-primary);
}
.wn-foot-update { min-width: 0; flex: 1 1 12rem; }
.wn-foot-update:empty { display: none; }

/* Room for the sparkles.

   The stars are drawn 0.75rem outside the button on every side, which used to
   fall inside the padding of the panel the button sat in. Flush against the
   right edge of the footer that overhang became eight pixels of scrollable
   width -- a horizontal scrollbar under a modal that has nothing to scroll
   sideways to. The gap is where the decoration lives, so it does not read as
   a misalignment. */
.wn-foot .wn-kofi-btn { margin-right: var(--space-3, 0.75rem); }

/* Smaller here than in the Config panels that share this class.

   There the button is the point of the panel it sits in. Here it is the last
   line of a modal somebody opened to read release notes: still present, still
   reachable, but sized like the footnote it now is rather than like the
   heaviest thing on screen. */
.wn-foot .wn-kofi-btn {
    gap: var(--space-1, 0.3rem);
    padding: var(--space-hairline, 0.18rem) var(--space-2, 0.5rem);
    border-radius: var(--radius-3, 0.3rem);
    font-size: calc(var(--font-size-controls) * 0.88);
}
.wn-foot .wn-kofi-icon {
    width: 13px;
    height: 13px;
}


.modal.keyboard-cheat-sheet-modal {
    max-width: min(52rem, calc(100vw - 2.5rem));
    width: min(52rem, calc(100vw - 2.5rem));
    margin: auto;
    max-height: 90vh;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    padding: var(--space-4, 1rem) 1.2rem;
}

.modal.keyboard-cheat-sheet-modal .modal-body {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding-right: var(--space-1, 0.25rem);
}

.modal.inbox-intro-modal {
    max-width: min(40rem, calc(100vw - 2.5rem));
    width: min(40rem, calc(100vw - 2.5rem));
    margin: auto;
    max-height: 90vh;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    padding: var(--space-4, 1rem) 1.2rem;
}

.modal.inbox-intro-modal .modal-body {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding-right: var(--space-1, 0.25rem);
}

.inbox-intro-content {
    display: flex;
    flex-direction: column;
    gap: var(--space-4, 1.1rem);
}

.inbox-intro-hero {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3, 0.85rem);
    padding: var(--space-3, 0.85rem) var(--space-4, 0.95rem);
    border-radius: var(--layout-radius-sm, 8px);
    border: 1px solid color-mix(in srgb, var(--accent-primary) 28%, var(--border-primary));
    border-left: 3px solid var(--accent-primary);
    background: color-mix(in srgb, var(--accent-primary) 8%, var(--background-secondary));
}

.inbox-intro-hero-icon {
    flex-shrink: 0;
    font-size: 1.65rem;
    line-height: 1;
    margin-top: var(--space-hairline, 0.05rem);
}

.inbox-intro-hero-text {
    min-width: 0;
}

.inbox-intro-lead {
    margin: 0;
    color: var(--text-primary);
    font-size: var(--font-size-controls);
    line-height: 1.55;
}

.inbox-intro-section {
    display: flex;
    flex-direction: column;
    gap: var(--space-2, 0.55rem);
    margin: 0;
    padding: 0;
}

.inbox-intro-section-title {
    display: flex;
    align-items: center;
    gap: var(--space-2, 0.45rem);
    margin: 0;
    font-size: var(--font-size-category);
    font-weight: var(--font-weight-semibold);
    color: var(--text-primary);
    letter-spacing: 0.01em;
}

.inbox-intro-section-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.35rem;
    height: 1.35rem;
    font-size: var(--text-base, 0.95rem);
    line-height: 1;
    flex-shrink: 0;
}

.inbox-intro-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: var(--space-2, 0.5rem);
}

.inbox-intro-item {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3, 0.65rem);
    padding: var(--space-2, 0.55rem) var(--space-3, 0.65rem);
    border-radius: var(--layout-radius-sm, 8px);
    border: 1px solid var(--border-primary);
    background: var(--background-secondary);
    line-height: 1.5;
}

.inbox-intro-item-icon {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.65rem;
    height: 1.65rem;
    border-radius: var(--radius-4, 0.4rem);
    font-size: var(--text-md, 1rem);
    line-height: 1;
    background: color-mix(in srgb, var(--accent-primary) 10%, var(--background-primary));
    border: 1px solid color-mix(in srgb, var(--accent-primary) 18%, var(--border-primary));
}

.inbox-intro-item-text {
    flex: 1;
    min-width: 0;
    font-size: var(--font-size-text);
    color: var(--text-secondary);
}

.inbox-intro-item-text strong {
    color: var(--text-primary);
    font-weight: var(--font-weight-semibold);
}

.inbox-intro-item-text code,
.inbox-intro-item-text kbd,
.inbox-intro-shortcut code,
.inbox-intro-shortcut kbd {
    font-family: var(--font-family-main);
    font-size: 0.88em;
    background: var(--background-primary);
    border: 1px solid var(--border-primary);
    padding: var(--space-hairline, 0.05rem) var(--space-1, 0.3rem);
    border-radius: var(--radius-3, 0.25rem);
    color: var(--text-primary);
}

.inbox-intro-shortcuts {
    display: grid;
    gap: var(--space-2, 0.45rem);
}

.inbox-intro-shortcut {
    display: flex;
    align-items: center;
    gap: var(--space-2, 0.5rem);
    padding: var(--space-2, 0.5rem) var(--space-3, 0.65rem);
    border-radius: var(--layout-radius-sm, 8px);
    border: 1px solid color-mix(in srgb, var(--accent-primary) 22%, var(--border-primary));
    background: color-mix(in srgb, var(--accent-primary) 6%, var(--background-secondary));
    font-size: var(--font-size-text);
    color: var(--text-secondary);
    line-height: 1.45;
}

.inbox-intro-shortcut kbd,
.inbox-intro-shortcut code {
    flex-shrink: 0;
    font-weight: var(--font-weight-semibold);
}

.modal.inbox-intro-modal .modal-header .modal-title {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1-5, 0.35rem);
}

/* Page overview — compact centered modal (dashboard header pages button) */
.modal.page-overview-modal {
    max-width: min(22rem, calc(100vw - 2.5rem));
    width: min(22rem, calc(100vw - 2.5rem));
    margin: auto;
    max-height: min(70vh, calc(100vh - 4rem));
    overflow: hidden;
    display: flex;
    flex-direction: column;
    padding: var(--space-4, 1rem) var(--space-4, 1.15rem);
}

.modal.page-overview-modal .modal-body {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding-right: var(--space-1, 0.2rem);
    /* The last child ends flush with this scroll box, and overflow clipping eats
       the final pixel row — the name input's bottom border went missing. A pixel
       of padding gives that border somewhere to land. */
    padding-bottom: 1px;
    margin-top: var(--space-2, 0.55rem);
    margin-bottom: var(--space-1, 0.25rem);
}

.page-overview-modal-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-2, 0.45rem);
    max-height: min(52vh, 24rem);
    overflow-y: auto;
}

.page-overview-modal-item {
    margin: 0;
    padding: 0;
}

.page-overview-modal-link {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    gap: var(--space-3, 0.65rem);
    width: 100%;
    margin: 0;
    padding: var(--space-2, 0.55rem) var(--space-3, 0.7rem);
    border: 1px solid var(--border-primary);
    border-radius: var(--layout-radius-sm, 8px);
    background: var(--background-primary);
    cursor: pointer;
    font: inherit;
    font-size: var(--font-size-text);
    text-align: left;
    color: var(--text-primary);
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
    transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}

.page-overview-modal-link:hover,
.page-overview-modal-link:focus-visible,
.page-overview-modal-item.is-focused .page-overview-modal-link {
    background: color-mix(in srgb, var(--accent-primary) 9%, transparent);
    border-color: color-mix(in srgb, var(--accent-primary) 24%, transparent);
    outline: none;
}

.page-overview-modal-item.is-current .page-overview-modal-name {
    color: var(--accent-primary);
    font-weight: var(--font-weight-semibold);
}

.page-overview-modal-lead {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1, 0.25rem);
    min-width: 1.5rem;
    justify-content: flex-end;
}

.page-overview-modal-num {
    font-size: 0.78em;
    color: var(--text-tertiary);
    font-variant-numeric: tabular-nums;
}

.page-overview-modal-body {
    min-width: 0;
}

.page-overview-modal-name {
    display: block;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.page-overview-modal-count {
    font-size: 0.82em;
    color: var(--text-tertiary);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* ─── "New page" — the overlay's one action ──────────────────────────────────
   It borrows .page-overview-modal-link for its shape so it lines up with the
   pages above, and is set apart by the rule above it and a muted, dashed
   border: a thing to do, not a page to go to. */

.page-overview-modal-actions {
    margin-top: var(--space-2, 0.55rem);
    padding-top: var(--space-2, 0.55rem);
    border-top: 1px solid var(--border-primary);
}

.page-overview-modal-new {
    border-style: dashed;
    color: var(--text-secondary);
}

.page-overview-modal-new[hidden] {
    display: none;
}

.page-overview-modal-plus {
    font-size: 1em;
    color: var(--text-secondary);
}

/* The shortcut hint sits where a page shows its bookmark count. */
.page-overview-modal-hintkey {
    opacity: 0.75;
}

/* The name row takes the button's place inside the same action block, so
   opening it does not change the modal's height. */
.page-overview-modal-actions .bookmark-inline-create {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1-5, 0.35rem);
}

.page-overview-modal-actions .bookmark-inline-create[hidden] {
    display: none;
}

.page-overview-modal-actions .bookmark-inline-create-input {
    flex: 1 1 8rem;
    min-width: 0;
    padding: var(--space-1-5, 0.4rem) var(--space-2, 0.5rem);
    border: 1px solid var(--border-primary);
    border-radius: var(--layout-radius-sm, 8px);
    background: var(--background-primary);
    color: var(--text-primary);
    font: inherit;
    font-size: var(--font-size-controls);
}

/* Colour and size come from .bookmark-inline-conflict in dashboard.css; only
   the full-width placement in this flex row belongs here. */
.page-overview-modal-actions .bookmark-inline-conflict {
    flex: 1 0 100%;
    margin-top: 0;
}

/* Fix hidden attribute override */
.modal-overlay[hidden] {
    display: none !important;
    opacity: 0 !important;
    visibility: hidden !important;
}

.modal-header {
    display: flex;
    align-items: center;
    font-size: var(--font-size-bookmark);
    font-weight: var(--font-weight-semibold);
    justify-content: flex-start;
}

.modal-title {
    margin-right: var(--space-2, 0.5rem);
    color: var(--text-primary);
}

.modal-body {
    margin-top: var(--space-5, 1.5rem);
    margin-bottom: var(--space-5, 1.5rem);
}

/* New bookmark modal textarea */
.new-bookmark-textarea {
    width: 100%;
    min-height: 4rem;
    padding: var(--space-2, 0.5rem);
    font-size: var(--text-base, 0.95rem);
    resize: vertical;
    margin-top: var(--space-1-5, 0.35rem);
}

.modal-text {
    min-width: 2ch;
    color: var(--text-primary);
    display: block;
}

/* The existing bookmark inside the "already saved" dialog: a link, because
   knowing where a copy is only helps if you can go there from here. */
.duplicate-existing {
    margin-top: var(--space-2, 0.6rem);
}

.duplicate-existing a {
    color: var(--accent-primary);
    word-break: break-word;
}

.duplicate-existing-hint {
    display: block;
    font-size: var(--text-xs, 0.75rem);
    color: var(--text-secondary);
}

.danger {
    color: var(--accent-error);
    font-weight: var(--font-weight-bold);
}

.modal-actions {
    display: flex;
    flex-direction: row;
    gap: var(--space-4, 1rem);
    justify-content: center;
    align-items: center;
}

.modal-button {
    padding: var(--space-2, 0.5rem) var(--space-4, 1rem);
    cursor: pointer;
    transition: all 0.3s ease;
    border: 1px solid var(--border-primary);
    border-radius: var(--layout-radius-sm, 6px);
    background: transparent;
    text-align: center;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 1;
    min-width: 0;
    color: var(--text-primary);
    font-family: var(--font-family-main);
    
    /* Remove tap highlight on mobile */
    -webkit-tap-highlight-color: transparent;
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

.modal-button:hover,
.modal-button.selected {
    color: var(--text-primary);
    transform: translateX(4px);
}

.modal-button-prefix {
    font-weight: var(--font-weight-bold);
    margin-right: var(--space-4, 1rem);
    min-width: 3ch;
    font-size: var(--font-size-controls);
    color: var(--accent-success);
}

.modal-button-name {
    font-size: var(--font-size-text);
    font-weight: var(--font-weight-semibold);
}

/* Danger button styles (red) */
.modal-button.danger {
    font-weight: var(--font-weight-bold);
    border: 1px solid var(--accent-error);
    color: var(--accent-error);
}

.modal-button.danger:hover,
.modal-button.danger.selected {
    background-color: var(--accent-error);
    color: var(--background-primary);
    transform: translateX(4px);
}

body.no-animations .modal-overlay,
body.no-animations .modal,
body.no-animations .modal-button {
    transition: none !important;
}

body.no-animations .modal {
    transform: none !important;
    opacity: 1 !important;
}

body.no-animations .modal-button:hover,
body.no-animations .modal-button.selected,
body.no-animations .modal-button.danger:hover,
body.no-animations .modal-button.danger.selected {
    transform: none !important;
}

.modal-button.danger .modal-button-prefix {
    color: var(--accent-error);
}

/* ESC to close hint */
.modal-esc-hint {
    text-align: center;
    margin-top: var(--space-3, 0.75rem);
    font-size: var(--text-2xs, 0.68rem);
    color: var(--text-tertiary);
    opacity: 0.6;
    user-select: none;
    pointer-events: none;
}

.modal-esc-hint kbd {
    font-family: var(--font-family-main);
    font-size: var(--text-2xs, 0.65rem);
    font-weight: var(--font-weight-semibold);
    padding: var(--space-hairline, 0.1rem) var(--space-1-5, 0.32rem);
    border: 1px solid var(--border-primary);
    background: var(--background-secondary);
    color: var(--text-tertiary);
    border-radius: var(--radius-2, 3px);
    letter-spacing: 0.04em;
    line-height: 1;
    vertical-align: baseline;
}

.modal-esc-hint:empty {
    display: none;
}

@media (pointer: coarse) {
    .modal-esc-hint { display: none; }
}

/* Modal select styling */
.modal-select {
    width: 100%;
    padding: var(--space-2, 0.5rem);
    border: 1px solid var(--border-primary);
    background: var(--background-primary);
    color: var(--text-primary);
    font-family: inherit;
    font-size: var(--font-size-controls);
    margin-top: var(--space-2, 0.5rem);
}

/* Modal page list styling */
.modal-page-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-2, 0.5rem);
    margin-top: var(--space-2, 0.5rem);
}

.modal-page-btn {
    padding: var(--space-2, 0.5rem);
    border: 1px solid var(--border-primary);
    background: var(--background-primary);
    color: var(--text-primary);
    cursor: pointer;
    text-align: left;
    font-family: inherit;
    font-size: var(--font-size-controls);
    transition: background-color 0.3s ease;
}

.modal-page-btn:hover:not(.current) {
    background-color: var(--background-secondary);
}

.modal-page-btn.current {
    opacity: 0.6;
    cursor: not-allowed;
}

/* ── Keyboard cheat sheet: accordion style ───────────────────────────────── */

.keyboard-cheat-sheet {
    width: 100%;
}

.cheat-sheet-filter {
    width: 100%;
    box-sizing: border-box;
    background: transparent;
    border: 1px solid var(--border-primary);
    border-radius: var(--layout-radius-sm, 8px);
    color: var(--text-primary);
    font-family: inherit;
    font-size: var(--text-sm, 0.82rem);
    margin-bottom: var(--space-3, 0.75rem);
    outline: none;
    padding: var(--space-1-5, 0.35rem) var(--space-3, 0.65rem);
    transition: border-color 0.12s;
}
.cheat-sheet-filter:focus {
    border-color: var(--accent-primary);
}
.cheat-sheet-filter::placeholder {
    color: var(--text-tertiary);
    opacity: 0.65;
}

.cheat-sheet-no-results {
    margin: var(--space-3, 0.75rem) 0 0;
    color: var(--text-tertiary);
    font-size: var(--text-base, 0.9rem);
}

/*
 * One line naming the view the sheet was opened from, above the filter. Only
 * shown outside the bookmarks default, where there is nothing to explain.
 */
.cheat-sheet-context-lead {
    margin: 0 0 var(--space-2, 0.6rem);
    font-size: var(--text-xs, 0.72rem);
    color: var(--text-secondary);
}

.cheat-sheet-group {
    border-bottom: 1px solid var(--border-primary);
}

/*
 * The section matching the current view. Marked rather than moved or isolated:
 * every other section stays in place and the filter still searches them all.
 */
.cheat-sheet-group--context > .cheat-sheet-group-title {
    border-left: 2px solid var(--accent-primary);
    padding-left: var(--space-2, 0.5rem);
    margin-left: -0.5rem;
    color: var(--accent-primary);
}

.cheat-sheet-group:last-child {
    border-bottom: none;
}

.cheat-sheet-group-title {
    list-style: none;
    cursor: pointer;
    padding: var(--space-2, 0.5rem) 0;
    font-size: var(--text-xs, 0.7rem);
    font-weight: var(--font-weight-bold);
    letter-spacing: 0.06em;
    text-transform: lowercase;
    color: var(--text-tertiary);
    display: flex;
    align-items: center;
    gap: var(--space-1-5, 0.4rem);
    user-select: none;
    border-radius: var(--layout-radius-xs, 6px);
}

.cheat-sheet-group-title::-webkit-details-marker {
    display: none;
}

.cheat-sheet-group-title::before {
    content: '▶';
    font-size: 0.5rem;
    color: var(--text-tertiary);
    transition: transform 0.15s ease;
    display: inline-block;
}

.cheat-sheet-group[open] .cheat-sheet-group-title::before {
    transform: rotate(90deg);
}

.cheat-sheet-group-title:hover {
    color: var(--text-secondary);
}

/* Keyboard clarity: always show where focus is inside cheat sheet modal. */
.keyboard-cheat-sheet-modal .cheat-sheet-filter:focus-visible,
.keyboard-cheat-sheet-modal .cheat-sheet-group-title:focus-visible,
.keyboard-cheat-sheet-modal .modal-button:focus-visible {
    outline: 2px solid color-mix(in srgb, var(--accent-primary) 82%, #fff 18%);
    outline-offset: 2px;
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-primary) 24%, transparent);
}

.keyboard-cheat-sheet-modal .cheat-sheet-group-title:focus-visible {
    color: var(--text-primary);
    background: color-mix(in srgb, var(--accent-primary) 12%, transparent);
}

.keyboard-cheat-sheet-table {
    width: 100%;
    border-collapse: collapse;
    table-layout: fixed;
    margin-bottom: var(--space-2, 0.5rem);
}

.keyboard-cheat-sheet-table td {
    padding: var(--space-1, 0.22rem) 0;
    border-bottom: 1px solid var(--border-primary);
    vertical-align: top;
    line-height: 1.35;
}

.keyboard-cheat-sheet-table tr:last-child td {
    border-bottom: none;
}

.keyboard-cheat-sheet-keys {
    font-family: "Source Code Pro", monospace;
    font-size: var(--text-sm, 0.8rem);
    font-weight: var(--font-weight-bold);
    color: var(--accent-primary);
    white-space: normal;
    word-break: break-word;
    padding-right: var(--space-4, 1rem);
    width: 40%;
}

.keyboard-cheat-sheet-description {
    color: var(--text-secondary);
    font-size: var(--text-base, 0.875rem);
    min-width: 0;
    overflow-wrap: anywhere;
    width: 60%;
}

@media (prefers-reduced-motion: reduce) {
    /* Modal open/close → keep opacity fade, remove scale + translate */
    .modal { transform: none !important; transition: opacity 0.15s ease !important; }
    .modal-overlay.show .modal { transform: none !important; }
    .modal-button,
    .modal-button:hover,
    .modal-button.selected,
    .modal-button.danger:hover,
    .modal-button.danger.selected { transition: none !important; transform: none !important; }
    .wn-kofi-btn--animated,
    .wn-kofi-btn--animated::before,
    .wn-kofi-star {
        animation: none !important;
    }
    .wn-kofi-btn--animated {
        box-shadow: 0 0 0.5rem 0.1rem color-mix(in srgb, var(--accent-primary) 28%, transparent);
    }
    .wn-kofi-star {
        opacity: 0.75;
        transform: scale(1);
    }
}

/* ==== css/app-notification.css ==== */
/* Unified toast / notification host (#app-notification) */

.app-notification {
    position: fixed;
    right: max(1rem, env(safe-area-inset-right));
    bottom: max(1rem, env(safe-area-inset-bottom));
    max-width: min(380px, calc(100vw - 1.5rem));
    padding: var(--space-3, 0.75rem) var(--space-4, 1rem);
    display: inline-flex;
    align-items: center;
    gap: var(--space-3, 0.75rem);
    font-size: var(--font-size-text);
    font-weight: var(--font-weight-semibold);
    font-family: var(--font-family-main);
    border: 1px solid var(--border-primary);
    background: var(--background-secondary);
    color: var(--text-primary);
    border-radius: var(--radius-4, 0.35rem);
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.14);
    opacity: 0;
    visibility: hidden;
    transform: translateY(12px);
    transition: opacity 0.24s ease, visibility 0.24s ease, transform 0.24s ease;
    z-index: 2001;
    pointer-events: none;
}

.app-notification.show {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    pointer-events: auto;
}

.app-notification.has-action {
    max-width: min(420px, calc(100vw - 1.5rem));
}

.app-notification.success {
    color: var(--accent-success);
    border-color: var(--accent-success);
}

.app-notification.error {
    color: var(--accent-error);
    border-color: var(--accent-error);
}

.app-notification.warning {
    color: var(--accent-warning);
    border-color: var(--accent-warning);
}

.app-notification.info {
    color: var(--text-secondary);
    border-color: var(--border-primary);
}

/* Software update available — accent brand colour, distinct from success/error. */
.app-notification.update {
    color: var(--accent-primary);
    border-color: var(--accent-primary);
    background: color-mix(in srgb, var(--accent-primary) 10%, var(--background-secondary));
}

/* One-time discoverability promos — themed like config setting highlights. */
.app-notification.promo {
    color: var(--text-primary);
    border-color: color-mix(in srgb, var(--accent-primary) 42%, var(--border-primary));
    background: color-mix(in srgb, var(--accent-primary) 11%, var(--background-secondary));
    box-shadow:
        0 4px 18px color-mix(in srgb, var(--accent-primary) 16%, transparent),
        0 0 0 1px color-mix(in srgb, var(--accent-primary) 14%, transparent);
}

.app-notification.promo .app-notification-text {
    font-weight: var(--font-weight-medium);
}

.app-notification-promo-label {
    display: inline-block;
    margin-right: var(--space-1-5, 0.35rem);
    padding: var(--space-hairline, 0.1rem) var(--space-1-5, 0.42rem);
    border-radius: var(--radius-pill, 999px);
    font-size: 0.72em;
    font-weight: var(--font-weight-semibold);
    letter-spacing: 0.05em;
    text-transform: uppercase;
    vertical-align: baseline;
    color: var(--accent-primary);
    background: color-mix(in srgb, var(--accent-primary) 18%, transparent);
}

/* Session tip: a longer, quieter toast that carries inline <code> keys. */
.app-notification.info .app-notification-text {
    font-weight: var(--font-weight-medium);
}

.app-notification-tip-label {
    color: var(--accent-warning);
    font-weight: var(--font-weight-semibold);
    text-transform: uppercase;
    font-size: 0.78em;
    letter-spacing: 0.06em;
    margin-right: var(--space-hairline, 0.15rem);
}

.app-notification-text code {
    font-family: var(--font-family-main);
    font-size: 0.88em;
    background: color-mix(in srgb, var(--text-primary) 8%, transparent);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-2, 3px);
    padding: var(--space-hairline, 0.03rem) var(--space-1, 0.26rem);
    color: var(--text-primary);
    white-space: nowrap;
}

.app-notification-text {
    flex: 1;
    min-width: 0;
    line-height: 1.35;
}

.app-notification-action {
    flex-shrink: 0;
    border: 1px solid currentColor;
    background: transparent;
    color: inherit;
    border-radius: var(--radius-3, 0.25rem);
    padding: var(--space-1, 0.25rem) var(--space-2, 0.55rem);
    font-size: var(--font-size-controls);
    font-family: inherit;
    font-weight: var(--font-weight-semibold);
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.15s ease, border-color 0.15s ease;
}

.app-notification-action:hover {
    background: color-mix(in srgb, currentColor 12%, transparent);
}

.app-notification:not(.persist) {
    position: fixed;
}

.app-notification:not(.persist)::after {
    content: '';
    position: absolute;
    left: 0;
    bottom: 0;
    width: 100%;
    height: 2px;
    background: currentColor;
    opacity: 0.35;
    transform-origin: left center;
    transform: scaleX(0);
}

.app-notification.show:not(.persist)::after {
    animation: app-notification-timeout var(--notification-duration, 5s) linear forwards;
}

@keyframes app-notification-timeout {
    from { transform: scaleX(1); }
    to { transform: scaleX(0); }
}

@media (max-width: 575px) {
    .app-notification {
        right: max(0.75rem, env(safe-area-inset-right));
        bottom: max(0.75rem, env(safe-area-inset-bottom));
    }
}

body.no-animations .app-notification {
    transition: none !important;
    transform: none !important;
}

body.no-animations .app-notification.show:not(.persist)::after {
    animation: none !important;
}

@media (prefers-reduced-motion: reduce) {
    .app-notification { transform: none !important; transition: opacity 0.15s ease, visibility 0.15s ease !important; }
    .app-notification.show:not(.persist)::after { animation: none !important; }
}

/* ==== css/favicon-prefetch.css ==== */
/**
 * The blocking progress overlay.
 *
 * Written for the favicon batch prefetch and now shared with the source imports
 * on Config -> Data & backups -> Sources, which is why every rule carries a
 * second, service-neutral selector. One look for "nextDash is working through
 * something and you should wait", rather than a second panel that is almost the
 * same.
 *
 * Shared by config (after an import) and the dashboard (`:favicons fetch`), so
 * it lives outside the config-only stylesheets. Colours come from the theme
 * variables only.
 */
.favicon-prefetch-overlay,
.progress-overlay {
    position: fixed;
    inset: 0;
    z-index: 12000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-4, 1rem);
    background: color-mix(in srgb, var(--background-primary) 55%, transparent);
    backdrop-filter: blur(2px);
}

.favicon-prefetch-overlay[hidden],
.progress-overlay[hidden] {
    display: none !important;
}

.favicon-prefetch-panel,
.progress-overlay-panel {
    width: min(22rem, 100%);
    padding: var(--space-4, 1rem) var(--space-4, 1.1rem);
    border: 1px solid var(--border-primary);
    background: var(--background-secondary, var(--background-primary));
    box-shadow: 0 8px 28px rgba(0, 0, 0, 0.18);
    display: flex;
    flex-direction: column;
    gap: var(--space-3, 0.65rem);
}

.favicon-prefetch-title,
.progress-overlay-title {
    margin: 0;
    font-size: var(--font-size-controls);
    font-weight: var(--font-weight-semibold);
    color: var(--text-primary);
}

.favicon-prefetch-bar-track,
.progress-overlay-track {
    height: 0.45rem;
    border-radius: var(--radius-pill, 999px);
    overflow: hidden;
    background: color-mix(in srgb, var(--text-secondary) 18%, transparent);
}

.favicon-prefetch-bar-fill,
.progress-overlay-fill {
    height: 100%;
    width: 0;
    border-radius: inherit;
    background: var(--accent-primary, var(--text-primary));
    transition: width 0.2s ease;
}

.favicon-prefetch-status,
.progress-overlay-status {
    margin: 0;
    font-size: var(--font-size-small);
    color: var(--text-secondary);
}

/*
 * The indeterminate bar.
 *
 * A source import is a single request: the server walks the service's API,
 * however many pages that takes, and answers once. There is no honest number to
 * put on that, and a bar sitting at zero until it jumps to a hundred reads as
 * frozen. So it sweeps instead -- which says "working", which is all that is
 * actually known.
 */
.progress-overlay-fill--indeterminate {
    width: 40%;
    transition: none;
    animation: progress-overlay-sweep 1.1s ease-in-out infinite;
}

@keyframes progress-overlay-sweep {
    0% { transform: translateX(-100%); }
    100% { transform: translateX(250%); }
}

/* Reduced motion gets a filled bar rather than a sweeping one. */
@media (prefers-reduced-motion: reduce) {
    .progress-overlay-fill--indeterminate {
        width: 100%;
        animation: none;
    }
}

/* ==== css/search.css ==== */
/* Search Component Styles */

/* Shortcut Search - anchored above bottom toolbar (classic + shared) */
.shortcut-search {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-end;
    padding: 0 max(1.25rem, 2vw) calc(var(--dashboard-toolbar-stack, 5.75rem) + env(safe-area-inset-bottom, 0px));
    box-sizing: border-box;
    z-index: 2000;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.2s ease-out, visibility 0s linear 0.2s;
    background: rgba(0, 0, 0, 0.3);
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
}

.shortcut-search.show {
    opacity: 1;
    visibility: visible;
    transition: opacity 0.2s ease-out, visibility 0s linear 0s;
}

.search-container {
    padding: var(--space-4, 1rem) 1.25rem var(--space-3, 0.75rem);
    width: min(52rem, calc(100vw - 2.5rem));
    max-width: 100%;
    border: 1px solid color-mix(in srgb, var(--border-primary) 75%, transparent);
    border-radius: var(--layout-radius-md, 12px);
    background: color-mix(in srgb, var(--background-modal) 90%, transparent);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    box-shadow: 0 24px 64px rgba(0, 0, 0, 0.28), 0 2px 8px rgba(0, 0, 0, 0.12);
    display: flex;
    flex-direction: column;
    max-height: min(70vh, calc(100vh - var(--dashboard-toolbar-stack, 5.75rem) - 2.5rem));
    overflow: hidden;
    box-sizing: border-box;
    /* exit: slide down toward toolbar */
    transform: translateY(10px) scale(0.98);
    opacity: 0;
    transition: transform 0.15s ease-in, opacity 0.15s ease-in;
}

.shortcut-search.show .search-container {
    transform: translateY(0) scale(1);
    opacity: 1;
    /* entrance: spring — vertraagt soepel aan het einde */
    transition: transform 0.38s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.22s ease-out;
}

.search-prompt {
    display: flex;
    align-items: center;
    font-size: var(--font-size-bookmark);
    font-weight: var(--font-weight-semibold);
    margin-bottom: var(--space-2, 0.5rem);
    justify-content: flex-start;
}

.search-prefix {
    font-family: var(--font-family-main);
    margin-right: var(--space-2, 0.5rem);
    color: var(--accent-success);
    font-size: 0.65em;
    font-weight: var(--font-weight-semibold);
    letter-spacing: 0.06em;
    padding: 0.15em 0.45em;
    border-radius: var(--layout-radius-sm, 8px);
    background: color-mix(in srgb, var(--accent-success) 15%, transparent);
    border: 1px solid color-mix(in srgb, var(--accent-success) 35%, transparent);
    transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
    white-space: nowrap;
    flex-shrink: 0;
}

.search-prefix[data-mode="command"] {
    color: var(--accent-primary);
    background: color-mix(in srgb, var(--accent-primary) 15%, transparent);
    border-color: color-mix(in srgb, var(--accent-primary) 35%, transparent);
}

.search-prefix[data-mode="finder"] {
    color: var(--accent-secondary);
    background: color-mix(in srgb, var(--accent-secondary) 15%, transparent);
    border-color: color-mix(in srgb, var(--accent-secondary) 35%, transparent);
}

.search-prefix[data-mode="fuzzy"] {
    color: var(--text-secondary);
    background: color-mix(in srgb, var(--text-secondary) 12%, transparent);
    border-color: color-mix(in srgb, var(--text-secondary) 25%, transparent);
}

.search-prefix[data-mode="search"],
.search-prefix:not([data-mode]) {
    color: var(--accent-success);
    background: color-mix(in srgb, var(--accent-success) 15%, transparent);
    border-color: color-mix(in srgb, var(--accent-success) 35%, transparent);
}

.search-query {
    font-family: var(--font-family-main);
    color: var(--text-primary);
    white-space: nowrap;
    overflow-x: auto;
    display: inline-block;
    max-width: 100%;
    scrollbar-width: none; /* Firefox */
}

.search-query::-webkit-scrollbar {
    display: none; /* Chrome, Safari, and Opera */
}

.search-cursor {
    font-family: var(--font-family-main);
    animation: blink 1s infinite;
    margin-left: var(--space-1, 0.25rem);
    color: var(--text-primary);
}

@keyframes blink {
    0%, 50% { opacity: 1; }
    51%, 100% { opacity: 0; }
}

/* Mobile input - positioned off-screen but visible to Safari */
.search-input-mobile {
    position: absolute;
    left: -9999px;
    top: -9999px;
    width: 1px;
    height: 1px;
    border: none;
    padding: 0;
    margin: 0;
    opacity: 0.01;
}

.search-matches {
    display: flex;
    flex-direction: column;
    gap: var(--space-hairline, 0.15rem);
    flex: 1 1 auto;
    min-height: 0;
    max-height: none;
    overflow-y: auto;
    overflow-x: hidden;
    padding-left: 0;
    margin-left: 0;
    position: relative;
    width: 100%;
    scrollbar-width: none;
}

@media (max-width: 575px) {
    .search-container {
        width: calc(100vw - 1.5rem);
        max-height: calc(100dvh - 1.5rem);
        padding: var(--space-3, 0.85rem) var(--space-4, 1rem) var(--space-3, 0.65rem);
        border-radius: var(--radius-6, 10px);
    }

    .search-matches {
        padding-left: 0;
        margin-left: 0;
    }
}

.search-matches::-webkit-scrollbar {
    display: none; /* Chrome, Safari, and Opera */
}

.search-matches:empty {
    min-height: 0;
}

.search-match {
    padding: var(--space-1-5, 0.4rem) var(--space-2, 0.5rem);
    cursor: pointer;
    transition: background 0.1s ease, color 0.1s ease, border-color 0.1s ease;
    border: none;
    border-left: 2px solid transparent;
    border-radius: var(--radius-3, 4px);
    background: transparent;
    text-align: left;
    display: flex;
    align-items: center;
    color: var(--text-primary);
    position: relative; /* Necesario para el ::before */
    /* Remove tap highlight on mobile */
    -webkit-tap-highlight-color: transparent;
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

.search-match:hover,
.search-match.keyboard-selected {
    color: var(--text-primary);
    background: color-mix(in srgb, var(--accent-primary) 9%, transparent);
    border-left-color: var(--accent-primary);
    transform: none;
}

.search-match.keyboard-selected::before {
    content: none;
}

.search-match.command-entry.command-just-applied {
    animation: command-palette-flash 0.55s ease;
}

@keyframes command-palette-flash {
    0%, 100% {
        background: color-mix(in srgb, var(--accent-primary) 9%, transparent);
    }
    45% {
        background: color-mix(in srgb, var(--accent-success) 28%, transparent);
        border-left-color: var(--accent-success);
    }
}

.search-match-shortcut {
    font-weight: var(--font-weight-bold);
    margin-right: var(--space-4, 1rem);
    min-width: 3ch;
    font-size: var(--font-size-controls);
    color: var(--accent-success);
}

.search-match-favicon-slot {
    width: 20px;
    height: 20px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-right: var(--space-2, 0.6rem);
    border-radius: var(--radius-4, 6px);
    overflow: hidden;
}

.search-match-favicon-slot .bookmark-icon {
    width: 16px;
    height: 16px;
    display: block;
}

.search-match-name {
    font-size: var(--font-size-text);
    font-weight: var(--font-weight-semibold);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    flex: 1;
    min-width: 0;
}

.search-match-meta {
    display: block;
    margin-top: var(--space-hairline, 0.08rem);
    font-size: var(--font-size-controls);
    font-weight: var(--font-weight-normal);
    color: var(--text-tertiary);
}

/* Command group headers */
.search-command-group-header {
    display: flex;
    align-items: center;
    gap: var(--space-2, 0.5rem);
    padding: var(--space-2, 0.5rem) var(--space-2, 0.5rem) var(--space-1, 0.2rem);
    cursor: pointer;
    user-select: none;
    margin-top: var(--space-1, 0.25rem);
    border-top: 1px solid color-mix(in srgb, var(--border-primary) 60%, transparent);
    opacity: 0.55;
    font-size: var(--text-xs, 0.72rem);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    transition: opacity 0.15s ease;
}

.search-command-group-header:first-child {
    border-top: none;
    margin-top: 0;
}

.search-command-group-header:hover,
.search-command-group-header.keyboard-selected {
    opacity: 1;
}

.search-command-group-header.keyboard-selected {
    color: var(--text-primary);
}

.search-command-group-header.keyboard-selected .search-command-group-label {
    color: var(--text-primary);
}

.search-command-group-arrow {
    font-size: 0.7em;
    width: 1em;
    text-align: center;
    color: var(--accent-success);
    flex-shrink: 0;
}

.search-command-group-label {
    font-size: var(--font-size-controls);
    font-weight: var(--font-weight-semibold);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-secondary);
    flex: 1;
}

.search-command-group-count {
    font-size: var(--font-size-controls);
    color: var(--text-tertiary);
    opacity: 0.7;
}

.search-match.command-group-child {
    padding-left: 1.25rem;
}

/* Special styling for config entry */
.search-match.config-entry .search-match-shortcut {
    color: var(--accent-warning);
}

.search-match.config-entry .search-match-name {
    color: var(--accent-warning);
}

.search-match.filter-completion-entry .search-match-shortcut {
    color: var(--accent-info, var(--accent-success));
}

.search-match.filter-completion-entry .search-match-name {
    color: var(--text-secondary);
}

.search-match.whats-new-entry .search-match-shortcut {
    color: var(--accent-warning, #f59e0b);
}

.search-match.whats-new-entry .search-match-name {
    color: var(--text-primary);
}

/* Finder entries */
.search-match.finder-entry .search-match-shortcut {
    color: var(--accent-info, #60a5fa);
}

/* No-match state */
.search-match.search-no-match-header {
    opacity: 0.9;
    cursor: default;
    pointer-events: none;
    padding: var(--space-2, 0.45rem) var(--space-3, 0.75rem);
}

.search-no-match-label {
    color: var(--text-secondary);
    margin-right: 0.3em;
}

.search-no-match-query {
    color: var(--text-primary);
    font-weight: var(--font-weight-semibold);
}

.search-match.search-no-match-header:hover,
.search-match.search-no-match-header.keyboard-selected {
    transform: none;
    color: inherit;
}

.search-match.search-hint-entry {
    opacity: 0.7;
    border-left: 2px solid transparent;
    padding-left: var(--space-1, 0.25rem);
    transition: opacity 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}

.search-match.search-hint-entry:hover {
    opacity: 1;
    border-left-color: var(--accent-primary, #4287f5);
}

.search-hint-shortcut {
    color: var(--accent-primary, #4287f5);
    font-family: "Source Code Pro", monospace;
    font-size: 0.82em;
    letter-spacing: 0.02em;
}

.search-hint-name {
    font-weight: var(--font-weight-normal);
    color: var(--text-secondary);
}

.search-match-use-count {
    flex-shrink: 0;
    font-size: var(--font-size-controls);
    color: var(--text-tertiary);
    font-weight: var(--font-weight-normal);
    min-width: 2ch;
    text-align: right;
    opacity: 0.7;
}

/* Button Container - Centered at bottom */
/* Fixed max width so tip text length does not change container width (avoids button row jumping). */
.button-container {
    position: fixed;
    bottom: 2rem;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-content: flex-start;
    gap: var(--space-2, 0.55rem);
    z-index: 1000;
    width: min(52rem, calc(100vw - 2.5rem));
    max-width: 100%;
    box-sizing: border-box;
    padding: var(--space-3, 0.7rem) 0 0;
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

.search-flow-hint {
    /* Absolutely positioned above the button row — not in flex flow, so
       hiding it causes zero layout shift to the buttons below. */
    position: absolute;
    bottom: calc(100% + 0.85rem);
    left: 0;
    right: 0;

    display: flex;
    justify-content: center;
    align-items: center;

    font-size: var(--font-size-controls);
    color: var(--text-tertiary);
    letter-spacing: 0.05em;
    pointer-events: none;

    opacity: 1;
    transform: translateY(0);
    transition: opacity 0.45s ease, transform 0.45s ease;
}

.search-flow-hint[hidden] {
    display: none !important;
}

.search-flow-hint.dismissing {
    opacity: 0;
    transform: translateY(-8px);
}

.search-flow-chip,
kbd.search-flow-chip {
    display: inline-block;
    margin: 0 var(--space-hairline, 0.05rem);
    color: var(--accent-primary);
    font-family: var(--font-family-main);
    font-weight: var(--font-weight-semibold);
    text-shadow: 0 0 10px color-mix(in srgb, var(--accent-primary) 55%, transparent);
    border: none;
    background: transparent;
    padding: 0;
}

.search-flow-hint .sfh-seg-label {
    margin-left: 0.15em;
}

/* Each text segment wipes in left-to-right via clip-path — no extra chars, no fade */
.sfh-seg {
    display: inline-block;
    animation: sfhWipe 0.3s cubic-bezier(0.22, 1, 0.36, 1) both;
}

/* Chip chars get an extra scale-bounce on top of the wipe */
.sfh-seg.search-flow-chip,
.sfh-seg kbd.search-flow-chip {
    animation: sfhWipe 0.3s cubic-bezier(0.22, 1, 0.36, 1) both,
               sfhPop  0.38s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

/* Staggered delays: chips pause slightly longer after each separator */
.search-flow-hint-text > .sfh-seg:nth-child(1)  { animation-delay: 0.15s; }
.search-flow-hint-text > .sfh-seg:nth-child(2)  { animation-delay: 0.32s; }
.search-flow-hint-text > .sfh-seg:nth-child(3)  { animation-delay: 0.60s; }
.search-flow-hint-text > .sfh-seg:nth-child(4)  { animation-delay: 0.82s; }
.search-flow-hint-text > .sfh-seg:nth-child(5)  { animation-delay: 0.98s; }
.search-flow-hint-text > .sfh-seg:nth-child(6)  { animation-delay: 1.26s; }
.search-flow-hint-text > .sfh-seg:nth-child(7)  { animation-delay: 1.46s; }
.search-flow-hint-text > .sfh-seg:nth-child(8)  { animation-delay: 1.60s; }
.search-flow-hint-text > .sfh-seg:nth-child(9)  { animation-delay: 1.88s; }
.search-flow-hint-text > .sfh-seg:nth-child(10) { animation-delay: 2.08s; }
.search-flow-hint-text > .sfh-seg:nth-child(11) { animation-delay: 2.22s; }

@keyframes sfhWipe {
    from { clip-path: inset(0 100% 0 0); }
    to   { clip-path: inset(0 0%   0 0); }
}

@keyframes sfhPop {
    from { transform: scale(0.5); text-shadow: 0 0 24px color-mix(in srgb, var(--accent-primary) 100%, transparent); }
    65%  { transform: scale(1.3); }
    to   { transform: scale(1);   text-shadow: 0 0 10px color-mix(in srgb, var(--accent-primary) 55%, transparent); }
}

body.no-animations .search-flow-hint {
    transition: none !important;
}

body.no-animations .sfh-seg {
    animation: none !important;
    clip-path: none !important;
}

/* Custom tooltips on search buttons — appear above on hover */
.search-button[data-tooltip] {
    position: relative;
}

.search-button[data-tooltip]::after {
    content: attr(data-tooltip);
    position: absolute;
    bottom: calc(100% + 6px);
    left: 50%;
    transform: translateX(-50%);
    white-space: nowrap;
    padding: var(--space-1, 0.2rem) var(--space-2, 0.5rem);
    background: var(--background-secondary);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-3, 0.3rem);
    color: var(--text-secondary);
    font-family: var(--font-family-main);
    font-size: calc(var(--font-size-controls) * 0.85);
    font-weight: var(--font-weight-normal);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
    pointer-events: none;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.15s ease, visibility 0.15s ease;
    z-index: 1001;
}

.search-button[data-tooltip]:hover::after {
    opacity: 1;
    visibility: visible;
}

/* No tooltips on touch — hover doesn't exist there */
@media (pointer: coarse) {
    .search-button[data-tooltip]::after {
        display: none;
    }
}

body.no-animations .search-button[data-tooltip]::after {
    transition: none !important;
}

/* Search Button */
.search-button {
    /* Button styling */
    padding: var(--space-2, 0.62rem) var(--space-4, 0.92rem);
    border: 1px solid var(--border-primary);
    /* --search-button-under is what the wash sits on: transparent by default,
       and the page background wherever the button floats over content with no
       plate beneath it. See the dock rule below. */
    background: color-mix(in srgb, var(--background-secondary) 74%, var(--search-button-under, transparent));
    color: var(--text-secondary);
    font-size: var(--font-size-controls);
    font-family: var(--font-family-main);
    font-weight: var(--font-weight-normal);
    cursor: pointer;
    
    /* TTY terminal style */
    display: flex;
    align-items: center;
    gap: var(--space-2, 0.5rem);
    border-radius: var(--layout-radius-md, 0.65rem);
    
    /* Transitions */
    transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
    
    /* Remove tap highlight on mobile */
    -webkit-tap-highlight-color: transparent;
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

/* The classic dock has no plate of its own -- .button-container is deliberately
   border-less, fill-less and blur-less -- so these buttons are the only thing
   between the page and the eye. At 74% over nothing, a scrolled grid reads
   straight through them: bookmark names run across the icons.
 *
 * The fix composites the same 74% over --background-primary instead of over
 * transparent. The face keeps exactly the colour it has today wherever the dock
 * sits over empty page, so nothing about the design changes -- it just stops
 * being a window.
 *
 * It moves a variable rather than the background itself on purpose. An override
 * specific enough to name the dock would also outrank `.search-button:hover`,
 * which sets `background` flat out, and the rest state would win over the hover
 * state. Feeding the base declaration instead leaves every interaction rule
 * exactly as specific as it was.
 *
 * Not for the modern layout: it puts a blurred plate under the row (centre) or
 * under each group (corners) and keeps the faces translucent against it, which
 * is a whole answer to the same question. Not for the rail either -- there the
 * buttons sit on the rail's own surface. */
body:not([data-layout-version="modern"]):not([data-rail]) .button-container .search-button,
body:not([data-layout-version="modern"]):not([data-rail]) .tag-cloud-toggle-btn.search-button {
    --search-button-under: var(--background-primary);
}

.search-button:hover {
    background: var(--background-secondary);
    border-color: var(--border-primary);
    color: var(--text-primary);
    transform: none;
    box-shadow: none;
}

.search-button:active {
    transform: none;
    background: var(--background-secondary);
    border-color: var(--border-primary);
    color: var(--text-primary);
    box-shadow: none;
}

.search-button:focus-visible {
    outline: 2px solid var(--border-primary);
    outline-offset: 2px;
    color: var(--text-primary);
}

.search-button-icon {
    display: inline-block; /* required for transform to work on <span> */
    color: var(--text-secondary);
    font-weight: var(--font-weight-bold);
    font-size: var(--font-size-category);
}

.search-button:hover .search-button-icon,
.search-button:focus-visible .search-button-icon {
    color: var(--text-primary);
}

/* ── Periodic attention pulse on search / commands / finders icons ───────
   Total cycle = 10.8 s: 0.8 s of motion (0 – 7.4 %), then 10 s idle.
   The three icons are staggered 3.6 s apart so they pulse one by one.   */
@keyframes btnIconPulse {
    0%   { opacity: 1; }
    4%   { opacity: 0.45; }
    8%   { opacity: 1; }
    100% { opacity: 1; }
}

#search-button .search-button-icon,
.add-bookmark-button .search-button-icon,
.commands-button .search-button-icon,
.finders-button .search-button-icon,
.recent-button .search-button-icon,
.help-button .search-button-icon {
    animation: btnIconPulse 10.8s ease-in-out infinite;
}
#search-button .search-button-icon         { animation-delay: 1s;   }
.add-bookmark-button .search-button-icon  { animation-delay: 2.8s; }
.commands-button .search-button-icon       { animation-delay: 4.6s; }
.finders-button .search-button-icon        { animation-delay: 6.4s; }
.recent-button .search-button-icon         { animation-delay: 8.2s; }
.help-button .search-button-icon           { animation-delay: 9.4s; }

body.no-animations #search-button .search-button-icon,
body.no-animations .add-bookmark-button .search-button-icon,
body.no-animations .commands-button .search-button-icon,
body.no-animations .finders-button .search-button-icon,
body.no-animations .recent-button .search-button-icon,
body.no-animations .help-button .search-button-icon {
    animation: none !important;
}

/* Hide buttons when search is active */
.shortcut-search.show ~ .button-container {
    opacity: 0;
    pointer-events: none;
}

/* Preserve transforms when animations are disabled */
body.no-animations .search-button {
    transform: none !important;
}

/* Disable hover transforms when animations are disabled */
body.no-animations .search-button:hover {
    transform: none !important;
}

body.no-animations .search-button:active {
    transform: none !important;
}

/* Disable search match hover transforms when animations are disabled */
body.no-animations .search-match:hover,
body.no-animations .search-match.keyboard-selected {
    transform: none !important;
}

/* ─── Btn-group wrappers ──────────────────────────────────────────────────── */
/* In bottom-modus: transparant voor de bestaande flex-layout van .button-container */
.btn-group {
    display: contents;
}


/* ─── Side-dock: gedeeld (left + right) ──────────────────────────────────── */
body[data-button-position="bottom-left"] .button-container,
body[data-button-position="bottom-right"] .button-container {
    flex-direction: row;
    align-items: flex-end;
    width: auto;
    padding: 0;
    gap: var(--space-1-5, 0.35rem);
    border-radius: 0;
    background: transparent;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border: none;
    box-shadow: none;
    left: unset;
    transform: none;
    bottom: 1.5rem;
}

body[data-button-position="bottom-left"] .btn-group,
body[data-button-position="bottom-right"] .btn-group {
    display: flex;
    flex-direction: column;
    gap: var(--space-1, 0.3rem);
}

body[data-button-position="bottom-left"] .btn-group-secondary,
body[data-button-position="bottom-right"] .btn-group-secondary {
    padding-top: 0;
    border-top: none;
}



/* search-flow-hint past niet in de dock */
body[data-button-position="bottom-left"] .search-flow-hint,
body[data-button-position="bottom-right"] .search-flow-hint {
    display: none !important;
}

/* ─── Rechter-dock ───────────────────────────────────────────────────────── */
body[data-button-position="bottom-right"] .button-container {
    right: 1rem;
}
body[data-button-position="bottom-right"] .btn-group-secondary { order: 0; }
body[data-button-position="bottom-right"] .btn-group-primary   { order: 1; }

/* ─── Linker-dock ────────────────────────────────────────────────────────── */
body[data-button-position="bottom-left"] .button-container {
    left: 1rem;
}
body[data-button-position="bottom-left"] .btn-group-primary   { order: 0; }
body[data-button-position="bottom-left"] .btn-group-secondary { order: 1; }

/* ─── Tooltips richting omdraaien in side-dock ───────────────────────────── */
body[data-button-position="bottom-left"] .search-button[data-tooltip]::after {
    bottom: auto;
    top: 50%;
    left: calc(100% + 8px);
    transform: translateY(-50%);
}
body[data-button-position="bottom-right"] .search-button[data-tooltip]::after {
    bottom: auto;
    top: 50%;
    right: calc(100% + 8px);
    left: auto;
    transform: translateY(-50%);
}


/* ─── Hover-richting: weg van de rand ────────────────────────────────────── */
body[data-button-position="bottom-right"] .search-button:hover {
    transform: translateX(-2px);
}
body[data-button-position="bottom-left"] .search-button:hover {
    transform: translateX(2px);
}
body[data-button-position="bottom-right"] .search-button:active,
body[data-button-position="bottom-left"] .search-button:active {
    transform: translateX(0);
}

/* Ensure button container remains centered when animations are disabled */
body.no-animations .button-container {
    transform: translateX(-50%) !important;
}
/* Side rail: no-animations must not re-center the rail */
body.no-animations[data-button-position="side-left"] .button-container {
    transform: none !important;
}

body.no-animations .shortcut-search,
body.no-animations .search-container {
    transition: none !important;
}

/* Fuzzy search highlight */
.fuzzy-highlight {
    font-weight: var(--font-weight-bold);
    text-transform: uppercase;
}

/* Query match highlight in search results */
mark.search-highlight {
    background: transparent;
    color: inherit;
    font-weight: var(--font-weight-bold);
    text-decoration: underline;
    text-decoration-color: var(--text-secondary);
    text-underline-offset: 2px;
}

/* Search mode tabs — shown at the bottom of the search container */
.search-mode-tabs {
    display: flex;
    gap: var(--space-1-5, 0.4rem);
    padding-top: var(--space-2, 0.6rem);
    margin-top: var(--space-1-5, 0.4rem);
    border-top: 1px solid color-mix(in srgb, var(--border-primary) 60%, transparent);
    flex-shrink: 0;
}

.search-mode-tab {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1, 0.3rem);
    padding: var(--space-1, 0.2rem) var(--space-2, 0.55rem);
    border: 1px solid color-mix(in srgb, var(--border-primary) 70%, transparent);
    border-radius: var(--radius-pill, 999px);
    background: transparent;
    color: var(--text-tertiary);
    font-family: var(--font-family-main);
    font-size: calc(var(--font-size-controls) * 0.9);
    cursor: pointer;
    transition: background 0.13s ease, border-color 0.13s ease, color 0.13s ease;
    -webkit-tap-highlight-color: transparent;
    user-select: none;
    white-space: nowrap;
}

.search-mode-tab:hover {
    color: var(--text-secondary);
    border-color: var(--border-primary);
    background: color-mix(in srgb, var(--background-secondary) 60%, transparent);
}

.search-mode-tab:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
    border-radius: var(--radius-pill, 999px);
}

.search-mode-tab-key {
    font-weight: var(--font-weight-bold);
    font-size: 1em;
    line-height: 1;
}

/* Active states — match the search-prefix badge colours */
.search-mode-tab.active[data-mode="search"] {
    color: var(--accent-success);
    background: color-mix(in srgb, var(--accent-success) 12%, transparent);
    border-color: color-mix(in srgb, var(--accent-success) 35%, transparent);
}

.search-mode-tab.active[data-mode="command"] {
    color: var(--accent-primary);
    background: color-mix(in srgb, var(--accent-primary) 12%, transparent);
    border-color: color-mix(in srgb, var(--accent-primary) 35%, transparent);
}

.search-mode-tab.active[data-mode="finder"] {
    color: var(--accent-secondary);
    background: color-mix(in srgb, var(--accent-secondary) 12%, transparent);
    border-color: color-mix(in srgb, var(--accent-secondary) 35%, transparent);
}

/* Hide on mobile extra-small to save vertical space */
@media (max-width: 479px) {
    .search-mode-tabs {
        display: none;
    }
}

body.no-animations .search-mode-tab {
    transition: none !important;
}

/* ─── Recent search chips ────────────────────────────────────── */
.search-history-chip-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1-5, 0.35rem);
    padding: var(--space-1-5, 0.4rem) 1.25rem var(--space-2, 0.5rem);
}

.search-history-chip-wrap {
    display: inline-flex;
    align-items: center;
    gap: var(--space-hairline, 0.1rem);
    max-width: 100%;
}

.search-history-chip {
    display: inline-flex;
    align-items: center;
    padding: var(--space-1, 0.2rem) var(--space-2, 0.6rem);
    border-radius: var(--radius-pill, 999px);
    border: 1px solid var(--border-primary);
    background: transparent;
    color: var(--text-secondary);
    font-size: var(--text-xs, 0.72rem);
    font-family: inherit;
    line-height: 1.4;
    cursor: pointer;
    white-space: nowrap;
    max-width: 18ch;
    overflow: hidden;
    text-overflow: ellipsis;
    transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}

.search-history-chip:hover {
    background: color-mix(in srgb, var(--accent-primary) 10%, transparent);
    border-color: var(--text-secondary);
    color: var(--text-primary);
}

.search-history-chip:focus-visible,
.search-history-chip.keyboard-selected-chip {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
    border-radius: var(--radius-pill, 999px);
}

.search-history-chip.keyboard-selected-chip {
    background: color-mix(in srgb, var(--accent-primary) 14%, transparent);
    border-color: var(--accent-primary);
    color: var(--text-primary);
}

.search-history-chip-row.keyboard-selected {
    background: color-mix(in srgb, var(--accent-primary) 6%, transparent);
    border-radius: var(--radius-4, 0.35rem);
}

.search-history-chip-remove,
.search-history-remove {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.25rem;
    height: 1.25rem;
    padding: 0;
    border: none;
    border-radius: var(--radius-pill, 999px);
    background: transparent;
    color: var(--text-tertiary);
    font-size: var(--text-base, 0.95rem);
    line-height: 1;
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.12s ease, color 0.12s ease, background 0.12s ease;
}

.search-history-chip-wrap:hover .search-history-chip-remove,
.search-match.history-entry:hover .search-history-remove,
.search-history-chip-remove:focus-visible,
.search-history-remove:focus-visible {
    opacity: 1;
    outline: 2px solid var(--accent-primary);
    outline-offset: 1px;
    border-radius: var(--radius-pill, 999px);
}

.search-history-chip-remove:hover,
.search-history-remove:hover {
    color: var(--accent-error);
    background: color-mix(in srgb, var(--accent-error) 12%, transparent);
}

.search-match.history-entry {
    display: flex;
    align-items: center;
    gap: var(--space-1-5, 0.35rem);
}

.search-match.history-entry .search-match-name {
    flex: 1 1 auto;
    min-width: 0;
}

body.no-animations .search-history-chip,
body.no-animations .search-history-chip-remove,
body.no-animations .search-history-remove {
    transition: none !important;
}

/* Side rail / corner docks: keep centered placement */
body[data-button-position="side-left"] .shortcut-search,
body[data-button-position="bottom-left"] .shortcut-search,
body[data-button-position="bottom-right"] .shortcut-search {
    justify-content: center;
    padding-bottom: max(var(--space-6, 2rem), env(safe-area-inset-bottom, 0px));
}

body[data-button-position="side-left"] .shortcut-search .search-container,
body[data-button-position="bottom-left"] .shortcut-search .search-container,
body[data-button-position="bottom-right"] .shortcut-search .search-container {
    max-height: calc(100vh - 2rem);
}

.search-command-chip {
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
    font-size: var(--text-2xs, 0.68rem);
    color: var(--accent-primary);
    border-color: color-mix(in srgb, var(--accent-primary) 35%, transparent);
}

.search-command-chip:hover {
    color: var(--accent-primary);
    border-color: var(--accent-primary);
}

@media (prefers-reduced-motion: reduce) {
    /* Spring entrance → opacity-only fade; remove scale/translate */
    .search-container {
        transform: none !important;
        transition: opacity 0.15s ease !important;
    }
    .shortcut-search.show .search-container {
        transition: opacity 0.2s ease !important;
    }
    /* Decorative blink cursor */
    .search-cursor { animation: none !important; opacity: 1 !important; }
    /* Staggered hint segments */
    .sfh-seg { animation: none !important; opacity: 1 !important; clip-path: none !important; }
    .search-flow-hint { transition: opacity 0.15s ease !important; }
}

/* The key that starts the mode, in front of the mode it starts. The tabs under
   the line name all four; this puts the convention where the typing happens, so
   "> to search" is legible without reading the legend — and so the way past a
   single-letter bookmark shortcut is visible when you need it. */
.search-chevron {
    font-family: var(--font-family-main);
    margin-right: var(--space-1, 0.3rem);
    font-size: 0.85em;
    font-weight: var(--font-weight-semibold);
    color: var(--accent-success);
    opacity: 0.85;
}

.search-chevron[data-mode="command"] { color: var(--accent-primary); }
.search-chevron[data-mode="finder"] { color: var(--accent-warning); }
.search-chevron[data-mode="global"],
.search-chevron[data-mode="fuzzy"] { color: var(--text-secondary); }

/* Shown only once there is something to clear. Escape does the same, but a line
   you typed into needs an exit you can point at — and touch has no Escape. */
.search-clear {
    margin-left: var(--space-2, 0.5rem);
    padding: 0 0.35em;
    border: 1px solid var(--border-secondary);
    border-radius: var(--layout-radius-sm, 8px);
    background: transparent;
    color: var(--text-secondary);
    font: inherit;
    font-size: 0.6em;
    line-height: 1.6;
    cursor: pointer;
}

.search-clear:hover,
.search-clear:focus-visible {
    color: var(--accent-error);
    border-color: color-mix(in srgb, var(--accent-error) 45%, transparent);
}

.search-clear[hidden] {
    display: none;
}

/*
 * The tail of a capped group.
 *
 * An open group stops at twelve rows so a common letter cannot fill the
 * overlay again. This row carries what is left, and reads as a way through
 * rather than as a result — quieter than a bookmark, louder than the header
 * above it, since it is the one row here you are meant to press.
 */
.search-group-more {
    display: flex;
    align-items: center;
    gap: var(--space-2, 0.5rem);
    padding: var(--space-1-5, 0.35rem) var(--space-2, 0.5rem);
    cursor: pointer;
    user-select: none;
    opacity: 0.6;
    font-size: var(--text-sm, 0.78rem);
    transition: opacity 0.15s ease;
}

.search-group-more:hover,
.search-group-more.keyboard-selected {
    opacity: 1;
}

.search-group-more.keyboard-selected {
    color: var(--text-primary);
    background: var(--bg-hover, color-mix(in srgb, var(--text-primary) 8%, transparent));
}

.search-group-more-arrow {
    opacity: 0.7;
}

/* ==== css/bookmark-form-modal.css ==== */
/* Shared bookmark add/edit form — modal shell around the inline editor form. */

.bookmark-form-modal {
    display: none;
    align-items: center;
    justify-content: center;
    /* Breathing room on a tall window, less of it on a short one, where those
       pixels are the difference between the form fitting and scrolling. */
    padding: clamp(8px, 3vh, 24px) 16px;
    overflow-y: auto;
    z-index: 10050;
}

.bookmark-form-modal.show {
    display: flex;
}

/*
 * The cap is what the overlay actually leaves, not a flat 90vh.
 *
 * The overlay already holds 24px of padding top and bottom, so a 90vh dialog
 * asked for 90% of the height inside a box that had 48px taken off it — the
 * form fits in 735px, and on an 800px-tall window the cap came out at 720 and
 * put a scrollbar on a form with 15px to spare. Common laptop heights (800,
 * 810) landed just under the line, so the modal scrolled for no visible reason
 * while the same form sat still on a taller screen.
 *
 * 100dvh minus that padding lets the dialog use the room the overlay is really
 * offering, and dvh rather than vh so a mobile browser's collapsing toolbar
 * does not reintroduce the same few missing pixels. The subtraction mirrors the
 * overlay's clamped padding above — the two have to agree, or the cap starts
 * describing space the overlay is not giving.
 */
.bookmark-form-modal-dialog {
    width: min(520px, 100%);
    max-height: min(calc(100dvh - (2 * clamp(8px, 3vh, 24px))), 920px);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    margin: auto;
    flex-shrink: 0;
    border-radius: var(--radius-7, 14px);
    border: 1px solid var(--border-color, rgba(128, 128, 128, 0.28));
    background: var(--background-primary, #fff);
    box-shadow: 0 18px 48px rgba(0, 0, 0, 0.28);
}

.bookmark-form-modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 14px 16px 10px;
    border-bottom: 1px solid var(--border-primary, rgba(128, 128, 128, 0.15));
}

.bookmark-form-modal-header h2 {
    margin: 0;
    font-size: calc(var(--font-size-text) * 1.12);
    font-weight: 600;
}

.bookmark-form-modal-close {
    flex-shrink: 0;
    width: 32px;
    height: 32px;
    border: none;
    border-radius: var(--radius-5, 8px);
    background: transparent;
    color: inherit;
    font-size: 1.35rem;
    line-height: 1;
    cursor: pointer;
}

.bookmark-form-modal-close:hover {
    background: var(--background-secondary, rgba(128, 128, 128, 0.12));
}

.bookmark-form-modal-body {
    overflow-y: auto;
    padding: 14px 16px 18px;
}

.bookmark-form-modal-body .bookmark-inline-form {
    max-width: none;
    margin: 0;
    padding: 0;
    border: none;
    background: transparent;
    box-shadow: none;
}

/*
 * A phone gets the short form.
 *
 * Icon and Note are the two fields a phone is worst at and needs least: the
 * icon row is a preview plus four buttons for a favicon that is fetched from
 * the page anyway, and the note is a textarea on a keyboard that covers half
 * the screen. Between them they are about 200px of a form someone is filling in
 * with a thumb.
 *
 * `display: none`, not removed from the form: the inputs keep whatever the
 * bookmark already had, so editing one on a phone leaves its note and its icon
 * exactly as they were. Both are there in full on any desktop.
 */
@media (pointer: coarse) {
    .bookmark-form-modal-body [data-field-block="icon"],
    .bookmark-form-modal-body [data-field-block="note"] {
        display: none;
    }
}

/*
 * One column or two.
 *
 * The columns are real elements so Tab runs down one and then the other; here
 * they are dissolved, which leaves the form exactly the flex column it always
 * was. The wide layout below is the only place they become boxes.
 */
.bookmark-inline-col {
    display: contents;
}

/*
 * Two columns once the window can hold them.
 *
 * 860px is where two columns of about 410 still hold a select, the icon row and
 * the availability control without wrapping; the dialog itself is capped at 900
 * and simply uses what the window gives it below that. Under the breakpoint
 * nothing changes at all.
 *
 * `pointer: fine` keeps a phone or tablet on the single column whatever its
 * width says: a landscape phone is nearly 950px wide and is still a phone,
 * where one column you scroll beats two you pinch at. It reports the primary
 * pointer, so a desktop that happens to have a touchscreen still gets both
 * columns.
 *
 * Height is the point, not width: eleven full-width fields made the form 735px
 * tall for adding and 763px for editing, and a laptop browser with tabs open
 * leaves around 660px. Side by side the two groups come to roughly 350px, so
 * the dialog lands near 480px and the actions stay on screen.
 */
@media (min-width: 860px) and (pointer: fine) {
    .bookmark-form-modal-dialog {
        width: min(900px, 100%);
    }

    .bookmark-form-modal-body .bookmark-inline-form {
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        column-gap: 20px;
        align-items: start;
    }

    .bookmark-form-modal-body .bookmark-inline-col {
        display: flex;
        flex-direction: column;
        gap: var(--space-1-5, 0.35rem);
        min-width: 0;
    }

    /* The buttons belong to the whole form, not to the column they follow. */
    .bookmark-form-modal-body .bookmark-inline-actions {
        grid-column: 1 / -1;
    }
}

body.bookmark-form-modal-open .bookmark-inline-form,
body.bookmark-form-modal-open .bookmark-inline-form .bookmark-inline-input,
body.bookmark-form-modal-open .bookmark-inline-form .bookmark-inline-select,
body.bookmark-form-modal-open .bookmark-inline-form .bookmark-inline-textarea,
body.bookmark-form-modal-open .bookmark-inline-form .bookmark-inline-action-btn,
body.bookmark-form-modal-open .bookmark-inline-form .bookmark-inline-create-btn,
body.bookmark-form-modal-open .bookmark-inline-form .bookmark-inline-icon-preview {
    background: var(--inline-edit-field-bg, var(--background-secondary));
}

/* Delete/discard confirms and other #app-modal prompts opened from the form. */
body.bookmark-form-modal-open #app-modal.modal-overlay.show {
    z-index: 10100;
}

/* Tag autocomplete dropdown (appended to body) must sit above the form modal. */
body.bookmark-form-modal-open .tag-ac-dropdown {
    z-index: 10150;
}

@media (max-width: 520px) {
    .bookmark-form-modal {
        padding: 12px 8px;
        align-items: center;
    }

    .bookmark-form-modal-dialog {
        max-height: none;
        min-height: min(90vh, 100%);
    }
}

/* ==== css/search-commands-new.css ==== */
/* New Bookmark Modal Styles */

/* The 520px cap used to cut the form off mid-way, so the fields between Tags
   and "More options" — availability checking among them — sat below the fold
   and had to be scrolled to before they could be seen at all. 760px shows the
   whole form down to the collapsed section on a normal window, while 90vh still
   keeps it inside a short one. The body scrolls either way. */
.modal-new-bookmark {
    max-width: 560px;
    width: min(560px, 92vw);
    padding: 0;
    overflow: hidden;
    max-height: min(90vh, 760px);
    display: flex;
    flex-direction: column;
}

.modal-new-bookmark.nbm-mobile-wizard {
    max-height: 90vh;
}

/* ── Header ── */
.nbm-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3, 0.75rem);
    padding: var(--space-3, 0.65rem) var(--space-4, 1rem);
    border-bottom: 1px solid var(--border-primary);
    background: var(--background-secondary);
    flex-shrink: 0;
}

.nbm-title {
    font-size: var(--font-size-text);
    font-weight: var(--font-weight-semibold);
    color: var(--text-primary);
}

.nbm-header-actions {
    display: flex;
    align-items: center;
    gap: var(--space-2, 0.5rem);
    flex-shrink: 0;
}

/* ── Form ── */
.new-bookmark-form {
    display: flex;
    flex-direction: column;
    overflow-y: auto;
}

.nbm-section {
    padding: var(--space-3, 0.65rem) var(--space-4, 1rem);
    border-bottom: 1px solid var(--border-primary);
    display: flex;
    flex-direction: column;
    gap: var(--space-1, 0.3rem);
}

.nbm-section-row {
    flex-direction: row;
    align-items: flex-end;
    gap: var(--space-3, 0.75rem);
    flex-wrap: wrap;
}

.nbm-section-toggles {
    flex-direction: row;
    gap: 1.25rem;
    align-items: center;
    flex-wrap: wrap;
}

/* Availability check and Shortcut share one row: the mode pills leave a column
   free beside them that the short input fits into exactly, which is what lets
   Shortcut sit above the fold without making the modal taller.
   Placed here beside .nbm-section-row rather than further down the file because
   it overrides .nbm-section's column direction at equal specificity — later
   rules win, so distance from that rule is what makes this work. */
.nbm-availability-row {
    flex-direction: row;
    align-items: flex-start;
    gap: var(--space-4, 1rem);
}

.nbm-availability-main {
    flex: 1 1 auto;
    min-width: 0;
}

.nbm-availability-aside {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    gap: var(--space-1, 0.3rem);
}

.nbm-availability-aside .nbm-input {
    width: 5.5rem;
}

/* Under the narrow breakpoint the two stack rather than squeezing the pills. */
@media (max-width: 520px) {
    .nbm-availability-row {
        flex-direction: column;
        gap: var(--space-2, 0.6rem);
    }

    .nbm-availability-aside {
        width: 100%;
    }
}

/* ── Labels ── */
.nbm-label {
    font-size: var(--font-size-controls);
    color: var(--text-secondary);
    font-weight: var(--font-weight-semibold);
    display: block;
}

.nbm-label-hint {
    font-weight: var(--font-weight-normal, 400);
    color: var(--text-tertiary);
    font-size: calc(var(--font-size-controls) * 0.9);
    margin-left: 0.3em;
}

/* ── Inputs ── */
.nbm-input {
    width: 100%;
    padding: var(--space-1-5, 0.38rem) var(--space-2, 0.5rem);
    border: 1px solid var(--border-primary);
    background: var(--background-primary);
    color: var(--text-primary);
    font-family: inherit;
    font-size: var(--font-size-text);
    transition: border-color 0.15s ease;
    box-sizing: border-box;
}

.nbm-input:focus {
    outline: none;
    border-color: var(--text-secondary);
}

.nbm-input::placeholder {
    color: var(--text-secondary);
    opacity: 0.5;
}

.nbm-note {
    resize: vertical;
    min-height: 3.5rem;
    line-height: 1.5;
    font-family: inherit;
}

.nbm-shortcut {
    text-transform: uppercase;
    text-align: center;
    max-width: 5.5rem;
}

.nbm-conflict-hint {
    display: inline;
    font-size: var(--text-xs, 0.72rem);
    color: var(--accent-error);
    white-space: nowrap;
    flex-shrink: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

.nbm-conflict-hint[hidden] {
    display: none !important;
}

.nbm-shortcut.field-conflict {
    border-color: var(--accent-error);
}

.nbm-input.field-conflict {
    border-color: var(--accent-error);
}

.nbm-url-conflict-hint {
    display: block;
    margin-top: var(--space-1-5, 0.35rem);
}

/* Not a conflict: the same link on another page is allowed, and this only says
   where it is. Muted rather than red for exactly that reason. */
.nbm-elsewhere-hint {
    display: block;
    margin-top: var(--space-1-5, 0.35rem);
    font-size: var(--text-xs, 0.72rem);
    color: var(--text-secondary);
    overflow: hidden;
    text-overflow: ellipsis;
}

.nbm-elsewhere-hint[hidden] {
    display: none !important;
}

.nbm-file-label {
    cursor: pointer;
}

.nbm-file-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

/* ── Row columns ── */
.nbm-col {
    display: flex;
    flex-direction: column;
    gap: var(--space-1, 0.3rem);
    flex: 1;
    min-width: 0;
}

.nbm-col-narrow {
    flex: 0 0 auto;
    min-width: 0;
}

/* Inline "create new page/category" row inside the add-bookmark modal. */
.nbm-new-option {
    font-style: italic;
}

.nbm-inline-create {
    display: flex;
    flex-direction: column;
    gap: var(--space-1, 0.3rem);
    margin-top: var(--space-1-5, 0.4rem);
    padding: var(--space-2, 0.5rem);
    border: 1px dashed var(--accent-success, #34d399);
    border-radius: var(--radius-5, 8px);
    background: color-mix(in srgb, var(--accent-success, #34d399) 8%, transparent);
}

/* The [hidden] attribute must win over the display above. */
.nbm-inline-create[hidden] {
    display: none;
}

.nbm-inline-create-hint {
    font-size: var(--text-sm, 0.78rem);
    font-weight: 600;
    color: var(--text-secondary, #9ca3af);
    letter-spacing: 0.02em;
}

.nbm-inline-create-row {
    display: flex;
    align-items: center;
    gap: var(--space-1-5, 0.35rem);
}

.nbm-inline-create-row .nbm-input {
    flex: 1 1 auto;
    min-width: 0;
}

.nbm-inline-create-ok,
.nbm-inline-create-cancel {
    flex: 0 0 auto;
    width: 2.1rem;
    height: 2.1rem;
    border-radius: var(--radius-5, 8px);
    border: 1px solid transparent;
    cursor: pointer;
    font-size: var(--text-md, 1rem);
    font-weight: 700;
    line-height: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: filter 0.15s ease, background 0.15s ease;
}

.nbm-inline-create-ok {
    background: var(--accent-success, #34d399);
    color: #06281d;
}

.nbm-inline-create-cancel {
    background: var(--background-secondary, rgba(128, 128, 128, 0.12));
    color: var(--text-secondary, #9ca3af);
    border-color: var(--border-primary, rgba(128, 128, 128, 0.3));
}

.nbm-inline-create-ok:hover {
    filter: brightness(1.08);
}

.nbm-inline-create-cancel:hover {
    color: var(--accent-error, #fb7185);
    border-color: var(--accent-error, #fb7185);
}

/* ── More options ── */
.nbm-more-options {
    border-bottom: 1px solid var(--border-primary);
}

.nbm-more-options > summary {
    padding: var(--space-2, 0.55rem) var(--space-4, 1rem);
    cursor: pointer;
    font-size: var(--font-size-controls);
    font-weight: var(--font-weight-semibold);
    color: var(--text-secondary);
    list-style: none;
    user-select: none;
}

.nbm-more-options > summary::-webkit-details-marker {
    display: none;
}

.nbm-more-options > summary::before {
    content: '▸ ';
    display: inline-block;
    transition: transform 0.15s ease;
}

.nbm-more-options[open] > summary::before {
    transform: rotate(90deg);
}

.nbm-more-content {
    display: flex;
    flex-direction: column;
    border-top: 1px solid var(--border-primary);
}

.nbm-more-content .nbm-section {
    border-bottom: none;
}

.nbm-more-content .nbm-section + .nbm-section {
    border-top: 1px solid var(--border-primary);
}

.nbm-more-content .bookmark-form-preview-section {
    padding: var(--space-3, 0.65rem) var(--space-4, 1rem);
    gap: var(--space-3, 0.65rem);
}

/* ── Mobile wizard ── */
.nbm-wizard-nav {
    display: none;
    align-items: center;
    gap: var(--space-2, 0.5rem);
    padding: var(--space-2, 0.5rem) var(--space-4, 1rem);
    border-bottom: 1px solid var(--border-primary);
    background: var(--background-secondary);
    flex-shrink: 0;
}

.nbm-mobile-wizard .nbm-wizard-nav {
    display: flex;
}

.nbm-wizard-step {
    flex: 1;
    text-align: center;
    font-size: var(--text-xs, 0.72rem);
    font-weight: var(--font-weight-semibold);
    color: var(--text-tertiary);
    padding: var(--space-1, 0.25rem) var(--space-1-5, 0.35rem);
    border: 1px solid transparent;
}

.nbm-wizard-step.is-active {
    color: var(--text-primary);
    border-color: var(--border-primary);
    background: var(--background-primary);
}

.nbm-wizard-step.is-done {
    color: var(--text-secondary);
}

.nbm-mobile-wizard.nbm-wizard-step-2 .nbm-wizard-step-1-panel {
    display: none;
}

.nbm-mobile-wizard .nbm-section.nbm-wizard-step-2-panel {
    display: none;
}

.nbm-mobile-wizard.nbm-wizard-step-2 .nbm-section.nbm-wizard-step-2-panel {
    display: flex;
}

.nbm-mobile-wizard .nbm-more-options {
    display: none;
}

.nbm-mobile-wizard.nbm-wizard-step-2 .nbm-more-options {
    display: block;
}

.nbm-wizard-only {
    display: none;
}

.nbm-mobile-wizard .nbm-wizard-only {
    display: inline-flex;
}

.nbm-mobile-wizard.nbm-wizard-step-1 #new-bookmark-create {
    display: none;
}

.nbm-mobile-wizard.nbm-wizard-step-1 #new-bookmark-wizard-back {
    display: none;
}

.nbm-mobile-wizard.nbm-wizard-step-2 #new-bookmark-wizard-next {
    display: none;
}

.nbm-mobile-wizard:not(.nbm-wizard-step-1):not(.nbm-wizard-step-2) #new-bookmark-wizard-back,
.nbm-mobile-wizard:not(.nbm-wizard-step-1):not(.nbm-wizard-step-2) #new-bookmark-wizard-next {
    display: none;
}

/* ── URL row ── */
.nbm-url-row {
    display: flex;
    gap: var(--space-1-5, 0.4rem);
    align-items: center;
}

.nbm-url-row .nbm-input {
    flex: 1;
    min-width: 0;
}

/* ── Icon row ── */
.nbm-icon-row {
    display: flex;
    gap: var(--space-1-5, 0.4rem);
    align-items: center;
}

.nbm-icon-preview {
    width: 2rem;
    height: 2rem;
    border: 1px solid var(--border-primary);
    background: var(--background-secondary);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    flex-shrink: 0;
    color: var(--text-tertiary);
}

.nbm-icon-preview img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
}

.nbm-icon-preview-empty {
    font-size: var(--text-xs, 0.7rem);
    line-height: 1;
}

.nbm-icon-row .nbm-input {
    flex: 1;
    min-width: 0;
}

.nbm-icon-clear {
    flex-shrink: 0;
    min-width: 2rem;
    padding: 0 var(--space-2, 0.45rem);
}

.nbm-icon-state {
    font-size: var(--font-size-controls);
    color: var(--text-tertiary);
    min-height: 1em;
    padding-top: var(--space-hairline, 0.1rem);
}

/* ── Toggles ── */
.nbm-toggle-label {
    display: flex;
    align-items: center;
    gap: var(--space-1-5, 0.4rem);
    cursor: pointer;
    font-size: var(--font-size-controls);
    color: var(--text-secondary);
    user-select: none;
}

.nbm-toggle-label input[type="checkbox"] {
    appearance: none;
    -webkit-appearance: none;
    width: 14px;
    height: 14px;
    border: 1px solid var(--border-primary);
    border-radius: 0;
    cursor: pointer;
    position: relative;
    margin: 0;
    flex-shrink: 0;
    background: var(--background-primary);
}

.nbm-toggle-label input[type="checkbox"]:checked {
    border-color: var(--accent-success);
}

.nbm-toggle-label input[type="checkbox"]:checked::before {
    content: 'X';
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-size: var(--font-size-checkbox);
    font-weight: var(--font-weight-black);
    line-height: 1;
    color: var(--accent-success);
}

/* ── Buttons ── */
.nbm-btn {
    padding: var(--space-1-5, 0.38rem) var(--space-3, 0.75rem);
    border: 1px solid var(--border-primary);
    background: transparent;
    color: var(--text-primary);
    font-family: inherit;
    font-size: var(--font-size-controls);
    font-weight: var(--font-weight-semibold);
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
    white-space: nowrap;
    flex-shrink: 0;
}

.nbm-btn:hover {
    background: var(--background-secondary);
}

.nbm-btn-primary {
    border-color: var(--text-secondary);
    color: var(--text-primary);
}

.nbm-btn-primary:hover {
    background: var(--background-secondary);
}

.nbm-btn-secondary {
    color: var(--text-secondary);
}

/* "Create + New" — a distinct accent so it reads apart from the neutral primary
   Create button: it saves and immediately reopens the form for another. */
.nbm-btn-create-another {
    border-color: var(--accent-success, #34d399);
    color: var(--accent-success, #34d399);
}

.nbm-btn-create-another:hover {
    background: color-mix(in srgb, var(--accent-success, #34d399) 12%, transparent);
    color: var(--accent-success, #34d399);
}

/* ── Footer ── */
.nbm-footer {
    display: flex;
    gap: var(--space-2, 0.5rem);
    justify-content: flex-end;
    padding: var(--space-3, 0.65rem) var(--space-4, 1rem);
    background: var(--background-secondary);
    flex-shrink: 0;
}

/* ── Responsive ── */
@media (max-width: 580px) {
    .modal-new-bookmark {
        width: min(560px, 96vw);
    }

    .nbm-section-row {
        flex-direction: column;
        gap: var(--space-2, 0.5rem);
    }

    .nbm-col-narrow {
        flex: 1;
    }
}

/* ==== css/bookmark-form-preview.css ==== */
/* Shared bookmark form preview (dashboard tile + link preview card) */

.bookmark-form-preview-section {
    display: flex;
    flex-direction: column;
    gap: var(--space-3, 0.75rem);
}

.bookmark-form-preview-block {
    display: flex;
    flex-direction: column;
    gap: var(--space-1-5, 0.35rem);
}

.bookmark-form-preview-block-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2, 0.5rem);
    flex-wrap: wrap;
}

.bookmark-form-preview-label {
    font-size: var(--font-size-controls, 0.8125rem);
    font-weight: var(--font-weight-semibold, 600);
    color: var(--text-secondary);
}

.bookmark-form-preview-hint {
    font-size: var(--text-xs, 0.72rem);
    color: var(--text-tertiary);
    font-weight: var(--font-weight-normal, 400);
}

.bookmark-form-preview-empty {
    font-size: var(--font-size-controls, 0.8125rem);
    color: var(--text-tertiary);
    margin: 0;
    line-height: 1.45;
}

.bookmark-detail-link-preview-actions {
    display: flex;
    gap: var(--space-1-5, 0.35rem);
    flex-shrink: 0;
}

.config-bookmark-preview-hover-zone {
    position: relative;
    max-width: 100%;
    outline: none;
}

.config-bookmark-preview {
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-4, 6px);
    background: color-mix(in srgb, var(--background-primary) 55%, transparent);
    padding: var(--space-1-5, 0.35rem) var(--space-2, 0.5rem);
}

.config-bookmark-preview--compact {
    max-width: 100%;
    cursor: default;
}

.config-bookmark-preview-popover {
    display: none;
    position: absolute;
    left: 0;
    top: calc(100% + 0.35rem);
    z-index: 40;
    min-width: min(22rem, 100%);
    max-width: min(28rem, calc(100vw - 2rem));
    padding: var(--space-2, 0.5rem) var(--space-3, 0.65rem);
    border: 1px solid color-mix(in srgb, var(--accent-primary) 35%, var(--border-primary));
    border-radius: var(--radius-5, 8px);
    background: var(--background-primary);
    box-shadow: 0 10px 28px color-mix(in srgb, var(--background-primary) 20%, transparent),
        0 0 0 1px color-mix(in srgb, var(--accent-primary) 12%, transparent);
    pointer-events: none;
}

.config-bookmark-preview-hover-zone:hover .config-bookmark-preview-popover,
.config-bookmark-preview-hover-zone:focus-within .config-bookmark-preview-popover {
    display: block;
}

.config-bookmark-preview-hover-zone:hover .config-bookmark-preview--compact,
.config-bookmark-preview-hover-zone:focus-within .config-bookmark-preview--compact {
    border-color: color-mix(in srgb, var(--accent-primary) 45%, var(--border-primary));
}

.config-bookmark-preview-tile.bookmark-link {
    display: grid;
    grid-template-columns: minmax(1rem, max-content) minmax(0, 1fr) max-content;
    align-items: center;
    column-gap: var(--space-1-5, 0.35rem);
    padding: var(--space-hairline, 0.1rem) 0;
    min-height: 1.55rem;
    font-size: var(--font-size-bookmark, 0.8125rem);
    font-weight: var(--font-weight-semibold, 600);
    color: var(--text-primary);
    white-space: nowrap;
    max-width: 100%;
}

.config-bookmark-preview-tile .bookmark-lead {
    grid-column: 1;
    grid-row: 1;
    display: inline-flex;
    align-items: center;
    min-width: 1.1em;
    cursor: default;
}

.config-bookmark-preview-tile .bookmark-open {
    grid-column: 2;
    grid-row: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: var(--space-1, 0.25rem);
    overflow: hidden;
}

.config-bookmark-preview-tile .bookmark-text {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

.config-bookmark-preview-tile--expanded .bookmark-text {
    overflow: visible;
    text-overflow: unset;
    white-space: normal;
    word-break: break-word;
}

.config-bookmark-preview-tile--expanded.bookmark-link {
    white-space: normal;
    min-height: 2rem;
    padding: var(--space-hairline, 0.15rem) 0;
}

.config-bookmark-preview-tile--expanded .bookmark-open {
    flex-wrap: wrap;
    overflow: visible;
}

.config-link-preview-card {
    display: grid;
    grid-template-columns: 5.5rem 1fr;
    gap: var(--space-3, 0.65rem);
    align-items: start;
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-5, 8px);
    padding: var(--space-2, 0.55rem) var(--space-3, 0.65rem);
    background: color-mix(in srgb, var(--background-primary) 55%, transparent);
    margin-top: var(--space-1, 0.25rem);
}

.config-link-preview-card-image-wrap {
    width: 5.5rem;
    height: 3.25rem;
    border-radius: var(--radius-3, 4px);
    overflow: hidden;
    background: var(--background-secondary);
    border: 1px solid var(--border-secondary);
}

.config-link-preview-card-image {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.config-link-preview-card-body {
    min-width: 0;
}

.config-link-preview-card-title {
    font-weight: var(--font-weight-semibold, 600);
    font-size: var(--font-size-text);
    color: var(--text-primary);
    line-height: 1.35;
    margin-bottom: var(--space-1, 0.2rem);
}

.config-link-preview-card-desc {
    font-size: var(--font-size-controls);
    color: var(--text-secondary);
    line-height: 1.45;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.config-link-preview-card-domain {
    margin-top: var(--space-1-5, 0.35rem);
    font-size: var(--text-xs, 0.75rem);
    color: var(--text-tertiary);
}

.config-link-preview-card:not(:has(.config-link-preview-card-image-wrap)) {
    grid-template-columns: 1fr;
}

.config-bookmark-preview-tile .bookmark-shortcut {
    grid-column: 3;
    grid-row: 1;
    font-size: 0.85em;
    opacity: 0.85;
    text-align: right;
}

.config-bookmark-preview-tile .bookmark-icon-slot {
    width: 1.1em;
    height: 1.1em;
}

.config-bookmark-preview-tile .bookmark-icon-slot--empty {
    border: 1px dashed var(--border-secondary);
    border-radius: var(--radius-2, 3px);
    opacity: 0.45;
}

.config-bookmark-preview-tile .bookmark-icon {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
}

.config-bookmark-preview-tile .status-text {
    flex-shrink: 0;
    font-size: 0.75em;
    font-weight: var(--font-weight-normal, 400);
}

.config-bookmark-preview-tile .bookmark-superscript-badge {
    flex-shrink: 0;
    width: 0.85em;
    height: 0.85em;
}

.config-bookmark-preview-tile .bookmark-superscript-badge svg {
    width: 100%;
    height: 100%;
    fill: currentColor;
}

.modal-new-bookmark .bookmark-form-preview-section {
    margin: var(--space-1, 0.25rem) 0 var(--space-2, 0.5rem);
}

.modal-new-bookmark .config-link-preview-card {
    grid-template-columns: 4.5rem 1fr;
}

.modal-new-bookmark .config-link-preview-card-image-wrap {
    width: 4.5rem;
    height: 2.75rem;
}

/* Compact preview strip (primary add-bookmark view) */
.bookmark-form-preview-strip {
    display: flex;
    align-items: center;
    gap: var(--space-2, 0.55rem);
    padding: var(--space-2, 0.45rem) var(--space-2, 0.55rem);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-4, 6px);
    background: color-mix(in srgb, var(--background-primary) 55%, transparent);
    min-height: 2.35rem;
}

.bookmark-form-preview-strip-icon {
    width: 1.35rem;
    height: 1.35rem;
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}

.bookmark-form-preview-strip-icon img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
}

.bookmark-form-preview-strip-icon-empty {
    display: block;
    width: 100%;
    height: 100%;
    border: 1px dashed var(--border-secondary);
    border-radius: var(--radius-2, 3px);
    opacity: 0.45;
}

.bookmark-form-preview-strip-meta {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-hairline, 0.05rem);
}

.bookmark-form-preview-strip-title {
    font-size: var(--font-size-text);
    font-weight: var(--font-weight-semibold, 600);
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.bookmark-form-preview-strip-domain {
    font-size: var(--text-xs, 0.72rem);
    color: var(--text-tertiary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.bookmark-form-preview-strip-tile {
    flex-shrink: 0;
    max-width: 42%;
    min-width: 0;
    overflow: hidden;
}

.bookmark-form-preview-strip-tile .config-bookmark-preview-tile.bookmark-link {
    font-size: var(--text-xs, 0.72rem);
    min-height: 1.35rem;
    padding: 0;
}

.nbm-section-compact-preview {
    padding-top: var(--space-1-5, 0.35rem);
    padding-bottom: var(--space-1-5, 0.35rem);
}

.ext-preview-section .config-link-preview-card {
    grid-template-columns: 3.5rem 1fr;
    padding: var(--space-2, 0.45rem) var(--space-2, 0.5rem);
}

.ext-preview-section .config-link-preview-card-image-wrap {
    width: 3.5rem;
    height: 2.25rem;
}

.ext-preview-section .bookmark-form-preview-block-header {
    margin-top: var(--space-1-5, 0.35rem);
}

/* The feed behind this bookmark, when Fresh knows of one.
   A quiet line under the preview: it answers "why does this one never say
   anything", which is a question asked once, not a fact to read every time. */
.bookmark-form-feed-line {
    display: flex;
    align-items: baseline;
    gap: var(--space-1-5, 0.4rem);
    margin: var(--space-2, 0.5rem) 0 0;
    font-size: var(--text-sm, 0.8rem);
    color: var(--text-secondary, #666);
    overflow: hidden;
}

.bookmark-form-feed-label {
    flex: 0 0 auto;
    color: var(--text-tertiary, #888);
}

.bookmark-form-feed-url {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-family: var(--font-mono, ui-monospace, monospace);
    font-size: var(--text-xs, 0.75rem);
}

/* ==== css/check-mode-control.css ==== */
/* ─── Availability check mode (shared) ───────────────────────────────────────
   The three-way Off / Periodic / Monitor control, used by both surfaces that
   offer the choice: the config bookmark panel and the dashboard's add-bookmark
   modal. It lives in its own file rather than in config-bookmarks-splitview.css
   because the dashboard does not load that stylesheet — copying the rules there
   is how the two would end up looking different.

   The .bookmark-detail-* names are kept even outside the config panel: the
   markup is identical on both surfaces on purpose, so one set of rules can
   style them both.
   ------------------------------------------------------------------------- */

/* Needed by the block below, and small enough to travel with it rather than
   pull the whole config panel stylesheet onto the dashboard. */
.bookmark-detail-label {
    font-size: var(--font-size-controls);
    color: var(--text-secondary);
    font-weight: var(--font-weight-semibold);
    display: block;
}

.bookmark-detail-field-hint {
    display: block;
    font-size: var(--font-size-small, 0.75rem);
    color: var(--text-tertiary);
    margin-top: var(--space-1, 0.2rem);
}

.bookmark-detail-checkmode-header {
    display: flex;
    align-items: center;
    gap: var(--space-1-5, 0.35rem);
}

.bookmark-detail-checkmode-options {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1, 0.25rem);
    margin-top: var(--space-1, 0.3rem);
}

.bookmark-detail-checkmode-input {
    position: absolute;
    width: 1px !important;
    height: 1px !important;
    margin: -1px !important;
    padding: 0 !important;
    border: 0 !important;
    opacity: 0 !important;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    pointer-events: none;
    appearance: none;
    -webkit-appearance: none;
}

.bookmark-detail-checkmode-option {
    padding: var(--space-1, 0.25rem) var(--space-2, 0.6rem);
    border: 1px solid var(--border-secondary);
    border-radius: var(--radius-pill, 999px);
    background: var(--background-primary);
    color: var(--text-secondary);
    font-size: var(--font-size-controls);
    line-height: 1.2;
    cursor: pointer;
    user-select: none;
    transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
}

.bookmark-detail-checkmode-option:hover {
    border-color: color-mix(in srgb, var(--accent-primary) 45%, var(--border-secondary));
    color: var(--text-primary);
}

.bookmark-detail-checkmode-input:checked + .bookmark-detail-checkmode-option {
    border-color: var(--accent-primary);
    background: color-mix(in srgb, var(--accent-primary) 14%, var(--background-primary));
    color: var(--text-primary);
    font-weight: var(--font-weight-semibold);
}

.bookmark-detail-checkmode-input:focus-visible + .bookmark-detail-checkmode-option {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}

.bookmark-detail-checkmode-info {
    width: 1.15rem;
    height: 1.15rem;
    padding: 0;
    border: 1px solid var(--border-secondary);
    border-radius: 50%;
    background: transparent;
    color: var(--text-tertiary);
    font-size: var(--text-xs, 0.7rem);
    font-style: italic;
    line-height: 1;
    cursor: pointer;
    flex: 0 0 auto;
}

.bookmark-detail-checkmode-info:hover {
    border-color: var(--accent-primary);
    color: var(--text-primary);
}

.bookmark-detail-checkmode .bookmark-detail-field-hint {
    margin-top: var(--space-1-5, 0.35rem);
}

.bookmark-detail-checkmode .custom-select-wrapper,
.bookmark-detail-checkmode .bookmark-detail-toggle-select {
    flex: 0 1 auto;
    width: auto;
    min-width: 0;
}

@media (prefers-reduced-motion: reduce) {
    .bookmark-detail-checkmode-option {
        transition: none;
    }
}

/* The modal is narrower than the config panel, so the row wraps rather than
   pushing the interval select off the edge. */
.modal-new-bookmark .bookmark-detail-checkmode-options {
    row-gap: var(--space-1-5, 0.35rem);
}

/* ─── Explainer stacking ─────────────────────────────────────────────────────
   The (i) explainer is opened from surfaces that are themselves modals — the
   add-bookmark modal, the inline editor — and every overlay in the app sits at
   z-index 2000. A tie is broken by DOM order, and #app-modal is created first,
   so without this the explanation opens *behind* the form that asked for it.
   ------------------------------------------------------------------------- */
#app-modal.modal-overlay:has(.check-mode-explain-modal) {
    z-index: 2100;
}


/* ─── Icon toggle (shared) ───────────────────────────────────────────────────
   The compact checkbox-as-pill used for Pinned. Moved out of config-lists.css
   so the dashboard's add-bookmark modal can use the same control — that page
   does not load the config stylesheets, and a second copy is how the two would
   drift apart.
   ------------------------------------------------------------------------- */
.icon-toggle {
    position: relative;
    display: inline-flex;
    align-items: center;
}

.icon-toggle input[type="checkbox"] {
    position: absolute;
    opacity: 0;
    width: 1px;
    height: 1px;
    pointer-events: none;
}

.icon-toggle-indicator {
    width: 1.75rem;
    height: 1.75rem;
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-3, 4px);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--text-secondary);
    background: var(--background-primary);
    cursor: pointer;
    position: relative;
}

.icon-toggle-indicator svg {
    width: 0.95rem;
    height: 0.95rem;
    fill: currentColor;
    display: block;
}

.icon-toggle input[type="checkbox"]:checked + .icon-toggle-indicator {
    color: var(--text-primary);
    border-color: var(--text-secondary);
}

.icon-toggle input[type="checkbox"]:focus-visible + .icon-toggle-indicator {
    outline: 2px solid var(--text-secondary);
    outline-offset: 2px;
}

.icon-toggle-indicator[data-tooltip]::after {
    content: attr(data-tooltip);
    position: absolute;
    bottom: calc(100% + 6px);
    left: 50%;
    transform: translateX(-50%);
    padding: var(--space-1, 0.2rem) var(--space-2, 0.45rem);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-3, 4px);
    background: var(--background-secondary);
    color: var(--text-primary);
    font-size: var(--text-xs, 0.72rem);
    white-space: nowrap;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.12s ease;
    z-index: 15;
}

.icon-toggle-indicator[data-tooltip]:hover::after,
.icon-toggle input[type="checkbox"]:focus-visible + .icon-toggle-indicator::after {
    opacity: 1;
}

/* The label beside the pill, and the modal's own spacing for it. */
.bookmark-detail-toggle {
    gap: var(--space-1-5, 0.4rem);
    cursor: pointer;
}

.bookmark-detail-toggle-label {
    font-size: var(--font-size-controls);
    color: var(--text-secondary);
}

.nbm-pin-toggle {
    margin-top: var(--space-hairline, 0.15rem);
}

/* ==== css/search-commands-note.css ==== */
/* :note command modal (AppModal + note-command-modal-inner) */

.modal.note-command-modal-inner {
    max-width: 480px;
    width: min(480px, 92vw);
    padding: 0;
    overflow: hidden;
}

.modal.note-command-modal-inner .modal-header {
    padding: var(--space-3, 0.65rem) var(--space-4, 1rem);
    border-bottom: 1px solid var(--border-primary);
    background: var(--background-secondary);
}

.modal.note-command-modal-inner .modal-title {
    font-size: var(--font-size-text);
    font-weight: var(--font-weight-semibold);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.modal.note-command-modal-inner .modal-body {
    padding: 0;
}

.modal.note-command-modal-inner .modal-actions {
    padding: var(--space-2, 0.6rem) var(--space-4, 1rem);
    border-top: 1px solid var(--border-primary);
    background: var(--background-secondary);
}

.note-cmd-body {
    padding: var(--space-3, 0.75rem) var(--space-4, 1rem);
}

.note-cmd-textarea {
    width: 100%;
    box-sizing: border-box;
    background: var(--background-primary);
    color: var(--text-primary);
    border: 1px solid var(--border-primary);
    border-radius: var(--border-radius-sm, 3px);
    padding: var(--space-2, 0.6rem) var(--space-3, 0.75rem);
    font-family: inherit;
    font-size: var(--font-size-text);
    line-height: 1.5;
    resize: vertical;
    outline: none;
    transition: border-color 0.15s;
}

.note-cmd-textarea:focus {
    border-color: var(--accent-info, #60a5fa);
}

.note-cmd-textarea::placeholder {
    color: var(--text-tertiary, var(--text-secondary));
    opacity: 0.7;
}

.note-cmd-hint {
    margin: var(--space-2, 0.5rem) 0 0;
    font-size: var(--font-size-small);
    color: var(--text-secondary);
}

/* ==== css/status.css ==== */
/* Status Monitoring Styles */

/* Theme-aware status tokens — tuned for contrast on every theme */
body:not(.status-color-lock) {
    --status-offline-fg: var(--accent-error);
    --status-checking-fg: var(--accent-warning);

    --status-offline-bg: color-mix(in srgb, var(--accent-error) 20%, var(--background-primary));
    --status-checking-bg: color-mix(in srgb, var(--accent-warning) 14%, var(--background-primary));

    --status-offline-bg-active: color-mix(in srgb, var(--accent-error) 30%, var(--background-primary));
    --status-checking-bg-active: color-mix(in srgb, var(--accent-warning) 22%, var(--background-primary));

    --status-offline-bar: var(--accent-error);
    --status-offline-ring: color-mix(in srgb, var(--accent-error) 55%, transparent);
    --status-checking-ring: color-mix(in srgb, var(--accent-warning) 48%, transparent);
}

/* Status indicators — colour/background only; avoid opacity/transform so rows keep stable height */
.bookmark-link.status-checking {
    transition: color var(--transition-normal), background-color var(--transition-normal), box-shadow var(--transition-normal);
}

.bookmark-link.status-online {
    position: relative;
    transition: color var(--transition-normal), background-color var(--transition-normal), box-shadow var(--transition-normal);
}

.bookmark-link.status-offline {
    position: relative;
    transition: color var(--transition-normal), background-color var(--transition-normal), box-shadow var(--transition-normal);
}

/* Status colors — offline/checking rows only (idle); hover/selection uses bookmark-row chrome */
body:not(.status-color-lock) .bookmarks-list[data-bookmarks-list="true"]:not([data-smart-collection="true"]) .bookmark-link.status-offline:not(:hover):not(.keyboard-selected):not(:focus-within):not(.bookmark-inline-editing) {
    color: var(--status-offline-fg, var(--accent-error, #ef4444));
    background: var(--status-offline-bg, color-mix(in srgb, var(--accent-error, #ef4444) 24%, var(--background-primary)));
    box-shadow: inset 3px 0 0 var(--status-offline-bar, var(--accent-error, #ef4444));
}

body:not(.status-color-lock) .bookmarks-list[data-bookmarks-list="true"]:not([data-smart-collection="true"]) .bookmark-link.status-checking:not(:hover):not(.keyboard-selected):not(:focus-within):not(.bookmark-inline-editing) {
    color: var(--status-checking-fg, var(--accent-warning, #f59e0b));
    background: var(--status-checking-bg, color-mix(in srgb, var(--accent-warning, #f59e0b) 16%, var(--background-primary)));
}

body:not(.status-color-lock) .bookmarks-list[data-bookmarks-list="true"]:not([data-smart-collection="true"]) .bookmark-link.status-offline .bookmark-open,
body:not(.status-color-lock) .bookmarks-list[data-bookmarks-list="true"]:not([data-smart-collection="true"]) .bookmark-link.status-offline .bookmark-text,
body:not(.status-color-lock) .bookmarks-list[data-bookmarks-list="true"]:not([data-smart-collection="true"]) .bookmark-link.status-checking .bookmark-open,
body:not(.status-color-lock) .bookmarks-list[data-bookmarks-list="true"]:not([data-smart-collection="true"]) .bookmark-link.status-checking .bookmark-text {
    color: inherit;
}

/* Optional: preserve bookmark row colors while still showing status state/ping (idle only) */
body.status-color-lock .bookmark-link.status-online:not(:hover):not(.keyboard-selected):not(:focus-within):not(.bookmark-inline-editing),
body.status-color-lock .bookmark-link.status-offline:not(:hover):not(.keyboard-selected):not(:focus-within):not(.bookmark-inline-editing),
body.status-color-lock .bookmark-link.status-checking:not(:hover):not(.keyboard-selected):not(:focus-within):not(.bookmark-inline-editing) {
    color: inherit;
    background: none;
    box-shadow: none;
    font-weight: inherit;
}

body.status-color-lock .bookmark-link.status-online:not(:hover):not(.keyboard-selected):not(:focus-within) .status-text,
body.status-color-lock .bookmark-link.status-offline:not(:hover):not(.keyboard-selected):not(:focus-within) .status-text,
body.status-color-lock .bookmark-link.status-checking:not(:hover):not(.keyboard-selected):not(:focus-within) .status-text {
    color: var(--text-secondary);
}

/* Status text (ping time) — dedicated grid column; fixed width via bookmarks-list[data-show-ping] */
.status-text {
    opacity: 0.9;
    transition: color 0.3s ease;
    color: var(--text-secondary);
    min-width: 0;
}

.status-text.is-empty {
    visibility: hidden;
}

/* Online status ping time — row stays neutral; badge shows reachability when idle */
.bookmark-link.status-online:not(:hover):not(.keyboard-selected):not(:focus-within) .status-text {
    color: var(--accent-success);
    border-color: color-mix(in srgb, var(--accent-success) 30%, transparent);
}

/* Hover/selection: ping badge matches other row badges (full row uses bookmark chrome) */
.bookmark-link.status-online:is(:hover, .keyboard-selected, :focus-within):not(.bookmark-inline-editing) .status-text,
.bookmark-link.status-offline:is(:hover, .keyboard-selected, :focus-within):not(.bookmark-inline-editing) .status-text,
.bookmark-link.status-checking:is(:hover, .keyboard-selected, :focus-within):not(.bookmark-inline-editing) .status-text {
    color: var(--text-secondary);
    border-color: color-mix(in srgb, var(--accent-primary) 35%, var(--border-secondary));
    background: transparent;
}

/* Offline status text (idle only) */
.bookmark-link.status-offline:not(:hover):not(.keyboard-selected):not(:focus-within) .status-text {
    color: var(--accent-error);
    border-color: color-mix(in srgb, var(--accent-error) 30%, transparent);
}

/* Checking status text (idle only) */
.bookmark-link.status-checking:not(:hover):not(.keyboard-selected):not(:focus-within) .status-text {
    color: var(--accent-warning);
    border-color: color-mix(in srgb, var(--accent-warning) 30%, transparent);
}

/* Status indicator dots - HIDDEN */
.status-indicator {
    display: none;
}

/* Status indicator styles removed - indicator hidden */

@keyframes pulse {
    0%, 100% {
        opacity: 1;
    }
    50% {
        opacity: 0.5;
    }
}

/* Configuration toggle for individual bookmarks */
.bookmark-status-toggle {
    font-size: var(--font-size-controls);
}

.bookmark-status-toggle input[type="checkbox"] {
    width: 14px !important;
    height: 14px !important;
    margin-right: var(--space-1, 0.25rem);
}

.bookmark-status-toggle label {
    font-size: var(--font-size-controls);
    opacity: 0.8;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: var(--space-1, 0.25rem);
    color: var(--text-secondary);
}

/* Status monitoring settings group */
.status-settings-group {
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-5, 8px);
    padding: var(--space-4, 1rem);
    margin-top: var(--space-4, 1rem);
    background: var(--background-secondary);
}

.status-settings-info {
    margin-top: var(--space-2, 0.5rem);
    margin-left: var(--space-5, 1.625rem); /* Align with checkbox text */
}

.status-settings-info small {
    font-size: var(--font-size-controls);
    opacity: 0.7;
    line-height: 1.4;
}

/* Nested setting for ping display */
.status-settings-nested {
    margin-left: var(--space-5, 1.625rem);
    margin-top: var(--space-3, 0.75rem);
    padding-left: var(--space-4, 1rem);
    border-left: 2px solid var(--border-primary);
}

/* Refresh button for manual status check */
.status-refresh-btn {
    margin-left: var(--space-2, 0.5rem);
    padding: var(--space-1, 0.25rem) var(--space-2, 0.5rem);
    font-size: var(--font-size-controls);
    font-family: var(--font-family-main);
    border: 1px solid var(--border-primary);
    border-radius: var(--layout-radius-sm, 8px);
    cursor: pointer;
    transition: all 0.2s ease;
    background: var(--background-secondary);
    color: var(--text-secondary);
}

.status-refresh-btn:hover {
    background: var(--background-secondary);
    border-color: var(--text-secondary);
    color: var(--text-primary);
}

/* Status Loading Indicator */
.status-loading-indicator {
    display: none; /* Hidden by default */
    font-family: var(--font-family-main);
    font-size: var(--font-size-controls);
    font-weight: var(--font-weight-bold);
    color: var(--accent-warning);
    opacity: 0.8;
    margin-left: var(--space-4, 1rem);
    align-items: center;
    gap: var(--space-1, 0.25rem);
    white-space: nowrap;
}

.status-loading-indicator.show {
    display: flex;
}

.status-loading-text {
    font-family: var(--font-family-main);
    letter-spacing: 0.05em;
}

.status-loading-dots::after {
    content: '';
    animation: loading-dots 1.5s infinite;
    font-weight: var(--font-weight-bold);
}

@keyframes loading-dots {
    0%, 30% {
        content: '.';
    }
    31%, 60% {
        content: '..';
    }
    61%, 100% {
        content: '...';
    }
}

/* Responsive adjustments */
@media (max-width: 768px) {
    .status-loading-indicator {
        font-size: calc(var(--font-size-controls) * 0.9);
        margin-left: var(--space-2, 0.5rem);
    }
}

/* ── Monitor emphasis ───────────────────────────────────────────────────────
   How loudly a monitored bookmark announces itself, chosen in Behavior →
   Status & health and mirrored onto <body> by setupDOM.

   The row always carries data-check-mode="monitor"; only the emphasis differs,
   so switching modes is a repaint rather than a re-render.

     problems (default) — a healthy monitor looks like any other bookmark; only
                          an outage draws the eye, via the status-offline rules
                          above. Nothing extra is needed here.
     always             — an accent edge on every monitor, healthy or not.
     never              — monitoring stays in the Health view, including the
                          outage colouring the other two modes rely on.
*/
body[data-monitor-emphasis="always"] .bookmarks-list[data-bookmarks-list="true"]:not([data-smart-collection="true"]) .bookmark-link[data-check-mode="monitor"]:not(.status-offline):not(:hover):not(.keyboard-selected):not(:focus-within):not(.bookmark-inline-editing) {
    box-shadow: inset 3px 0 0 var(--monitor-emphasis-bar, var(--accent-primary));
}

/* Deliberately also suppresses the offline colouring: "never stand out" that
   still turned a row red would not be never. The Health view and the header
   badge remain the places an outage is reported. */
body[data-monitor-emphasis="never"] .bookmark-link[data-check-mode="monitor"]:not(:hover):not(.keyboard-selected):not(:focus-within):not(.bookmark-inline-editing) {
    color: inherit;
    background: none;
    box-shadow: none;
}

/* ==== css/select.css ==== */
/* Custom Terminal-Style Select Component */

.custom-select-wrapper {
    position: relative;
    display: inline-block;
    min-width: 0;
}

.page-selector-wrapper .custom-select-wrapper {
    min-width: 200px;
    width: 100%;
}

.custom-select {
    position: relative;
    cursor: pointer;
}

.custom-select-trigger {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-1-5, 0.375rem) var(--space-2, 0.5rem);
    border: none;
    border: 1px solid var(--border-primary);
    background: var(--background-primary);
    color: var(--text-primary);
    font-size: var(--font-size-text);
    font-family: var(--font-family-main);
    cursor: pointer;
    user-select: none;
    transition: border-color 0.3s ease;
}

.custom-select-trigger:hover {
    opacity: 0.8;
}

.custom-select.open .custom-select-trigger {
    border-color: var(--text-secondary);
}

.custom-select-arrow {
    font-size: var(--font-size-controls);
    margin-left: var(--space-2, 0.5rem);
    transition: transform 0.2s ease;
}

.custom-select.open .custom-select-arrow {
    transform: rotate(180deg);
}

.custom-select-options {
    position: fixed;
    border: 1px solid var(--border-primary);
    max-height: 250px;
    overflow-y: auto;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.2s ease, visibility 0.2s ease;
    z-index: 9999;
    background-color: var(--background-primary);
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
}

.custom-select-options.is-open {
    opacity: 1;
    visibility: visible;
}

.custom-select-option {
    padding: var(--space-2, 0.5rem) var(--space-3, 0.75rem);
    font-size: var(--font-size-text);
    font-family: var(--font-family-main);
    cursor: pointer;
    transition: background-color 0.15s ease;
    user-select: none;
    display: flex;
    align-items: center;
    gap: var(--space-2, 0.5rem);
    color: var(--text-primary);
}

.custom-select-option:hover {
    background-color: var(--background-secondary);
}

.custom-select-option.selected {
    font-weight: var(--font-weight-semibold);
    color: var(--accent-success);
}

.custom-select-option.selected::before {
    content: '>';
    font-weight: var(--font-weight-black);
    margin-right: var(--space-1, 0.25rem);
}

.custom-select-option.highlighted {
    font-weight: var(--font-weight-semibold);
}

/* Scrollbar styling for options */
.custom-select-options::-webkit-scrollbar {
    width: 8px;
}

.custom-select-options::-webkit-scrollbar-track {
    background: var(--background-primary);
}

.custom-select-options::-webkit-scrollbar-thumb {
    background: var(--border-primary);
    border-radius: 0;
}

.custom-select-options::-webkit-scrollbar-thumb:hover {
    background: var(--text-secondary);
}

/* Hide original select */
.custom-select-wrapper select {
    display: none;
}

/* ==== css/font-size.css ==== */
/* Font Size System - CSS Variables */

/* Global font for the entire app */
:root {
    --font-family-main: 'Source Code Pro', monospace;
    font-family: var(--font-family-main);
}

/* Font Size Variables - Default (Medium/m - Level 3) */
:root {
    /* Main Categories - Only 5 core variables */
    --font-size-title: 3rem;           /* Dashboard title (big title) */
    --font-size-date: 1.125rem;        /* Date in header */
    --font-size-category: 1.125rem;    /* Category/column titles */
    --font-size-bookmark: 1.25rem;     /* Bookmark links */
    --font-size-controls: 0.875rem;    /* Tabs, buttons, config link */
    --font-size-text: 0.875rem;        /* General text (forms, config, body text, etc.) */
    /*
     * Captions, hints, and the word under a figure.
     *
     * Used in about forty places and defined in none of them until now, so
     * every rule that asked for it without spelling out a fallback was an
     * invalid declaration and the text rendered at whatever it inherited --
     * measured at 18px where the surrounding body text was also 18px. The
     * sixteen call sites that did carry a fallback are why it looked
     * inconsistent rather than broken: the same intended size came out
     * 0.66rem in one place and full size in the next.
     *
     * Derived from --font-size-text rather than fixed, so it follows the
     * reader's size setting the way everything else on this scale does. At
     * the default that is 0.717rem, which is where the hand-written fallbacks
     * already clustered.
     */
    --font-size-small: calc(var(--font-size-text) * 0.82);
    
    /* Font Weight Variables - 4 core weights */
    --font-weight-normal: 400;         /* Normal text weight */
    --font-weight-medium: 500;         /* Medium - asked for in nine places, defined in none */
    --font-weight-semibold: 600;       /* Semibold - labels, emphasis */
    --font-weight-bold: 700;           /* Bold - headings, important text */
    --font-weight-black: 900;          /* Extra bold - titles, strong emphasis */
}

/* Extra Small - xs (Level 0) */
body.font-size-xs {
    --font-size-title: 1.5rem;
    --font-size-date: 0.75rem;
    --font-size-category: 0.75rem;
    --font-size-bookmark: 0.813rem;
    --font-size-controls: 0.625rem;
    --font-size-text: 0.625rem;
}

/* Small - s (Level 1) */
body.font-size-s {
    --font-size-title: 1.75rem;
    --font-size-date: 0.813rem;
    --font-size-category: 0.813rem;
    --font-size-bookmark: 0.938rem;
    --font-size-controls: 0.688rem;
    --font-size-text: 0.688rem;
}

/* Small-Medium - sm (Level 2) */
body.font-size-sm {
    --font-size-title: 2rem;
    --font-size-date: 0.938rem;
    --font-size-category: 0.938rem;
    --font-size-bookmark: 1rem;
    --font-size-controls: 0.75rem;
    --font-size-text: 0.75rem;
}

/* Medium - m (Level 3 - Default) */
body.font-size-m {
    --font-size-title: 3rem;
    --font-size-date: 1.125rem;
    --font-size-category: 1.125rem;
    --font-size-bookmark: 1.25rem;
    --font-size-controls: 0.875rem;
    --font-size-text: 0.875rem;
}

/* Large-Medium - lg (Level 4) */
body.font-size-lg {
    --font-size-title: 3.5rem;
    --font-size-date: 1.25rem;
    --font-size-category: 1.25rem;
    --font-size-bookmark: 1.375rem;
    --font-size-controls: 0.938rem;
    --font-size-text: 0.938rem;
}

/* Large - l (Level 5) */
body.font-size-l {
    --font-size-title: 4rem;
    --font-size-date: 1.375rem;
    --font-size-category: 1.375rem;
    --font-size-bookmark: 1.5rem;
    --font-size-controls: 1rem;
    --font-size-text: 1rem;
}

/* Extra Large - xl (Level 6) */
body.font-size-xl {
    --font-size-title: 4.5rem;
    --font-size-date: 1.5rem;
    --font-size-category: 1.5rem;
    --font-size-bookmark: 1.625rem;
    --font-size-controls: 1.125rem;
    --font-size-text: 1.125rem;
}

/* Extra Large - xl (Level 6) */
body.font-size-xl {
    --font-size-title: 4.5rem;
    --font-size-date: 1.5rem;
    --font-size-category: 1.5rem;
    --font-size-bookmark: 1.625rem;
    --font-size-controls: 1.125rem;
    --font-size-text: 1.125rem;
}

/* Responsive adjustments for font sizes */
@media (max-width: 767px) {
    body.font-size-xl {
        --font-size-title: 3.5rem;
    }
    
    body.font-size-l {
        --font-size-title: 3rem;
    }
    
    body.font-size-lg {
        --font-size-title: 2.5rem;
    }
}

@media (max-width: 479px) {
    body.font-size-xl {
        --font-size-title: 3rem;
    }
    
    body.font-size-l {
        --font-size-title: 2.5rem;
    }
    
    body.font-size-lg {
        --font-size-title: 2.25rem;
    }
    
    body.font-size-m {
        --font-size-title: 2rem;
    }
    
    body.font-size-sm {
        --font-size-title: 1.75rem;
    }
    
    body.font-size-s {
        --font-size-title: 1.5rem;
    }
    
    body.font-size-xs {
        --font-size-title: 1.25rem;
    }
}

/* ==== css/responsive.css ==== */
/* Responsive Design System */

/* General mobile button styles */
.button-container {
    gap: var(--space-2, 0.5rem);
}

/* Large Desktop (1200px+) - Default styles already applied */

/* Desktop (992px - 1199px) */
@media (max-width: 1199px) {
    .dashboard-section.section-controls,
    .config-section.section-header {
        padding-top: 1.75rem;
    }
    
    .dashboard-section.section-content {
        padding-bottom: var(--space-7, 3rem); /* Space for search button */
    }
    
    .config-section.section-content {
        padding-top: var(--space-3, 0.875rem);
        padding-bottom: 1.75rem;
    }
    
    .container {
        /* Roomier clamp() margin (not a percentage) so the columns sit closer
           together, shrinking toward the floor as the window narrows. */
        max-width: 100%;
        padding: 0 clamp(var(--space-5, 1.5rem), 5vw, 6rem);
    }

    /* Keep the config page shell in step with the dashboard .container at this
       breakpoint so the config header/nav stay aligned with the dashboard. */
    html:has(#config-main) .config-page-shell {
        grid-template-columns: minmax(0, 1fr);
        max-width: 1600px;
        padding-inline: clamp(var(--space-5, 1.5rem), 5vw, 6rem);
    }

    /* Same for the health shell, so its header/nav stay aligned too. */
    .health-shell {
        max-width: 100%;
        padding-left: clamp(var(--space-5, 1.5rem), 5vw, 6rem);
        padding-right: clamp(var(--space-5, 1.5rem), 5vw, 6rem);
    }

    .dashboard-grid {
        gap: var(--space-6, 2rem);
    }
    
    .search-container {
        width: min(52rem, calc(100vw - 2.5rem));
        max-width: 100%;
    }
    
    /* No `bottom` here (or in the narrower breakpoints below): the button bar is
       positioned by .button-container, and these buttons are flex children of
       it. The declaration was a leftover from when each button was fixed
       individually. It was inert on the `static` ones, but .search-button
       [data-tooltip] sets position: relative for the tooltip anchor — so the
       one button the JS tooltip system does not manage (fold-all, which keeps
       its data-tooltip attribute) was lifted out of the row by exactly that
       many pixels as soon as a breakpoint applied. */
    .search-button {
        padding: var(--space-3, 0.8rem) var(--space-4, 1rem);
    }
    
    /* Tabs responsive - desktop */
    .tabs {
        flex-wrap: wrap;
    }
    
    /* List items responsive - desktop */
    .page-item,
    .category-item,
    .bookmark-item,
    .finder-item {
        flex-wrap: wrap;
        gap: var(--space-2, 0.5rem);
    }
    
    .form-group {
        flex-wrap: wrap;
    }
    
    .config-actions-top {
        flex-wrap: wrap;
        gap: var(--space-3, 0.75rem);
    }
}

/* Tablet (768px - 991px) */
@media (max-width: 991px) {
    .dashboard-section.section-controls,
    .config-section.section-header {
        padding-top: var(--space-5, 1.5rem);
    }
    
    .dashboard-section.section-content {
        padding-bottom: var(--space-7, 3rem); /* Space for search button */
    }
    
    .config-section.section-content {
        padding-top: var(--space-3, 0.75rem);
        padding-bottom: var(--space-5, 1.5rem);
    }
    
    .container {
        max-width: 95%;
        padding: 0 var(--space-3, 0.75rem);
    }
    
    .dashboard-grid {
        grid-template-columns: repeat(2, 1fr) !important;
        gap: var(--space-5, 1.5rem);
    }

    body[data-layout-version="modern"] .dashboard-grid {
        grid-template-columns: unset !important;
    }

    .dashboard-grid.packed-columns {
        display: flex !important;
        flex-wrap: wrap;
        justify-content: center;
        grid-template-columns: unset !important;
        gap: var(--space-5, 1.5rem);
    }

    body[data-layout-version="modern"] .dashboard-grid.packed-columns {
        display: grid !important;
        flex-wrap: unset;
        justify-content: unset;
    }

    .dashboard-grid.packed-columns .dashboard-column {
        flex: 1 1 calc(50% - 0.75rem);
        gap: var(--space-5, 1.5rem);
    }

    body[data-layout-version="modern"] .dashboard-grid.packed-columns .dashboard-column {
        flex: unset;
    }
    
    .title {
        font-size: var(--font-size-title);
    }
    
    .header-actions {
        gap: var(--header-control-gap, 0.5rem);
        margin-left: auto;
    }
    
    .page-nav-btn {
        padding: var(--space-1-5, 0.35rem) var(--space-2, 0.5rem);
        font-size: var(--font-size-controls);
    }
    
    .config-link a {
        font-size: var(--font-size-controls);
    }
    
    .bookmark-link {
        padding: var(--space-1-5, 0.4rem) 0;
    }
    
    .config-button {
        padding: var(--space-3, 0.75rem) var(--space-4, 1rem);
    }
    
    .search-container {
        width: min(52rem, calc(100vw - 2rem));
        max-width: 100%;
        padding: 1.25rem var(--space-5, 1.5rem) var(--space-3, 0.875rem) var(--space-6, 2rem);
    }
    
    .search-button {
        padding: var(--space-3, 0.8rem) var(--space-4, 1rem);
        font-size: var(--font-size-controls);
    }
    
    /* Config page tablet styles */
    .bookmark-item {
        display: grid;
        grid-template-columns: 1fr 1fr 100px 120px 80px;
        gap: var(--space-3, 0.75rem);
    }
    
    .finder-item {
        display: grid;
        grid-template-columns: 1fr 1fr 100px 80px;
        gap: var(--space-3, 0.75rem);
    }
    
    .category-item {
        display: grid;
        grid-template-columns: 1fr 80px;
    }
    
    .section {
        margin-bottom: var(--space-6, 2rem);
    }
    
    /* Colors page - stack vertically on tablet */
    .config-main,
    .custom-themes-main-grid {
        grid-template-columns: 1fr;
        gap: var(--space-6, 2rem);
    }
    
    /* Tabs responsive - tablet */
    .tabs {
        flex-wrap: wrap;
    }
    
    .tab-button {
        padding: var(--space-2, 0.625rem) 1.25rem;
        font-size: var(--font-size-controls);
    }
    
    .config-actions-top {
        flex-wrap: wrap;
        gap: var(--space-3, 0.75rem);
    }
}

/* Mobile Large (576px - 767px) */
@media (max-width: 767px) {
    .container {
        padding: var(--space-4, 1rem) var(--space-2, 0.5rem);
    }
    
    .dashboard-grid {
        grid-template-columns: 1fr !important;
        gap: var(--space-5, 1.5rem);
    }

    body[data-layout-version="modern"] .dashboard-grid {
        grid-template-columns: unset !important;
    }
    
    .category h3 {
        margin-bottom: var(--space-3, 0.75rem);
    }
    
    .bookmark-link {
        padding: var(--space-1-5, 0.35rem) 0;
    }
    
    .config-button {
        padding: var(--space-2, 0.625rem) var(--space-3, 0.875rem);
    }
    
    .shortcut-hint {
        bottom: 1rem;
        right: 1rem;
        left: 1rem;
        text-align: center;
        padding: var(--space-2, 0.5rem);
    }
    
    .search-container {
        width: calc(100vw - 2rem);
        max-width: 100%;
        padding: var(--space-4, 1rem) 1.25rem var(--space-3, 0.75rem) var(--space-6, 2rem);
    }
    
    /* Prevent horizontal scroll on small screens */
    .search-prompt,
    .search-query,
    .search-match-name {
        max-width: 100%;
        overflow-x: hidden;
    }
    
    .search-match {
        padding: var(--space-1-5, 0.4rem) 0;
    }
    
    .search-button {
        padding: var(--space-3, 0.8rem) var(--space-4, 1rem);
        font-size: var(--font-size-controls);
    }
    
    .button-container {
        width: calc(100% - 2rem);
        justify-content: center;
        bottom: 1rem;
    }
    
    /* Header responsive - mobile large */
    .dashboard-section,
    .config-section {
        padding: 0;
    }
    
    .dashboard-section.section-controls,
    .config-section.section-header {
        padding-top: var(--space-5, 1.5rem);
    }

    .dashboard-section.section-controls {
        padding-bottom: 0;
    }

    .config-section.section-header {
        padding-bottom: 1.25rem;
    }
    
    .dashboard-section.section-title,
    .config-section.section-controls {
        padding-top: 0;
        padding-bottom: 0;
    }
    
    .dashboard-section.section-content {
        padding-top: 0;
        padding-bottom: var(--space-7, 3rem); /* Space for search button */
    }
    
    .config-section.section-content {
        padding-top: var(--space-3, 0.75rem);
        padding-bottom: var(--space-5, 1.5rem);
    }
    
    .container {
        padding: 0 var(--space-3, 0.75rem);
    }
    
    .header-top {
        flex-direction: row;
        align-items: center;
        gap: var(--space-2, 0.5rem);
    }

    .date {
        display: none;
    }

    .date-badge-mobile:not(:empty) {
        display: flex;
        align-items: center;
        font-size: calc(var(--font-size-controls) * 0.88);
        font-weight: var(--font-weight-semibold);
        color: var(--text-secondary);
        background: var(--background-secondary);
        border: 1px solid var(--border-primary);
        border-radius: 20px;
        padding: var(--space-hairline, 0.18rem) var(--space-2, 0.6rem);
        cursor: pointer;
        white-space: nowrap;
        user-select: none;
        transition: border-color 0.12s ease, color 0.12s ease;
        order: -1;
    }

    .date-badge-mobile:not(:empty):hover,
    .date-badge-mobile:not(:empty):focus-visible {
        border-color: var(--accent-primary);
        color: var(--text-primary);
        outline: none;
    }

    .title {
        font-size: var(--font-size-title);
    }

    .header-actions {
        flex-direction: row;
        align-items: center;
        gap: var(--space-3, 0.75rem);
        margin-left: auto;
        min-width: 0;
    }

    .header {
        flex-direction: column;
        align-items: center;
        gap: var(--space-3, 0.75rem);
    }

    .header .nav-links,
    .header .header-links {
        order: -1;
        display: flex;
        flex-direction: column;
        gap: var(--space-3, 0.75rem);
    }

    .header.config-header-top {
        flex-direction: row;
        align-items: flex-start;
        justify-content: space-between;
        gap: var(--space-2, 0.5rem) var(--space-3, 0.75rem);
    }

    .header.config-header-top .header-links {
        order: 0;
        flex-direction: row;
        align-items: center;
        flex-wrap: wrap;
        gap: var(--space-3, 0.75rem) var(--space-4, 1rem);
    }

    .config-header-top .header-actions {
        margin-left: auto;
    }

    .page-navigation {
        gap: var(--space-hairline, 0.125rem);
        flex-wrap: nowrap;
        overflow-x: auto;
        scroll-snap-type: x mandatory;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
        min-width: 0;
    }

    .page-navigation::-webkit-scrollbar {
        display: none;
    }

    .page-nav-btn {
        scroll-snap-align: start;
        flex-shrink: 0;
    }

    .page-nav-btn {
        padding: var(--space-1, 0.3rem) var(--space-2, 0.45rem);
        font-size: var(--font-size-controls);
    }

    .page-swipe-hint {
        display: flex;
        align-items: center;
        font-size: calc(var(--font-size-controls) * 0.82);
        color: var(--text-tertiary);
        white-space: nowrap;
        flex-shrink: 0;
        opacity: 0;
        animation: swipeHintPulse 2.4s ease-in-out 0.6s 3 forwards;
        pointer-events: none;
        letter-spacing: 0.04em;
    }

    @keyframes swipeHintPulse {
        0%   { opacity: 0; }
        25%  { opacity: 0.7; }
        75%  { opacity: 0.7; }
        100% { opacity: 0; }
    }
    
    .config-link a {
        font-size: var(--font-size-controls);
    }
    
    /* Config page mobile large styles */
    .section {
        margin-bottom: var(--space-5, 1.5rem);
    }
    
    .page-selector-wrapper {
        flex-direction: column;
        align-items: stretch;
        gap: var(--space-3, 0.75rem);
    }
    
    .page-selector-wrapper label {
        white-space: normal;
    }
    
    .page-selector {
        max-width: 100%;
        width: 100%;
    }
    
    .category-item,
    .bookmark-item,
    .finder-item {
        display: flex;
        flex-direction: column;
        align-items: stretch;
        gap: var(--space-3, 0.75rem);
    }
    
    .category-item input,
    .bookmark-item input,
    .bookmark-item select,
    .finder-item input {
        width: 100%;
        margin-bottom: var(--space-2, 0.5rem);
    }
    
    .config-actions {
        flex-direction: column;
        gap: var(--space-3, 0.75rem);
    }
    
    .config-actions .btn {
        width: 100%;
    }
    
    .notification {
        bottom: max(1rem, env(safe-area-inset-bottom));
        right: max(1rem, env(safe-area-inset-right));
        left: auto;
        margin: 0;
        max-width: 320px;
        text-align: left;
    }
    
    /* Modal responsive */
    .modal {
        max-width: 320px;
        width: 95%;
        padding: var(--space-4, 1rem) 1.25rem;
    }
    
    .modal-body {
        margin-bottom: 1.25rem;
    }
    
    .modal-button {
        padding: var(--space-1-5, 0.375rem) var(--space-3, 0.75rem);
    }
    
    .modal-button-prefix {
        min-width: 2.5ch;
    }
    
    /* Colors page - stack vertically on mobile */
    .config-main,
    .custom-themes-main-grid {
        grid-template-columns: 1fr;
        gap: var(--space-6, 2rem);
    }
    
    .colors-grid {
        gap: var(--space-6, 2rem);
    }
    
    /* Tabs responsive - mobile large */
    .tabs {
        flex-wrap: wrap;
        gap: 0;
        top: 65px;
    }
    
    .tab-button {
        padding: var(--space-2, 0.5rem) var(--space-4, 1rem);
        font-size: var(--font-size-controls);
        flex: 1 1 auto;
        min-width: 120px;
    }
    
    .config-actions-top {
        flex-wrap: wrap;
        gap: var(--space-2, 0.625rem);
    }
    
    .config-actions-top .btn {
        flex: 1 1 auto;
        min-width: 140px;
    }
}

/* Mobile Small (480px - 575px) */
@media (max-width: 575px) {
    .container {
        padding: var(--space-3, 0.75rem) var(--space-1-5, 0.375rem);
    }
    
    .dashboard-grid {
        gap: 1.25rem;
    }
    
    .bookmark-link {
        padding: var(--space-1, 0.3rem) 0;
    }
    
    .config-button {
        padding: var(--space-2, 0.5rem) var(--space-3, 0.75rem);
    }
    
    .search-container {
        width: calc(100% - 2rem);
        max-width: 100%;
        margin: 0 auto;
        padding: var(--space-3, 0.875rem) var(--space-4, 1rem) var(--space-2, 0.625rem) var(--space-6, 2rem);
    }
    
    .search-prompt {
        margin-bottom: var(--space-2, 0.5rem);
    }
    
    .search-button {
        padding: var(--space-3, 0.8rem) var(--space-4, 1rem);
        font-size: var(--font-size-text);
    }
    
    .button-container {
        width: calc(100% - 2rem);
        justify-content: center;
    }
    
    /* Header responsive - mobile small */
    .dashboard-section,
    .config-section {
        padding: 0;
    }
    
    .dashboard-section.section-controls,
    .config-section.section-header {
        padding-top: 1.25rem;
    }

    .dashboard-section.section-controls {
        padding-bottom: 0;
    }

    .config-section.section-header {
        padding-bottom: var(--space-4, 1.125rem);
    }
    
    .dashboard-section.section-title,
    .config-section.section-controls {
        padding-top: 0;
        padding-bottom: 0;
    }
    
    .dashboard-section.section-content {
        padding-top: 0;
        padding-bottom: var(--space-7, 3rem); /* Space for search button */
    }
    
    .config-section.section-content {
        padding-top: var(--space-2, 0.625rem);
        padding-bottom: 1.25rem;
    }
    
    .container {
        padding: 0 var(--space-2, 0.5rem);
    }
    
    .header-top {
        flex-direction: row;
        align-items: center;
        gap: var(--space-2, 0.5rem);
    }

    .title {
        font-size: var(--font-size-title);
    }

    .header-actions {
        gap: var(--space-2, 0.625rem);
        margin-left: auto;
    }
    
    .header {
        flex-direction: column;
        align-items: center;
        gap: var(--space-2, 0.625rem);
    }
    
    .header .nav-links,
    .header .header-links {
        order: -1;
        display: flex;
        flex-direction: column;
        gap: var(--space-2, 0.625rem);
    }

    .header.config-header-top {
        flex-direction: row;
        align-items: flex-start;
        justify-content: space-between;
        gap: var(--space-2, 0.5rem) var(--space-2, 0.625rem);
    }

    .header.config-header-top .header-links {
        order: 0;
        flex-direction: row;
        align-items: center;
        flex-wrap: wrap;
        gap: var(--space-2, 0.625rem) var(--space-3, 0.85rem);
    }

    .config-header-top .header-actions {
        margin-left: auto;
    }
    
    .page-nav-btn {
        padding: var(--space-1, 0.25rem) var(--space-1-5, 0.4rem);
        font-size: var(--font-size-controls);
    }

    .config-link a {
        font-size: var(--font-size-text);
    }
    
    /* Config page mobile small styles */
    .section {
        margin-bottom: 1.25rem;
    }
    
    .form-group {
        margin-bottom: var(--space-4, 1rem);
    }
    
    .btn {
        padding: var(--space-2, 0.625rem) var(--space-4, 1rem);
    }
    
    /* Tabs responsive - mobile small */
    .tabs {
        top: auto;
        position: relative;
    }
    
    .tab-button {
        padding: var(--space-2, 0.5rem) var(--space-3, 0.75rem);
        font-size: var(--font-size-controls);
        flex: 1 1 auto;
        min-width: 100px;
    }
    
    .config-actions-top {
        flex-direction: column;
    }
    
    .config-actions-top .btn {
        width: 100%;
    }
}

/* Mobile Extra Small (up to 479px) */
@media (max-width: 479px) {
    .container {
        padding: var(--space-2, 0.5rem) var(--space-1, 0.25rem);
    }
    
    .dashboard-grid {
        /* Deliberately no gap on the narrowest screens, so this is a plain 0
           rather than a token: the spacing scale has no zero step, and
           --space-hairline turned it into 2.4px. */
        gap: 0;
    }
    
    .category {
        margin-bottom: var(--space-5, 1.5rem);
    }
    
    .category h3 {
        margin-bottom: var(--space-2, 0.5rem);
    }
    
    .bookmark-link {
        padding: var(--space-1, 0.25rem) 0;
    }
    
    .config-button {
        top: 0.5rem;
        right: 0.5rem;
        padding: var(--space-1-5, 0.4rem) var(--space-2, 0.625rem);
    }
    
    .shortcut-hint {
        padding: var(--space-1-5, 0.375rem);
        bottom: 0.5rem;
        right: 0.5rem;
        left: 0.5rem;
    }
    
    .search-container {
        width: calc(100% - 2rem);
        max-width: 100%;
        margin: 0 auto;
        padding: var(--space-3, 0.75rem) var(--space-3, 0.875rem) var(--space-2, 0.5rem) var(--space-6, 2rem);
    }
    
    .search-prompt {
        margin-bottom: var(--space-2, 0.5rem);
    }
    
    .search-match {
        padding: var(--space-1, 0.3rem) 0;
    }
    
    .search-match-shortcut {
        min-width: 2.5ch;
    }
    
    .search-button {
        padding: var(--space-3, 0.8rem) var(--space-4, 1rem);
        font-size: var(--font-size-controls);
    }
    
    .button-container {
        width: calc(100% - 2rem);
        justify-content: center;
    }
    
    .search-button-icon {
        font-size: var(--font-size-controls);
    }
    
    /* Header responsive - mobile extra small */
    .dashboard-section,
    .config-section {
        padding: 0;
    }
    
    .dashboard-section.section-controls,
    .config-section.section-header {
        padding-top: var(--space-4, 1rem);
    }

    .dashboard-section.section-controls {
        padding-bottom: 0;
    }

    .config-section.section-header {
        padding-bottom: var(--space-4, 1rem);
    }
    
    .dashboard-section.section-title,
    .config-section.section-controls {
        padding-top: 0;
        padding-bottom: 0;
    }
    
    .dashboard-section.section-content {
        padding-top: 0;
        padding-bottom: var(--space-7, 3rem); /* Space for search button */
    }
    
    .config-section.section-content {
        padding-top: var(--space-2, 0.5rem);
        padding-bottom: var(--space-4, 1rem);
    }
    
    .container {
        padding: 0 var(--space-2, 0.5rem);
    }
    
    .header-top {
        flex-direction: row;
        align-items: center;
        gap: var(--space-2, 0.5rem);
    }

    .title {
        font-size: var(--font-size-title);
    }

    .header-actions {
        gap: var(--space-2, 0.5rem);
        margin-left: auto;
    }
    
    .header {
        flex-direction: column;
        align-items: center;
        gap: var(--space-2, 0.5rem);
    }
    
    .header .nav-links,
    .header .header-links {
        order: -1;
        display: flex;
        flex-direction: column;
        gap: var(--space-2, 0.5rem);
    }

    .header.config-header-top {
        flex-direction: row;
        align-items: flex-start;
        justify-content: space-between;
        gap: var(--space-2, 0.5rem);
    }

    .header.config-header-top .header-links {
        order: 0;
        flex-direction: row;
        align-items: center;
        flex-wrap: wrap;
        gap: var(--space-2, 0.5rem) var(--space-3, 0.75rem);
    }

    .config-header-top .header-actions {
        margin-left: auto;
    }
    
    .page-navigation {
        flex-wrap: nowrap;
        overflow-x: auto;
        gap: var(--space-hairline, 0.125rem);
        justify-content: flex-start;
    }

    .page-nav-btn {
        padding: var(--space-1, 0.25rem) var(--space-1-5, 0.35rem);
        font-size: var(--font-size-controls);
        white-space: nowrap;
    }

    .config-link a {
        font-size: var(--font-size-controls);
        white-space: nowrap;
    }
    
    /* Config page mobile extra small styles */
    .section {
        margin-bottom: var(--space-4, 1rem);
    }
    
    .section h2 {
        margin-bottom: var(--space-3, 0.75rem);
    }
    
    .form-group {
        margin-bottom: var(--space-3, 0.875rem);
    }
    
    .form-control {
        padding: var(--space-2, 0.5rem);
    }
    
    .btn {
        padding: var(--space-2, 0.5rem) var(--space-3, 0.75rem);
    }
    
    .config-actions {
        gap: var(--space-2, 0.5rem);
    }
    
    .notification {
        bottom: max(0.5rem, env(safe-area-inset-bottom));
        right: max(0.5rem, env(safe-area-inset-right));
        left: auto;
        margin: 0;
        max-width: 280px;
        padding: var(--space-3, 0.75rem);
        text-align: left;
    }
    
    .category-item,
    .bookmark-item,
    .finder-item {
        gap: var(--space-2, 0.5rem);
    }
    
    .category-item input,
    .bookmark-item input,
    .bookmark-item select,
    .finder-item input {
        margin-bottom: var(--space-1-5, 0.375rem);
    }
    
    /* Modal extra small */
    .modal {
        max-width: 280px;
        width: 90%;
        padding: var(--space-3, 0.875rem) var(--space-4, 1rem);
    }
    
    .modal-body {
        margin-bottom: var(--space-4, 1rem);
    }
    
    .modal-actions {
        gap: var(--space-3, 0.75rem);
    }
    
    .modal-button {
        padding: var(--space-1, 0.3rem) var(--space-2, 0.5rem);
    }
    
    .modal-button-prefix {
        min-width: 2ch;
        margin-right: var(--space-3, 0.75rem);
    }
    
    /* Tabs responsive - mobile extra small */
    .tabs {
        position: relative;
        top: auto;
        flex-direction: column;
    }
    
    .tab-button {
        padding: var(--space-2, 0.5rem);
        font-size: var(--font-size-controls);
        text-align: center;
        width: 100%;
        border-bottom: 1px solid var(--border-primary);
    }
    
    .tab-button:last-child {
        border-bottom: none;
    }

    /* Fix for theme and language selectors in config general tab */
    .form-group:has(#theme-select),
    .form-group:has(#language-select) {
        flex-direction: column;
        align-items: flex-start;
        gap: var(--space-2, 0.5rem);
    }

    .form-group:has(#theme-select) label,
    .form-group:has(#language-select) label {
        min-width: auto;
    }

    .form-group--theme-select .custom-select-wrapper,
    .form-group--theme-select select.config-select-wide {
        min-width: 100%;
        max-width: 100%;
        flex: 1 1 100%;
    }
}

@media (prefers-reduced-motion: reduce) {
    /* Swipe hint → show statically, no pulse animation */
    .page-swipe-hint { animation: none !important; opacity: 0.55 !important; }
}

/* ==== css/dashboard-enhancements.css ==== */
/* Dashboard discoverability: header, mobile bar, mini status, a11y, kbd tooltips */

:root {
    --header-chip-radius: 999px;
    --header-chip-pad-block: 0.38rem;
    --header-chip-pad-inline: 0.8rem;
    --layout-radius-xs: 4px;
    --layout-radius-sm: 8px;
    --layout-radius-md: 12px;
    --dashboard-toolbar-stack: 5.75rem;
}

kbd,
.keyboard-cheat-sheet-keys kbd {
    display: inline-block;
    padding: 0.05em 0.35em;
    border: 1px solid color-mix(in srgb, var(--text-secondary) 40%, transparent);
    border-radius: 0.2em;
    background: color-mix(in srgb, var(--background-secondary) 90%, transparent);
    font-family: var(--font-family-main, "Source Code Pro", monospace);
    font-size: 0.92em;
    font-weight: var(--font-weight-bold);
    line-height: 1.3;
    color: var(--text-primary);
}

.kbd-sep,
.kbd-or {
    margin: 0 0.2em;
    color: var(--text-secondary);
    font-size: 0.85em;
    font-weight: var(--font-weight-normal);
}

.toolbar-kbd-tooltip {
    position: fixed;
    z-index: 10050;
    /* --kbd-tooltip-shift nudges the box back inside the window when centring on
       the anchor would push it past the left or right edge. */
    --kbd-tooltip-shift: 0px;
    transform: translate(calc(-50% + var(--kbd-tooltip-shift)), calc(-100% - 8px));
    display: none;
    flex-direction: column;
    align-items: center;
    gap: var(--space-1, 0.2rem);
    padding: var(--space-1-5, 0.35rem) var(--space-2, 0.55rem);
    background: var(--background-secondary);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-3, 0.3rem);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
    pointer-events: none;
    white-space: nowrap;
    visibility: hidden;
    opacity: 0;
}

.toolbar-kbd-tooltip.is-visible {
    display: flex;
    visibility: visible;
    opacity: 1;
}

/* Header icons: tooltip appears BELOW the button, because there is no room
   above them at the top of the window. */
.toolbar-kbd-tooltip--below {
    transform: translate(calc(-50% + var(--kbd-tooltip-shift)), 8px);
}

/* Side rail: tooltip appears to the RIGHT of the button */
.toolbar-kbd-tooltip--side-rail {
    transform: translateY(-50%);
}

.toolbar-kbd-tooltip-label {
    font-size: calc(var(--font-size-controls) * 0.82);
    color: var(--text-secondary);
    text-transform: lowercase;
}

.toolbar-kbd-tooltip-keys {
    display: inline-flex;
    align-items: center;
    gap: var(--space-hairline, 0.15rem);
    flex-wrap: wrap;
    justify-content: center;
}

/*
 * These chips name the same shortcuts as the toolbar buttons at the bottom of
 * the screen, so they carry that button's fill. The bare `kbd` rule in
 * enhanced-features.css loads later and would otherwise win with
 * --background-primary, which reads as a different, denser block here.
 */
#toolbar-kbd-tooltip .toolbar-kbd-tooltip-keys kbd {
    background: color-mix(in srgb, var(--background-secondary) 74%, transparent);
    border-color: var(--border-primary);
}

/* Dashboard discoverability: header, mobile bar, mini status, a11y */

.skip-to-content {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 10000;
    padding: var(--space-2, 0.5rem) var(--space-4, 1rem);
    background: var(--bg-primary, #000);
    color: var(--text-primary, #fff);
    border: 1px solid var(--text-primary);
    font-family: var(--font-family-main);
    text-decoration: none;
}

.skip-to-content:focus-visible {
    left: 0.5rem;
    top: 0.5rem;
}

@media (max-width: 768px), (hover: none) and (pointer: coarse) {
    .quick-add-modal-header-actions kbd {
        display: none;
    }
}

/* Dashboard discoverability: header, mobile bar, mini status */

.header-top {
    flex-wrap: wrap;
    gap: var(--space-3, 0.75rem) var(--space-4, 1rem);
}

.header-top-primary {
    display: flex;
    flex-direction: column;
    gap: var(--space-1-5, 0.35rem);
    flex: 1 1 12rem;
    min-width: 0;
}

.dashboard-mini-status {
    display: none;
    font-size: calc(var(--font-size-controls) * 0.95);
    color: var(--text-tertiary, var(--text-secondary));
    font-family: var(--font-family-main);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
}

.dashboard-mini-status .mini-sep {
    opacity: 0.45;
    margin: 0 0.35em;
}

body[data-layout-version="classic"] .dashboard-mini-status,
body[data-layout-version="classic"] .status-loading-indicator.show,
body[data-layout-version="classic"] .date-badge-mobile:not(:empty),
body[data-layout-version="classic"] .page-swipe-hint {
    padding: var(--header-chip-pad-block, 0.38rem) var(--header-chip-pad-inline, 0.8rem);
    border-radius: var(--header-chip-radius, 999px);
    border: 1px solid color-mix(in srgb, var(--text-secondary) 35%, transparent);
    background: color-mix(in srgb, var(--background-secondary) 55%, transparent);
}

/* Pages overview icon chrome lives in dashboard.css (.pages-link--icon), shared
   with inbox, health, and config; spacing is .header-actions gap throughout. */

.search-button-labeled {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-hairline, 0.15rem);
    min-width: 2.75rem;
    padding: var(--space-2, 0.55rem) var(--space-2, 0.45rem) !important;
}

.search-button-label {
    display: none;
    font-size: var(--text-2xs, 0.58rem);
    line-height: 1;
    text-transform: lowercase;
    color: var(--text-secondary);
    font-family: var(--font-family-main);
}

.search-flow-hint .sfh-seg-swipe.hidden {
    display: none;
}

@media (max-width: 768px), (hover: none) and (pointer: coarse) {
    .search-button-labeled .search-button-label {
        display: block;
    }

    .dashboard-mini-status {
        display: block;
    }

    .search-flow-hint .sfh-seg-swipe.hidden {
        display: inline;
    }

    .search-button[data-tooltip]::after {
        display: none !important;
    }
}

@media (max-width: 575px) {
    .date {
        display: none;
    }
}

.save-status-indicator.is-saved-flash {
    animation: config-saved-flash 1.4s ease;
}

.search-button.first-bookmark-pulse {
    animation: first-bookmark-pulse 1s ease 3;
}

@keyframes first-bookmark-pulse {
    0%, 100% { box-shadow: 0 0 0 0 transparent; }
    50% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-success, #00ff9c) 55%, transparent); }
}

@keyframes config-saved-flash {
    0% { opacity: 0.5; transform: scale(0.96); }
    30% { opacity: 1; transform: scale(1.02); color: var(--accent-success, #00ff9c); }
    100% { opacity: 1; transform: scale(1); }
}

@media (max-width: 768px), (hover: none) and (pointer: coarse) {
    :root {
        --dashboard-toolbar-stack: 6.75rem;
    }
}

:is(
    .dashboard-search-promo,
    .dashboard-g-jump-promo,
    .dashboard-grid-kbd-promo,
    .dashboard-smart-collection-promo,
    .dashboard-feature-promo
) kbd {
    border-radius: 0.2em;
}

/* ==== css/skeleton-loading.css ==== */
/* Shared skeleton placeholders while body.loading */

body.loading {
    overflow-x: hidden;
}

.skeleton-root {
    display: none;
    pointer-events: none;
    user-select: none;
}

body.loading .skeleton-root {
    display: block;
}

body.loading .skeleton-dashboard.skeleton-root,
body.loading .skeleton-health-summary.skeleton-root,
body.loading .skeleton-colors-grid.skeleton-root {
    display: grid;
}

body.loading .skeleton-hide-while-loading {
    visibility: hidden !important;
}

body.loading #config-main > :not([data-skeleton-root]),
body.loading .colors-main > :not([data-skeleton-root]) {
    visibility: hidden;
}

.skeleton-shimmer {
    background: linear-gradient(
        90deg,
        color-mix(in srgb, var(--text-secondary, #888) 12%, transparent) 0%,
        color-mix(in srgb, var(--text-secondary, #888) 22%, transparent) 50%,
        color-mix(in srgb, var(--text-secondary, #888) 12%, transparent) 100%
    );
    background-size: 200% 100%;
    animation: skeleton-shimmer 0.75s ease-in-out infinite;
    border-radius: var(--radius-1, 2px);
}

@keyframes skeleton-shimmer {
    0% {
        background-position: 100% 0;
    }
    100% {
        background-position: -100% 0;
    }
}

.skeleton-line {
    height: 0.85rem;
    margin-bottom: var(--space-2, 0.5rem);
}

.skeleton-line--sm {
    height: 0.65rem;
    width: 40%;
}

.skeleton-line--title {
    height: 1.1rem;
    width: 55%;
    margin-bottom: var(--space-3, 0.75rem);
}

.skeleton-line--wide {
    width: 85%;
}

.skeleton-line--pill {
    height: 1.5rem;
    width: 4.5rem;
    border-radius: var(--radius-pill, 999px);
    display: inline-block;
    margin-right: var(--space-2, 0.5rem);
}

/* Dashboard */
.skeleton-dashboard {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: var(--space-5, 1.5rem);
    width: 100%;
    padding: var(--space-1, 0.25rem) 0;
}

.skeleton-dashboard-col {
    display: flex;
    flex-direction: column;
    gap: var(--space-1-5, 0.35rem);
}

.skeleton-bookmark-row {
    height: 1.35rem;
    width: 100%;
}

.skeleton-bookmark-row--short {
    width: 72%;
}

/* Config */
.skeleton-config-panel {
    padding: var(--space-2, 0.5rem) 0 var(--space-6, 2rem);
}

.skeleton-config-cards {
    display: grid;
    gap: var(--space-4, 1rem);
    max-width: 720px;
}

.skeleton-config-card {
    border: 1px solid color-mix(in srgb, var(--text-secondary, #888) 25%, transparent);
    padding: var(--space-4, 1rem);
    min-height: 5rem;
}

/* Health */
.skeleton-health-summary {
    display: grid;
    grid-template-columns: repeat(9, minmax(0, 1fr));
    gap: 6px;
    margin-bottom: var(--space-3, 0.75rem);
}

.skeleton-health-card {
    min-height: 2.35rem;
    border: 1px solid color-mix(in srgb, var(--text-secondary, #888) 25%, transparent);
    padding: var(--space-1-5, 0.35rem) var(--space-2, 0.45rem);
}

.skeleton-health-issues {
    display: flex;
    flex-direction: column;
    gap: var(--space-2, 0.5rem);
}

.skeleton-health-row {
    height: 2.75rem;
    width: 100%;
}

/* Colors */
.skeleton-colors-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: var(--space-4, 1rem);
}

.skeleton-colors-group {
    min-height: 8rem;
    border: 1px solid color-mix(in srgb, var(--text-secondary, #888) 25%, transparent);
    padding: var(--space-3, 0.75rem);
}

.skeleton-header-pills {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1-5, 0.35rem);
    margin-top: var(--space-1-5, 0.35rem);
}

@media (prefers-reduced-motion: reduce) {
    .skeleton-shimmer {
        animation: none;
        background: color-mix(in srgb, var(--text-secondary, #888) 18%, transparent);
    }
}

body.no-animations .skeleton-shimmer {
    animation: none;
    background: color-mix(in srgb, var(--text-secondary, #888) 18%, transparent);
}

/* ==== css/dashboard-quickstart.css ==== */
/*
 * Quick-start card (checklist + compact first-run setup).
 * Non-blocking, bottom-left, theme-driven. Mirrors feature-spotlight conventions
 * so it reads as part of the same system.
 */

.quickstart-card {
    position: fixed;
    left: max(1rem, env(safe-area-inset-left));
    bottom: max(1rem, env(safe-area-inset-bottom));
    width: 340px;
    max-width: calc(100vw - 2rem);
    background: var(--background-secondary);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-5, 0.5rem);
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.18);
    z-index: 2147482800;
    opacity: 0;
    visibility: hidden;
    transform: translateY(12px);
    transition: opacity 0.26s ease, visibility 0.26s ease, transform 0.26s ease;
    overflow: hidden;
    font-family: var(--font-family-main, inherit);
}

.quickstart-card.show {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

.quickstart-stripe {
    height: 3px;
    background: var(--accent-primary);
}

.quickstart-inner {
    padding: var(--space-3, 0.875rem) var(--space-4, 1rem) var(--space-4, 0.9rem);
}

.quickstart-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2, 0.5rem);
}

.quickstart-title {
    font-size: var(--font-size-controls, 0.82rem);
    font-weight: 600;
    color: var(--text-primary);
    margin: 0 0 var(--space-1-5, 0.35rem);
    line-height: 1.3;
}

.quickstart-head .quickstart-title {
    margin-bottom: 0;
}

.quickstart-text {
    font-size: var(--font-size-small, 0.72rem);
    color: var(--text-secondary);
    margin: 0 0 var(--space-2, 0.6rem);
    line-height: 1.5;
}

.quickstart-close {
    flex-shrink: 0;
    background: transparent;
    border: none;
    color: var(--text-secondary);
    font-size: var(--text-lg, 1.15rem);
    line-height: 1;
    cursor: pointer;
    padding: 0 var(--space-1, 0.2rem);
    border-radius: var(--radius-3, 0.25rem);
}

.quickstart-close:hover,
.quickstart-close:focus-visible {
    color: var(--text-primary);
    background: color-mix(in srgb, var(--text-primary) 8%, transparent);
}

/* ---- Checklist ---- */

.quickstart-progress {
    font-size: var(--font-size-small, 0.7rem);
    color: var(--text-secondary);
    margin: var(--space-1-5, 0.4rem) 0 var(--space-2, 0.55rem);
    letter-spacing: 0.02em;
}

.quickstart-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-1-5, 0.4rem);
}

.quickstart-item {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2, 0.55rem);
}

/* Actionable rows: the button becomes the flex row so the layout matches
   a plain item exactly. */
.quickstart-item.is-actionable {
    display: block;
}

.quickstart-item-action {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2, 0.55rem);
    width: 100%;
    margin: -0.15rem -0.3rem;
    padding: var(--space-hairline, 0.15rem) var(--space-1, 0.3rem);
    border: 0;
    border-radius: var(--radius-3, 5px);
    background: none;
    font-family: inherit;
    text-align: left;
    cursor: pointer;
}

.quickstart-item-action:hover,
.quickstart-item-action:focus-visible {
    background: color-mix(in srgb, var(--accent-primary) 10%, transparent);
    outline: none;
}

.quickstart-item-action:focus-visible {
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-primary) 35%, transparent);
}

/* Underline only while not done — .is-done owns text-decoration for the
   strikethrough, and the shorthand would otherwise cancel it. */
.quickstart-item.is-actionable:not(.is-done) .quickstart-item-label {
    text-decoration: underline;
    text-decoration-color: color-mix(in srgb, var(--accent-primary) 45%, transparent);
    text-underline-offset: 2px;
}

.quickstart-check {
    flex-shrink: 0;
    width: 18px;
    height: 18px;
    margin-top: 1px;
    border-radius: var(--radius-pill, 999px);
    border: 1.5px solid var(--border-primary);
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    transition: background 0.2s ease, border-color 0.2s ease;
}

.quickstart-item.is-done .quickstart-check {
    background: var(--accent-primary);
    border-color: var(--accent-primary);
}

.quickstart-check svg {
    width: 11px;
    height: 11px;
    stroke: currentColor;
    fill: none;
    stroke-width: 3;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.quickstart-item-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.quickstart-item-label {
    font-size: var(--font-size-small, 0.74rem);
    color: var(--text-primary);
    line-height: 1.35;
}

.quickstart-item.is-done .quickstart-item-label {
    color: var(--text-secondary);
    text-decoration: line-through;
    text-decoration-color: color-mix(in srgb, var(--text-secondary) 55%, transparent);
}

.quickstart-item-hint {
    font-size: var(--font-size-small, 0.66rem);
    color: var(--text-tertiary, var(--text-secondary));
    line-height: 1.35;
    margin-top: var(--space-hairline, 0.05rem);
}

.quickstart-card.is-complete .quickstart-stripe {
    background: var(--accent-success, var(--accent-primary));
}

/* ---- Compact setup card ---- */

.quickstart-field {
    display: flex;
    flex-direction: column;
    gap: var(--space-1, 0.3rem);
    margin-bottom: var(--space-3, 0.7rem);
}

/* [hidden] must win over the flex display above (e.g. weather location field). */
.quickstart-card [hidden] {
    display: none !important;
}

.quickstart-field > span,
.quickstart-fieldset legend {
    font-size: var(--font-size-small, 0.7rem);
    font-weight: 600;
    color: var(--text-primary);
}

.quickstart-select,
.quickstart-input {
    width: 100%;
    padding: var(--space-1-5, 0.4rem) var(--space-2, 0.5rem);
    font-family: inherit;
    font-size: var(--font-size-small, 0.74rem);
    color: var(--text-primary);
    background: var(--background-primary);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-4, 0.35rem);
}

.quickstart-setup-fields {
    max-height: 46vh;
    overflow-y: auto;
    margin-bottom: var(--space-1, 0.25rem);
}

.quickstart-field-hint {
    font-size: var(--font-size-small, 0.66rem);
    color: var(--text-tertiary, var(--text-secondary));
    line-height: 1.4;
    margin: 0 0 var(--space-1-5, 0.35rem);
}

.quickstart-fieldset {
    border: none;
    margin: 0 0 var(--space-3, 0.75rem);
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-1, 0.3rem);
}

.quickstart-fieldset legend {
    margin-bottom: var(--space-1, 0.3rem);
    padding: 0;
}

.quickstart-radio {
    display: flex;
    align-items: center;
    gap: var(--space-2, 0.45rem);
    font-size: var(--font-size-small, 0.74rem);
    color: var(--text-primary);
    cursor: pointer;
}

.quickstart-radio input {
    accent-color: var(--accent-primary);
}

/* Starting-point step: two stacked option cards with a label + description. */
.quickstart-startpoint {
    gap: var(--space-2, 0.5rem);
}

.quickstart-radio-block {
    align-items: flex-start;
    gap: var(--space-2, 0.55rem);
    padding: var(--space-2, 0.6rem) var(--space-3, 0.7rem);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-5, 8px);
    background: var(--background-secondary);
    transition: border-color 0.12s ease, box-shadow 0.12s ease;
}

.quickstart-radio-block input {
    margin-top: var(--space-hairline, 0.15rem);
    flex-shrink: 0;
}

.quickstart-radio-block:has(input:checked) {
    border-color: var(--accent-primary);
    box-shadow: inset 0 0 0 1px var(--accent-primary);
}

.quickstart-radio-text {
    display: flex;
    flex-direction: column;
    gap: var(--space-hairline, 0.15rem);
    min-width: 0;
}

.quickstart-radio-label {
    font-weight: var(--font-weight-semibold, 600);
    color: var(--text-primary);
}

.quickstart-radio-hint {
    font-size: var(--font-size-small, 0.66rem);
    color: var(--text-tertiary, var(--text-secondary));
    line-height: 1.4;
}

.quickstart-actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2, 0.5rem);
    margin-top: var(--space-1-5, 0.35rem);
}

.quickstart-btn {
    font-family: inherit;
    font-size: var(--font-size-small, 0.74rem);
    padding: var(--space-1-5, 0.4rem) var(--space-3, 0.85rem);
    border-radius: var(--radius-4, 0.35rem);
    border: 1px solid var(--border-primary);
    cursor: pointer;
    background: var(--background-primary);
    color: var(--text-primary);
}

.quickstart-btn-ghost {
    background: transparent;
}

.quickstart-btn-primary {
    background: var(--accent-primary);
    border-color: var(--accent-primary);
    color: #fff;
    font-weight: 600;
}

.quickstart-btn:hover,
.quickstart-btn:focus-visible {
    filter: brightness(1.05);
}

@media (prefers-reduced-motion: reduce) {
    .quickstart-card {
        transition: opacity 0.01s linear;
        transform: none;
    }
}

/* On small screens, span the width so it doesn't crowd the corner. */
@media (max-width: 560px) {
    .quickstart-card {
        left: 0.75rem;
        right: 0.75rem;
        width: auto;
        max-width: none;
    }
}

/* ── Notice cards ───────────────────────────────────────────────────────
   The bottom-left invitations (analytics, side rail, and any future one) all
   reuse .quickstart-card for placement and chrome; these are the shared parts
   of the body. Built by NoticeCard.define — see static/js/notice-card.js.
   Previously duplicated per card, which is how they drifted apart. */
.notice-card-text {
    margin: var(--space-1-5, 0.35rem) 0 0;
    font-size: var(--font-size-small, 0.72rem);
    color: var(--text-secondary);
    line-height: 1.5;
}

/* A footnote rather than part of the pitch: quieter than the body text so it
   reads as a caveat rather than a second paragraph. */
.notice-card-caveat {
    margin: var(--space-2, 0.5rem) 0 0;
    padding-left: var(--space-2, 0.55rem);
    border-left: 2px solid var(--border-color, rgba(128, 128, 128, 0.35));
    font-size: var(--font-size-small, 0.72rem);
    color: var(--text-muted, var(--text-secondary));
    line-height: 1.45;
    opacity: 0.9;
}

/* Why an action failed, kept on the card rather than in a toast that fades
   before it can be read. Styled like the caveat, in the error colour. */
.notice-card-error {
    margin: var(--space-2, 0.5rem) 0 0;
    padding-left: var(--space-2, 0.55rem);
    border-left: 2px solid var(--accent-error, #c0392b);
    font-size: var(--font-size-small, 0.72rem);
    color: var(--accent-error, #c0392b);
    line-height: 1.45;
}

.notice-card-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2, 0.5rem);
    margin-top: var(--space-3, 0.8rem);
}

/* Shown only after the invitation has been taken up: the command that does the
   same thing again, for the one person who has just proved they care about it.
   Two cards, one shape -- keep them selecting the same rules rather than
   letting a second copy drift. */
.side-rail-notice-hint,
.theme-browser-notice-hint {
    margin: var(--space-2, 0.6rem) 0 0;
    width: 100%;
    font-size: var(--font-size-small, 0.72rem);
    color: var(--text-tertiary, var(--text-secondary));
    line-height: 1.5;
}

.side-rail-notice-hint kbd,
.theme-browser-notice-hint kbd {
    font-family: var(--font-family-main);
    font-size: var(--text-2xs, 0.68rem);
    background: var(--background-secondary);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-2, 3px);
    padding: var(--space-hairline, 0.05rem) var(--space-1, 0.3rem);
    color: var(--text-secondary);
}

/* The shared .whats-new-modal shell only scrolls once it hits max-height; this
   modal is shorter than that, so cap the body itself or the last paragraph runs
   under the buttons. */
.analytics-notice-modal .modal-body {
    /* Roomy enough that the three sections normally fit whole; scrolls only on
       short viewports rather than clipping a line mid-sentence. */
    max-height: min(72vh, 40rem);
    overflow-y: auto;
    /* Overlay-style scrollbars hide until you scroll, so the cut-off text reads
       as clipped rather than scrollable. Keep the track visible instead. */
    scrollbar-width: thin;
    scrollbar-gutter: stable;
}

.analytics-notice-modal .modal-body::-webkit-scrollbar {
    width: 8px;
}

.analytics-notice-modal .modal-body::-webkit-scrollbar-thumb {
    background: var(--border-primary);
    border-radius: var(--radius-3, 4px);
}

/* Modal body: same rhythm as the what's-new release sections. */
.analytics-notice-modal-body p {
    margin: 0 0 var(--space-4, 1rem);
    line-height: 1.6;
    color: var(--text-secondary);
}

.analytics-notice-modal-body p:last-child {
    margin-bottom: 0;
}

.analytics-notice-heading {
    margin: 0 0 var(--space-1-5, 0.35rem);
    font-size: var(--font-size-text, 0.85rem);
    font-weight: var(--font-weight-semibold, 600);
    color: var(--text-primary);
}

.analytics-notice-modal-body .analytics-notice-heading:not(:first-child) {
    margin-top: 1.25rem;
}

/* ─── Modern layout ─────────────────────────────────────────────────────────
   Scoped to body[data-layout-version="modern"]; classic above is untouched.
   Tokens come from layout-modern-tokens.css.
   ------------------------------------------------------------------------- */

/* The card floats over the dashboard, so it follows the overlay grammar in
   overlays-modern.css (rounded, tinted surface, layered shadow) rather than the
   flat panel treatment the in-page views use. */
body[data-layout-version="modern"] .quickstart-card {
    border-radius: var(--layout-radius-lg);
    border-color: var(--layout-surface-border);
    background: var(--layout-surface);
    box-shadow: var(--layout-shadow-lg);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
}

/* The stripe spans the full width at the very top of the card, so a rounded
   card clips its upper corners. Round its own top edge to match. */
body[data-layout-version="modern"] .quickstart-stripe {
    border-radius: var(--layout-radius-lg) var(--layout-radius-lg) 0 0;
}

body[data-layout-version="modern"] .quickstart-item-action {
    border-radius: var(--layout-radius-sm);
    transition: background-color var(--layout-transition);
}

body[data-layout-version="modern"] .quickstart-item-action:focus-visible {
    outline: var(--layout-focus-ring);
    outline-offset: 1px;
    box-shadow: none;
}

body[data-layout-version="modern"] .quickstart-close {
    border-radius: var(--layout-radius-sm);
    transition:
        background-color var(--layout-transition),
        color var(--layout-transition);
}

body[data-layout-version="modern"] .quickstart-close:focus-visible {
    outline: var(--layout-focus-ring);
    outline-offset: 1px;
}

@media (prefers-reduced-motion: reduce) {
    body[data-layout-version="modern"] .quickstart-item-action,
    body[data-layout-version="modern"] .quickstart-close {
        transition: none;
    }
}

/*
 * The two ways into an import, on the last step of the setup card.
 *
 * Shaped like the radio blocks above rather than like the card's own buttons:
 * they are the same kind of thing to read -- a label with a line explaining
 * what it gets you -- and the difference is only that these go somewhere
 * instead of choosing something. Kept visually quieter than the primary
 * button, because Finish is still the way out of the card.
 */
.quickstart-setup-links {
    display: flex;
    flex-direction: column;
    gap: var(--space-2, 0.5rem);
}

.quickstart-setup-link {
    display: flex;
    flex-direction: column;
    gap: var(--space-hairline, 0.15rem);
    width: 100%;
    padding: var(--space-2, 0.6rem) var(--space-3, 0.7rem);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-5, 8px);
    background: var(--background-secondary);
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
    transition: border-color 0.12s ease, box-shadow 0.12s ease;
}

.quickstart-setup-link:hover,
.quickstart-setup-link:focus-visible {
    border-color: var(--accent-primary);
    box-shadow: inset 0 0 0 1px var(--accent-primary);
}

.quickstart-setup-link-label {
    font-weight: var(--font-weight-semibold, 600);
    color: var(--text-primary);
}

.quickstart-setup-link-hint {
    font-size: var(--font-size-small, 0.66rem);
    color: var(--text-tertiary, var(--text-secondary));
    line-height: 1.4;
}

/* ==== css/dashboard-tag-cloud.css ==== */
/* Dashboard tag word cloud — / FAB + anchored modal (desktop / wide layout only) */

.dashboard-tag-cloud-wrap {
    position: fixed;
    inset: 0;
    z-index: 1001;
    display: none;
    pointer-events: none;
    overflow: visible;
}

body[data-show-tag-cloud-button="true"] .dashboard-tag-cloud-wrap.is-eligible {
    display: block;
}

body[data-show-tag-cloud-button="false"] #tag-cloud-toggle-btn,
#tag-cloud-toggle-btn:not(.is-eligible) {
    display: none !important;
}

/* / FAB — fixed like launcher / what's new; same palette as toolbar buttons */
.tag-cloud-toggle-btn.search-button {
    position: fixed;
    z-index: 1002;
    left: var(--corner-fab-edge);
    bottom: calc(var(--corner-fab-edge) + var(--corner-fab-height) + var(--corner-fab-gap));
    padding: var(--space-2, 0.62rem) var(--space-3, 0.82rem);
    pointer-events: auto;
}

.tag-cloud-toggle-btn .search-button-icon {
    animation: btnIconPulse 10.8s ease-in-out infinite;
    animation-delay: 6.4s;
}

body.no-animations .tag-cloud-toggle-btn .search-button-icon {
    animation: none !important;
}

.tag-cloud-toggle-btn.is-active {
    border-color: var(--accent-primary);
    color: var(--text-primary);
}

.tag-cloud-toggle-btn.is-active .search-button-icon {
    color: var(--accent-primary);
}

.tag-cloud-toggle-btn[data-tooltip]::after {
    content: attr(data-tooltip);
    position: absolute;
    bottom: calc(100% + 6px);
    left: 50%;
    right: auto;
    transform: translateX(-50%);
    max-width: min(14rem, calc(100vw - 2 * var(--corner-fab-edge)));
    white-space: normal;
    text-align: center;
    line-height: 1.35;
    padding: var(--space-1, 0.28rem) var(--space-2, 0.5rem);
    background: var(--background-secondary);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-3, 0.3rem);
    color: var(--text-secondary);
    font-family: var(--font-family-main);
    font-size: calc(var(--font-size-controls) * 0.85);
    font-weight: var(--font-weight-normal);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
    pointer-events: none;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.15s ease, visibility 0.15s ease;
    z-index: 1003;
}

.tag-cloud-toggle-btn[data-tooltip]:hover::after {
    opacity: 1;
    visibility: visible;
    transform: translateX(-50%);
}

@media (pointer: coarse) {
    .tag-cloud-toggle-btn[data-tooltip]::after {
        display: none;
    }
}

/* Backdrop (click outside) */
.tag-cloud-modal-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1000;
    background: rgba(0, 0, 0, 0.22);
    opacity: 0;
    transition: opacity 0.18s ease;
    pointer-events: none;
}

.tag-cloud-modal-backdrop.is-open {
    opacity: 1;
    pointer-events: auto;
}

body[data-tag-filter-active="true"] .tag-cloud-modal-backdrop.is-open {
    pointer-events: none;
}

/* Anchored modal — matches dashboard button surfaces */
.tag-cloud-modal {
    position: fixed;
    z-index: 1002;
    height: auto;
    max-height: min(88vh, var(--tag-cloud-modal-max-height, 88vh));
    display: flex;
    flex-direction: column;
    border: 1px solid var(--border-primary);
    background: color-mix(in srgb, var(--background-secondary) 88%, transparent);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border-radius: var(--radius-6, 0.65rem);
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.16);
    opacity: 0;
    transform: translateY(8px) scale(0.97);
    transform-origin: bottom left;
    transition:
        opacity 0.18s ease,
        transform 0.22s ease;
    pointer-events: auto;
    overflow: hidden;
}

.tag-cloud-modal.is-open {
    opacity: 1;
    transform: translateY(0) scale(1);
}

.tag-cloud-modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2, 0.5rem);
    padding: var(--space-2, 0.55rem) var(--space-3, 0.65rem) var(--space-1-5, 0.35rem);
    border-bottom: 1px solid var(--border-primary);
    flex-shrink: 0;
}

.tag-cloud-modal-title {
    font-size: var(--font-size-controls);
    font-weight: var(--font-weight-semibold);
    color: var(--text-secondary);
}

.tag-cloud-modal-close {
    border: 1px solid transparent;
    background: transparent;
    color: var(--text-tertiary);
    font-size: var(--text-xl, 1.25rem);
    line-height: 1;
    cursor: pointer;
    padding: var(--space-hairline, 0.15rem) var(--space-1-5, 0.35rem);
    border-radius: var(--radius-4, 0.35rem);
    transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.tag-cloud-modal-close:hover {
    color: var(--accent-primary);
    border-color: var(--border-primary);
    background: color-mix(in srgb, var(--background-primary) 94%, transparent);
}

.tag-cloud-modal-body {
    flex: 0 1 auto;
    overflow-y: hidden;
    padding: var(--space-4, 1rem) var(--space-3, 0.85rem) var(--space-3, 0.75rem);
    min-height: 4.5rem;
    scrollbar-width: thin;
}

.tag-cloud-modal-body.is-scrollable {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}

/* Word cloud: wrapped rows, accent palette, gentle drift */
.tag-cloud-wordcloud {
    display: flex;
    flex-wrap: wrap;
    align-content: flex-start;
    justify-content: center;
    align-items: baseline;
    gap: var(--space-2, 0.55rem) var(--space-3, 0.75rem);
    text-align: center;
    line-height: 1.35;
}

@keyframes tagCloudWordEnter {
    from {
        opacity: 0;
        transform: rotate(var(--tag-rotate, 0deg)) translateY(6px) scale(0.92);
    }
    to {
        opacity: 1;
        transform: rotate(var(--tag-rotate, 0deg)) translateY(0) scale(1);
    }
}

@keyframes tagCloudWordDrift {
    0%,
    100% {
        transform: rotate(var(--tag-rotate, 0deg)) translateY(0);
    }
    50% {
        transform: rotate(var(--tag-rotate, 0deg)) translateY(-2px);
    }
}

.tag-cloud-word {
    display: inline-flex;
    align-items: baseline;
    gap: 0.1em;
    border: none;
    background: transparent;
    font-family: var(--font-family-main);
    font-size: calc(0.82rem + var(--tag-scale, 0.5) * 1.55rem);
    font-weight: calc(500 + var(--tag-scale, 0.5) * 300);
    padding: 0.2em 0.28em;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    letter-spacing: 0.02em;
    white-space: nowrap;
    transition:
        color 0.15s ease,
        text-shadow 0.15s ease,
        transform 0.2s ease;
}

.tag-cloud-wordcloud--live .tag-cloud-word {
    animation:
        tagCloudWordEnter 0.32s ease backwards,
        tagCloudWordDrift 4.5s ease-in-out infinite;
    animation-delay:
        calc(var(--tag-index, 0) * 0.025s),
        calc(0.35s + var(--tag-index, 0) * 0.11s);
}

body.no-animations .tag-cloud-wordcloud--live .tag-cloud-word {
    animation: none !important;
}

.tag-cloud-word-hash {
    font-weight: var(--font-weight-bold);
    font-size: 0.9em;
    color: color-mix(in srgb, var(--accent-primary) 82%, var(--text-secondary));
}

.tag-cloud-word-label {
    line-height: 1.2;
    color: color-mix(
        in srgb,
        var(--text-secondary) calc((1 - var(--tag-scale, 0.5)) * 45%),
        var(--accent-primary) calc(25% + var(--tag-scale, 0.5) * 55%)
    );
}

.tag-cloud-word--tier-xl .tag-cloud-word-label {
    color: var(--accent-primary);
}

.tag-cloud-word--tier-lg .tag-cloud-word-label {
    color: color-mix(in srgb, var(--accent-primary) 88%, var(--text-primary));
}

.tag-cloud-word--tier-xs .tag-cloud-word-label {
    color: var(--text-secondary);
}

.tag-cloud-word--tier-xl {
    letter-spacing: 0.03em;
}

.tag-cloud-word:hover {
    transform: rotate(var(--tag-rotate, 0deg)) scale(1.06) translateY(-1px);
}

.tag-cloud-word:hover .tag-cloud-word-hash,
.tag-cloud-word:hover .tag-cloud-word-label {
    color: var(--accent-primary);
    text-shadow: 0 0 12px color-mix(in srgb, var(--accent-primary) 45%, transparent);
}

.tag-cloud-word.is-selected .tag-cloud-word-hash,
.tag-cloud-word.is-selected .tag-cloud-word-label {
    color: var(--accent-primary);
}

.tag-cloud-word.is-selected .tag-cloud-word-label {
    text-decoration: underline;
    text-underline-offset: 0.18em;
}

.tag-cloud-word:focus,
.tag-cloud-word:focus-visible,
.tag-cloud-word.is-keyboard-focused {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}

.tag-cloud-modal-empty {
    margin: 0;
    font-size: var(--font-size-small);
    color: var(--text-tertiary);
    text-align: center;
}

.tag-cloud-modal-footer {
    padding: var(--space-1-5, 0.35rem) var(--space-3, 0.65rem) var(--space-2, 0.55rem);
    flex-shrink: 0;
}

.tag-cloud-clear-filter {
    width: 100%;
    border: 1px solid var(--border-primary);
    background: color-mix(in srgb, var(--background-secondary) 74%, transparent);
    color: var(--text-secondary);
    font-family: var(--font-family-main);
    font-size: var(--font-size-small);
    padding: var(--space-1-5, 0.35rem) var(--space-2, 0.5rem);
    border-radius: var(--radius-6, 0.65rem);
    cursor: pointer;
    transition:
        background-color 0.2s ease,
        border-color 0.2s ease,
        color 0.2s ease,
        transform 0.2s ease,
        box-shadow 0.2s ease;
}

.tag-cloud-clear-filter:hover {
    background: color-mix(in srgb, var(--background-primary) 94%, transparent);
    border-color: var(--accent-primary);
    color: var(--text-primary);
    transform: translateY(-1px);
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.16);
}

.tag-cloud-clear-filter:focus,
.tag-cloud-clear-filter:focus-visible,
.tag-cloud-clear-filter.is-keyboard-focused {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
    border-color: var(--accent-primary);
    color: var(--text-primary);
}

/* Dock corners */
body[data-button-position="bottom-left"] .tag-cloud-toggle-btn.search-button {
    left: auto;
    right: var(--corner-fab-edge);
}

body[data-button-position="bottom-left"] .tag-cloud-modal {
    transform-origin: bottom right;
}

body[data-button-position="bottom-right"] .tag-cloud-toggle-btn.search-button {
    left: var(--corner-fab-edge);
    right: auto;
}

body[data-button-position="bottom-right"] .tag-cloud-modal {
    transform-origin: bottom left;
}

body[data-button-position="side-left"] .tag-cloud-modal {
    transform-origin: top left;
}

/* ==== css/feed-row.css ==== */
/* The bookmark feed row, shared by Health, Inbox and Config → Bookmarks.
 *
 * The three used to carry their own copy of this rule — `.health-view-item`,
 * `.inbox-item` and `.config-bm-item` — and the copies had drifted into being
 * byte-identical, which is the worst of both: one design, three places to change
 * it, and no way to tell whether a difference was meant.
 *
 * What genuinely differs between the three is expressed as modifiers below: the
 * checkbox column Health keeps in the grid, and the coloured left edge each view
 * uses for its own state (unread, broken, ticked).
 *
 * Loaded before health-view.css, config-view.css and dashboard-inbox.css so
 * those files can still override where a view really needs it.
 */

.feed-row {
    position: relative;
    display: grid;
    grid-template-columns: 3rem 1fr;
    gap: var(--space-3, 0.85rem);
    padding: var(--space-3, 0.75rem) var(--space-3, 0.85rem);
    border: 1px solid var(--border-primary);
    border-radius: var(--layout-radius-sm, 8px);
    background: var(--background-secondary);
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

/* Health keeps the checkbox in the grid rather than floating it over the icon:
   its rows are scanned in bulk, so the column is always there and only fades. */
.feed-row--with-select {
    grid-template-columns: 1.1rem 3rem 1fr;
}

.feed-row:hover {
    border-color: color-mix(in srgb, var(--accent-primary) 42%, var(--border-primary));
    background: color-mix(in srgb, var(--accent-primary) 7%, var(--background-secondary));
}

.feed-row.keyboard-selected {
    border-color: var(--accent-primary);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-primary) 28%, transparent);
    background: color-mix(in srgb, var(--accent-primary) 9%, var(--background-secondary));
}

/* The flash after jumping to a row from somewhere else. */
.feed-row--highlight {
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-primary) 35%, transparent);
}

/* Classic layout has no focus ring on the row at all — the keyboard cursor is
   drawn by .keyboard-selected, but Tab-ing to a row with tabindex="-1" showed
   nothing. Modern layout already had this; now both do. */
.feed-row:focus-visible {
    outline: var(--layout-focus-ring, 2px solid color-mix(in srgb, var(--accent-primary) 72%, transparent));
    outline-offset: 2px;
}

/* The state edge. Each view names its own state; the shape is shared. */
.feed-row--edge-accent {
    border-left: 3px solid var(--accent-primary);
}

.feed-row--edge-error {
    border-left: 3px solid var(--accent-error);
}

.feed-row--edge-warning {
    border-left: 3px solid var(--accent-warning);
}

/* Rounded corners would clip a left border, so modern redraws the edge as an
   inset shadow. Kept here with the edge it belongs to rather than repeated in
   each view's modern block. */
body[data-layout-version="modern"] .feed-row {
    border-radius: var(--layout-radius-md, 12px);
    border-color: var(--layout-surface-border);
    background: var(--layout-surface);
    box-shadow: var(--layout-shadow-sm);
}

body[data-layout-version="modern"] .feed-row--edge-accent,
body[data-layout-version="modern"] .feed-row--edge-error,
body[data-layout-version="modern"] .feed-row--edge-warning {
    border-left-width: 1px;
}

body[data-layout-version="modern"] .feed-row--edge-accent {
    box-shadow: var(--layout-shadow-sm), inset 3px 0 0 0 var(--accent-primary);
}

body[data-layout-version="modern"] .feed-row--edge-error {
    box-shadow: var(--layout-shadow-sm), inset 3px 0 0 0 var(--accent-error);
}

body[data-layout-version="modern"] .feed-row--edge-warning {
    box-shadow: var(--layout-shadow-sm), inset 3px 0 0 0 var(--accent-warning);
}

/* The feed itself: a column of cards, no dividers and no striping — the card
   border and the gap do the separating. */
.feed-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-3, 0.65rem);
}

/* The row's action bar, revealed on hover, focus or the keyboard cursor.
   grid-template-rows rather than height, so it animates from nothing to its
   own content height without a magic number. */
.feed-row-actions {
    display: grid;
    grid-template-rows: 0fr;
    margin-top: 0;
    opacity: 0;
    transition:
        grid-template-rows 0.14s ease,
        opacity 0.12s ease,
        margin-top 0.14s ease;
}

.feed-row-actions > * {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1-5, 0.35rem);
    min-height: 0;
    overflow: hidden;
}

.feed-row:hover .feed-row-actions,
.feed-row:focus-within .feed-row-actions,
.feed-row.keyboard-selected .feed-row-actions {
    grid-template-rows: 1fr;
    margin-top: var(--space-2, 0.55rem);
    opacity: 1;
}

/* The collapsing wrapper clips its content, which would cut off an open menu.
   Only while a menu is open does the row let it escape — the height animation is
   finished by then, so nothing jumps. Matches both the shared menu class and the
   one Health names its own popovers with. */
.feed-row-actions:has(.health-view-menu:not([hidden])),
.feed-row-actions:has(.health-view-menu:not([hidden])) > * {
    overflow: visible;
}

/* Nothing hovers on a touch screen, and the actions are the point of the row. */
@media (hover: none) {
    .feed-row-actions {
        grid-template-rows: 1fr;
        margin-top: var(--space-2, 0.55rem);
        opacity: 1;
    }
}

@media (max-width: 520px) {
    .feed-row-actions {
        grid-template-rows: 1fr;
        margin-top: var(--space-2, 0.55rem);
        opacity: 1;
    }
}

/* ==== css/spread-tutorial.css ==== */
/**
 * The spread-across-columns walkthrough.
 *
 * Same shape as inbox-tutorial.css and health-tutorial.css: the app's modal
 * chrome carries the title and the buttons, and only the reading column between
 * them is styled here. Its own sheet, so no tour's visuals drift into another's.
 *
 * The visuals in this one are diagrams rather than copies of a control — the
 * subject is a shape on the page, so stand-in columns and rows say more than a
 * screenshot of a menu would.
 */
body:has(#dashboard-layout) #app-modal.modal-overlay:has(.spread-tutorial-modal) .modal {
    transform: translateY(10px) scale(0.98);
    transition: transform 0.15s ease-in, opacity 0.15s ease-in;
}

body:has(#dashboard-layout) #app-modal.modal-overlay.show:has(.spread-tutorial-modal) .modal {
    transform: translateY(0) scale(1);
    transition: transform 0.38s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.22s ease-out;
}

body.no-animations:has(#dashboard-layout) #app-modal.modal-overlay:has(.spread-tutorial-modal) .modal {
    transform: none !important;
    transition: opacity 0.15s ease !important;
}

.spread-tutorial-progress {
    font-size: var(--text-xs, 0.72rem);
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--text-tertiary);
    margin: 0 0 var(--space-1-5, 0.4rem);
}

.spread-tutorial-step-title {
    margin: 0 0 var(--space-3, 0.7rem);
    font-size: var(--text-lg, 1.1rem);
    color: var(--text-primary);
}

.spread-tutorial-step-body p {
    margin: 0 0 var(--space-3, 0.7rem);
    color: var(--text-secondary);
    line-height: 1.55;
}

.spread-tutorial-step-body p:last-child {
    margin-bottom: 0;
}

.spread-tutorial-visual {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2, 0.5rem);
    padding: var(--space-3, 0.75rem);
    margin: 0 0 var(--space-4, 0.9rem);
    border: 1px dashed var(--border-secondary);
    border-radius: var(--radius-5, 0.5rem);
    background: var(--background-secondary);
}

.spread-tutorial-visual--compare,
.spread-tutorial-visual--sum {
    justify-content: center;
}

.spread-tutorial-block {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-1, 0.3rem);
}

.spread-tutorial-cap {
    font-size: var(--text-2xs, 0.66rem);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-tertiary);
}

/* The stand-in category: columns of blank rows, at the proportions a real one
   has, so the before/after difference is a shape rather than a sentence. */
.spread-tutorial-grid {
    display: flex;
    gap: var(--space-1, 0.3rem);
    padding: var(--space-1-5, 0.35rem);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-4, 0.35rem);
    background: var(--background-primary);
}

.spread-tutorial-col {
    display: flex;
    flex-direction: column;
    gap: var(--space-1, 0.25rem);
    width: 3.1rem;
}

.spread-tutorial-row {
    height: 0.4rem;
    border-radius: var(--radius-1, 0.15rem);
    background: color-mix(in srgb, var(--accent-primary) 30%, var(--border-primary));
}

/* Every second row a little shorter: a flat stack of identical bars reads as a
   loading skeleton rather than as bookmarks. */
.spread-tutorial-row:nth-child(even) {
    width: 78%;
}

.spread-tutorial-arrow,
.spread-tutorial-op {
    color: var(--text-tertiary);
    font-size: var(--text-base, 0.95rem);
}

.spread-tutorial-chip {
    padding: var(--space-hairline, 0.15rem) var(--space-2, 0.45rem);
    border: 1px solid var(--border-secondary);
    border-radius: var(--radius-pill, 999px);
    font-size: var(--text-xs, 0.72rem);
    color: var(--text-secondary);
    white-space: nowrap;
}

.spread-tutorial-key {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1-5, 0.35rem);
    font-size: var(--text-sm, 0.78rem);
    color: var(--text-secondary);
}

.spread-tutorial-key kbd {
    padding: 0 var(--space-1, 0.3rem);
    border: 1px solid var(--border-secondary);
    border-radius: var(--radius-3, 0.25rem);
    font-family: inherit;
    font-size: var(--text-xs, 0.72rem);
    color: var(--text-tertiary);
}

.spread-tutorial-hint {
    flex: 1 1 10rem;
    min-width: 0;
    font-size: var(--text-xs, 0.75rem);
    color: var(--text-tertiary);
}

/* The menu step: three rows of the real right-click menu, with the one this
   tour is about picked out. */
.spread-tutorial-menu {
    display: flex;
    flex-direction: column;
    gap: var(--space-hairline, 0.15rem);
    width: 100%;
    padding: var(--space-1, 0.3rem);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-5, 0.45rem);
    background: var(--background-primary);
}

.spread-tutorial-menu-row {
    display: flex;
    align-items: center;
    gap: var(--space-2, 0.45rem);
    padding: var(--space-1, 0.3rem) var(--space-2, 0.45rem);
    border-radius: var(--radius-3, 0.3rem);
    font-size: var(--text-sm, 0.78rem);
    color: var(--text-tertiary);
}

.spread-tutorial-menu-row.is-current {
    background: color-mix(in srgb, var(--accent-primary) 12%, transparent);
    color: var(--text-primary);
}

.spread-tutorial-menu-row kbd {
    margin-left: auto;
    padding: 0 var(--space-1, 0.25rem);
    border: 1px solid var(--border-secondary);
    border-radius: var(--radius-2, 0.2rem);
    font-family: inherit;
    font-size: var(--text-2xs, 0.68rem);
    color: var(--text-tertiary);
}

.spread-tutorial-dots {
    display: flex;
    gap: var(--space-1, 0.3rem);
    margin-top: var(--space-4, 0.9rem);
}

.spread-tutorial-dot {
    width: 0.4rem;
    height: 0.4rem;
    border-radius: 50%;
    background: var(--border-secondary);
}

.spread-tutorial-dot.is-active {
    background: var(--accent-primary);
}

/* ==== css/fresh-tutorial.css ==== */
/**
 * The Fresh walkthrough.
 *
 * Same shape as spread-tutorial.css, inbox-tutorial.css and health-tutorial.css:
 * the app's modal chrome carries the title and the buttons, and only the reading
 * column between them is styled here. Its own sheet, so no tour's visuals drift
 * into another's.
 *
 * The visuals are stand-in bookmark rows rather than diagrams, because what
 * Fresh changes is a row you already know the look of — one small count on the
 * right of it, and a collection made of the same rows.
 */
body:has(#dashboard-layout) #app-modal.modal-overlay:has(.fresh-tutorial-modal) .modal {
    transform: translateY(10px) scale(0.98);
    transition: transform 0.15s ease-in, opacity 0.15s ease-in;
}

body:has(#dashboard-layout) #app-modal.modal-overlay.show:has(.fresh-tutorial-modal) .modal {
    transform: translateY(0) scale(1);
    transition: transform 0.38s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.22s ease-out;
}

body.no-animations:has(#dashboard-layout) #app-modal.modal-overlay:has(.fresh-tutorial-modal) .modal {
    transform: none !important;
    transition: opacity 0.15s ease !important;
}

.fresh-tutorial-progress {
    font-size: var(--text-xs, 0.72rem);
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--text-tertiary);
    margin: 0 0 var(--space-1-5, 0.4rem);
}

.fresh-tutorial-step-title {
    margin: 0 0 var(--space-3, 0.7rem);
    font-size: var(--text-lg, 1.1rem);
    color: var(--text-primary);
}

.fresh-tutorial-step-body p {
    margin: 0 0 var(--space-3, 0.7rem);
    color: var(--text-secondary);
    line-height: 1.55;
}

.fresh-tutorial-step-body p:last-child {
    margin-bottom: 0;
}

.fresh-tutorial-visual {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2, 0.5rem);
    padding: var(--space-3, 0.75rem);
    margin: 0 0 var(--space-4, 0.9rem);
    border: 1px dashed var(--border-secondary);
    border-radius: var(--radius-5, 0.5rem);
    background: var(--background-secondary);
}

.fresh-tutorial-visual--flow {
    justify-content: center;
}

.fresh-tutorial-stack,
.fresh-tutorial-collection {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    gap: var(--space-1, 0.3rem);
    padding: var(--space-1-5, 0.4rem);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-4, 0.35rem);
    background: var(--background-primary);
}

.fresh-tutorial-collection-title {
    display: flex;
    align-items: center;
    gap: var(--space-1, 0.3rem);
    margin-bottom: var(--space-hairline, 0.15rem);
    font-size: var(--text-sm, 0.78rem);
    color: var(--text-secondary);
}

.fresh-tutorial-count {
    color: var(--text-tertiary);
    font-variant-numeric: tabular-nums;
}

/* A stand-in row: icon, name, and the one thing Fresh adds on the right. */
.fresh-tutorial-row {
    display: flex;
    align-items: center;
    gap: var(--space-2, 0.45rem);
    padding: var(--space-1, 0.25rem) var(--space-1-5, 0.35rem);
    border-radius: var(--radius-3, 0.25rem);
    background: color-mix(in srgb, var(--accent-primary) 6%, transparent);
}

.fresh-tutorial-icon {
    width: 0.7rem;
    height: 0.7rem;
    flex: 0 0 auto;
    border-radius: var(--radius-2, 0.18rem);
    background: color-mix(in srgb, var(--accent-primary) 45%, transparent);
}

.fresh-tutorial-name {
    flex: 1 1 auto;
    font-size: var(--text-sm, 0.8rem);
    color: var(--text-secondary);
}

/* The count itself, drawn as the row draws it: small, quiet, on the right. */
.fresh-tutorial-badge {
    flex: 0 0 auto;
    min-width: 1.15rem;
    padding: var(--space-hairline, 0.05rem) var(--space-1, 0.3rem);
    border-radius: var(--radius-pill, 999px);
    background: var(--accent-primary);
    color: var(--background-primary);
    font-size: var(--text-2xs, 0.68rem);
    font-weight: var(--font-weight-semibold);
    line-height: 1.4;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

.fresh-tutorial-arrow {
    color: var(--text-tertiary);
    font-size: var(--text-base, 0.95rem);
}

.fresh-tutorial-chip {
    padding: var(--space-hairline, 0.15rem) var(--space-2, 0.45rem);
    border: 1px solid var(--border-secondary);
    border-radius: var(--radius-pill, 999px);
    font-size: var(--text-xs, 0.72rem);
    color: var(--text-secondary);
    white-space: nowrap;
}

/* The conditional request's answer: the cheap end of the route, so it is drawn
   as the quiet one. */
.fresh-tutorial-chip--quiet {
    border-style: dashed;
    color: var(--text-tertiary);
    font-family: var(--font-mono, ui-monospace, monospace);
}

.fresh-tutorial-note {
    flex: 1 1 auto;
    font-size: var(--text-sm, 0.8rem);
    line-height: 1.5;
    color: var(--text-secondary);
}

.fresh-tutorial-dots {
    display: flex;
    justify-content: center;
    gap: var(--space-1-5, 0.35rem);
    margin-top: var(--space-4, 0.9rem);
}

.fresh-tutorial-dot {
    width: 0.4rem;
    height: 0.4rem;
    border-radius: var(--radius-pill, 999px);
    background: var(--border-primary);
}

.fresh-tutorial-dot.is-active {
    background: var(--accent-primary);
}

/* ==== css/setting-art.css ==== */
/* ─── Setting art ───────────────────────────────────────────────────────────
   Stand-in drawings for the settings whose difference is a shape: how many
   columns, how much room between rows, how wide the margins are. Same grammar
   as the spread tour's visuals — blank bars at the proportions of the real
   thing — so config, help and the tour all draw a bookmark the same way.

   Decorative: every drawing sits beside the label that names it, so the markup
   is aria-hidden and none of this is in the accessibility tree. */

.setting-art {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1-5, 0.4rem);
    pointer-events: none;
}

.setting-art-frame {
    display: flex;
    align-items: center;
    gap: var(--space-1, 0.25rem);
    padding: var(--space-1, 0.3rem);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-4, 0.35rem);
    background: var(--background-primary);
}

.setting-art-col {
    display: flex;
    flex-direction: column;
    gap: var(--space-1, 0.2rem);
    width: 1.5rem;
}

.setting-art-row {
    height: 0.28rem;
    border-radius: var(--radius-1, 0.12rem);
    background: color-mix(in srgb, var(--accent-primary) 30%, var(--border-primary));
}

/* A stack of identical bars reads as a loading skeleton; the short one every
   other line reads as text. */
.setting-art-row:nth-child(even) {
    width: 76%;
}

.setting-art-more {
    align-self: flex-end;
    font-size: var(--text-xs, 0.7rem);
    line-height: 1;
    color: var(--text-tertiary);
}

/* Density is row height and row gap, and nothing else. */
.setting-art-col.is-comfortable { gap: var(--space-1, 0.24rem); }
.setting-art-col.is-compact { gap: var(--space-hairline, 0.14rem); }
.setting-art-col.is-dense { gap: var(--space-hairline, 0.07rem); }
.setting-art-col.is-auto { gap: var(--space-hairline, 0.14rem); }
.setting-art-col.is-dense .setting-art-row { height: 0.2rem; }
.setting-art-col.is-auto .setting-art-row:nth-child(n + 4) { height: 0.2rem; }

.setting-art-frame--density .setting-art-col {
    width: 2.6rem;
}

/* Spacing: three category blocks, moved apart. */
.setting-art-stack {
    display: flex;
    flex-direction: column;
    width: 2.8rem;
}

.setting-art-stack.is-snug { gap: var(--space-hairline, 0.12rem); }
.setting-art-stack.is-balanced { gap: var(--space-1, 0.3rem); }
.setting-art-stack.is-airy { gap: var(--space-2, 0.5rem); }

.setting-art-band {
    height: 0.34rem;
    border-radius: var(--radius-1, 0.12rem);
    background: color-mix(in srgb, var(--accent-primary) 28%, var(--border-primary));
}

/* Margins: the page, with the edges the setting leaves empty. */
.setting-art-page {
    display: flex;
    align-items: stretch;
    width: 5.2rem;
}

.setting-art-gutter {
    flex: 0 0 auto;
    background: repeating-linear-gradient(
        45deg,
        color-mix(in srgb, var(--text-tertiary) 22%, transparent) 0 2px,
        transparent 2px 4px
    );
    border-radius: var(--radius-1, 0.12rem);
}

.setting-art-page.is-snug .setting-art-gutter { width: 0.18rem; }
.setting-art-page.is-balanced .setting-art-gutter { width: 0.42rem; }
.setting-art-page.is-airy .setting-art-gutter { width: 0.75rem; }

.setting-art-content {
    display: flex;
    flex: 1 1 auto;
    gap: var(--space-hairline, 0.18rem);
    padding: 0 var(--space-hairline, 0.16rem);
}

.setting-art-content .setting-art-col {
    width: auto;
    flex: 1 1 0;
}

/* Type size: the only setting here whose subject is the letterform itself. */
.setting-art-type {
    display: inline-flex;
    align-items: baseline;
    justify-content: center;
    min-width: 1.9rem;
    color: var(--text-secondary);
    font-weight: var(--font-weight-semibold);
    line-height: 1;
}

/* All seven steps the picker offers, drawn at their own proportions rather
   than at three sizes standing in for seven. */
.setting-art-type.is-xs { font-size: var(--text-2xs, 0.6rem); }
.setting-art-type.is-s { font-size: var(--text-xs, 0.72rem); }
.setting-art-type.is-sm { font-size: var(--text-sm, 0.85rem); }
.setting-art-type.is-m { font-size: var(--text-md, 0.98rem); }
.setting-art-type.is-lg { font-size: var(--text-lg, 1.15rem); }
.setting-art-type.is-l { font-size: var(--text-xl, 1.32rem); }
.setting-art-type.is-xl { font-size: 1.55rem; }

/* A route rather than a shape: where a pasted link ends up, what a promote
   leaves behind. */
.setting-art-flow {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1, 0.3rem);
    flex-wrap: wrap;
}

.setting-art-chip {
    padding: var(--space-hairline, 0.1rem) var(--space-1-5, 0.4rem);
    border: 1px solid var(--border-secondary);
    border-radius: var(--radius-pill, 999px);
    background: var(--background-secondary);
    font-size: var(--text-xs, 0.7rem);
    color: var(--text-secondary);
    white-space: nowrap;
}

.setting-art-arrow {
    font-size: var(--text-xs, 0.75rem);
    color: var(--text-tertiary);
}

/* ─── Where the drawings sit ───────────────────────────────────────────────
   On a choice card the art leads, above the title. Beside an ordinary control
   it sits on the row's right, where the reset and info buttons are, so the
   column of labels down the left is not disturbed. */
.config-choice-card .setting-art {
    margin-bottom: var(--space-1-5, 0.35rem);
}

.config-field-art {
    display: flex;
    align-items: center;
    gap: var(--space-2, 0.5rem);
    margin: -0.15rem 0 var(--space-2, 0.5rem) auto;
    padding-right: var(--space-hairline, 0.1rem);
}

/* In help, the drawing is the panel's opening line. */
.config-help-art {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3, 0.75rem);
    margin: 0 0 var(--space-3, 0.7rem);
    padding: var(--space-2, 0.6rem) var(--space-3, 0.7rem);
    border: 1px solid var(--border-secondary);
    border-radius: var(--radius-4, 0.4rem);
    background: var(--background-secondary);
}

.config-help-art .setting-art {
    gap: var(--space-1-5, 0.35rem);
}

.config-help-art-caption {
    font-size: var(--text-xs, 0.72rem);
    color: var(--text-tertiary);
}

@media (max-width: 640px) {
    .config-field-art {
        margin-left: 0;
    }
}

/* A choice button that leads with its drawing: the letters above, the name of
   the size under them. */
.config-choice--art {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-hairline, 0.15rem);
    padding: var(--space-1-5, 0.35rem) var(--space-2, 0.6rem);
    line-height: 1.1;
}

.config-choice--art .setting-art {
    min-height: 1.6rem;
}

.config-choice--art .config-choice-label {
    font-size: var(--text-xs, 0.72rem);
}

/* Layout version: the same two categories, flat on the page or each on its own
   raised card — which is the difference a reader is actually choosing between. */
.setting-art-layout {
    display: flex;
    gap: var(--space-1, 0.25rem);
}

.setting-art-card {
    padding: var(--space-hairline, 0.12rem);
    border-radius: var(--radius-1, 0.1rem);
}

.setting-art-layout.is-modern .setting-art-card {
    padding: var(--space-hairline, 0.18rem);
    border: 1px solid var(--border-secondary);
    border-radius: var(--radius-3, 0.28rem);
    background: var(--background-secondary);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.14);
}

.setting-art-layout .setting-art-col {
    width: 1.2rem;
}

/* Button bar: the page, with the bar where the setting puts it. */
.setting-art-screen {
    position: relative;
    display: block;
    width: 4.4rem;
    height: 2.5rem;
    padding: var(--space-1, 0.22rem);
    border-radius: var(--radius-2, 0.2rem);
    background: var(--background-secondary);
}

.setting-art-screen-body {
    display: flex;
    gap: var(--space-hairline, 0.16rem);
    height: 100%;
}

.setting-art-screen-body .setting-art-col {
    width: auto;
    flex: 1 1 0;
}

/* The bar really does float over the grid, so the overlap is honest — but the
   bookmarks behind it are backdrop here, not the subject, and at full strength
   they compete with the one element the drawing is about. */
.setting-art-screen-body .setting-art-row {
    background: color-mix(in srgb, var(--text-tertiary) 30%, transparent);
}

.setting-art-bar {
    position: absolute;
    border-radius: var(--radius-pill, 999px);
    background: var(--accent-primary);
}

/* Horizontal bar along the bottom; the three positions differ in where along
   that edge it sits. */
.setting-art-screen.is-center-bottom .setting-art-bar,
.setting-art-screen.is-bottom-left .setting-art-bar,
.setting-art-screen.is-bottom-right .setting-art-bar {
    bottom: 0.18rem;
    height: 0.22rem;
    width: 1.5rem;
}

.setting-art-screen.is-center-bottom .setting-art-bar {
    left: 50%;
    transform: translateX(-50%);
}

.setting-art-screen.is-bottom-left .setting-art-bar { left: 0.2rem; }
.setting-art-screen.is-bottom-right .setting-art-bar { right: 0.2rem; }

/* The rails stand the same bar on its end against one edge. */
.setting-art-screen.is-side-left .setting-art-bar,
.setting-art-screen.is-side-right .setting-art-bar {
    top: 50%;
    transform: translateY(-50%);
    width: 0.22rem;
    height: 1.5rem;
}

.setting-art-screen.is-side-left .setting-art-bar { left: 0.18rem; }
.setting-art-screen.is-side-right .setting-art-bar { right: 0.18rem; }

/* Two endings from one arrow: "ask each time" does not end anywhere single. */
.setting-art-branch {
    display: inline-flex;
    flex-direction: column;
    gap: var(--space-hairline, 0.15rem);
}

/* ─── The drawings help opens with ─────────────────────────────────────────
   Help is prose about a program, and half of what it describes is a shape, a
   route, a state or a key. These are the vocabulary for the other half: the
   same blank-bar grammar as above, extended so every help panel can open with
   the picture its own subject has, rather than with a paragraph that has to be
   decoded into one. Same rules as the rest of this file — markup and CSS only,
   theme tokens throughout, aria-hidden because the sentence beside each one
   already says it. */

/* Steps: the number carries the order, which is the whole claim. */
.setting-art-steps {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1-5, 0.35rem);
    flex-wrap: wrap;
}

.setting-art-step {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1, 0.25rem);
}

.setting-art-step-n {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 0.95rem;
    height: 0.95rem;
    border-radius: var(--radius-pill, 999px);
    background: color-mix(in srgb, var(--accent-primary) 22%, var(--background-secondary));
    color: var(--text-secondary);
    font-size: var(--text-2xs, 0.6rem);
    font-weight: var(--font-weight-semibold);
    line-height: 1;
}

.setting-art-step-label {
    font-size: var(--text-xs, 0.7rem);
    color: var(--text-secondary);
    white-space: nowrap;
}

/* Keycaps, drawn the way the cheat sheet draws them so a key looks like a key
   wherever nextDash prints one. */
.setting-art-keys {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1, 0.3rem);
    flex-wrap: wrap;
}

.setting-art-key {
    min-width: 1.05rem;
    padding: var(--space-hairline, 0.08rem) var(--space-1-5, 0.32rem);
    border: 1px solid var(--border-primary);
    border-bottom-width: 2px;
    border-radius: var(--radius-3, 0.25rem);
    background: var(--background-primary);
    color: var(--text-secondary);
    font-family: var(--font-mono, ui-monospace, monospace);
    font-size: var(--text-2xs, 0.68rem);
    line-height: 1.25;
    text-align: center;
    white-space: nowrap;
}

/* A legend, in the colours the rows themselves wear. */
.setting-art-states {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2, 0.5rem);
    flex-wrap: wrap;
}

.setting-art-state {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1, 0.25rem);
}

.setting-art-state-label {
    font-size: var(--text-xs, 0.7rem);
    color: var(--text-secondary);
    white-space: nowrap;
}

.setting-art-dot {
    width: 0.45rem;
    height: 0.45rem;
    border-radius: var(--radius-pill, 999px);
    background: var(--text-tertiary);
    flex: 0 0 auto;
}

.setting-art-dot.is-ok { background: var(--accent-success); }
.setting-art-dot.is-warn { background: var(--accent-warning); }
.setting-art-dot.is-bad { background: var(--accent-error); }
.setting-art-dot.is-idle { background: color-mix(in srgb, var(--accent-primary) 55%, transparent); }
/* Off is drawn as a ring rather than a pale dot: nothing is being reported,
   which is not the same as a reading that happens to be quiet. */
.setting-art-dot.is-off {
    background: transparent;
    box-shadow: inset 0 0 0 1px var(--text-tertiary);
}

/* The overlay with something typed in it. */
.setting-art-frame--field { padding: var(--space-1, 0.22rem) var(--space-1-5, 0.35rem); }

.setting-art-field {
    display: inline-flex;
    align-items: center;
    gap: var(--space-hairline, 0.15rem);
    min-width: 6.5rem;
    font-family: var(--font-mono, ui-monospace, monospace);
    font-size: var(--text-xs, 0.72rem);
    line-height: 1.3;
}

.setting-art-q.is-prefix {
    padding: 0 var(--space-1, 0.2rem);
    border-radius: var(--radius-2, 0.2rem);
    background: color-mix(in srgb, var(--accent-primary) 28%, transparent);
    color: var(--text-primary);
    font-weight: var(--font-weight-semibold);
}

.setting-art-q.is-token {
    padding: 0 var(--space-1, 0.2rem);
    border-radius: var(--radius-2, 0.2rem);
    background: color-mix(in srgb, var(--accent-primary) 14%, transparent);
    color: var(--text-secondary);
}

.setting-art-q.is-text { color: var(--text-secondary); }

.setting-art-caret {
    width: 1px;
    height: 0.8rem;
    margin-left: var(--space-hairline, 0.1rem);
    background: var(--accent-primary);
}

/* Counts, with the one being talked about picked out. */
.setting-art-frame--bars { align-items: flex-end; }

.setting-art-bars {
    display: flex;
    align-items: flex-end;
    gap: var(--space-hairline, 0.18rem);
    height: 1.5rem;
}

.setting-art-bar-col {
    width: 0.32rem;
    border-radius: 0.1rem 0.1rem 0 0;
    background: color-mix(in srgb, var(--accent-primary) 30%, var(--border-primary));
}

.setting-art-bar-col.is-mark { background: var(--accent-primary); }
.setting-art-bar-col.is-mark.is-bad { background: var(--accent-error); }
.setting-art-bar-col.is-mark.is-warn { background: var(--accent-warning); }

/* A trend, drawn as the line the real row draws. */
.setting-art-spark {
    display: block;
    width: 3.4rem;
    height: 1.2rem;
    color: var(--accent-primary);
    overflow: visible;
}

/* How far along, and where it starts to matter. */
.setting-art-meter {
    position: relative;
    display: block;
    width: 4.2rem;
    height: 0.45rem;
    border-radius: var(--radius-pill, 999px);
    background: color-mix(in srgb, var(--text-tertiary) 22%, transparent);
    overflow: hidden;
}

.setting-art-meter-fill {
    position: absolute;
    inset: 0 auto 0 0;
    border-radius: var(--radius-pill, 999px);
    background: var(--accent-primary);
}

.setting-art-meter-fill.is-warn { background: var(--accent-warning); }
.setting-art-meter-fill.is-bad { background: var(--accent-error); }

.setting-art-meter-mark {
    position: absolute;
    top: -0.1rem;
    bottom: -0.1rem;
    width: 1px;
    background: var(--text-secondary);
}

/* A day, with the hours you expect to be down shaded. */
.setting-art-day {
    display: flex;
    gap: 1px;
}

.setting-art-hour {
    width: 0.11rem;
    height: 0.85rem;
    border-radius: var(--radius-1, 0.05rem);
    background: color-mix(in srgb, var(--text-tertiary) 25%, transparent);
}

.setting-art-hour.is-window {
    background: var(--accent-warning);
}

/* Switches: some on, some off, which is what a settings tab looks like. */
.setting-art-toggles {
    display: flex;
    flex-direction: column;
    gap: var(--space-1, 0.2rem);
}

.setting-art-toggle {
    position: relative;
    display: block;
    width: 1.35rem;
    height: 0.55rem;
    border-radius: var(--radius-pill, 999px);
    background: color-mix(in srgb, var(--text-tertiary) 28%, transparent);
}

.setting-art-toggle.is-on {
    background: color-mix(in srgb, var(--accent-primary) 70%, transparent);
}

.setting-art-knob {
    position: absolute;
    top: 0.08rem;
    left: 0.08rem;
    width: 0.39rem;
    height: 0.39rem;
    border-radius: var(--radius-pill, 999px);
    background: var(--background-primary);
}

.setting-art-toggle.is-on .setting-art-knob {
    left: auto;
    right: 0.08rem;
}

/* Colour plates. Light and dark are halves of one theme, so they are drawn
   touching rather than as two separate chips. */
.setting-art-swatches {
    display: inline-flex;
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-3, 0.3rem);
    overflow: hidden;
}

.setting-art-swatch {
    width: 1.1rem;
    height: 1.1rem;
}

.setting-art-swatch.is-light { background: #f4f4f5; }
.setting-art-swatch.is-dark { background: #1f2024; }
.setting-art-swatch.is-accent { background: var(--accent-primary); }

/* The line your data does not cross. */
.setting-art-boundary-wrap {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1-5, 0.35rem);
}

.setting-art-boundary {
    display: inline-flex;
    flex-direction: column;
    gap: var(--space-1, 0.2rem);
    padding: var(--space-1, 0.25rem) var(--space-1-5, 0.35rem);
    border: 1px dashed var(--border-primary);
    border-radius: var(--radius-4, 0.35rem);
    background: var(--background-primary);
}

.setting-art-boundary-label {
    font-size: var(--text-2xs, 0.62rem);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-tertiary);
}

.setting-art-boundary-body {
    display: inline-flex;
    gap: var(--space-1, 0.25rem);
    flex-wrap: wrap;
}

.setting-art-blocked {
    font-size: var(--text-sm, 0.85rem);
    line-height: 1;
    color: var(--accent-error);
}

/* One row, with the parts it can carry. */
.setting-art-bmrow {
    display: flex;
    align-items: center;
    gap: var(--space-1, 0.25rem);
    width: 8rem;
}

.setting-art-bm-icon {
    width: 0.5rem;
    height: 0.5rem;
    border-radius: var(--radius-1, 0.12rem);
    background: color-mix(in srgb, var(--accent-primary) 45%, transparent);
    flex: 0 0 auto;
}

.setting-art-bm-name {
    flex: 1 1 auto;
    height: 0.28rem;
    border-radius: var(--radius-1, 0.12rem);
    background: color-mix(in srgb, var(--accent-primary) 30%, var(--border-primary));
}

.setting-art-bm-tag {
    width: 0.85rem;
    height: 0.35rem;
    border-radius: var(--radius-pill, 999px);
    background: color-mix(in srgb, var(--accent-primary) 18%, var(--border-primary));
    flex: 0 0 auto;
}

/* Config drawn as itself: rail, tabs, panel — the three places a sentence
   about "where the setting is" can mean. */
.setting-art-config {
    display: flex;
    gap: var(--space-1, 0.2rem);
    width: 4.6rem;
    height: 2.2rem;
}

.setting-art-rail {
    width: 0.6rem;
    border-radius: var(--radius-1, 0.15rem);
    background: color-mix(in srgb, var(--text-tertiary) 22%, transparent);
}

.setting-art-config-main {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    gap: var(--space-1, 0.2rem);
}

.setting-art-tabs {
    height: 0.3rem;
    border-radius: var(--radius-1, 0.15rem);
    background: color-mix(in srgb, var(--text-tertiary) 22%, transparent);
}

.setting-art-config-body {
    display: flex;
    flex: 1 1 auto;
    gap: var(--space-1, 0.2rem);
    padding: var(--space-hairline, 0.12rem);
    border-radius: var(--radius-1, 0.15rem);
    background: color-mix(in srgb, var(--text-tertiary) 12%, transparent);
}

.setting-art-config-body .setting-art-col { width: auto; flex: 1 1 0; }

.setting-art-rail.is-active,
.setting-art-tabs.is-active { background: var(--accent-primary); }

.setting-art-config-body.is-active {
    background: color-mix(in srgb, var(--accent-primary) 18%, transparent);
}

/* Help stacks several drawings per panel now, so the strip wraps rather than
   running off the panel on a narrow window, and each drawing keeps its caption
   under it rather than beside the next drawing's. */
.config-help-art-item {
    display: inline-flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-1, 0.2rem);
}

/* ─── v1.4.0 drawings ───────────────────────────────────────────────────────
   Four subjects that arrived with the theme engine and the widgets, each one a
   relationship rather than a value: how far a card sits off the page, what
   shape the backdrop is, how wide a block is beside its neighbours, and what a
   grid of themes looks like next to a list of names. */

/* Depth: the same card at three distances from the page behind it.

   Drawn structurally rather than tonally. The first attempt used a tint, a soft
   shadow and a wash — all three real, all three invisible at 3.4rem on a dark
   theme, so flat, soft and rich came out as three identical squares. A drawing
   that cannot be told apart is worse than no drawing: it says the settings do
   not differ.

   So the elevation is a plate offset behind the card, which reads at any size
   and in any palette: none at flat, one at soft, two at rich. */
.setting-art-frame--depth { padding: 0; }

.setting-art-depth {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 3.4rem;
    height: 2.2rem;
    border-radius: var(--radius-3, 0.3rem);
    overflow: hidden;
    background: var(--background-primary);
    /* Its own stacking context, so the plates below can sit behind the card
       without falling behind the panel the whole drawing is painted on. */
    isolation: isolate;
}
.setting-art-depth.is-soft { background: color-mix(in srgb, var(--accent-primary) 10%, var(--background-primary)); }
.setting-art-depth.is-rich { background: color-mix(in srgb, var(--accent-primary) 20%, var(--background-primary)); }

/* The wash behind the page, which only rich really has. */
.setting-art-depth-wash {
    position: absolute;
    inset: -50% -30% auto -30%;
    height: 150%;
    background: radial-gradient(
        closest-side,
        color-mix(in srgb, var(--accent-primary) 55%, transparent),
        transparent 70%);
    opacity: 0;
}
.setting-art-depth.is-soft .setting-art-depth-wash { opacity: 0.35; }
.setting-art-depth.is-rich .setting-art-depth-wash { opacity: 0.85; }

.setting-art-depth-card {
    position: relative;
    z-index: 1;
    display: flex;
    gap: var(--space-hairline, 0.18rem);
    padding: var(--space-1, 0.28rem) var(--space-1-5, 0.32rem);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-3, 0.22rem);
    background: var(--background-secondary);
}

/* The plates. ::before is the near one, ::after the far one — so soft shows a
   single step off the page and rich shows a stack. */
.setting-art-depth-card::before,
.setting-art-depth-card::after {
    content: "";
    position: absolute;
    inset: 0;
    border: 1px solid color-mix(in srgb, var(--text-secondary) 55%, transparent);
    border-radius: inherit;
    background: var(--background-secondary);
    /* Behind the card, in front of the page: zero rather than a negative,
       which would leave the drawing's own container. */
    z-index: 0;
    opacity: 0;
}
.setting-art-depth-card::before { transform: translate(3px, 3px); }
.setting-art-depth-card::after { transform: translate(6px, 6px); }

/* The rows sit above the plates: a positioned pseudo paints over ordinary
   content, so without this the card's own contents disappear the moment it is
   given any elevation at all. */
.setting-art-depth-card .setting-art-col {
    position: relative;
    z-index: 2;
}

.setting-art-depth.is-flat .setting-art-depth-card {
    background: var(--background-primary);
}
.setting-art-depth.is-soft .setting-art-depth-card::before { opacity: 1; }
.setting-art-depth.is-rich .setting-art-depth-card::before,
.setting-art-depth.is-rich .setting-art-depth-card::after { opacity: 1; }

/* The backdrop, in each of its five shapes. */
.setting-art-frame--pattern { padding: 0; }

.setting-art-pattern {
    display: block;
    width: 3.4rem;
    height: 2.2rem;
    border-radius: var(--radius-3, 0.3rem);
    background-color: var(--background-primary);
    --art-ink: color-mix(in srgb, var(--text-secondary) 45%, transparent);
}
.setting-art-pattern.is-dots {
    background-image: radial-gradient(var(--art-ink) 1px, transparent 1px);
    background-size: 6px 6px;
}
.setting-art-pattern.is-grid {
    background-image:
        linear-gradient(var(--art-ink) 1px, transparent 1px),
        linear-gradient(90deg, var(--art-ink) 1px, transparent 1px);
    background-size: 7px 7px;
}
.setting-art-pattern.is-lines {
    background-image: linear-gradient(var(--art-ink) 1px, transparent 1px);
    background-size: 100% 6px;
}
.setting-art-pattern.is-hatch {
    background-image: repeating-linear-gradient(
        45deg, var(--art-ink) 0 1px, transparent 1px 6px);
}
.setting-art-pattern.is-auto {
    background-image: radial-gradient(var(--art-ink) 1px, transparent 1px);
    background-size: 6px 6px;
    opacity: 0.6;
}
/* "None" is drawn as the empty frame it is, not as a missing picture. */
.setting-art-pattern.is-none { background-image: none; }

/* A widget beside its neighbours, at one column or two. */
.setting-art-blocks {
    display: flex;
    align-items: flex-start;
    gap: var(--space-1, 0.25rem);
}

.setting-art-block {
    display: flex;
    flex-direction: column;
    gap: var(--space-1, 0.2rem);
    width: 0.85rem;
    padding: var(--space-1, 0.2rem);
    border: 1px solid color-mix(in srgb, var(--accent-primary) 45%, transparent);
    border-radius: var(--radius-2, 0.2rem);
    background: color-mix(in srgb, var(--accent-primary) 10%, transparent);
}
.setting-art-block.is-wide { width: 1.95rem; }
.setting-art-block .setting-art-band {
    height: 3px;
    border-radius: var(--radius-1, 1px);
    background: color-mix(in srgb, var(--accent-primary) 55%, transparent);
}

/* Cards of themes, which is what replaced a list of their names. */
.setting-art-themes {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-1, 0.2rem);
}

.setting-art-theme {
    display: flex;
    align-items: flex-end;
    width: 0.95rem;
    height: 0.7rem;
    padding: var(--space-hairline, 0.12rem);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-1, 0.15rem);
}
.setting-art-theme-bar {
    width: 100%;
    height: 2px;
    border-radius: var(--radius-1, 1px);
}
/* Six plates that differ from one another rather than naming real themes: the
   argument is that you can see them, not which ones they are. */
.setting-art-theme.is-a { background: #15171c; }
.setting-art-theme.is-a .setting-art-theme-bar { background: #7aa2f7; }
.setting-art-theme.is-b { background: #faf4ed; }
.setting-art-theme.is-b .setting-art-theme-bar { background: #b4637a; }
.setting-art-theme.is-c { background: #1b1e28; }
.setting-art-theme.is-c .setting-art-theme-bar { background: #a6e3a1; }
.setting-art-theme.is-d { background: #fdf6e3; }
.setting-art-theme.is-d .setting-art-theme-bar { background: #cb4b16; }
.setting-art-theme.is-e { background: #101a28; }
.setting-art-theme.is-e .setting-art-theme-bar { background: #f5c2e7; }
.setting-art-theme.is-f { background: #eef2f7; }
.setting-art-theme.is-f .setting-art-theme-bar { background: #2f6f9f; }

/* ==== css/dashboard-multi-select.css ==== */
/* Multi-select on the bookmark grid.
 *
 * Deliberately built from the same tokens as .inbox-selection-bar: the two are
 * the same idea in two views, and a second visual language for "these rows are
 * ticked" would read as a different feature.
 */

.multi-select-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2, 0.5rem);
    width: 100%;
    margin-bottom: var(--space-3, 0.75rem);
    padding: var(--space-2, 0.45rem) var(--space-3, 0.65rem);
    border-radius: var(--layout-radius-sm, 8px);
    border: 1px solid color-mix(in srgb, var(--accent-primary) 40%, transparent);
    background: color-mix(in srgb, var(--accent-primary) 8%, var(--background-secondary));
    /* The grid scrolls under it; the toolbar acts on a selection that may sit
     * off-screen, so it has to stay reachable. */
    position: sticky;
    top: 0;
    z-index: 5;
}

.multi-select-count {
    margin-right: var(--space-1, 0.25rem);
    font-size: var(--text-sm, 0.8rem);
    font-weight: 600;
    color: var(--accent-primary);
    font-variant-numeric: tabular-nums;
}

.multi-select-btn {
    padding: var(--space-1, 0.3rem) var(--space-2, 0.6rem);
    font-size: var(--text-sm, 0.8rem);
    font-family: inherit;
    color: var(--text-primary);
    background: var(--background-primary);
    border: 1px solid color-mix(in srgb, var(--text-primary) 18%, transparent);
    border-radius: var(--layout-radius-sm, 6px);
    cursor: pointer;
}

.multi-select-btn:hover {
    border-color: color-mix(in srgb, var(--accent-primary) 55%, transparent);
    color: var(--accent-primary);
}

.multi-select-btn:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}

.multi-select-btn.danger:hover {
    border-color: color-mix(in srgb, var(--accent-error) 55%, transparent);
    color: var(--accent-error);
}

/* Ticked rows.
 *
 * An inset ring rather than a background swap, so it stacks with the keyboard
 * cursor and the status accent edge instead of competing with them: a row can
 * be selected, keyboard-focused and offline at the same time.
 */
.bookmark-link.is-multi-selected {
    box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--accent-primary) 60%, transparent);
    background: color-mix(in srgb, var(--accent-primary) 10%, transparent);
}

.bookmark-link.is-multi-selected.keyboard-selected {
    box-shadow: inset 0 0 0 2px var(--accent-primary);
}

body[data-layout-version="modern"] .multi-select-toolbar {
    border-radius: var(--layout-radius-md);
    box-shadow: var(--layout-shadow-sm);
}

body[data-layout-version="modern"] .bookmark-link.is-multi-selected {
    border-radius: var(--layout-radius-sm, 6px);
}

/* While a selection is open, a plain click clears it rather than opening the
 * link, so the pointer should not promise navigation. */
body.has-multi-selection .bookmark-link a.bookmark-open {
    cursor: default;
}

@media (prefers-reduced-motion: reduce) {
    .bookmark-link.is-multi-selected {
        transition: none;
    }
}

/* ==== css/view-explainers.css ==== */
/* ─── Explaining a view ──────────────────────────────────────────────────────

   The health and inbox views both explain themselves the same way: a sentence
   under the toolbar saying what the active filter selects, and an ℹ button that
   opens the longer "how this works" modal. Both were written for health first;
   this is that styling with the view prefix taken off, so the second view does
   not carry a near-verbatim copy that drifts from the first.

   View-specific stylesheets keep their own `.health-view-*` / `.inbox-*` aliases
   pointing at these rules, so existing markup and tests keep working.
   ────────────────────────────────────────────────────────────────────────── */

/* One sentence about the active filter, under the toolbar. Quiet enough to be
   skipped once it is known, present enough to be read the first time. */
.view-filter-note {
    margin: -0.2rem 0 var(--space-3, 0.7rem);
    padding: 0 var(--space-1-5, 0.35rem);
    font-size: var(--text-xs, 0.74rem);
    line-height: 1.5;
    color: var(--text-secondary);
    max-width: 70ch;
}

/* The ℹ in the toolbar. Pushed to the far end so it never sits between two
   actions and gets hit by accident. */
.view-help-btn {
    margin-left: auto;
    flex: 0 0 auto;
    width: 1.6rem;
    height: 1.6rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-4, 0.4rem);
    background: transparent;
    color: var(--text-secondary);
    font-size: var(--text-sm, 0.78rem);
    line-height: 1;
    cursor: pointer;
}

.view-help-btn:hover,
.view-help-btn:focus-visible {
    border-color: var(--accent-primary);
    color: var(--text-primary);
}

/* The explainer modal.

   Without a max-height of its own the panel grows to whatever its sections need
   — health's six were measured at 786px in an 800px viewport, so it touched the
   bottom edge and left 14px at the top. Capping it and letting the body scroll
   keeps a clear margin above and below at any window height, and the caps are
   expressed against both vh and a fixed inset so a short window still gets its
   gap. */
.modal.view-explain-modal {
    max-height: min(78vh, calc(100vh - 6rem));
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

.modal.view-explain-modal .modal-body {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
}

/* Config renders its own overlay above this one, where the viewport is already
   framed by the config chrome; the tighter cap keeps the same visual margin. */
body:has(#config-main) #app-modal.modal-overlay:has(.view-explain-modal) .modal {
    max-height: min(70vh, calc(100vh - 4rem));
}

/* Tighter than the 0.9rem gap the check-mode explainer uses: six sections at
   that spacing is what made health's too tall to fit. */
.view-explain-row + .view-explain-row {
    margin-top: var(--space-3, 0.65rem);
}

.view-explain-row h4 {
    margin: 0 0 var(--space-hairline, 0.1rem);
    font-size: var(--text-base, 0.88rem);
    color: var(--text-primary);
}

.view-explain-row p {
    margin: 0;
    font-size: var(--text-sm, 0.82rem);
    color: var(--text-secondary);
    line-height: 1.45;
}

/* ==== css/health-tutorial.css ==== */
/**
 * The one-time Health tutorial modal.
 *
 * Reuses the app's modal chrome (title, confirm/cancel buttons) entirely —
 * only the body content between them is custom, sized narrow like the
 * whats-new modal (a single reading column, not a wide two-pane surface).
 */
body:has(#dashboard-layout) #app-modal.modal-overlay:has(.health-tutorial-modal) .modal {
    transform: translateY(10px) scale(0.98);
    transition: transform 0.15s ease-in, opacity 0.15s ease-in;
}

body:has(#dashboard-layout) #app-modal.modal-overlay.show:has(.health-tutorial-modal) .modal {
    transform: translateY(0) scale(1);
    transition: transform 0.38s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.22s ease-out;
}

body.no-animations:has(#dashboard-layout) #app-modal.modal-overlay:has(.health-tutorial-modal) .modal {
    transform: none !important;
    transition: opacity 0.15s ease !important;
}

.health-tutorial-progress {
    font-size: var(--text-xs, 0.72rem);
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--text-tertiary);
    margin: 0 0 var(--space-1-5, 0.4rem);
}

.health-tutorial-step-title {
    margin: 0 0 var(--space-3, 0.7rem);
    font-size: var(--text-lg, 1.1rem);
    color: var(--text-primary);
}

.health-tutorial-step-body p {
    margin: 0 0 var(--space-3, 0.7rem);
    color: var(--text-secondary);
    line-height: 1.55;
}

.health-tutorial-step-body p:last-child {
    margin-bottom: 0;
}

.health-tutorial-closing {
    color: var(--text-primary) !important;
    font-style: italic;
}

/* The small "here is the actual control" preview above each step's prose. */
.health-tutorial-visual {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1-5, 0.4rem);
    padding: var(--space-2, 0.6rem) var(--space-3, 0.75rem);
    margin: 0 0 var(--space-4, 0.9rem);
    border: 1px dashed var(--border-secondary);
    border-radius: var(--radius-5, 0.5rem);
    background: var(--background-secondary);
}

.health-tutorial-visual-url {
    font-family: var(--font-mono, monospace);
    font-size: var(--text-sm, 0.78rem);
    color: var(--text-secondary);
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.health-tutorial-visual-modes {
    display: inline-flex;
    gap: var(--space-1, 0.3rem);
    flex: 0 0 auto;
}

.health-tutorial-visual code {
    font-family: var(--font-mono, monospace);
    font-size: var(--text-sm, 0.8rem);
    padding: var(--space-hairline, 0.15rem) var(--space-2, 0.45rem);
    border-radius: var(--radius-4, 0.35rem);
    background: var(--background-primary);
    border: 1px solid var(--border-secondary);
    color: var(--text-primary);
}

.health-tutorial-visual-hint {
    font-size: var(--text-xs, 0.72rem);
    color: var(--text-tertiary);
}

.health-tutorial-visual--window {
    justify-content: space-between;
}

.health-tutorial-visual--services {
    gap: var(--space-1-5, 0.35rem);
}

.health-tutorial-visual-chip {
    font-size: var(--text-xs, 0.72rem);
    padding: var(--space-1, 0.2rem) var(--space-2, 0.55rem);
    border-radius: var(--radius-pill, 999px);
    border: 1px solid var(--border-secondary);
    color: var(--text-secondary);
    background: var(--background-primary);
}

/* Mirrors .health-drift-badge / .health-cert-badge exactly, so a reader who
   later sees the real badge on a row recognises it. */
.health-tutorial-badge {
    display: inline-flex;
    align-items: center;
    padding: var(--space-hairline, 0.05rem) var(--space-1-5, 0.4rem);
    border: 1px solid currentColor;
    border-radius: var(--radius-pill, 999px);
    font-size: var(--text-xs, 0.7rem);
    white-space: nowrap;
    opacity: 0.9;
    color: var(--accent-warning, #b45309);
}

.health-tutorial-badge--moved,
.health-tutorial-badge--retitled,
.health-tutorial-badge--changed {
    color: var(--accent-warning, #b45309);
}

.health-tutorial-dots {
    display: flex;
    justify-content: center;
    gap: var(--space-1-5, 0.4rem);
    margin-top: 1.2rem;
}

.health-tutorial-dot {
    width: 0.4rem;
    height: 0.4rem;
    border-radius: var(--radius-pill, 999px);
    background: var(--border-secondary);
    transition: background-color 0.15s ease, transform 0.15s ease;
}

.health-tutorial-dot.is-active {
    background: var(--accent-primary);
    transform: scale(1.3);
}

@media (prefers-reduced-motion: reduce) {
    body:has(#dashboard-layout) #app-modal.modal-overlay:has(.health-tutorial-modal) .modal,
    body:has(#dashboard-layout) #app-modal.modal-overlay.show:has(.health-tutorial-modal) .modal {
        transform: none !important;
        transition: opacity 0.15s ease !important;
    }

    .health-tutorial-dot {
        transition: none;
    }
}

/* ==== css/health-focus.css ==== */
/*
 * Focus mode: one health issue at a time, over the list it came from.
 *
 * Styled as an overlay rather than a separate page on purpose — the list stays
 * visible behind it, so working through a queue never feels like leaving the
 * view. The card is deliberately narrow: the whole point is that one decision
 * is on screen and nothing else is competing for the click.
 */

.health-focus-overlay {
    position: fixed;
    inset: 0;
    z-index: 1200;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-5, 1.5rem);
    background: var(--background-modal, rgba(0, 0, 0, 0.6));
    animation: health-focus-fade 0.14s ease-out;
}

.health-focus-card {
    width: min(34rem, 100%);
    max-height: 85vh;
    overflow-y: auto;
    padding: var(--space-5, 1.5rem);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-7, 0.75rem);
    background: var(--background-secondary);
    color: var(--text-primary);
    box-shadow: 0 18px 48px rgba(0, 0, 0, 0.35);
    animation: health-focus-rise 0.16s ease-out;
}

/* While an action is in flight the card stays put and stops taking clicks,
   rather than swapping in a spinner that would move everything underneath the
   pointer mid-decision. */
.health-focus-overlay.is-busy .health-focus-card {
    opacity: 0.6;
    pointer-events: none;
}

.health-focus-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4, 1rem);
    margin-bottom: var(--space-3, 0.75rem);
}

.health-focus-progress {
    font-size: var(--text-sm, 0.78rem);
    color: var(--text-secondary);
    font-variant-numeric: tabular-nums;
}

.health-focus-close {
    border: none;
    background: transparent;
    color: var(--text-secondary);
    font-size: 1.4rem;
    line-height: 1;
    padding: 0 var(--space-1, 0.25rem);
    cursor: pointer;
    border-radius: var(--radius-3, 0.3rem);
}

.health-focus-close:hover {
    color: var(--text-primary);
    background: color-mix(in srgb, var(--text-primary) 8%, transparent);
}

/* The favicon rides beside the name, the same pairing the list row uses, so a
   card is recognisable before it is read. */
.health-focus-identity {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2, 0.6rem);
    margin-bottom: var(--space-2, 0.5rem);
}

.health-focus-identity-text {
    min-width: 0;
    flex: 1;
}

.health-focus-icon {
    flex: 0 0 auto;
    width: 1.75rem;
    height: 1.75rem;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--text-md, 1rem);
    line-height: 1;
}

.health-focus-icon-img {
    width: 1.5rem;
    height: 1.5rem;
    object-fit: contain;
    border-radius: var(--radius-3, 0.25rem);
}

.health-focus-title {
    margin: 0 0 var(--space-1, 0.25rem);
    font-size: var(--text-lg, 1.15rem);
    line-height: 1.3;
    /* Long bookmark names break rather than widening the card past the
       measure the rest of the layout is built on. */
    overflow-wrap: anywhere;
}

.health-focus-url {
    margin: 0;
    font-size: var(--text-sm, 0.82rem);
    color: var(--text-secondary);
    overflow-wrap: anywhere;
}

/* The line the Open button changes. Never-opened is called out in the warning
   colour because it is one of the findings, not a neutral timestamp. */
.health-focus-opened {
    margin: 0 0 var(--space-3, 0.75rem);
    font-size: var(--text-sm, 0.78rem);
    color: var(--text-secondary);
    font-variant-numeric: tabular-nums;
}

.health-focus-opened.is-never {
    color: var(--accent-warning, var(--text-secondary));
}

.health-focus-badges {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1-5, 0.35rem);
    margin-bottom: var(--space-3, 0.75rem);
}

/* Collapses to nothing when the row has no badges, so the gap above the
   reasons does not depend on whether any were rendered. */
.health-focus-badges:empty {
    display: none;
    margin-bottom: 0;
}

/* What the page says about itself. Bounded on purpose: the preview informs the
   decision, it is not the decision, so it never grows tall enough to push the
   buttons off the card. */
.health-focus-preview {
    margin: 0 0 var(--space-4, 0.9rem);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-5, 0.5rem);
    background: var(--background-primary);
    overflow: hidden;
}

.health-focus-preview-toggle {
    display: flex;
    align-items: center;
    gap: var(--space-1-5, 0.35rem);
    width: 100%;
    padding: var(--space-1-5, 0.4rem) var(--space-2, 0.6rem);
    border: none;
    background: transparent;
    color: var(--text-secondary);
    font-family: inherit;
    font-size: var(--text-sm, 0.78rem);
    text-align: left;
    cursor: pointer;
}

.health-focus-preview-toggle:hover {
    color: var(--text-primary);
}

.health-focus-preview-caret {
    font-size: var(--text-xs, 0.7rem);
    opacity: 0.7;
}

/* Collapsed is the toggle and nothing else, so folding the preview away does
   not change where the buttons sit by more than the one row it removes. */
.health-focus-preview.is-collapsed {
    background: transparent;
    border-color: transparent;
}

.health-focus-preview.is-collapsed .health-focus-preview-toggle {
    padding-left: 0;
}

.health-focus-preview-body {
    padding: 0 var(--space-2, 0.6rem) var(--space-2, 0.6rem);
}

.health-focus-preview-image {
    margin-bottom: var(--space-2, 0.5rem);
    border-radius: var(--radius-4, 0.35rem);
    overflow: hidden;
    background: color-mix(in srgb, var(--text-primary) 5%, transparent);
}

.health-focus-preview-image img {
    display: block;
    width: 100%;
    max-height: 9rem;
    object-fit: cover;
}

.health-focus-preview-title {
    margin: 0 0 var(--space-1, 0.2rem);
    font-size: var(--text-base, 0.86rem);
    color: var(--text-primary);
    overflow-wrap: anywhere;
}

.health-focus-preview-desc {
    margin: 0;
    font-size: var(--text-sm, 0.8rem);
    line-height: 1.45;
    color: var(--text-secondary);
    /* Three lines is enough to tell what a page is about; more turns the card
       into a reading surface and buries the decision. */
    display: -webkit-box;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.health-focus-preview-empty {
    margin: 0;
    font-size: var(--text-sm, 0.78rem);
    color: var(--text-tertiary, var(--text-secondary));
}

.health-focus-preview.is-loading .health-focus-preview-body {
    opacity: 0.55;
}

.health-focus-reasons {
    margin: 0 0 var(--space-4, 1.1rem);
    padding-left: var(--space-4, 1.1rem);
    font-size: var(--text-base, 0.86rem);
    color: var(--text-secondary);
    line-height: 1.5;
}

.health-focus-reasons li + li {
    margin-top: var(--space-1, 0.2rem);
}

/* A reason answered on this card. Struck rather than removed: the score and the
   list behind still count it until the next report, and deleting the line would
   make the card disagree with both. */
.health-focus-reasons li.is-resolved {
    text-decoration: line-through;
    opacity: 0.6;
}

.health-focus-reason-done {
    text-decoration: none;
    font-size: var(--text-xs, 0.74rem);
    color: var(--accent-success, var(--text-secondary));
}

.health-focus-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2, 0.5rem);
    margin-bottom: var(--space-4, 0.9rem);
}

/* The key hint rides inside its button, the same pairing the row actions use,
   so the shortcut is learned from the control rather than from a legend. */
.health-focus-actions kbd {
    margin-left: var(--space-1-5, 0.4rem);
    padding: var(--space-hairline, 0.05rem) var(--space-1, 0.3rem);
    border: 1px solid var(--border-secondary, var(--border-primary));
    border-radius: var(--radius-3, 0.25rem);
    background: color-mix(in srgb, var(--text-primary) 6%, transparent);
    font-family: inherit;
    font-size: var(--text-2xs, 0.68rem);
    opacity: 0.8;
}

.health-focus-legend {
    margin: 0;
    font-size: var(--text-xs, 0.74rem);
    color: var(--text-tertiary, var(--text-secondary));
}

@keyframes health-focus-fade {
    from { opacity: 0; }
    to { opacity: 1; }
}

@keyframes health-focus-rise {
    from { opacity: 0; transform: translateY(8px) scale(0.99); }
    to { opacity: 1; transform: translateY(0) scale(1); }
}

@media (prefers-reduced-motion: reduce) {
    .health-focus-overlay,
    .health-focus-card {
        animation: none;
    }
}

/* On a phone the card is the screen: full width, and the actions stack so no
   button ends up too narrow to hit. */
@media (max-width: 30rem) {
    .health-focus-overlay {
        padding: var(--space-3, 0.75rem);
    }

    .health-focus-card {
        width: 100%;
        padding: var(--space-4, 1.1rem);
    }

    .health-focus-actions .config-btn {
        flex: 1 1 100%;
    }
}

/* The toolbar entry point. Sized and coloured like the other toolbar buttons;
   only the key hint is extra. */
.health-view-focus-btn {
    display: inline-flex;
    align-items: center;
    gap: var(--space-hairline, 0.1rem);
    padding: var(--space-1, 0.3rem) var(--space-2, 0.6rem);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-4, 0.4rem);
    background: var(--background-primary);
    color: var(--text-secondary);
    font-family: inherit;
    font-size: var(--text-sm, 0.78rem);
    cursor: pointer;
}

.health-view-focus-btn:hover {
    color: var(--text-primary);
    border-color: var(--border-secondary, var(--border-primary));
}

.health-view-focus-btn kbd {
    margin-left: var(--space-1, 0.3rem);
    padding: var(--space-hairline, 0.02rem) var(--space-1, 0.28rem);
    border: 1px solid var(--border-secondary, var(--border-primary));
    border-radius: var(--radius-3, 0.25rem);
    font-family: inherit;
    font-size: var(--text-2xs, 0.66rem);
    opacity: 0.75;
}

/* The end of a review session. Same card, no row on it — what is left to show
   is a count and a decision about tomorrow. */
.health-focus-card--done {
    text-align: center;
}

.health-focus-done-count {
    margin: var(--space-3, 0.75rem) 0 var(--space-1, 0.25rem);
    font-size: var(--text-md, 1.05rem);
    color: var(--text-primary);
}

.health-focus-done-rest {
    margin: 0 0 1.25rem;
    font-size: var(--text-sm, 0.85rem);
    color: var(--text-secondary);
}

.health-focus-card--done .health-focus-actions {
    justify-content: center;
}

.health-focus-done-primary {
    border-color: var(--accent-primary);
    color: var(--accent-primary);
}

/* ==== css/config-setting-promo.css ==== */
/* One-time config setting highlight popovers (see config-setting-promo.js) */

.config-setting-promo-anchor-highlight {
    position: relative;
    z-index: 1;
    border-radius: var(--radius-6, 10px);
    outline: 2px solid color-mix(in srgb, var(--accent-primary, #4a90d9) 55%, transparent);
    outline-offset: 4px;
    animation: config-setting-promo-anchor-pulse 2.4s ease-in-out infinite;
}

@keyframes config-setting-promo-anchor-pulse {
    0%, 100% { outline-color: color-mix(in srgb, var(--accent-primary, #4a90d9) 40%, transparent); }
    50% { outline-color: color-mix(in srgb, var(--accent-primary, #4a90d9) 78%, transparent); }
}

.config-setting-promo {
    position: fixed;
    z-index: 1200;
    max-width: min(300px, calc(100vw - 24px));
    pointer-events: auto;
    opacity: 0;
    transform: translateY(8px) scale(0.98);
    transition: opacity 0.22s ease, transform 0.22s ease;
}

.config-setting-promo.is-visible {
    opacity: 1;
    transform: translateY(0) scale(1);
}

@keyframes config-setting-promo-float {
    0%, 100% { transform: translateY(0) scale(1); }
    50% { transform: translateY(-3px) scale(1); }
}

.config-setting-promo-inner {
    position: relative;
    background: var(--background-secondary, var(--card-bg, #1e1e1e));
    border: 1px solid var(--border-primary, var(--border-color, rgba(128, 128, 128, 0.35)));
    border-radius: var(--radius-7, 0.75rem);
    padding: var(--space-3, 0.85rem) var(--space-4, 0.95rem) var(--space-4, 0.9rem);
    box-shadow:
        0 10px 28px rgba(0, 0, 0, 0.22),
        0 0 0 1px color-mix(in srgb, var(--accent-primary, #4a90d9) 18%, transparent);
}

.config-setting-promo-badge {
    display: inline-block;
    margin-bottom: var(--space-1-5, 0.35rem);
    padding: var(--space-hairline, 0.12rem) var(--space-2, 0.45rem);
    border-radius: var(--radius-pill, 999px);
    font-size: var(--text-2xs, 0.68rem);
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--accent-primary, #4a90d9);
    background: color-mix(in srgb, var(--accent-primary, #4a90d9) 16%, transparent);
}

.config-setting-promo-title {
    margin: 0 0 var(--space-1-5, 0.35rem);
    font-size: calc(var(--font-size-text, 14px) * 1.05);
    font-weight: 600;
    color: var(--text-primary, inherit);
    line-height: 1.3;
}

.config-setting-promo-body {
    margin: 0 0 var(--space-3, 0.75rem);
    font-size: calc(var(--font-size-text, 14px) * 0.94);
    color: var(--text-secondary, var(--text-muted, #aaa));
    line-height: 1.45;
}

.config-setting-promo-dismiss {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-1-5, 0.35rem) var(--space-3, 0.75rem);
    border: none;
    border-radius: var(--radius-5, 0.45rem);
    font: inherit;
    font-size: calc(var(--font-size-text, 14px) * 0.94);
    font-weight: 600;
    cursor: pointer;
    color: var(--background-primary, #fff);
    background: var(--accent-primary, #4a90d9);
    transition: filter 0.12s ease, transform 0.12s ease;
}

.config-setting-promo-dismiss:hover {
    filter: brightness(1.08);
}

.config-setting-promo-dismiss:focus-visible {
    outline: 2px solid var(--accent-primary, #4a90d9);
    outline-offset: 2px;
}

.config-setting-promo-arrow {
    position: absolute;
    width: 10px;
    height: 10px;
    background: var(--background-secondary, var(--card-bg, #1e1e1e));
    border: 1px solid var(--border-primary, var(--border-color, rgba(128, 128, 128, 0.35)));
    transform: rotate(45deg);
    box-shadow: -2px 2px 4px rgba(0, 0, 0, 0.08);
}

/* Below the control (default) */
.config-setting-promo.is-below .config-setting-promo-arrow {
    top: -5px;
    left: var(--promo-arrow-left, 50%);
    margin-left: -5px;
    border-right: none;
    border-bottom: none;
}

/* Above the control when there is no room below */
.config-setting-promo.is-above .config-setting-promo-arrow {
    bottom: -5px;
    left: var(--promo-arrow-left, 50%);
    margin-left: -5px;
    border-left: none;
    border-top: none;
}

/* Beside the control (optional placement) */
.config-setting-promo.is-right .config-setting-promo-arrow,
.config-setting-promo.is-left .config-setting-promo-arrow {
    top: 50%;
    margin-top: -5px;
    margin-left: 0;
}

.config-setting-promo.is-right .config-setting-promo-arrow {
    left: -5px;
    border-right: none;
    border-top: none;
}

.config-setting-promo.is-left .config-setting-promo-arrow {
    right: -5px;
    border-left: none;
    border-bottom: none;
}

@media (max-width: 720px) {
    .config-setting-promo {
        display: none !important;
    }

    .config-setting-promo-anchor-highlight {
        animation: none;
        outline-width: 1px;
    }
}

@media (prefers-reduced-motion: reduce) {
    .config-setting-promo,
    .config-setting-promo.is-visible,
    .config-setting-promo-anchor-highlight {
        animation: none !important;
        transition: none !important;
    }

    .config-setting-promo {
        opacity: 1;
        transform: none;
    }
}

body.no-animations .config-setting-promo,
body.no-animations .config-setting-promo.is-visible,
body.no-animations .config-setting-promo-anchor-highlight {
    animation: none !important;
    transition: none !important;
}

body.no-animations .config-setting-promo {
    opacity: 1;
    transform: none;
}

/* ==== css/colors.css ==== */
/* Colors editor — embedded in Config (#colors tab) */

.theme-colors-editor {
    display: flex;
    flex-direction: column;
    gap: var(--config-tab-gap, 0.85rem);
    width: 100%;
    max-width: 100%;
    box-sizing: border-box;
}

.theme-colors-editor--readonly .theme-colors-actions button:not(.colors-readonly-allowed),
.theme-colors-editor--readonly #add-custom-theme-btn,
.theme-colors-editor--readonly .custom-theme-move-btn,
.theme-colors-editor--readonly .custom-theme-list-item .btn-danger,
.theme-colors-editor--readonly .custom-theme-list-item input {
    opacity: 0.55;
    pointer-events: none;
}

.colors-mobile-readonly-banner {
    margin: 0;
    padding: var(--space-3, 0.65rem) var(--space-3, 0.85rem);
    border: 1px solid var(--border-primary);
    background: var(--background-secondary);
    color: var(--text-secondary);
    font-size: var(--font-size-text);
    line-height: 1.45;
}

.colors-contrast-hint {
    margin: 0;
    padding: var(--space-2, 0.5rem) var(--space-3, 0.75rem);
    border-left: 3px solid var(--accent-warning);
    background: color-mix(in srgb, var(--accent-warning) 12%, transparent);
    color: var(--text-secondary);
    font-size: var(--font-size-text);
}

.color-text-input.color-input-invalid,
.color-text-input-full.color-input-invalid {
    border-color: var(--accent-error);
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent-error) 35%, transparent);
}

.colors-palette-live-preview {
    margin-bottom: 1.25rem;
    position: static;
    top: auto;
}

.custom-theme-list-item {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2, 0.5rem);
}

.custom-theme-reorder-btns {
    display: flex;
    flex-direction: column;
    gap: var(--space-hairline, 0.15rem);
}

.custom-theme-move-btn {
    min-width: 2rem;
    padding: var(--space-hairline, 0.15rem) var(--space-1-5, 0.35rem);
    line-height: 1;
}

.theme-colors-intro .simple-tab-intro {
    display: block;
    line-height: 1.5;
}

.theme-colors-intro .help-text,
.theme-colors-intro p {
    margin: 0 0 var(--space-2, 0.5rem);
    color: var(--text-secondary);
    font-size: var(--font-size-text);
    line-height: 1.5;
}

.theme-colors-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3, 0.75rem) var(--space-4, 1rem);
    padding-bottom: var(--space-2, 0.5rem);
    border-bottom: 1px solid var(--border-primary);
}

.theme-colors-toolbar.config-tab-toolbar--in-surface {
    border: none;
    border-radius: 0;
    box-shadow: none;
    margin: 0;
    width: 100%;
    max-width: 100%;
    padding-bottom: var(--config-toolbar-padding-block, 0.5rem);
    background: var(--config-surface-toolbar, color-mix(in srgb, var(--background-primary) 35%, transparent));
}

/* Theme tab — fused surface (B10/C11) */
.theme-colors-tab-surface .theme-colors-body {
    display: flex;
    flex-direction: column;
    gap: 0;
    width: 100%;
    max-width: 100%;
    box-sizing: border-box;
}

.theme-colors-tab-surface .colors-tab-panel {
    padding: 0;
    border: none;
    border-radius: 0;
    box-shadow: none;
    background: transparent;
}

.theme-colors-tab-surface .colors-tab-panel .config-section {
    padding: var(--space-4, 0.9rem) var(--space-4, 1rem);
    border: none;
}

.theme-colors-tab-surface .colors-panel-hint,
.theme-colors-tab-surface .colors-tab-panel > .config-section > .help-text {
    margin: 0 0 var(--space-3, 0.75rem);
}

.theme-colors-tab-surface .page-selector-wrapper {
    margin: 0 0 var(--space-3, 0.75rem);
    padding: 0;
    border: none;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
}

.theme-colors-tab-surface .colors-grid {
    display: flex;
    flex-direction: column;
    gap: 0;
    margin-top: var(--space-3, 0.75rem);
    border: 1px solid var(--border-primary);
    background: var(--config-surface-list, transparent);
}

.theme-colors-tab-surface .color-group {
    padding: var(--space-3, 0.75rem) var(--space-4, 1rem);
    border-bottom: 1px solid var(--border-primary);
}

.theme-colors-tab-surface .color-group:last-child {
    border-bottom: none;
}

.theme-colors-tab-surface .color-group h3 {
    margin: 0 0 var(--space-2, 0.5rem);
}

.theme-colors-tab-surface .color-item {
    margin-bottom: 0;
    padding: var(--space-2, 0.45rem) 0;
    min-height: 2.25rem;
    box-sizing: border-box;
    border-bottom: 1px solid var(--border-primary);
    background: transparent;
    transition: background-color 0.12s ease;
}

.theme-colors-tab-surface .color-group .color-item:last-child {
    border-bottom: none;
}

.theme-colors-tab-surface .color-item:hover {
    background: var(--config-row-hover, color-mix(in srgb, var(--accent-primary) 8%, transparent));
}

.theme-colors-tab-surface #custom-themes-list.categories-list {
    border: 1px solid var(--border-primary);
    border-radius: 0;
    background: transparent;
    box-shadow: none;
}

.theme-colors-tab-surface .custom-themes-main-grid {
    gap: 0;
    margin-top: var(--space-4, 1rem);
    border-top: 1px solid var(--border-primary);
}

.theme-colors-tab-surface .custom-themes-list-section {
    padding: var(--space-4, 0.9rem) var(--space-4, 1rem) var(--space-4, 1rem) 0;
    border-right: 1px solid var(--border-primary);
}

.theme-colors-tab-surface .custom-theme-colors-section {
    border-left: none;
    padding: var(--space-4, 0.9rem) 0 var(--space-4, 1rem) var(--space-4, 1rem);
    margin-top: 0;
}

.theme-colors-tab-surface .theme-preview-card {
    border-radius: 0;
    box-shadow: none;
    border: 1px solid var(--border-primary);
    margin-bottom: var(--space-4, 1rem);
}

.theme-colors-tab-surface .theme-preview-label {
    background: var(--config-surface-toolbar, color-mix(in srgb, var(--background-primary) 25%, transparent));
}

@media (max-width: 1024px) {
    .theme-colors-tab-surface .custom-themes-main-grid {
        border-top: 1px solid var(--border-primary);
    }

    .theme-colors-tab-surface .custom-themes-list-section {
        padding: var(--space-4, 0.9rem) var(--space-4, 1rem) 0;
        border-right: none;
        border-bottom: 1px solid var(--border-primary);
    }

    .theme-colors-tab-surface .custom-theme-colors-section {
        padding: var(--space-4, 0.9rem) var(--space-4, 1rem) var(--space-4, 1rem);
    }
}

.theme-colors-toolbar {
    justify-content: space-between;
}

.theme-colors-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-2, 0.5rem);
    margin-left: auto;
}

.theme-colors-info-btn {
    flex-shrink: 0;
    align-self: center;
}

.colors-subtabs-header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2, 0.5rem);
}

.theme-colors-section-heading {
    display: flex;
    align-items: center;
    gap: var(--space-1-5, 0.35rem);
    margin-bottom: var(--space-4, 1rem);
    font-size: var(--font-size-text);
    font-weight: var(--font-weight-bold);
    letter-spacing: 0.05em;
    color: var(--text-tertiary);
    text-transform: lowercase;
}

.theme-colors-section-heading::before {
    content: '// ';
}

.colors-panel-hint {
    margin: 0 0 var(--space-4, 1rem);
}

/* colors unsaved badge: config-unsaved-state.css (.unsaved-badge) */

.colors-subtabs {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 0;
    padding: 0;
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-3, 4px);
    overflow: visible;
    background: transparent;
}

.colors-tab-button {
    appearance: none;
    border: none;
    border-radius: 0;
    background: transparent;
    color: var(--text-secondary);
    font: inherit;
    font-size: var(--text-sm, 0.85rem);
    font-weight: inherit;
    padding: var(--space-1-5, 0.35rem) var(--space-3, 0.75rem);
    cursor: pointer;
    text-transform: lowercase;
    transition: background 0.15s ease, color 0.15s ease;
    position: relative;
}

.colors-tab-button + .colors-tab-button {
    border-left: 1px solid var(--border-primary);
}

.colors-tab-button:hover {
    color: var(--text-primary);
    background: color-mix(in srgb, var(--accent-primary) 12%, transparent);
}

.colors-tab-button.active {
    color: var(--text-primary);
    background: color-mix(in srgb, var(--accent-primary) 22%, transparent);
    font-weight: var(--font-weight-bold, 600);
}

.colors-tab-button:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}

.colors-tab-panel[hidden] {
    display: none !important;
}

.colors-grid {
    display: flex;
    flex-direction: column;
    gap: var(--space-7, 3rem);
    margin-top: var(--space-4, 1rem);
}

.color-group {
    border: none;
    padding: 0;
}

.color-group h3 {
    margin-top: 0;
    margin-bottom: var(--space-4, 1rem);
    font-size: var(--font-size-text);
    font-weight: var(--font-weight-bold);
    letter-spacing: 0.05em;
    color: var(--text-tertiary);
    text-transform: lowercase;
    border: none;
    padding: 0;
}

.color-group h3::before {
    content: '// ';
}

.color-item {
    margin-bottom: var(--space-4, 1rem);
    display: flex;
    align-items: center;
    gap: var(--space-4, 1rem);
}

.color-item:last-child {
    margin-bottom: 0;
}

.color-item label {
    font-size: var(--font-size-text);
    color: var(--text-secondary);
    font-weight: var(--font-weight-semibold);
    min-width: var(--config-label-width, 10rem);
    margin: 0;
}

.color-input-wrapper {
    display: flex;
    gap: var(--space-2, 0.5rem);
    align-items: center;
    flex: 1;
}

.color-input-wrapper input[type="color"] {
    width: 40px;
    height: 28px;
    border: none;
    border-bottom: 1px solid var(--border-primary);
    border-radius: 0;
    cursor: pointer;
    background: transparent;
    padding: 0;
    transition: border-color 0.2s ease;
}

.color-input-wrapper input[type="color"]:hover {
    border-bottom-color: var(--text-secondary);
}

.color-input-wrapper input[type="color"]:focus {
    outline: none;
    border-bottom-color: var(--text-primary);
}

.color-text-input {
    flex: 1;
    min-width: 120px;
    padding: var(--space-1-5, 0.375rem) var(--space-2, 0.5rem);
    border: 1px solid var(--border-primary);
    background: var(--background-primary);
    color: var(--text-primary);
    font-family: inherit;
    font-size: var(--font-size-text);
    transition: border-color 0.3s ease;
}

.color-text-input-full {
    width: 100%;
    max-width: 300px;
    padding: var(--space-1-5, 0.375rem) var(--space-2, 0.5rem);
    border: 1px solid var(--border-primary);
    background: var(--background-primary);
    color: var(--text-primary);
    font-family: inherit;
    font-size: var(--font-size-text);
    transition: border-color 0.3s ease;
}

.color-text-input:focus,
.color-text-input-full:focus {
    outline: none;
    border-color: var(--text-secondary);
}

.color-text-input:hover,
.color-text-input-full:hover {
    border-color: var(--text-secondary);
}

/* Responsive adjustments for colors page */
@media (max-width: 768px) {
    .theme-colors-toolbar {
        flex-direction: column;
        align-items: stretch;
    }

    .theme-colors-actions,
    .colors-subtabs {
        width: 100%;
    }

    .colors-subtabs {
        overflow-x: auto;
        flex-wrap: nowrap;
        padding-bottom: var(--space-1, 0.25rem);
    }

    .color-item {
        flex-direction: column;
        align-items: flex-start;
        gap: var(--space-2, 0.5rem);
    }

    .color-item label {
        min-width: auto;
    }

    .color-input-wrapper {
        width: 100%;
    }

    .color-text-input {
        max-width: 100%;
    }

    .color-input-wrapper input[type="color"] {
        width: 36px;
        height: 24px;
    }
}

/* Custom Themes Section */
.custom-themes-main-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-7, 3rem);
    margin-top: var(--space-6, 2rem);
}

.custom-themes-list-section h3 {
    margin-bottom: var(--space-4, 1rem);
    font-size: var(--font-size-text);
    font-weight: var(--font-weight-bold);
    letter-spacing: 0.05em;
    color: var(--text-tertiary);
    text-transform: lowercase;
}

.custom-themes-list-section h3::before {
    content: '// ';
}

#custom-themes-list {
    margin-bottom: var(--space-4, 1rem);
}

.custom-themes-list-section .category-item {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: var(--space-3, 0.75rem);
    align-items: center;
}

.custom-themes-list-section .category-item input {
    width: 100%;
}

.custom-themes-list-section .btn-danger {
    min-width: 88px;
    padding: var(--space-1-5, 0.375rem) var(--space-2, 0.625rem);
}

#add-custom-theme-btn {
    margin-top: var(--space-1, 0.25rem);
}

.custom-theme-colors-section {
    border-left: 1px solid var(--border-primary);
    padding-left: var(--space-6, 2rem);
}

.custom-theme-colors-section .colors-grid {
    margin-top: 0;
}

/* Theme preview card */
.theme-preview-card {
    position: sticky;
    top: 6rem;
    margin-bottom: var(--space-6, 2rem);
    border: 1px solid var(--border-primary);
    background: var(--background-primary);
    font-size: var(--font-size-controls);
    user-select: none;
    pointer-events: none;
}

.theme-preview-label {
    padding: var(--space-1-5, 0.35rem) var(--space-3, 0.75rem);
    font-size: 0.72em;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--text-secondary);
    border-bottom: 1px solid var(--border-primary);
    background: var(--background-secondary);
}

.theme-preview-body {
    padding: var(--space-3, 0.75rem);
    display: flex;
    flex-direction: column;
    gap: var(--space-3, 0.75rem);
}

.theme-preview-category {
    border: 1px solid var(--border-primary);
    background: var(--background-secondary);
    padding: var(--space-2, 0.5rem) var(--space-3, 0.65rem);
}

.theme-preview-category-title {
    display: block;
    font-size: 0.78em;
    font-weight: var(--font-weight-bold);
    color: var(--text-tertiary);
    margin-bottom: var(--space-1-5, 0.4rem);
}

.theme-preview-bookmark {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: var(--space-1, 0.25rem) 0;
    border-bottom: 1px solid var(--border-primary);
}

.theme-preview-bookmark:last-child {
    border-bottom: none;
}

.theme-preview-bm-name {
    color: var(--text-primary);
    font-size: 0.9em;
}

.theme-preview-bm-shortcut {
    color: var(--text-tertiary);
    font-size: 0.75em;
    opacity: 0.65;
}

.theme-preview-controls {
    display: flex;
    gap: var(--space-1-5, 0.4rem);
    flex-wrap: wrap;
    align-items: center;
}

.theme-preview-input {
    flex: 1;
    min-width: 0;
    padding: var(--space-1, 0.25rem) var(--space-2, 0.5rem);
    border: 1px solid var(--border-primary);
    background: var(--background-primary);
    color: var(--text-secondary);
    font-family: inherit;
    font-size: 0.85em;
}

.theme-preview-btn {
    padding: var(--space-1, 0.25rem) var(--space-2, 0.6rem);
    border: 1px solid var(--border-primary);
    background: var(--background-secondary);
    color: var(--text-primary);
    font-family: inherit;
    font-size: 0.85em;
    cursor: default;
}

.theme-preview-btn-success {
    border-color: var(--accent-success);
    color: var(--accent-success);
}

.theme-preview-btn-danger {
    border-color: var(--accent-error);
    color: var(--accent-error);
}

.theme-preview-badges {
    display: flex;
    gap: var(--space-1-5, 0.4rem);
    flex-wrap: wrap;
}

.theme-preview-badge {
    padding: var(--space-hairline, 0.15rem) var(--space-2, 0.5rem);
    font-size: 0.78em;
    border: 1px solid currentColor;
}

.theme-preview-badge-success { color: var(--accent-success); }
.theme-preview-badge-warning { color: var(--accent-warning); }
.theme-preview-badge-error   { color: var(--accent-error); }

@media (max-width: 1024px) {
    .custom-themes-main-grid {
        grid-template-columns: 1fr;
        gap: var(--space-6, 2rem);
    }

    .custom-theme-colors-section {
        border-left: none;
        border-top: 1px solid var(--border-primary);
        padding-left: 0;
        padding-top: var(--space-5, 1.5rem);
    }

    .theme-preview-card {
        position: static;
    }
}
/* ==== css/enhanced-features.css ==== */
/* Enhanced Features CSS */

/* The bare `kbd` rule below is the one that actually applies: this file
   loads after dashboard-enhancements.css, which declares kbd too, so this
   copy wins on order. It is kept here rather than folded into that file to
   avoid changing how every keyboard hint renders. */
kbd {
    background: var(--background-primary);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-3, 4px);
    padding: 4px 8px;
    font-family: monospace;
    font-size: 12px;
    color: var(--text-primary);
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    white-space: nowrap;
}

/* ===== Empty States ===== */
.empty-state {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    padding: var(--space-7, 3rem) 0;
    text-align: left;
    color: var(--text-secondary);
    max-width: 40ch;
}

.empty-state-label {
    font-size: var(--font-size-category);
    font-weight: var(--font-weight-bold);
    color: var(--text-tertiary);
    margin-bottom: var(--space-3, 0.75rem);
    letter-spacing: 0.04em;
}

.empty-state-text {
    font-size: var(--font-size-text);
    font-weight: var(--font-weight-semibold);
    color: var(--text-primary);
    margin-bottom: var(--space-2, 0.5rem);
}

.empty-state-action {
    margin-top: var(--space-5, 1.5rem);
    display: flex;
    gap: var(--space-3, 0.75rem);
    flex-wrap: wrap;
}

.empty-state-actions {
    margin-top: 1.25rem;
    display: flex;
    flex-direction: column;
    gap: var(--space-1-5, 0.4rem);
}

.empty-state-hint {
    margin: var(--space-3, 0.85rem) 0 0;
    font-size: calc(var(--font-size-controls) * 0.92);
    color: var(--text-tertiary, var(--text-secondary));
    line-height: 1.45;
    max-width: 22rem;
}

.empty-state-action-btn--primary {
    border-color: color-mix(in srgb, var(--accent-primary, var(--text-primary)) 65%, var(--border-primary));
    background: color-mix(in srgb, var(--accent-primary, var(--text-primary)) 14%, var(--background-primary));
    font-weight: var(--font-weight-semibold);
}

.empty-state-action-btn--primary:hover {
    background: color-mix(in srgb, var(--accent-primary, var(--text-primary)) 22%, var(--background-primary));
}

.empty-state-action-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.5em;
    background: none;
    border: none;
    padding: var(--space-1, 0.2rem) 0;
    font-size: var(--font-size-controls);
    color: var(--text-secondary);
    cursor: pointer;
    text-align: left;
    transition: color 0.12s ease;
}

.empty-state-action-btn:hover {
    color: var(--text-primary);
}

.empty-state-action-btn kbd {
    font-family: var(--font-family-main);
    font-size: 0.9em;
    color: var(--text-primary);
    background: var(--background-secondary);
    border: 1px solid var(--border-primary);
    border-radius: var(--radius-2, 3px);
    padding: 0 0.3em;
    transition: background 0.12s ease;
}

.empty-state-action-btn:hover kbd {
    background: color-mix(in srgb, var(--accent-primary) 15%, var(--background-secondary));
    border-color: var(--accent-primary);
}

/* Secondary text links under the fresh empty state (add modal, config, import).
   Styled locally because the global .btn styles are config-only and don't load
   on the dashboard — bare <a> would otherwise render as raw browser links. */
.empty-state-links {
    margin-top: var(--space-5, 1.5rem);
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2, 0.55rem);
}

.empty-state-link {
    display: inline-flex;
    align-items: center;
    background: none;
    border: none;
    padding: 0;
    font-family: inherit;
    font-size: calc(var(--font-size-controls) * 0.92);
    color: var(--text-secondary);
    text-decoration: none;
    border-bottom: 1px dotted color-mix(in srgb, var(--text-secondary) 55%, transparent);
    cursor: pointer;
    transition: color 0.12s ease, border-color 0.12s ease;
}

.empty-state-link:hover {
    color: var(--text-primary);
    border-bottom-color: var(--accent-primary, var(--text-primary));
}

/* ===== Lazy Load Placeholders ===== */
.icon-placeholder {
    background: linear-gradient(90deg, var(--background-modal) 25%, var(--background-primary) 50%, var(--background-modal) 75%);
    background-size: 200% 100%;
    animation: loading 1.5s infinite;
    border-radius: var(--radius-3, 4px);
    width: 1.15em;
    height: 1.15em;
    display: inline-block;
}

@keyframes loading {
    0% { background-position: 200% 0; }
    100% { background-position: -200% 0; }
}

/* ==== css/mobile-experience.css ==== */
/* ── Mobile / compact layout overrides ── */

.mobile-experience-banner {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-2, 0.5rem) var(--space-3, 0.75rem);
    margin: 0 0 var(--space-3, 0.75rem);
    padding: var(--space-3, 0.65rem) var(--space-3, 0.85rem);
    border: 1px solid color-mix(in srgb, var(--accent-primary) 45%, var(--border-primary));
    background: color-mix(in srgb, var(--accent-primary) 8%, var(--background-primary));
    border-radius: var(--radius-4, 0.35rem);
    font-size: var(--font-size-controls);
    line-height: 1.45;
    color: var(--text-secondary);
}

.mobile-experience-banner[hidden] {
    display: none !important;
}

.mobile-experience-banner-text {
    margin: 0;
    flex: 1 1 14rem;
}

.mobile-experience-banner-text a {
    color: var(--accent-primary);
    font-weight: var(--font-weight-semibold);
}

.mobile-experience-banner-actions {
    display: flex;
    flex-shrink: 0;
    align-items: center;
    gap: var(--space-1-5, 0.35rem);
}

.mobile-experience-banner-dismiss {
    border: 1px solid var(--border-primary);
    background: var(--background-secondary);
    color: var(--text-secondary);
    width: 2rem;
    height: 2rem;
    border-radius: var(--radius-3, 0.25rem);
    cursor: pointer;
    font-size: var(--text-xl, 1.25rem);
    line-height: 1;
    padding: 0;
}

.mobile-experience-banner-dismiss:hover {
    color: var(--text-primary);
}

/* ── Dashboard: footer — search + add bookmark on mobile ── */
body[data-mobile-layout="true"] #finders-button,
body[data-mobile-layout="true"] #commands-button,
body[data-mobile-layout="true"] #recent-bookmarks-button,
body[data-mobile-layout="true"] #help-button,
body[data-mobile-layout="true"] #whats-new-btn,
body[data-mobile-layout="true"] .btn-group-secondary {
    display: none !important;
}

body[data-mobile-layout="true"] #quick-add-toolbar-btn {
    display: flex !important;
    order: -1;
    flex: 1 1 auto;
    min-height: 44px;
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
}

body[data-mobile-layout="true"] .button-container {
    justify-content: center;
    width: calc(100% - 2rem);
    flex-wrap: wrap;
    row-gap: var(--space-1-5, 0.35rem);
}

body[data-mobile-layout="true"] .btn-group-primary {
    justify-content: center;
    width: 100%;
    gap: var(--space-2, 0.5rem);
}

body[data-mobile-layout="true"] #search-button {
    flex: 1 1 auto;
    max-width: none;
    min-height: 44px;
}

/* Dashboard: single column on phone — ignore columnsPerRow / packed layout */
@media (max-width: 767px) {
    body[data-dashboard-stack-categories="true"] .dashboard-grid,
    .dashboard-grid.columns-2,
    .dashboard-grid.columns-3,
    .dashboard-grid.columns-4,
    .dashboard-grid.columns-5,
    .dashboard-grid.columns-6 {
        display: grid !important;
        grid-template-columns: 1fr !important;
    }

    body[data-dashboard-stack-categories="true"] .dashboard-grid.packed-columns,
    .dashboard-grid.packed-columns {
        display: flex !important;
        flex-direction: column !important;
        flex-wrap: nowrap !important;
    }

    body[data-dashboard-stack-categories="true"] .dashboard-grid.packed-columns .dashboard-column,
    .dashboard-grid.packed-columns .dashboard-column {
        flex: 1 1 100% !important;
        max-width: 100% !important;
        width: 100% !important;
    }
}

/* Search mode tabs visible on mobile (override search.css ≤479px hide) */
body[data-mobile-layout="true"] .search-mode-tabs {
    display: flex !important;
}

body[data-mobile-layout="true"] .search-mode-tab {
    min-height: 44px;
    flex: 1 1 0;
}

/* Header simplification */
body[data-mobile-layout="true"] .health-link,
body[data-mobile-layout="true"] #page-navigation,
body[data-mobile-layout="true"] #page-nav-inbox-host,
body[data-mobile-layout="true"] #dashboard-tag-cloud-wrap,
body[data-mobile-layout="true"] #status-loading-indicator {
    display: none !important;
}

/* Header: Pages + config only — touch-friendly targets */
body[data-mobile-layout="true"] .header-actions {
    gap: var(--space-2, 0.5rem);
    flex-shrink: 0;
}

body[data-mobile-layout="true"] #page-overview-header-btn,
body[data-mobile-layout="true"] .pages-link--icon .pages-link-anchor {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    min-width: 44px;
    padding: var(--space-2, 0.55rem);
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
}

body[data-mobile-layout="true"] #page-overview-header-btn:active,
body[data-mobile-layout="true"] .pages-link--icon .pages-link-anchor:active {
    background: color-mix(in srgb, var(--accent-primary) 12%, var(--background-secondary));
}

body[data-mobile-layout="true"] .config-link a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    min-width: 44px;
    /* Symmetric now the label is an icon; the wider side padding was there to
       sit around the word "config". */
    padding: var(--space-2, 0.55rem);
    border: 1px solid color-mix(in srgb, var(--text-secondary) 35%, transparent);
    border-radius: var(--header-chip-radius, var(--layout-radius-pill, 999px));
    background: color-mix(in srgb, var(--background-secondary) 55%, transparent);
    text-transform: capitalize;
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
}

/* The icon variant's negative pull is for sitting beside the health icon, which
   mobile hides — without this the chip would overlap the pages button. */
body[data-mobile-layout="true"] .config-link--icon {
    margin-left: 0;
}

body[data-mobile-layout="true"] .config-link a:active,
body[data-mobile-layout="true"] #page-overview-header-btn:active,
body[data-mobile-layout="true"] .pages-link--icon .pages-link-anchor:active {
    background: color-mix(in srgb, var(--accent-primary) 10%, transparent);
    color: var(--text-primary);
    border-color: var(--text-primary);
}

/* Link preview cards — no hover on touch */
body[data-mobile-hide-previews="true"] .link-preview-card,
body[data-mobile-hide-previews="true"] .bookmark-preview-popup {
    display: none !important;
}

/* ── Config: tabs & general panels (phone only — tablets keep full config) ── */
body[data-phone-layout="true"] .tab-button:not([data-tab="general"]):not([data-tab="help"]):not([data-tab="colors"]),
body[data-phone-layout="true"] .config-keyboard-chip {
    display: none !important;
}

body[data-phone-layout="true"] [data-general-panel][data-mobile-panel-hidden="true"],
body[data-phone-layout="true"] #general-layer-toolbar {
    display: none !important;
}

/* Config General — touch-friendly theme & language selects */
body[data-mobile-layout="true"] .form-group--theme-select .custom-select-trigger,
body[data-mobile-layout="true"] .form-group:has(#language-select) .custom-select-trigger {
    min-height: 44px;
    padding: var(--space-3, 0.65rem) var(--space-3, 0.85rem);
    touch-action: manipulation;
}

body[data-mobile-layout="true"] .custom-select-option {
    min-height: 44px;
    padding: var(--space-3, 0.65rem) var(--space-3, 0.85rem);
    display: flex;
    align-items: center;
}

body[data-mobile-layout="true"] .form-group--theme-select label,
body[data-mobile-layout="true"] .form-group:has(#language-select) label {
    font-weight: var(--font-weight-semibold);
}

body[data-mobile-layout="true"] .general-keyboard-link-row {
    display: none !important;
}

/* Search-flow hint — desktop discoverability only */
body[data-mobile-layout="true"] .search-flow-hint {
    display: none !important;
}

/* Toasts above the mobile search footer */
body[data-mobile-layout="true"] .app-notification {
    bottom: max(4.75rem, calc(env(safe-area-inset-bottom) + 3.75rem));
    max-width: min(420px, calc(100vw - 1.25rem));
}

/* ── Modals: mobile sheet ──
   Touch-only, mirroring isMobileLayout() in mobile-experience.js. Width alone
   would turn every narrow desktop window into a full-screen sheet. */
@media (hover: none) and (pointer: coarse) and (max-width: 991px) {
    .modal-overlay.show .modal,
    .modal-overlay.show .modal.modal--mobile-sheet {
        width: 100% !important;
        max-width: none !important;
        margin: 0;
        border-radius: 0;
        min-height: min(100dvh, 100vh);
        max-height: min(100dvh, 100vh);
    }

    .modal-overlay.show .modal .modal-body {
        max-height: calc(100dvh - 9rem);
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
    }

    .modal-overlay.show .modal .modal-actions {
        flex-direction: column;
        align-items: stretch;
        gap: var(--space-2, 0.5rem);
    }

    .modal-overlay.show .modal .modal-button {
        min-height: 44px;
        width: 100%;
        justify-content: center;
    }

    .shortcut-search .search-container {
        max-height: calc(100dvh - 2rem);
    }
}

/* ==== css/layout-modern.css ==== */
/* Modern layout overrides — scoped to body[data-layout-version="modern"].
   Classic layout (default) is unchanged; all rules live in dashboard.css.

   Tokens (--layout-*, --modern-*) come from layout-modern-tokens.css, which
   loads first. Do not redefine them here. */

/* ─── Shared focus ring ──────────────────────────────────────────────────── */

body[data-layout-version="modern"] .page-nav-btn:focus-visible,
body[data-layout-version="modern"] .page-overview-header-btn:focus-visible,
body[data-layout-version="modern"] .pages-link--icon .pages-link-anchor:focus-visible,
body[data-layout-version="modern"] .health-link a:focus-visible,
body[data-layout-version="modern"] .config-link a:focus-visible,
body[data-layout-version="modern"] .search-button:focus-visible,
body[data-layout-version="modern"] .tag-filter-indicator-chip:focus-visible,
body[data-layout-version="modern"] .tag-filter-indicator-clear:focus-visible,
body[data-layout-version="modern"] .page-overview-close:focus-visible,
body[data-layout-version="modern"] .page-overview-link:focus-visible,
body[data-layout-version="modern"] .page-overview-modal-link:focus-visible,
body[data-layout-version="modern"] .skip-to-content:focus-visible {
    outline: var(--layout-focus-ring);
    outline-offset: 2px;
}

/* ─── Skip link ──────────────────────────────────────────────────────────── */

body[data-layout-version="modern"] .skip-to-content {
    border-radius: var(--layout-radius-sm);
    background: var(--background-secondary);
    border-color: var(--border-primary);
    box-shadow: var(--layout-shadow-sm);
}

/* ─── Header shell ───────────────────────────────────────────────────────── */

body[data-layout-version="modern"] .dashboard-section.section-controls {
    padding-bottom: var(--space-4, 1.15rem);
    border-bottom: 1px solid color-mix(in srgb, var(--border-primary) 32%, transparent);
    overflow-x: clip;
    max-width: 100%;
}

body[data-layout-version="modern"] .header-top {
    gap: var(--layout-gap-header) 1.5rem;
    align-items: flex-start;
    min-width: 0;
    max-width: 100%;
}

body[data-layout-version="modern"] .header-top-primary {
    flex-shrink: 0;
}

/* Centre the row: the children are different heights (the page-tab strip is a
   container with its own padding, the rest are single controls), so without
   this they align to the top and the pills sit a few pixels above the strip. */
body[data-layout-version="modern"] .header-actions {
    gap: var(--header-control-gap, 0.5rem);
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    min-width: 0;
    flex: 1 1 auto;
    max-width: 100%;
}

/* Classic pulled health/config back by 1rem when they sat in the main header
   row gap. Destinations now share .header-destinations (0.5rem) instead. */
body[data-layout-version="modern"] .health-link--icon,
body[data-layout-version="modern"] .config-link--icon,
body[data-layout-version="modern"] .pages-link--icon {
    margin-left: 0;
}

body[data-layout-version="modern"] .header-destinations {
    gap: var(--header-control-gap, 0.5rem);
}

/* ─── Date & status ──────────────────────────────────────────────────────── */

body[data-layout-version="modern"] .date-time-line {
    display: inline-flex;
    align-items: center;
    width: fit-content;
    padding: var(--space-1, 0.2rem) var(--space-2, 0.55rem);
    margin: -0.2rem -0.55rem;
    border-radius: var(--layout-radius-sm);
    transition:
        opacity var(--layout-transition),
        background-color var(--layout-transition);
}

body[data-layout-version="modern"] .date-time-line:hover {
    opacity: 1;
    background: color-mix(in srgb, var(--background-secondary) 70%, transparent);
}

body[data-layout-version="modern"] .date-weather-line {
    color: color-mix(in srgb, var(--text-tertiary) 88%, var(--text-secondary));
}

body[data-layout-version="modern"] .dashboard-mini-status {
    padding: var(--space-hairline, 0.15rem) var(--space-2, 0.5rem);
    border-radius: var(--layout-radius-pill);
    background: color-mix(in srgb, var(--background-secondary) 55%, transparent);
    border: 1px solid color-mix(in srgb, var(--border-primary) 35%, transparent);
}

/* ─── Header links (health / config) ─────────────────────────────────────── */

body[data-layout-version="modern"] .health-link a,
body[data-layout-version="modern"] .config-link a,
body[data-layout-version="modern"] .pages-link--icon .pages-link-anchor,
body[data-layout-version="modern"] .header-destinations .page-nav-btn--inbox {
    display: inline-flex;
    align-items: center;
    padding: var(--space-1-5, 0.38rem) var(--space-3, 0.8rem);
    border-radius: var(--layout-radius-pill);
    border: 1px solid var(--layout-surface-border);
    background: var(--layout-surface);
    transition:
        color var(--layout-transition),
        border-color var(--layout-transition),
        background-color var(--layout-transition),
        box-shadow var(--layout-transition);
}

body[data-layout-version="modern"] .health-link a:hover,
body[data-layout-version="modern"] .config-link a:hover,
body[data-layout-version="modern"] .pages-link--icon .pages-link-anchor:hover,
body[data-layout-version="modern"] .header-destinations .page-nav-btn--inbox:hover {
    color: var(--text-primary);
    border-color: color-mix(in srgb, var(--accent-primary) 40%, var(--border-primary));
    background: color-mix(in srgb, var(--accent-primary) 10%, var(--background-secondary));
    box-shadow: var(--layout-shadow-sm);
}

body[data-layout-version="modern"] .health-link a {
    animation: none;
    text-shadow: none;
}

/* ─── Pages overview button ──────────────────────────────────────────────── */

body[data-layout-version="modern"] .pages-link--icon .pages-link-anchor,
body[data-layout-version="modern"] .page-overview-header-btn {
    padding: var(--space-1-5, 0.38rem) var(--space-3, 0.8rem);
    border-radius: var(--layout-radius-pill);
    border: 1px solid var(--layout-surface-border);
    background: var(--layout-surface);
    transition:
        color var(--layout-transition),
        border-color var(--layout-transition),
        background-color var(--layout-transition),
        box-shadow var(--layout-transition);
}

body[data-layout-version="modern"] .page-overview-header-btn:hover,
body[data-layout-version="modern"] .page-overview-header-btn:focus-visible,
body[data-layout-version="modern"] .pages-link--icon .pages-link-anchor:hover,
body[data-layout-version="modern"] .pages-link--icon .pages-link-anchor:focus-visible {
    border-color: color-mix(in srgb, var(--accent-primary) 40%, var(--border-primary));
    background: color-mix(in srgb, var(--accent-primary) 10%, var(--background-secondary));
    box-shadow: var(--layout-shadow-sm);
}

/* ─── Page tabs ──────────────────────────────────────────────────────────── */

/* The page-tab strip is its own pill; the pages overview icon sits beside it,
   then inbox · health · config share .header-destinations — all at .header-actions gap. */
body[data-layout-version="modern"] .page-navigation {
    gap: var(--space-1, 0.2rem);
    padding: var(--space-hairline, 0.18rem);
    border-radius: var(--layout-radius-pill);
    background: var(--layout-surface);
    border: 1px solid var(--layout-surface-border);
    min-width: 0;
    max-width: 100%;
    overflow-x: auto;
    flex-wrap: nowrap;
    scrollbar-width: thin;
}

body[data-layout-version="modern"] .page-nav-btn[data-view-tab] {
    background: var(--layout-surface);
    box-shadow: inset 0 0 0 1px var(--layout-surface-border);
}

body[data-layout-version="modern"] .page-nav-btn {
    border-bottom: none;
    border-radius: var(--layout-radius-pill);
    padding: var(--space-1, 0.22rem) var(--space-3, 0.7rem);
    transition:
        color var(--layout-transition),
        background-color var(--layout-transition),
        box-shadow var(--layout-transition);
}

body[data-layout-version="modern"] .page-nav-btn:hover {
    color: var(--text-primary);
    background: color-mix(in srgb, var(--background-primary) 55%, transparent);
}

body[data-layout-version="modern"] .page-nav-btn.active {
    background: color-mix(in srgb, var(--accent-primary) 16%, var(--background-secondary));
    color: var(--text-primary);
    font-weight: var(--font-weight-bold);
    box-shadow:
        inset 0 0 0 1px color-mix(in srgb, var(--accent-primary) 32%, transparent),
        var(--layout-shadow-sm);
}

/* Modern marks the current tab with a tinted pill rather than an underline; the
   health and config icons are the same kind of destination, so they follow suit
   here too. */
body[data-layout-version="modern"] .health-link--icon a.active,
body[data-layout-version="modern"] .config-link--icon a.active,
body[data-layout-version="modern"] .header-destinations .page-nav-btn--inbox.active {
    background: color-mix(in srgb, var(--accent-primary) 16%, var(--background-secondary));
    color: var(--text-primary);
    border-bottom-color: transparent;
    /* Modern is a fully-rounded pill (no underline), so restore the bottom radius
       the default layout squares off for its straight underline. */
    border-bottom-left-radius: var(--layout-radius-sm, 8px);
    border-bottom-right-radius: var(--layout-radius-sm, 8px);
    box-shadow:
        inset 0 0 0 1px color-mix(in srgb, var(--accent-primary) 32%, transparent),
        var(--layout-shadow-sm);
}

body[data-layout-version="modern"] .page-tab-popover {
    border-radius: var(--layout-radius-md);
    box-shadow: var(--layout-shadow-md);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
}

body[data-layout-version="modern"] .page-tab-popover-icon,
body[data-layout-version="modern"] .page-tab-popover-name {
    border-radius: var(--layout-radius-sm);
}

body[data-layout-version="modern"] .move-popover {
    border-radius: var(--layout-radius-md);
    border-color: var(--layout-surface-border, var(--border-primary));
    box-shadow: var(--layout-shadow-md);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
}

body[data-layout-version="modern"] .move-popover-item {
    border-radius: var(--layout-radius-sm);
}

body[data-layout-version="modern"] .move-popover-item.is-focused {
    background: color-mix(in srgb, var(--accent-primary) 14%, var(--background-primary));
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent-primary) 22%, transparent);
}

/* ─── Title section ──────────────────────────────────────────────────────── */

body[data-layout-version="modern"] .dashboard-section.section-title {
    padding-top: var(--space-1-5, 0.35rem);
}

body[data-layout-version="modern"] .title {
    opacity: 0.94;
    letter-spacing: -0.015em;
    line-height: 1.15;
}

/* ─── Tag filter chip (header) ───────────────────────────────────────────── */

body[data-layout-version="modern"] .tag-filter-indicator-chip {
    border-radius: var(--layout-radius-pill);
    box-shadow: var(--layout-shadow-sm);
}

body[data-layout-version="modern"] .tag-filter-indicator-clear {
    border-radius: var(--layout-radius-pill);
}

/* ─── Bottom toolbar (center dock) ───────────────────────────────────────── */

body[data-layout-version="modern"]:not([data-button-position="side-left"]) .button-container {
    width: fit-content;
    max-width: min(52rem, calc(100vw - 2.5rem));
    padding: var(--space-2, 0.55rem) var(--space-3, 0.8rem) var(--space-3, 0.65rem);
    border-radius: var(--layout-radius-lg);
    background: var(--layout-surface);
    border: 1px solid var(--layout-surface-border);
    box-shadow: var(--layout-shadow-md);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    gap: var(--space-2, 0.45rem);
}

/* Collapse button groups when every child is disabled via settings */
body[data-layout-version="modern"][data-show-add-bookmark-button="false"][data-show-search-button="false"][data-show-commands-button="false"][data-show-finders-button="false"] .btn-group-primary {
    display: none !important;
}

body[data-layout-version="modern"] .search-button {
    border-radius: var(--layout-radius-md);
    background: color-mix(in srgb, var(--background-primary) 68%, transparent);
    transition:
        background-color var(--layout-transition),
        border-color var(--layout-transition),
        color var(--layout-transition),
        transform var(--layout-transition),
        box-shadow var(--layout-transition);
}

body[data-layout-version="modern"] .search-button:hover {
    background: var(--layout-surface);
    border-color: var(--layout-surface-border);
    color: var(--text-primary);
    transform: translateY(-1px);
    box-shadow: var(--layout-shadow-sm);
}

body[data-layout-version="modern"] .search-button:active {
    transform: translateY(0);
    box-shadow: none;
}

body[data-layout-version="modern"] .search-button[data-tooltip]::after {
    border-radius: var(--layout-radius-sm);
    box-shadow: var(--layout-shadow-sm);
}

/* ─── Side dock button groups ────────────────────────────────────────────── */

body[data-layout-version="modern"][data-button-position="bottom-left"] .button-container,
body[data-layout-version="modern"][data-button-position="bottom-right"] .button-container {
    padding: 0;
    border: none;
    background: transparent;
    box-shadow: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

body[data-layout-version="modern"][data-button-position="bottom-left"] .btn-group,
body[data-layout-version="modern"][data-button-position="bottom-right"] .btn-group {
    width: fit-content;
    padding: var(--space-1-5, 0.4rem);
    border-radius: var(--layout-radius-md);
    background: var(--layout-surface);
    border: 1px solid var(--layout-surface-border);
    box-shadow: var(--layout-shadow-md);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
}

/* ─── Tag cloud toggle (corner when not in rail) ─────────────────────────── */

body[data-layout-version="modern"] .tag-cloud-toggle-btn.search-button {
    border-radius: var(--layout-radius-md);
    background: var(--layout-surface);
    box-shadow: var(--layout-shadow-md);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
}

/* ─── Side rail: rail only (no bottom dock / corner FAB duplicate) ─────────── */

body[data-layout-version="modern"][data-button-position="side-left"] .button-container {
    width: var(--rail-width, 44px);
    max-width: var(--rail-width, 44px);
    padding: 0;
    border-radius: 0;
    border-top: none;
    border-bottom: none;
    border-left: none;
    gap: 0;
}

body[data-layout-version="modern"][data-button-position="side-left"] .search-button {
    border-radius: 0;
    border: none;
    background: transparent;
    box-shadow: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

body[data-layout-version="modern"][data-button-position="side-left"] .search-button:hover {
    transform: translateX(2px);
    box-shadow: none;
}

body[data-layout-version="modern"][data-button-position="side-left"] .search-button:active {
    transform: translateX(0);
}

body[data-layout-version="modern"][data-button-position="side-left"] .tag-cloud-toggle-btn.search-button {
    border-radius: 0;
    background: transparent;
    box-shadow: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

/* ─── Date popover ─────────────────────────────────────────────────────────── */

body[data-layout-version="modern"] .date-popover {
    border-radius: var(--layout-radius-md);
    box-shadow: var(--layout-shadow-lg);
    border-color: var(--layout-surface-border, var(--border-primary));
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
}

body[data-layout-version="modern"] .date-popover-day {
    border-radius: var(--layout-radius-sm);
}

body[data-layout-version="modern"] .date-popover-day.is-today {
    background: color-mix(in srgb, var(--accent-primary) 18%, var(--background-primary));
    box-shadow: var(--layout-shadow-sm);
}

/* ─── Motion preferences ───────────────────────────────────────────────────── */

body[data-layout-version="modern"].no-animations .search-button:hover,
body[data-layout-version="modern"].no-animations .search-button:active,
body[data-layout-version="modern"].no-animations .tag-filter-indicator-chip:hover {
    transform: none !important;
}

/* ─── Responsive (modern only) ───────────────────────────────────────────── */

@media (max-width: 991px) {
    body[data-layout-version="modern"] .page-navigation {
        max-width: 100%;
        overflow-x: auto;
        flex-wrap: nowrap;
        scrollbar-width: thin;
        -webkit-overflow-scrolling: touch;
    }

    body[data-layout-version="modern"] .page-nav-btn {
        flex-shrink: 0;
    }
}

@media (max-width: 768px), (hover: none) and (pointer: coarse) {
    body[data-layout-version="modern"]:not([data-mobile-layout="true"]):not([data-button-position="side-left"]) .button-container {
        border-radius: var(--layout-radius-md);
        padding: var(--space-2, 0.5rem) var(--space-2, 0.55rem) var(--space-2, 0.58rem);
    }

    body[data-layout-version="modern"] .search-button-labeled {
        border-radius: var(--layout-radius-sm);
    }

    body[data-layout-version="modern"] .header-actions {
        width: 100%;
        justify-content: flex-start;
    }

    body[data-layout-version="modern"] .page-navigation {
        width: 100%;
    }
}

@media (max-width: 575px) {
    body[data-layout-version="modern"] .dashboard-section.section-controls {
        padding-bottom: var(--space-3, 0.85rem);
    }

    body[data-layout-version="modern"] .health-link a,
    body[data-layout-version="modern"] .config-link a,
    body[data-layout-version="modern"] .pages-link--icon .pages-link-anchor,
    body[data-layout-version="modern"] .header-destinations .page-nav-btn--inbox,
    body[data-layout-version="modern"] .page-overview-header-btn {
        padding: var(--space-1-5, 0.32rem) var(--space-2, 0.62rem);
    }
}

/* ═══════════════════════════════════════════════════════════════════════════
   Phase 2 — Bookmark grid (all presets, density, badges, preview cards)
   ═══════════════════════════════════════════════════════════════════════════ */

body[data-layout-version="modern"] .dashboard-grid {
    --modern-row-radius: var(--layout-radius-sm);
    --modern-category-radius: var(--layout-radius-md);
}

/* ─── Category headers ───────────────────────────────────────────────────── */

body[data-layout-version="modern"] .category-title {
    letter-spacing: 0.04em;
    transition:
        color var(--layout-transition),
        background-color var(--layout-transition),
        box-shadow var(--layout-transition);
}

body[data-layout-version="modern"] .category-title:hover {
    border-radius: var(--layout-radius-sm);
    padding: var(--space-hairline, 0.15rem) var(--space-2, 0.45rem);
    margin: -0.15rem -0.45rem;
    background: color-mix(in srgb, var(--background-secondary) 72%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--border-primary) 35%, transparent);
}

body[data-layout-version="modern"] .smart-collection-title {
    letter-spacing: 0.03em;
}

body[data-layout-version="modern"] .smart-collection-why-btn {
    border-radius: var(--layout-radius-pill);
}

body[data-layout-version="modern"] .smart-collection-why-popover {
    border-radius: var(--layout-radius-sm);
    box-shadow: var(--layout-shadow-md);
}

/* ─── Shared bookmark row chrome ─────────────────────────────────────────── */

body[data-layout-version="modern"] .dashboard-grid:not(.layout-launcher) .bookmark-link {
    border-radius: var(--modern-row-radius);
    transition:
        color var(--layout-transition),
        background-color var(--layout-transition),
        box-shadow var(--layout-transition),
        transform var(--layout-transition);
}

body[data-layout-version="modern"] .dashboard-grid:not(.layout-launcher) .bookmark-link:is(:hover, .keyboard-selected, :focus-within):not(.bookmark-inline-editing) {
    color: var(--text-primary);
    background: var(--modern-bookmark-row-hover-bg);
    box-shadow: none;
    border-radius: var(--modern-row-radius);
}

body[data-layout-version="modern"] .dashboard-grid:not(.layout-launcher) .bookmark-link:is(.keyboard-selected, :focus-within):not(.bookmark-inline-editing) {
    background: var(--modern-bookmark-row-selected-bg);
    box-shadow: none;
}

body[data-layout-version="modern"] .dashboard-grid:not(.layout-launcher) .bookmark-link:is(:hover, .keyboard-selected, :focus-within):not(.bookmark-inline-editing) .bookmark-superscript-badge,
body[data-layout-version="modern"] .dashboard-grid:not(.layout-launcher) .bookmark-link:is(:hover, .keyboard-selected, :focus-within):not(.bookmark-inline-editing) .status-text,
body[data-layout-version="modern"] .dashboard-grid:not(.layout-launcher) .bookmark-link:is(:hover, .keyboard-selected, :focus-within):not(.bookmark-inline-editing) .bookmark-shortcut {
    background: transparent;
    border-color: color-mix(in srgb, var(--accent-primary) 35%, var(--border-secondary));
    color: var(--text-secondary);
}

body[data-layout-version="modern"]:not(.status-color-lock) .dashboard-grid:not(.layout-launcher) .bookmark-link.status-offline:not(:hover):not(.keyboard-selected):not(:focus-within):not(.bookmark-inline-editing) {
    color: var(--status-offline-fg, var(--accent-error));
    background: color-mix(in srgb, var(--accent-error) 18%, var(--background-secondary));
    box-shadow: inset 3px 0 0 var(--status-offline-bar, var(--accent-error));
    font-weight: var(--font-weight-semibold, 600);
}

body[data-layout-version="modern"]:not(.status-color-lock) .dashboard-grid:not(.layout-launcher) .bookmark-link.status-checking:not(:hover):not(.keyboard-selected):not(:focus-within):not(.bookmark-inline-editing) {
    color: var(--status-checking-fg, var(--accent-warning));
    background: color-mix(in srgb, var(--accent-warning) 12%, var(--background-secondary));
}

body[data-layout-version="modern"] .bookmark-shortcut {
    border-radius: var(--layout-radius-sm);
    background: color-mix(in srgb, var(--background-secondary) 88%, transparent);
}

body[data-layout-version="modern"] .bookmark-superscript-badge,
body[data-layout-version="modern"] .bookmark-tag-chip {
    border-radius: var(--layout-radius-pill);
}

body[data-layout-version="modern"] .bookmark-note-badge[data-note-tooltip]::after {
    border-radius: var(--layout-radius-sm);
    box-shadow: var(--layout-shadow-sm);
}

body[data-layout-version="modern"] .bookmark-icon-slot {
    border-radius: var(--layout-radius-sm);
    overflow: hidden;
}

body[data-layout-version="modern"] .bookmarks-list.reorder-container .bookmark-drop-placeholder,
body[data-layout-version="modern"].bookmark-dragging .bookmarks-list.reorder-container:not([data-smart-collection="true"]):not(:has(.bookmark-link))::after {
    border-radius: var(--modern-row-radius);
}

/* ─── Default preset ───────────────────────────────────────────────────────── */

body[data-layout-version="modern"][data-layout-preset="default"] .dashboard-grid {
    row-gap: 2.75rem;
    column-gap: var(--space-5, 1.35rem);
}

body[data-layout-version="modern"][data-layout-preset="default"] .category {
    gap: var(--space-3, 0.85rem);
}

/* ─── Compact preset ─────────────────────────────────────────────────────── */

body[data-layout-version="modern"][data-layout-preset="compact"] .dashboard-grid {
    row-gap: var(--space-5, 1.35rem);
    column-gap: var(--space-4, 0.9rem);
}

body[data-layout-version="modern"][data-layout-preset="compact"] .category {
    gap: var(--space-2, 0.55rem);
}

body[data-layout-version="modern"] .dashboard-grid.layout-compact .bookmarks-list {
    gap: var(--space-1, 0.22rem);
}

/* ─── Cards preset ─────────────────────────────────────────────────────────── */

body[data-layout-version="modern"] .dashboard-grid.layout-cards .category {
    border-radius: var(--modern-category-radius);
    background: color-mix(in srgb, var(--background-secondary) 58%, var(--background-primary));
    border-color: color-mix(in srgb, var(--border-primary) 55%, transparent);
    box-shadow: var(--layout-shadow-sm);
    padding: var(--space-4, 1rem);
}

body[data-layout-version="modern"] .dashboard-grid.layout-cards .bookmark-link {
    border-radius: var(--layout-radius-sm);
    background: transparent;
    border: none;
    box-shadow: none;
    transition:
        background-color var(--layout-transition),
        border-color var(--layout-transition),
        box-shadow var(--layout-transition);
}

body[data-layout-version="modern"] .dashboard-grid.layout-cards .bookmark-link:hover:not(.bookmark-inline-editing) {
    background: color-mix(in srgb, var(--accent-primary) 8%, var(--background-primary));
    border-color: transparent;
    box-shadow: none;
}

body[data-layout-version="modern"] .dashboard-grid.layout-cards .bookmark-link:is(.keyboard-selected, :focus-within):not(.bookmark-inline-editing) {
    color: var(--text-primary);
    background: var(--modern-bookmark-row-selected-bg);
    border-color: color-mix(in srgb, var(--accent-primary) 30%, transparent);
    box-shadow: none;
}

/* ─── Terminal preset ────────────────────────────────────────────────────── */

body[data-layout-version="modern"] .dashboard-grid.layout-terminal .category-title-label::before {
    color: color-mix(in srgb, var(--accent-primary) 70%, var(--text-tertiary));
}

body[data-layout-version="modern"] .dashboard-grid.layout-terminal .bookmark-link {
    border-radius: var(--layout-radius-sm);
    background: color-mix(in srgb, var(--background-secondary) 35%, transparent);
}

body[data-layout-version="modern"] .dashboard-grid.layout-terminal .bookmark-link::before {
    color: var(--accent-primary);
    opacity: 0.8;
}

body[data-layout-version="modern"] .dashboard-grid.layout-terminal .bookmark-link:hover:not(.bookmark-inline-editing) {
    background: color-mix(in srgb, var(--accent-primary) 12%, var(--background-secondary));
}

body[data-layout-version="modern"] .dashboard-grid.layout-terminal .bookmark-link:is(.keyboard-selected, :focus-within):not(.bookmark-inline-editing) {
    color: var(--text-primary);
    background: var(--modern-bookmark-row-selected-bg);
    box-shadow: none;
}

body[data-layout-version="modern"] .dashboard-grid.layout-terminal .bookmark-shortcut {
    border-radius: var(--layout-radius-sm);
    font-family: var(--font-family-main, "Source Code Pro", monospace);
}

/* ─── Masonry preset ─────────────────────────────────────────────────────── */

body[data-layout-version="modern"] .dashboard-grid.layout-masonry .category {
    gap: var(--space-3, 0.65rem);
}

body[data-layout-version="modern"] .dashboard-grid.layout-masonry .bookmark-link {
    border-radius: var(--layout-radius-sm);
    border: 1px solid color-mix(in srgb, var(--border-primary) 38%, transparent);
    background: color-mix(in srgb, var(--background-secondary) 48%, transparent);
    padding-inline: var(--space-2, 0.55rem);
}

body[data-layout-version="modern"] .dashboard-grid.layout-masonry .bookmark-link:hover:not(.bookmark-inline-editing) {
    border-color: color-mix(in srgb, var(--accent-primary) 36%, var(--border-primary));
    box-shadow: var(--layout-shadow-sm);
}

body[data-layout-version="modern"] .dashboard-grid.layout-masonry .bookmark-link:is(.keyboard-selected, :focus-within):not(.bookmark-inline-editing) {
    color: var(--text-primary);
    background: var(--modern-bookmark-row-selected-bg);
    border-color: color-mix(in srgb, var(--accent-primary) 36%, var(--border-primary));
    box-shadow: none;
}

/* ─── List preset ──────────────────────────────────────────────────────────── */

body[data-layout-version="modern"] .dashboard-grid.layout-list .category {
    gap: var(--space-1-5, 0.35rem);
}

body[data-layout-version="modern"] .dashboard-grid.layout-list .bookmark-link {
    border-radius: 0;
    padding: var(--space-2, 0.6rem) var(--space-1-5, 0.4rem);
    margin-bottom: 0;
    background: transparent;
    border-bottom: 1px solid var(--border-primary);
}

body[data-layout-version="modern"] .dashboard-grid.layout-list .bookmark-link:hover:not(.bookmark-inline-editing) {
    border-radius: 0;
    background: color-mix(in srgb, var(--accent-primary) 11%, var(--background-secondary));
    box-shadow: none;
}

body[data-layout-version="modern"] .dashboard-grid.layout-list .bookmark-link:is(.keyboard-selected, :focus-within):not(.bookmark-inline-editing) {
    border-radius: 0;
    color: var(--text-primary);
    background: var(--modern-bookmark-row-selected-bg);
    box-shadow: none;
}

body[data-layout-version="modern"] .dashboard-grid.layout-list .bookmark-text {
    line-height: 1.35;
}

/* ─── Widgets preset ───────────────────────────────────────────────────────── */

body[data-layout-version="modern"] .dashboard-grid.layout-widgets .category {
    border-radius: var(--layout-radius-lg);
    background: color-mix(in srgb, var(--background-secondary) 62%, var(--background-primary));
    border-color: color-mix(in srgb, var(--border-primary) 50%, transparent);
    box-shadow: var(--layout-shadow-md);
    padding: var(--space-4, 1.1rem);
}

body[data-layout-version="modern"] .dashboard-grid.layout-widgets .bookmark-link {
    border-radius: var(--layout-radius-sm);
    background: transparent;
    border: none;
    box-shadow: none;
    transition:
        background-color var(--layout-transition),
        border-color var(--layout-transition),
        box-shadow var(--layout-transition);
}

body[data-layout-version="modern"] .dashboard-grid.layout-widgets .bookmark-link:hover:not(.bookmark-inline-editing) {
    background: color-mix(in srgb, var(--accent-primary) 8%, var(--background-primary));
    border-color: transparent;
    box-shadow: none;
}

body[data-layout-version="modern"] .dashboard-grid.layout-widgets .bookmark-link:is(.keyboard-selected, :focus-within):not(.bookmark-inline-editing) {
    color: var(--text-primary);
    background: var(--modern-bookmark-row-selected-bg);
    border-color: transparent;
    box-shadow: none;
}

/* ─── Launcher preset ────────────────────────────────────────────────────── */

body[data-layout-version="modern"] .layout-launcher .category-title {
    padding: var(--space-hairline, 0.1rem) var(--space-1-5, 0.35rem);
    border-radius: var(--layout-radius-sm);
}

body[data-layout-version="modern"] .layout-launcher .bookmark-link {
    border-radius: var(--layout-radius-md);
    background: color-mix(in srgb, var(--background-secondary) 55%, transparent);
    border: 1px solid color-mix(in srgb, var(--border-primary) 42%, transparent);
    box-shadow: var(--layout-shadow-sm);
    transition:
        background-color var(--layout-transition),
        border-color var(--layout-transition),
        box-shadow var(--layout-transition),
        transform var(--layout-transition);
}

body[data-layout-version="modern"] .layout-launcher .bookmark-link:hover {
    background: color-mix(in srgb, var(--accent-primary) 12%, var(--background-secondary));
    border-color: color-mix(in srgb, var(--accent-primary) 38%, var(--border-primary));
    box-shadow: var(--layout-shadow-md);
    transform: translateY(-2px);
}

body[data-layout-version="modern"] .layout-launcher .bookmark-link.keyboard-selected,
body[data-layout-version="modern"] .layout-launcher .bookmark-link:focus-within {
    background: color-mix(in srgb, var(--accent-primary) 16%, var(--background-secondary));
    border-color: color-mix(in srgb, var(--accent-primary) 45%, var(--border-primary));
    box-shadow: var(--layout-shadow-md);
}

body[data-layout-version="modern"].bookmark-kbd-selection-dimmed .dashboard-grid:not(.layout-launcher) .bookmark-link.keyboard-selected:not(:hover):not(.bookmark-inline-editing) {
    background: var(--modern-bookmark-row-dimmed-bg);
    box-shadow: none;
}

body[data-layout-version="modern"].bookmark-kbd-selection-dimmed .layout-launcher .bookmark-link.keyboard-selected:not(:hover):not(.bookmark-inline-editing) {
    background: color-mix(in srgb, var(--accent-primary) 6%, var(--background-secondary));
    border-color: color-mix(in srgb, var(--accent-primary) 18%, var(--border-primary));
    box-shadow: var(--layout-shadow-sm);
    transform: none;
}

body[data-layout-version="modern"] .layout-launcher .bookmark-icon-slot {
    border-radius: var(--layout-radius-sm);
}

/* ─── Density modes ──────────────────────────────────────────────────────── */

body[data-layout-version="modern"][data-density-mode="comfortable"] .bookmarks-list {
    gap: var(--space-2, 0.48rem);
}

body[data-layout-version="modern"][data-density-mode="compact"] .bookmarks-list,
body[data-layout-version="modern"][data-density-mode="auto"] .bookmarks-list {
    gap: var(--space-1, 0.28rem);
}

body[data-layout-version="modern"][data-density-mode="dense"] .bookmarks-list {
    gap: var(--space-hairline, 0.14rem);
}

body[data-layout-version="modern"][data-density-mode="comfortable"] .dashboard-grid:not(.layout-launcher) .bookmark-link {
    min-height: 2.15rem;
}

body[data-layout-version="modern"][data-density-mode="dense"] .dashboard-grid:not(.layout-launcher) .bookmark-link {
    min-height: 1.55rem;
    column-gap: var(--space-1-5, 0.4rem);
}

/* ─── Badge visibility polish ────────────────────────────────────────────── */

body[data-layout-version="modern"][data-shortcut-display="always"] .bookmark-shortcut:not(.is-empty) {
    opacity: 1;
}

body[data-layout-version="modern"][data-show-pin-icon="true"] .bookmark-pin-badge:not(.is-empty),
body[data-layout-version="modern"][data-show-note-icon="true"] .bookmark-note-badge:not(.is-empty) {
    background: color-mix(in srgb, var(--background-secondary) 90%, transparent);
    border-color: color-mix(in srgb, var(--border-primary) 55%, transparent);
}

/* ─── Link preview cards ───────────────────────────────────────────────────── */

body[data-layout-version="modern"] .bookmark-preview-card {
    border-radius: var(--layout-radius-lg);
    border-color: var(--layout-surface-border);
    background: color-mix(in srgb, var(--background-secondary) 92%, transparent);
    box-shadow: var(--layout-shadow-lg);
    backdrop-filter: blur(18px);
    -webkit-backdrop-filter: blur(18px);
}

body[data-layout-version="modern"] .bookmark-preview-card-refresh {
    border-radius: var(--layout-radius-sm);
}

body[data-layout-version="modern"] .bookmark-preview-card-image-wrap {
    border-bottom: 1px solid color-mix(in srgb, var(--border-primary) 35%, transparent);
}

/* ─── Inline edit & tag filter view ────────────────────────────────────────── */

body[data-layout-version="modern"] .bookmark-link.bookmark-inline-editing,
body[data-layout-version="modern"] .bookmark-link.bookmark-inline-editing:hover,
body[data-layout-version="modern"] .bookmark-link.bookmark-inline-editing.keyboard-selected,
body[data-layout-version="modern"] .bookmark-link.bookmark-inline-editing:focus-within {
    background: transparent;
    border: none;
    box-shadow: none;
    transform: none;
}

body[data-layout-version="modern"] .layout-launcher .bookmark-link.bookmark-inline-editing {
    width: auto;
    min-width: min(420px, 90vw);
    max-width: min(520px, 95vw);
    flex: 1 1 100%;
    align-items: stretch;
    padding: 0;
}

body[data-layout-version="modern"] .bookmark-inline-form {
    background-color: var(--inline-edit-panel-bg);
    border: 2px solid color-mix(in srgb, var(--border-primary) 70%, var(--text-secondary));
    box-shadow: var(--layout-shadow-md);
}

body[data-layout-version="modern"] .bookmark-inline-input,
body[data-layout-version="modern"] .bookmark-inline-select,
body[data-layout-version="modern"] .bookmark-inline-action-btn,
body[data-layout-version="modern"] .bookmark-inline-icon-preview {
    border-radius: var(--layout-radius-sm);
}

body[data-layout-version="modern"] #dashboard-layout.tag-filter-view .tag-filter-indicator-chip {
    box-shadow: var(--layout-shadow-sm);
}

body[data-layout-version="modern"] .tag-ac-dropdown {
    border-radius: var(--layout-radius-sm);
    box-shadow: var(--layout-shadow-md);
}

/* ─── Grid motion preferences ──────────────────────────────────────────────── */

body[data-layout-version="modern"].no-animations .layout-launcher .bookmark-link:hover {
    transform: none !important;
}

body[data-layout-version="modern"].no-animations .dashboard-grid:not(.layout-launcher) .bookmark-link:is(:hover, .keyboard-selected, :focus-within):not(.bookmark-inline-editing) {
    transition: none !important;
}

/* ─── Grid responsive (modern) ─────────────────────────────────────────────── */

@media (max-width: 991px) {
    body[data-layout-version="modern"] .dashboard-grid.layout-cards .category,
    body[data-layout-version="modern"] .dashboard-grid.layout-widgets .category {
        padding: var(--space-3, 0.85rem);
    }

    body[data-layout-version="modern"] .dashboard-grid.layout-list .bookmark-link {
        padding: var(--space-2, 0.5rem) var(--space-1-5, 0.4rem);
    }
}

@media (max-width: 768px), (hover: none) and (pointer: coarse) {
    body[data-layout-version="modern"] .layout-launcher .bookmark-link {
        border-radius: var(--layout-radius-md);
    }

    body[data-layout-version="modern"] .layout-launcher .bookmark-link:hover {
        transform: none;
    }
}

/* ═══════════════════════════════════════════════════════════════════════════
   Phase 4 — Mobile modern layout
   ═══════════════════════════════════════════════════════════════════════════ */

body[data-layout-version="modern"][data-mobile-layout="true"] .mobile-experience-banner {
    border-radius: var(--layout-radius-md);
    box-shadow: var(--layout-shadow-sm);
}

body[data-layout-version="modern"][data-mobile-layout="true"] .mobile-experience-banner-dismiss {
    border-radius: var(--layout-radius-sm);
}

body[data-layout-version="modern"][data-mobile-layout="true"] .dashboard-section.section-controls {
    padding-bottom: var(--space-3, 0.75rem);
}

body[data-layout-version="modern"][data-mobile-layout="true"] #page-overview-header-btn,
body[data-layout-version="modern"][data-mobile-layout="true"] .pages-link--icon .pages-link-anchor,
body[data-layout-version="modern"][data-mobile-layout="true"] .config-link a,
body[data-layout-version="modern"][data-mobile-layout="true"] .header-destinations .page-nav-btn--inbox {
    border-radius: var(--layout-radius-pill);
    border: 1px solid var(--layout-surface-border);
    background: var(--layout-surface);
}

body[data-layout-version="modern"][data-mobile-layout="true"] #page-overview-header-btn:active,
body[data-layout-version="modern"][data-mobile-layout="true"] .pages-link--icon .pages-link-anchor:active,
body[data-layout-version="modern"][data-mobile-layout="true"] .config-link a:active,
body[data-layout-version="modern"][data-mobile-layout="true"] .header-destinations .page-nav-btn--inbox:active {
    background: color-mix(in srgb, var(--accent-primary) 12%, var(--background-secondary));
    border-color: color-mix(in srgb, var(--accent-primary) 40%, var(--border-primary));
}

body[data-layout-version="modern"][data-mobile-layout="true"] .button-container {
    width: fit-content;
    max-width: calc(100% - 2rem);
    border-radius: var(--layout-radius-lg);
    padding: var(--space-2, 0.5rem) var(--space-3, 0.65rem) var(--space-2, 0.58rem);
}

body[data-layout-version="modern"][data-mobile-layout="true"] .btn-group-primary {
    width: auto;
    gap: var(--space-2, 0.45rem);
}

body[data-layout-version="modern"][data-mobile-layout="true"] .search-button-labeled {
    border-radius: var(--layout-radius-md);
    min-height: 44px;
}

body[data-layout-version="modern"][data-mobile-layout="true"] .shortcut-search .search-container {
    border-radius: var(--layout-radius-md);
    box-shadow: var(--layout-shadow-lg);
}

body[data-layout-version="modern"][data-mobile-layout="true"] .search-mode-tab {
    border-radius: var(--layout-radius-sm);
}

body[data-layout-version="modern"][data-mobile-layout="true"] .app-notification {
    border-radius: var(--layout-radius-md);
    box-shadow: var(--layout-shadow-md);
}

body[data-layout-version="modern"][data-mobile-layout="true"] .dashboard-grid .category-title:hover {
    margin: -0.15rem -0.35rem;
    padding: var(--space-hairline, 0.15rem) var(--space-1-5, 0.35rem);
}

body[data-layout-version="modern"][data-mobile-layout="true"] .dashboard-grid:not(.layout-launcher) .bookmark-link {
    min-height: 44px;
}

/* ─── Status row colors (override preset chrome) ─────────────────────────── */

body[data-layout-version="modern"]:not(.status-color-lock) .bookmarks-list[data-bookmarks-list="true"]:not([data-smart-collection="true"]) .bookmark-link.status-offline:not(:hover):not(.keyboard-selected):not(:focus-within):not(.bookmark-inline-editing) {
    color: var(--status-offline-fg, var(--accent-error, #ef4444));
    background: color-mix(in srgb, var(--accent-error, #ef4444) 22%, var(--background-secondary));
    box-shadow: inset 3px 0 0 var(--status-offline-bar, var(--accent-error, #ef4444));
    font-weight: var(--font-weight-semibold, 600);
}

body[data-layout-version="modern"]:not(.status-color-lock) .bookmarks-list[data-bookmarks-list="true"]:not([data-smart-collection="true"]) .bookmark-link.status-checking:not(:hover):not(.keyboard-selected):not(:focus-within):not(.bookmark-inline-editing) {
    color: var(--status-checking-fg, var(--accent-warning, #f59e0b));
    background: color-mix(in srgb, var(--accent-warning, #f59e0b) 14%, var(--background-secondary));
}

body[data-layout-version="modern"]:not(.status-color-lock) .dashboard-grid.layout-list .bookmarks-list[data-bookmarks-list="true"]:not([data-smart-collection="true"]) .bookmark-link.status-offline:not(:hover):not(.keyboard-selected):not(:focus-within):not(.bookmark-inline-editing),
body[data-layout-version="modern"]:not(.status-color-lock) .dashboard-grid.layout-cards .bookmarks-list[data-bookmarks-list="true"]:not([data-smart-collection="true"]) .bookmark-link.status-offline:not(:hover):not(.keyboard-selected):not(:focus-within):not(.bookmark-inline-editing),
body[data-layout-version="modern"]:not(.status-color-lock) .dashboard-grid.layout-masonry .bookmarks-list[data-bookmarks-list="true"]:not([data-smart-collection="true"]) .bookmark-link.status-offline:not(:hover):not(.keyboard-selected):not(:focus-within):not(.bookmark-inline-editing),
body[data-layout-version="modern"]:not(.status-color-lock) .dashboard-grid.layout-terminal .bookmarks-list[data-bookmarks-list="true"]:not([data-smart-collection="true"]) .bookmark-link.status-offline:not(:hover):not(.keyboard-selected):not(:focus-within):not(.bookmark-inline-editing),
body[data-layout-version="modern"]:not(.status-color-lock) .dashboard-grid.layout-widgets .bookmarks-list[data-bookmarks-list="true"]:not([data-smart-collection="true"]) .bookmark-link.status-offline:not(:hover):not(.keyboard-selected):not(:focus-within):not(.bookmark-inline-editing) {
    color: var(--status-offline-fg, var(--accent-error, #ef4444));
    background: color-mix(in srgb, var(--accent-error, #ef4444) 24%, var(--background-secondary));
    box-shadow: inset 3px 0 0 var(--status-offline-bar, var(--accent-error, #ef4444));
}

body[data-layout-version="modern"]:not(.status-color-lock) .bookmarks-list[data-bookmarks-list="true"]:not([data-smart-collection="true"]) .bookmark-link.status-offline:is(:hover, .keyboard-selected, :focus-within):not(.bookmark-inline-editing),
body[data-layout-version="modern"]:not(.status-color-lock) .bookmarks-list[data-bookmarks-list="true"]:not([data-smart-collection="true"]) .bookmark-link.status-checking:is(:hover, .keyboard-selected, :focus-within):not(.bookmark-inline-editing) {
    border-radius: var(--modern-row-radius, 4px);
}

body[data-layout-version="modern"].bookmark-kbd-selection-dimmed .dashboard-grid:not(.layout-launcher) .bookmark-link.keyboard-selected:not(:hover):not(.bookmark-inline-editing) .bookmark-superscript-badge,
body[data-layout-version="modern"].bookmark-kbd-selection-dimmed .dashboard-grid:not(.layout-launcher) .bookmark-link.keyboard-selected:not(:hover):not(.bookmark-inline-editing) .status-text,
body[data-layout-version="modern"].bookmark-kbd-selection-dimmed .dashboard-grid:not(.layout-launcher) .bookmark-link.keyboard-selected:not(:hover):not(.bookmark-inline-editing) .bookmark-shortcut {
    background: color-mix(in srgb, var(--background-primary) 70%, transparent);
    border-color: color-mix(in srgb, var(--border-primary) 55%, transparent);
    color: var(--text-tertiary);
}

/* ─── Empty dashboard states (page / fresh) ───────────────────────────────── */

body[data-layout-version="modern"] #dashboard-layout > .empty-state {
    width: 100%;
    max-width: none;
    margin: 0;
    padding: var(--space-6, 2rem) var(--space-5, 1.5rem);
    box-sizing: border-box;
    border-radius: var(--layout-radius-md);
    border: 1px solid var(--layout-surface-border);
    background: var(--layout-surface);
    box-shadow: var(--layout-shadow-sm);
}

body[data-layout-version="modern"] .dashboard-grid.layout-cards .smart-collection-empty,
body[data-layout-version="modern"] .dashboard-grid.layout-widgets .smart-collection-empty {
    border-style: solid;
    border-color: color-mix(in srgb, var(--layout-surface-border) 85%, transparent);
    background: color-mix(in srgb, var(--layout-surface) 72%, transparent);
}

/* ==== css/overlays-modern.css ==== */
/* Modern overlay UI — search, modals, toasts, tag cloud, selects.
   Scoped to body[data-layout-version="modern"]. Classic unchanged.

   Tokens (--layout-*) come from layout-modern-tokens.css, which loads first.
   Do not redefine them here. */

/* ─── Search overlay ───────────────────────────────────────────────────────── */

body[data-layout-version="modern"] .shortcut-search {
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
}

body[data-layout-version="modern"] .search-container {
    border-radius: var(--layout-radius-lg);
    border-color: var(--layout-surface-border);
    background: var(--layout-surface);
    box-shadow: var(--layout-shadow-md);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
}

body[data-layout-version="modern"]:not([data-button-position="side-left"]):not([data-button-position="bottom-left"]):not([data-button-position="bottom-right"]) .search-container {
    width: min(52rem, calc(100vw - 2.5rem));
    max-width: 100%;
}

body[data-layout-version="modern"] .search-prefix {
    border-radius: var(--layout-radius-sm);
}

body[data-layout-version="modern"] .search-match {
    border-radius: var(--layout-radius-sm);
    border-left: none;
}

body[data-layout-version="modern"] .search-match:hover,
body[data-layout-version="modern"] .search-match.keyboard-selected {
    background: color-mix(in srgb, var(--accent-primary) 11%, var(--background-secondary));
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent-primary) 24%, transparent);
}

body[data-layout-version="modern"] .search-command-group-header {
    margin-top: var(--space-1-5, 0.35rem);
    padding: var(--space-2, 0.45rem) var(--space-2, 0.55rem) var(--space-1, 0.25rem);
    border-top: none;
    border-radius: var(--layout-radius-sm);
    opacity: 0.72;
}

body[data-layout-version="modern"] .search-command-group-header:hover,
body[data-layout-version="modern"] .search-command-group-header.keyboard-selected {
    opacity: 1;
    background: color-mix(in srgb, var(--background-primary) 55%, transparent);
}

body[data-layout-version="modern"] .search-command-group-header.keyboard-selected {
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent-primary) 22%, transparent);
}

body[data-layout-version="modern"] .search-mode-tabs {
    gap: var(--space-1-5, 0.35rem);
    padding: var(--space-1-5, 0.35rem);
    margin-top: var(--space-2, 0.5rem);
    border-top: none;
    border-radius: var(--layout-radius-md);
    background: var(--background-secondary);
    border: 1px solid var(--border-primary);
}

body[data-layout-version="modern"] .search-mode-tab {
    border-radius: var(--layout-radius-pill);
    border: 1px solid transparent;
    transition:
        color var(--layout-transition),
        background-color var(--layout-transition),
        border-color var(--layout-transition),
        box-shadow var(--layout-transition);
}

body[data-layout-version="modern"] .search-mode-tab.active {
    box-shadow: var(--layout-shadow-sm);
}

body[data-layout-version="modern"] .search-mode-tab:hover {
    background: color-mix(in srgb, var(--background-primary) 70%, transparent);
}

body[data-layout-version="modern"] .search-history-chip,
body[data-layout-version="modern"] .search-command-chip {
    border-radius: var(--layout-radius-pill);
    background: color-mix(in srgb, var(--background-secondary) 80%, transparent);
}

body[data-layout-version="modern"] .search-history-chip:hover,
body[data-layout-version="modern"] .search-command-chip:hover {
    background: color-mix(in srgb, var(--accent-primary) 10%, var(--background-secondary));
}

/* ─── Modals ─────────────────────────────────────────────────────────────── */

body[data-layout-version="modern"] .modal-overlay {
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
}

body[data-layout-version="modern"] .modal {
    border-radius: var(--layout-radius-lg);
    box-shadow: var(--layout-shadow-lg);
    overflow: hidden;
    background: var(--layout-modal-bg);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

body[data-layout-version="modern"] .modal-header {
    background: var(--background-secondary);
    border-bottom-color: color-mix(in srgb, var(--border-primary) 45%, transparent);
}

body[data-layout-version="modern"] .modal-body {
    background: var(--background-primary);
}

body[data-layout-version="modern"] .modal-new-bookmark {
    border-radius: var(--layout-radius-lg);
}

body[data-layout-version="modern"] .nbm-header {
    background: var(--background-secondary);
}

body[data-layout-version="modern"] .nbm-input,
body[data-layout-version="modern"] .nbm-select {
    border-radius: var(--layout-radius-sm);
}

body[data-layout-version="modern"] .nbm-input:focus,
body[data-layout-version="modern"] .nbm-select:focus {
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-primary) 18%, transparent);
}

body[data-layout-version="modern"] .wn-intro {
    border-radius: var(--layout-radius-sm);
    background: var(--background-secondary);
}

body[data-layout-version="modern"] .wn-kofi-btn,
body[data-layout-version="modern"] .wn-release-tag {
    border-radius: var(--layout-radius-sm);
}

body[data-layout-version="modern"] .modal-button,
body[data-layout-version="modern"] .modal .btn {
    border-radius: var(--layout-radius-sm);
}

/* Dashboard + config toolbar modals — surface parity with search overlay */
body[data-layout-version="modern"]:has(#dashboard-layout) #app-modal.modal-overlay:has(.recent-bookmarks-modal) .modal,
body[data-layout-version="modern"]:has(#dashboard-layout) #app-modal.modal-overlay:has(.keyboard-cheat-sheet-modal) .modal,
body[data-layout-version="modern"]:has(#dashboard-layout) #app-modal.modal-overlay:has(.whats-new-modal) .modal,
body[data-layout-version="modern"]:has(#dashboard-layout) #app-modal.modal-overlay:has(.inbox-intro-modal) .modal,
body[data-layout-version="modern"]:has(#config-main) #app-modal.modal-overlay:has(.whats-new-modal) .modal {
    border-radius: var(--layout-radius-lg);
    border-color: var(--layout-surface-border);
    background: var(--layout-modal-bg);
    box-shadow: var(--layout-shadow-md);
}

body[data-layout-version="modern"] .recent-bookmarks-modal-item,
body[data-layout-version="modern"] .recent-bookmarks-skeleton-row,
body[data-layout-version="modern"] .page-overview-modal-link {
    border-radius: var(--layout-radius-sm);
}

body[data-layout-version="modern"] .recent-bookmarks-modal-item:hover,
body[data-layout-version="modern"] .page-overview-modal-link:hover,
body[data-layout-version="modern"] .page-overview-modal-item.is-focused .page-overview-modal-link {
    background: color-mix(in srgb, var(--accent-primary) 11%, var(--background-secondary));
    border-color: color-mix(in srgb, var(--accent-primary) 24%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent-primary) 24%, transparent);
}

/* ─── Toasts ─────────────────────────────────────────────────────────────── */

body[data-layout-version="modern"] .app-notification {
    border-radius: var(--layout-radius-md);
    background: var(--background-secondary);
    box-shadow: var(--layout-shadow-md);
}

body[data-layout-version="modern"] .app-notification-action {
    border-radius: var(--layout-radius-sm);
}

/* ─── Tag cloud ──────────────────────────────────────────────────────────── */

body[data-layout-version="modern"] .tag-cloud-modal {
    border-radius: var(--layout-radius-lg);
    background: var(--background-secondary);
    border-color: var(--border-primary);
    box-shadow: var(--layout-shadow-lg);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

body[data-layout-version="modern"] .tag-cloud-modal-close {
    border-radius: var(--layout-radius-sm);
}

body[data-layout-version="modern"] .tag-cloud-modal-close:hover {
    background: color-mix(in srgb, var(--accent-primary) 10%, var(--background-primary));
}

body[data-layout-version="modern"] .tag-cloud-word:hover,
body[data-layout-version="modern"] .tag-cloud-word.is-selected {
    border-radius: var(--layout-radius-sm);
    background: color-mix(in srgb, var(--accent-primary) 12%, transparent);
}

body[data-layout-version="modern"] .tag-cloud-clear-filter {
    border-radius: var(--layout-radius-pill);
}

/* ─── Custom selects (dashboard + config) ────────────────────────────────── */

body[data-layout-version="modern"] .custom-select-trigger {
    border-radius: var(--layout-radius-sm);
    transition:
        border-color var(--layout-transition),
        box-shadow var(--layout-transition);
}

body[data-layout-version="modern"] .custom-select.open .custom-select-trigger,
body[data-layout-version="modern"] .custom-select-trigger:focus-visible {
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-primary) 16%, transparent);
}

body[data-layout-version="modern"] .custom-select-options {
    border-radius: var(--layout-radius-sm);
    box-shadow: var(--layout-shadow-md);
}

body[data-layout-version="modern"] .custom-select-option:hover,
body[data-layout-version="modern"] .custom-select-option.selected {
    background: color-mix(in srgb, var(--accent-primary) 10%, var(--background-primary));
}

/* Tag autocomplete (config bookmarks form — portaled to body) */
body[data-layout-version="modern"] .tag-ac-dropdown {
    border-radius: var(--layout-radius-sm);
    box-shadow: var(--layout-shadow-md);
}

body[data-layout-version="modern"] .tag-ac-item:hover,
body[data-layout-version="modern"] .tag-ac-item-active {
    background: color-mix(in srgb, var(--accent-primary) 10%, var(--background-secondary));
    border-radius: var(--layout-radius-sm);
}

/* ─── Feature tour (dashboard onboarding) ────────────────────────────────── */

body[data-layout-version="modern"] .feature-tour-card {
    border-radius: var(--layout-radius-lg);
    box-shadow: var(--layout-shadow-lg);
    background: var(--layout-overlay-bg);
}

body[data-layout-version="modern"] .feature-tour-input,
body[data-layout-version="modern"] .feature-tour-fieldset {
    border-radius: var(--layout-radius-sm);
}

body[data-layout-version="modern"] .feature-tour-input:focus {
    outline: none;
    border-color: color-mix(in srgb, var(--accent-primary) 45%, var(--border-primary));
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-primary) 16%, transparent);
}

body[data-layout-version="modern"] .feature-tour-btn {
    border-radius: var(--layout-radius-sm);
    transition:
        background-color var(--layout-transition),
        border-color var(--layout-transition),
        box-shadow var(--layout-transition);
}

body[data-layout-version="modern"] .feature-tour-btn:hover:not(:disabled) {
    box-shadow: var(--layout-shadow-sm);
}

body[data-layout-version="modern"] .feature-tour-focus-ring {
    border-radius: var(--layout-radius-sm);
}

/* ─── Config guided tours ─────────────────────────────────────────────────── */

body[data-layout-version="modern"] [class*="-tour-card"] {
    border-radius: var(--layout-radius-lg);
    box-shadow: var(--layout-shadow-lg);
    background: var(--layout-overlay-bg);
}

body[data-layout-version="modern"] [class*="-tour-card"] .btn,
body[data-layout-version="modern"] [class*="-tour-card"] button {
    border-radius: var(--layout-radius-sm);
}

/* ==== css/layout-side-rail.css ==== */
/* Side Rail navigation — body[data-rail]
   Converts the bottom button bar into a 44px vertical rail on a screen edge.
   All other layouts (classic / modern) are unaffected.

   The rail exists on both edges. Rather than duplicating every rule for
   side-right, the selectors match a side-agnostic [data-rail] hook and the few
   properties that actually carry a direction read from variables below. Adding
   the right-hand rail is then the variable block, not 68 mirrored rules.

   --rail-edge / --rail-edge-far: which physical side the rail is pinned to.
   --rail-out: the direction "away from the rail", used by tooltips and popovers
   that must open over the content rather than off-screen. */

/* ── Variables ─────────────────────────────────────────────────────────────── */
body[data-rail] {
    --rail-width: 44px;
    --rail-bg: color-mix(in srgb, var(--background-secondary) 88%, transparent);
    --rail-border: color-mix(in srgb, var(--border-primary) 45%, transparent);
    --rail-btn-radius: 0.45rem;
    --rail-btn-padding: var(--space-2, 0.62rem) 0;
    --rail-sep-color: color-mix(in srgb, var(--border-primary) 35%, transparent);
}

/* Left rail (default) and its mirror. Only these two blocks know about sides. */
body[data-button-position="side-left"] {
    --rail-edge: 0;
    --rail-edge-far: auto;
    --rail-out: 1;               /* content lies to the right */
}

body[data-button-position="side-right"] {
    --rail-edge: auto;
    --rail-edge-far: 0;
    --rail-out: -1;              /* content lies to the left */
    /* Tooltips and hints open leftward, over the content, not off-screen. */
    --rail-tip-left: auto;
    --rail-tip-right: calc(100% + 8px);
    --rail-hint-left: auto;
    --rail-hint-right: calc(var(--rail-width) + 0.65rem);
    --rail-content-left: 0;
    --rail-content-right: var(--rail-width);
    --rail-border-right: none;
    --rail-border-left: 1px solid var(--rail-border);
}

/* ── Rail container ─────────────────────────────────────────────────────────── */
body[data-rail] .button-container {
    position: fixed;
    left: var(--rail-edge);
    right: var(--rail-edge-far);
    top: 0;
    bottom: 0;
    transform: none;
    width: var(--rail-width);
    max-width: var(--rail-width);

    flex-direction: column;
    flex-wrap: nowrap;
    justify-content: flex-start;
    align-items: stretch;

    /* bottom room for ! + ★ fixed elements */
    padding: 0 0 calc(var(--corner-fab-height, 2.75rem) * 2 + 0.3rem);
    gap: 0;

    background: var(--rail-bg);
    /* The divider faces the content, so it swaps sides with the rail. */
    border-right: var(--rail-border-right, 1px solid var(--rail-border));
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);

    /* Above tag-cloud-wrap (1001) and toggle (1002) so rail buttons receive events */
    z-index: 1003;
    overflow: visible;
    box-shadow: none;
    border-radius: 0;
    border-top: none;
    border-bottom: none;
    border-left: var(--rail-border-left, none);
}

/* ── Button groups: flatten into rail flex flow ──────────────────────────────── */
body[data-rail] .btn-group {
    display: contents;
}

/* ── Button order & spacers ──────────────────────────────────────────────────── */
/* 1. add bookmark — top */
body[data-rail] #quick-add-toolbar-btn   { order: 1; }
/* spacer (one button height) before search */
body[data-rail] #search-button           { order: 2; margin-top: var(--corner-fab-height, 2.75rem); }
/* finders before commands */
body[data-rail] #finders-button          { order: 3; }
body[data-rail] #commands-button         { order: 4; }
body[data-rail] #recent-bookmarks-button { order: 5; }
body[data-rail] #tag-cloud-toggle-btn { order: 6; }

/* cheatsheet: fixed directly above the what's new button */
body[data-rail] #help-button {
    position: fixed;
    bottom: var(--corner-fab-height, 2.75rem);
    left: 0;
    width: var(--rail-width);
    height: var(--corner-fab-height, 2.75rem);
    padding: 0;
    justify-content: center;
    border-radius: 0;
    border-top: 1px solid var(--rail-sep-color);
    border-left: none;
    border-right: none;
    border-bottom: none;
    background: transparent;
    transform: none;
    z-index: 1004;
    order: unset;
    margin-top: 0;
}

/* ── Individual buttons: same style as what's new ────────────────────────────── */
body[data-rail] .search-button {
    width: var(--rail-width);
    height: var(--corner-fab-height, 2.75rem);
    padding: 0;
    margin: 0;
    justify-content: center;
    border-radius: 0;
    border: none;
    background: transparent;
    min-width: 0;
    box-shadow: none;
}

body[data-rail] .search-button-labeled {
    flex-direction: column;
    min-width: 0;
    gap: 0;
}

body[data-rail] .search-button-label {
    display: none !important;
}

body[data-rail] .search-button:hover {
    transform: translateX(calc(var(--rail-out, 1) * 2px));
    background: color-mix(in srgb, var(--accent-primary) 8%, transparent);
    border-color: var(--accent-primary);
    box-shadow: none;
}

body[data-rail] .search-button:active {
    transform: translateX(0);
}

body[data-rail] #help-button:hover {
    transform: translateX(calc(var(--rail-out, 1) * 2px));
    background: color-mix(in srgb, var(--accent-primary) 8%, transparent);
    border-color: var(--accent-primary);
    box-shadow: none;
}

/* ── Tooltips: show to the right of the rail ─────────────────────────────────── */
body[data-rail] .search-button[data-tooltip]::after {
    bottom: auto;
    top: 50%;
    left: var(--rail-tip-left, calc(100% + 8px));
    right: var(--rail-tip-right, auto);
    transform: translateY(-50%);
    white-space: nowrap;
}

/* ── Tag cloud: in rail flow directly under recents ─────────────────────────── */
body[data-rail] #tag-cloud-toggle-btn.search-button {
    position: static;
    width: var(--rail-width);
    height: var(--corner-fab-height, 2.75rem);
    padding: 0;
    margin: 0;
    justify-content: center;
    border-radius: 0;
    border: none;
    border-top: 1px solid var(--rail-sep-color);
    background: transparent;
    transform: none;
    z-index: auto;
    pointer-events: auto;
}

body[data-rail] #tag-cloud-toggle-btn[data-tooltip]::after {
    bottom: auto;
    top: 50%;
    left: var(--rail-tip-left, calc(100% + 8px));
    right: var(--rail-tip-right, auto);
    transform: translateY(-50%);
    white-space: nowrap;
}

body[data-rail] #tag-cloud-toggle-btn:hover {
    transform: translateX(calc(var(--rail-out, 1) * 2px));
    background: color-mix(in srgb, var(--accent-primary) 8%, transparent);
}

/* ── What's new button: anchor to rail bottom ────────────────────────────────── */
body[data-rail] .whats-new-btn {
    left: var(--rail-edge);
    right: var(--rail-edge-far);
    bottom: 0;
    width: var(--rail-width);
    height: calc(var(--corner-fab-height, 2.75rem));
    padding: 0;
    justify-content: center;
    border-radius: 0;
    border-top: 1px solid var(--rail-sep-color);
    border-left: none;
    border-right: none;
    border-bottom: none;
    background: transparent;
    z-index: 1004;
}

body[data-rail] .whats-new-btn:hover {
    transform: translateX(calc(var(--rail-out, 1) * 2px));
    box-shadow: none;
    border-color: var(--accent-primary);
    background: color-mix(in srgb, var(--accent-primary) 8%, transparent);
}

body[data-rail] .whats-new-btn[data-tooltip]::after {
    bottom: auto;
    top: 50%;
    left: var(--rail-tip-left, calc(100% + 8px));
    right: var(--rail-tip-right, auto);
    transform: translateY(-50%);
}

/* ── Hints: beside the rail (parity with bottom-toolbar discoverability) ───── */
body[data-rail] .search-flow-hint {
    position: fixed;
    left: var(--rail-hint-left, calc(var(--rail-width) + 0.65rem));
    right: var(--rail-hint-right, auto);
    top: calc(var(--corner-fab-height, 2.75rem) * 2 + 0.35rem);
    bottom: auto;
    transform: none;
    justify-content: flex-start;
    align-items: flex-start;
    max-width: min(13.5rem, calc(100vw - var(--rail-width) - 1.5rem));
    z-index: 1002;
    pointer-events: none;
}

body[data-rail] .search-flow-hint-text {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-start;
    gap: var(--space-hairline, 0.12rem) var(--space-1, 0.3rem);
    text-align: left;
    line-height: 1.45;
}

body[data-rail] .search-flow-hint-text > .sfh-seg:nth-child(even) {
    display: none;
}

/* ── One-time side-rail legend (icon → label map) ───────────────────────────── */
.side-rail-legend {
    position: fixed;
    left: var(--rail-hint-left, calc(var(--rail-width) + 0.65rem));
    right: var(--rail-hint-right, auto);
    top: calc(var(--corner-fab-height, 2.75rem) + 0.35rem);
    z-index: 1002;
    width: min(13.5rem, calc(100vw - var(--rail-width) - 1.5rem));
    margin: 0;
    padding: var(--space-2, 0.55rem) var(--space-3, 0.65rem) var(--space-2, 0.5rem);
    border: 1px solid color-mix(in srgb, var(--border-primary) 55%, transparent);
    border-radius: var(--layout-radius-sm, 8px);
    background: color-mix(in srgb, var(--background-secondary) 92%, transparent);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    box-shadow: 0 4px 18px color-mix(in srgb, var(--text-primary) 10%, transparent);
    color: var(--text-secondary);
    font-size: calc(var(--font-size-controls) * 0.95);
    line-height: 1.35;
    pointer-events: auto;
}

.side-rail-legend[hidden] {
    display: none !important;
}

.side-rail-legend.is-dismissing {
    opacity: 0;
    transform: translateX(-6px);
    transition: opacity 0.35s ease, transform 0.35s ease;
}

.side-rail-legend-title {
    margin: 0 0 var(--space-1-5, 0.35rem);
    font-size: calc(var(--font-size-controls) * 0.88);
    font-weight: var(--font-weight-semibold);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-tertiary);
}

.side-rail-legend-list {
    margin: 0;
    padding: 0;
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: var(--space-hairline, 0.18rem);
}

.side-rail-legend-item {
    display: flex;
    align-items: baseline;
    gap: var(--space-2, 0.45rem);
    min-width: 0;
}

.side-rail-legend-key {
    flex-shrink: 0;
    min-width: 1.15rem;
    text-align: center;
    color: var(--accent-primary);
    font-weight: var(--font-weight-semibold);
}

.side-rail-legend-label {
    min-width: 0;
    color: var(--text-primary);
}

.side-rail-legend-foot {
    margin: var(--space-1-5, 0.4rem) 0 var(--space-2, 0.45rem);
    font-size: calc(var(--font-size-controls) * 0.88);
    color: var(--text-tertiary);
}

.side-rail-legend-dismiss {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin: 0;
    padding: var(--space-1, 0.28rem) var(--space-2, 0.55rem);
    border: 1px solid var(--border-primary);
    border-radius: var(--layout-radius-xs, 4px);
    background: var(--background-primary);
    color: var(--text-primary);
    font: inherit;
    font-size: calc(var(--font-size-controls) * 0.92);
    cursor: pointer;
}

.side-rail-legend-dismiss:hover {
    border-color: var(--accent-primary);
    color: var(--accent-primary);
}

body[data-rail] .side-rail-legend kbd {
    font: inherit;
    font-weight: var(--font-weight-semibold);
    border: none;
    background: none;
    padding: 0;
    color: inherit;
}

/* ── Content area: shift right to clear the rail ────────────────────────────── */
body[data-rail] .container {
    margin-left: var(--rail-content-left, var(--rail-width));
    margin-right: var(--rail-content-right, 0);
}

/* ── Page section: reduce bottom padding (no floating bar) ───────────────────── */
body[data-rail] .dashboard-section.section-content {
    padding-bottom: var(--space-7, 3rem);
}

/* ── Page tabs: keep numbers/names visible (respect showPageNamesInTabs) ─────── */
body[data-rail] .page-nav-btn {
    font-size: var(--font-size-controls);
    padding: var(--space-1-5, 0.4rem) var(--space-2, 0.6rem);
    min-width: 0;
}

body[data-rail] .page-tab-dot {
    display: inline-block;
    width: 0.45em;
    height: 0.45em;
    margin-right: 0.35em;
    vertical-align: middle;
    flex-shrink: 0;
}

body[data-rail] .page-tab-icon {
    display: inline;
}

body[data-rail] .page-nav-btn.active {
    background: var(--background-secondary);
    border-bottom-color: var(--accent-primary);
}

body[data-rail] .page-nav-btn.active .page-tab-dot {
    box-shadow: none;
    width: 0.45em;
    height: 0.45em;
}

body[data-rail] .page-nav-btn:hover .page-tab-dot {
    box-shadow: none;
}

/* ── Inline bookmark edit: keep rail visible ─────────────────────────────────── */
body.bookmark-inline-edit-active[data-rail] .button-container {
    opacity: 1;
    pointer-events: auto;
}

/* ── Prevent no-animations from re-centering the rail ────────────────────────── */
body.no-animations[data-rail] .button-container {
    transform: none !important;
}

/* ── Modern layout overrides ─────────────────────────────────────────────────── */
body[data-layout-version="modern"][data-rail] .button-container {
    background: var(--layout-surface);
    border-right-color: var(--layout-surface-border);
    box-shadow: 2px 0 12px color-mix(in srgb, var(--text-primary) 8%, transparent);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
}

body[data-layout-version="modern"][data-rail] .search-button {
    transition:
        background-color var(--layout-transition),
        border-color var(--layout-transition),
        color var(--layout-transition),
        transform var(--layout-transition);
}

body[data-layout-version="modern"][data-rail] .whats-new-btn {
    background: transparent;
    border-top-color: var(--layout-surface-border);
}
/* ── Mobile: fall back to centered bottom bar ────────────────────────────────── */
@media (max-width: 768px) {
    body[data-rail] .button-container {
        position: fixed;
        left: 50%;
        top: auto;
        bottom: 2rem;
        transform: translateX(-50%);
        width: min(52rem, calc(100vw - 2.5rem));
        max-width: 100%;
        height: auto;
        flex-direction: row;
        flex-wrap: wrap;
        justify-content: center;
        padding: var(--space-3, 0.7rem) 0 0;
        gap: 0;
        background: transparent;
        border-right: none;
        border: 0;
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
        box-shadow: none;
        border-radius: 0;
    }

    /* undo display:contents so groups become rows again */
    body[data-rail] .btn-group {
        display: flex;
        flex-direction: row;
        width: auto;
        padding: 0;
        gap: var(--space-2, 0.55rem);
    }

    /* reset button ordering and spacers */
    body[data-rail] #quick-add-toolbar-btn,
    body[data-rail] #search-button,
    body[data-rail] #finders-button,
    body[data-rail] #commands-button,
    body[data-rail] #recent-bookmarks-button,
    body[data-rail] #tag-cloud-toggle-btn,
    body[data-rail] #help-button {
        order: unset;
        margin-top: 0;
    }

    /* tag cloud: back into toolbar flow on mobile */
    body[data-rail] #tag-cloud-toggle-btn.search-button {
        position: static;
        left: auto;
        right: auto;
        bottom: auto;
        width: auto;
        padding: var(--space-2, 0.62rem) var(--space-3, 0.82rem);
        margin: 0;
        transform: none;
        border-top: none;
    }

    /* cheatsheet: back into normal flow on mobile */
    body[data-rail] #help-button {
        position: static;
        bottom: auto;
        left: auto;
        width: auto;
        z-index: auto;
    }

    body[data-rail] .search-button {
        padding: var(--space-2, 0.62rem) var(--space-4, 0.92rem);
        width: auto;
        height: auto;
        border-radius: var(--radius-6, 0.65rem);
        border: 1px solid var(--border-primary);
        background: var(--button-secondary-bg, var(--background-secondary));
    }

    body[data-rail] .search-button:hover {
        transform: none;
    }

    body[data-rail] .container {
        margin-left: 0;
    }

    body[data-rail] .dashboard-section.section-content {
        padding-bottom: 8rem;
    }

    body[data-rail] .page-nav-btn {
        font-size: var(--font-size-controls);
        padding: var(--space-1-5, 0.4rem) var(--space-2, 0.6rem);
    }

    body[data-rail] .page-tab-dot {
        width: 0.45em;
        height: 0.45em;
        margin-right: 0.35em;
    }

    body[data-rail] .page-tab-icon {
        display: inline;
    }

    body[data-rail] .page-nav-btn.active .page-tab-dot {
        box-shadow: none;
        width: 0.45em;
        height: 0.45em;
    }

    body[data-rail] .tag-cloud-toggle-btn.search-button {
        left: auto;
        right: var(--corner-fab-edge, 1.5rem);
        bottom: calc(var(--corner-fab-edge, 1.5rem) + var(--corner-fab-height, 2.75rem) + var(--corner-fab-gap, 0.4rem));
        width: auto;
        padding: var(--space-2, 0.62rem) var(--space-3, 0.82rem);
        margin: 0;
        transform: none;
    }

    body[data-rail] #tag-cloud-toggle-btn.search-button {
        position: static;
        right: auto;
        bottom: auto;
    }

    body[data-rail] .whats-new-btn {
        left: auto;
        right: var(--corner-fab-edge, 1.5rem);
        bottom: var(--corner-fab-edge, 1.5rem);
        width: auto;
        height: auto;
        padding: var(--space-2, 0.62rem) var(--space-3, 0.72rem);
        border-radius: var(--radius-6, 0.65rem);
        border-top: 1px solid var(--border-primary);
    }

    body.no-animations[data-rail] .button-container {
        transform: translateX(-50%) !important;
    }

    body[data-rail] .search-flow-hint {
        position: absolute;
        left: 0;
        right: 0;
        top: auto;
        bottom: calc(100% + 0.85rem);
        max-width: none;
        justify-content: center;
        align-items: center;
    }

    body[data-rail] .search-flow-hint-text {
        flex-wrap: nowrap;
        justify-content: center;
        text-align: center;
    }

    body[data-rail] .search-flow-hint-text > .sfh-seg:nth-child(even) {
        display: inline;
    }

    body[data-rail] .side-rail-legend {
        display: none !important;
    }
}

/* ==== css/dashboard-inline-edit-surface.css ==== */
/* Inline edit opaque surfaces — loaded last; wins over layout tokens. */

body.bookmark-inline-edit-active {
    --inline-edit-panel-bg: var(--background-primary);
    --inline-edit-field-bg: var(--background-secondary);
}

@supports (background: rgb(from red r g b)) {
    body.bookmark-inline-edit-active {
        --inline-edit-panel-bg: rgb(from var(--background-primary) r g b / 1);
        --inline-edit-field-bg: rgb(from var(--background-secondary) r g b / 1);
    }
}

body.bookmark-inline-edit-active .bookmark-link.bookmark-inline-editing {
    background: var(--inline-edit-panel-bg);
    border-radius: var(--radius-5, 8px);
    padding: var(--space-1-5, 0.35rem);
    box-shadow: 0 16px 48px color-mix(in srgb, var(--text-primary) 22%, transparent);
}

body.bookmark-inline-edit-active .layout-launcher .bookmark-link.bookmark-inline-editing {
    padding: 0;
    background: transparent;
    box-shadow: none;
}

body.bookmark-inline-edit-active .bookmark-inline-form {
    background: var(--inline-edit-panel-bg);
    background-image: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

body.bookmark-inline-edit-active .bookmark-inline-form .bookmark-inline-input,
body.bookmark-inline-edit-active .bookmark-inline-form .bookmark-inline-select,
body.bookmark-inline-edit-active .bookmark-inline-form .bookmark-inline-textarea,
body.bookmark-inline-edit-active .bookmark-inline-form .bookmark-inline-action-btn,
body.bookmark-inline-edit-active .bookmark-inline-form .bookmark-inline-icon-preview {
    background: var(--inline-edit-field-bg);
}

/* ==== css/reduced-motion.css ==== */
/* Global reduced-motion honouring.
 *
 * Animation suppression used to be opt-in per component: each effect paired a
 * `body.no-animations` rule (the in-app setting) with a matching
 * prefers-reduced-motion block. That left gaps — newer stylesheets such as
 * dashboard-tag-cloud.css and layout-side-rail.css never got the second half,
 * so an infinitely pulsing FAB kept running for someone who had asked their OS
 * to stop exactly that.
 *
 * The OS preference is an accessibility signal, not a styling preference, so it
 * wins outright rather than being merged with the in-app toggle. There is
 * deliberately no escape hatch: animationsEnabled defaults to true and is a
 * plain bool, so "explicitly on" is indistinguishable from "never touched" —
 * any opt-out would fire for every default user and undo the whole rule.
 *
 * Loaded last so this beats the `!important` declarations components already
 * use. Durations are near-zero rather than `animation: none`, because code that
 * waits on animationend (see dashboard-visual.js showHealthAlert) still needs
 * the event to fire; `none` would strand those listeners.
 */

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        animation-delay: 0s !important;
        transition-duration: 0.01ms !important;
        transition-delay: 0s !important;
        scroll-behavior: auto !important;
    }
}

/* ==== css/fonts.css ==== */
/* Self-hosted Source Code Pro (variable font) — replaces the Google Fonts CDN.
 * One woff2 per subset covers all weights (200–900). Keeps the app offline-first
 * and avoids a render-blocking third-party request. */

/* latin-ext (accented Latin, extra currency/symbol glyphs) */
@font-face {
    font-family: 'Source Code Pro';
    font-style: normal;
    font-weight: 200 900;
    font-display: swap;
    src: url('/static/fonts/source-code-pro-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* latin (ASCII + common punctuation/symbols) */
@font-face {
    font-family: 'Source Code Pro';
    font-style: normal;
    font-weight: 200 900;
    font-display: swap;
    src: url('/static/fonts/source-code-pro-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
