/* ============================================================
   ONE SCROLLBAR FOR THE WHOLE APP
   Monochrome, dark + light, transparent track, no stepper buttons.

   Loaded by every page that renders app UI (index, login, app, book,
   saas-admin). It lives here and nowhere else: a second copy becomes a
   second design the moment one of them is touched.

   TRAP - do NOT add `scrollbar-width` or `scrollbar-color` to a scroll
   container anywhere in the app. Chromium ignores EVERY ::-webkit-scrollbar
   rule on an element as soon as one of those two standard properties is set
   on it, and falls back to the native bar (grey block, arrow buttons at both
   ends). This app used to set both right beside every ::-webkit-scrollbar
   block, in ~28 places, which is why none of that styling was ever visible
   on screen. The one allowed use is `scrollbar-width: none` to HIDE a bar
   entirely (tab strips, pill rows) - hiding works, decorating does not.

   Firefox has no ::-webkit-scrollbar and needs those same properties, so it
   gets them from the @supports block at the bottom, where Chromium cannot
   see them.
   ============================================================ */
::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}

::-webkit-scrollbar-track,
::-webkit-scrollbar-corner {
    background: transparent;
}

/* The native steppers are the ugliest part of the default bar. */
::-webkit-scrollbar-button {
    display: none;
    width: 0;
    height: 0;
}

::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.18);
    /* Transparent border + padding-box clip leaves a 6px pill in a 10px
       gutter, so the bar never sits flush against the content edge. */
    border: 2px solid transparent;
    background-clip: padding-box;
    border-radius: 999px;
}

/* Min length per axis, or a very long list shrinks the thumb to an
   ungrabbable nub. Scoped by orientation - a bare min-height would also hit
   the horizontal thumb, whose height is the bar itself. */
::-webkit-scrollbar-thumb:vertical {
    min-height: 36px;
}

::-webkit-scrollbar-thumb:horizontal {
    min-width: 36px;
}

/* `background` is a shorthand and resets background-clip, so every state has
   to restate the clip or the pill swells into its own border. */
::-webkit-scrollbar-thumb:hover {
    background: rgba(255, 255, 255, 0.30);
    background-clip: padding-box;
}

::-webkit-scrollbar-thumb:active {
    background: rgba(255, 255, 255, 0.42);
    background-clip: padding-box;
}

@media (prefers-color-scheme: light) {
    ::-webkit-scrollbar-thumb {
        background: rgba(0, 0, 0, 0.22);
        background-clip: padding-box;
    }

    ::-webkit-scrollbar-thumb:hover {
        background: rgba(0, 0, 0, 0.34);
        background-clip: padding-box;
    }

    ::-webkit-scrollbar-thumb:active {
        background: rgba(0, 0, 0, 0.46);
        background-clip: padding-box;
    }
}

/* Firefox only. `@supports not selector(::-webkit-scrollbar)` is false in
   Chromium (verified), so the properties that would kill everything above
   never reach it. */
@supports not selector(::-webkit-scrollbar) {
    html {
        scrollbar-width: thin;
        scrollbar-color: rgba(255, 255, 255, 0.22) transparent;
    }

    /* `scrollbar-color` inherits and reaches every scroll container from <html>
       above — `scrollbar-width` does NOT, so without this every container
       except the page itself kept the platform's fat default bar in a thin
       page. The universal selector carries no specificity, so the handful of
       elements that hide their bar with `scrollbar-width: none` (tab strips,
       pill rows) still win. */
    * {
        scrollbar-width: thin;
    }

    @media (prefers-color-scheme: light) {
        html {
            scrollbar-color: rgba(0, 0, 0, 0.26) transparent;
        }
    }

    /* The page bar below is hand-drawn; hide the native one here too. */
    html.has-overlay-scrollbar body {
        scrollbar-width: none;
    }
}

/* ============================================================
   THE PAGE'S OWN BAR, DRAWN OVER THE CONTENT

   A classic bar takes its width out of the scroll container's content box, so
   the page was 10px narrower while it was there and 10px wider the moment a
   dialog froze the scroll (`overflow: hidden` on <body>) — the whole layout
   jumped sideways on every settings open. Reserving the gutter for good
   (`scrollbar-gutter: stable`) fixes the jump but keeps the strip empty
   forever, so /index draws its page bar itself and floats it above the
   content, where it costs no layout: modules/overlay-scrollbar.js builds it,
   sets `has-overlay-scrollbar` and does the maths. The colours below are the
   native thumb's, one file, one design.

   Class-gated on purpose: every page loads this file, but only the one that
   mounts the module hides its native bar.
   ============================================================ */
html.has-overlay-scrollbar body::-webkit-scrollbar {
    width: 0;
    height: 0;
}

.app-scrollbar {
    position: fixed;
    /* Starts under the page header, which sticks to the top of the scroller and
       therefore always covers the window's top edge — --head-h is its measured
       height (modules/page-scroll.js). Same place body's own bar started when
       the header was still a fixed 51px topbar. */
    top: var(--head-h, 220px);
    right: 0;
    bottom: 0;
    width: 10px;
    /* Above the page and its floating buttons (1200), below the topbar (1500)
       and every overlay — those freeze the scroll, which hides this bar. */
    z-index: 1450;
    /* The track lies ON the content, so it must not swallow clicks meant for
       it: only the thumb takes the pointer. Clicking the track to page up/down
       is what that costs, and a dead strip down the full height of the page is
       the worse trade. */
    pointer-events: none;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.15s ease, visibility 0s linear 0.15s;
}

.app-scrollbar.is-visible {
    opacity: 1;
    visibility: visible;
    transition: opacity 0.15s ease;
}

.app-scrollbar__thumb {
    position: absolute;
    top: 0;
    right: 0;
    width: 100%;
    /* Same pill as the native thumb: transparent border + padding-box clip
       leave 6px inside the 10px gutter. border-box because this app has no
       global one and JS writes the height. */
    box-sizing: border-box;
    border: 2px solid transparent;
    background-clip: padding-box;
    background-color: rgba(255, 255, 255, 0.18);
    border-radius: 999px;
    pointer-events: auto;
    touch-action: none; /* the drag owns the gesture */
    transition: background-color 0.15s ease;
}

.app-scrollbar__thumb:hover {
    background-color: rgba(255, 255, 255, 0.30);
}

/* While a dialog freezes the page the bar stays exactly where it is — it just
   stops taking the pointer, because scrollTop still moves on a frozen
   container and a drag would scroll the page behind the dialog. It keeps the
   z-index it has, so the dialog's scrim dims it along with the rest of the
   page instead of leaving a bright pill floating over it. */
.app-scrollbar.is-frozen .app-scrollbar__thumb {
    pointer-events: none;
}

.app-scrollbar.is-dragging .app-scrollbar__thumb {
    background-color: rgba(255, 255, 255, 0.42);
}

/* Dragging the thumb must not select the text it is dragging across. */
html.app-scrollbar-dragging,
html.app-scrollbar-dragging body {
    user-select: none;
}

@media (prefers-color-scheme: light) {
    .app-scrollbar__thumb {
        background-color: rgba(0, 0, 0, 0.22);
    }

    .app-scrollbar__thumb:hover {
        background-color: rgba(0, 0, 0, 0.34);
    }

    .app-scrollbar.is-dragging .app-scrollbar__thumb {
        background-color: rgba(0, 0, 0, 0.46);
    }
}
