/* Shared slide-in drawer (F5 consolidation, ADR-125).

   One bespoke base for the right-side drawers that used to copy-paste the same
   CSS into per-feature inline <style> blocks. The look is kept pixel-identical
   to those drawers. New behavior: HubSpot-style PUSH at >=1200px — the panel
   slides in and the main content resizes beside it and stays interactive, with
   no scrim. Below 1200px it falls back to the old OVERLAY + scrim.

   The push line moved from 992px to 1200px on 2026-07-28. What the old line
   missed is that the sidebar only joins the arithmetic at >=1024px: below that
   it is off-canvas and costs nothing, at or above it holds a 256px column
   (--sidebar-width, tokens.css) that the content never gets back. So the worst
   viewport under the old rule was not 992 — it was 1024, where the 256px
   sidebar and the panel and the 24px .global-app-content gutters left about 240px to
   read in. Page content that sits in two columns has nowhere to go at that
   width; the availability page was cutting its working-hours dropdowns in half
   with no scrollbar to reach them.

   1200px is the floor being accepted: 1200 - 256 - 500 - 48 leaves ~396px with
   the sidebar out, ~596px on the collapsed 56px rail. Narrower than that,
   overlaying is the honest answer.

   EVERY drawer in the app is 500px (Nick, 2026-07-29 — design frozen). One
   token, no per-instance override: widths used to be passed per template to
   skillbridge.drawer.open(), which drifted to nine different values between
   380px and 640px. That capability is gone from drawer.js on purpose, so a new
   template cannot reintroduce the drift. Wide content adapts INSIDE the drawer
   (its own overflow-x: auto container); the drawer never widens to fit it.
   z comes from the ADR-125 ladder: scrim var(--z-scrim), panel var(--z-modal). */

:root {
    --drawer-width: 500px;
}

/* How far an open drawer has pushed the content in, or 0px when nothing is
   pushing — the push below is the only thing that sets it.

   It is published on .global-app-layout, not on the .global-app-content the push moves,
   because .global-app-content is a DEAD END for this: it is the sticky .global-app-header's
   SIBLING, and custom properties inherit down, never sideways. .global-app-layout is
   the nearest element above both, and it is the one carrying the push classes,
   so a rule in either child can read how far the content moved.

   The one reader today is the band the pinned header paints
   (components/page-header.css, "--pinned"): the bar it hangs off stays full width
   while the content narrows, so the band has to subtract this or it overhangs
   the card by the drawer's whole width. Reading the same token the push is
   written from is what keeps them in step — a band that hardcoded 500px would
   be wrong the day the drawer resizes, and silently, since nothing about the
   drawer itself would look off. */
.global-app-layout {
    --content-push: 0px;
}

body.is-drawer-lock {
    overflow: hidden;
}

.global-drawer-scrim {
    position: fixed;
    top: var(--drawer-top);
    right: 0;
    bottom: 0;
    left: var(--sidebar-width, 0px);
    background: rgba(17, 24, 39, .12);
    backdrop-filter: blur(2.5px);
    -webkit-backdrop-filter: blur(2.5px);
    z-index: var(--z-scrim);
    opacity: 0;
    transition: opacity .2s ease;
    pointer-events: none;
}

.global-drawer-scrim.is-open {
    opacity: 1;
    pointer-events: auto;
}

.global-drawer-panel {
    position: fixed;
    top: var(--drawer-top);
    right: 0;
    bottom: 0;
    /* The floor is 100%, not the 100vw the offcanvas skin uses: this panel is
       position:fixed, so 100% is the viewport minus the scrollbar, while 100vw
       includes it. Below 500px that difference is a scrollbar's worth of
       overflow; the skin keeps 100vw because that is Bootstrap's own rule. */
    width: min(var(--drawer-width), 100%);
    background: var(--bs-body-bg);
    /* Overlay-mode dress: below the push line this panel floats over the page
       behind a scrim, so it wears the overlay rung like any dialog. At >=1200px
       it stops floating and the PUSH block below re-dresses it in the page
       containers' own skin (Nick, 08-17: an open drawer is just another
       container on the page, not something hovering over it). */
    box-shadow: var(--shadow-modal);
    z-index: var(--z-modal);
    display: flex;
    flex-direction: column;
    transform: translateX(100%);
    transition: transform .2s ease;
}

.global-drawer-panel.is-open {
    transform: translateX(0);
}

/* A closing panel still covers the page for its .2s slide out, and a tap there
   would land on a drawer that is already gone. */
.global-drawer-panel:not(.is-open) {
    pointer-events: none;
}

/* Inset top divider — used by the payments and opportunity drawers. */
.global-drawer-panel--inset {
    border-top: 1px solid rgba(15, 23, 42, .08);
    box-shadow: var(--shadow-modal),
                inset 0 6px 6px -6px rgba(15, 23, 42, .12);
}

.global-drawer-body {
    padding: var(--spacing-mdlg);
    /* Zero extra on desktop; the home bar on a phone. */
    padding-bottom: calc(var(--spacing-mdlg) + var(--safe-bottom));
    overflow-y: auto;
    overflow-x: hidden;
    overscroll-behavior: contain;
    flex: 1 1 auto;
}

.global-drawer-sub {
    font-size: var(--font-size-md);
    /* Slate-500 secondary text, shared via tokens.css :root (not --color-text-muted,
       which is a lighter #9ca3af). */
    color: var(--color-text-secondary);
    margin-bottom: var(--spacing-mdlg);
}

/* ── HEAD + CLOSE ──────────────────────────────────────────────────────────
   The one drawer header: the drawer's name on the left, the X on the right,
   pinned above the scrolling .global-drawer-body. Write it with the shared partial
   (shared/components/drawer_header.html), not by hand — the fleet had five
   different header classes and five different ways out (an X, a "Done"
   button, a Bootstrap .btn-close, a footer Cancel, and eight drawers with no
   header affordance at all), so the way to leave a drawer changed from page
   to page.

   The X matters most in PUSH mode: at >=1200px the scrim is hidden and the
   page stays live beside the panel, so clicking off it does nothing and this
   button (with the root's Escape handler) is the only way out.

   Borderless on purpose — .global-icon-btn's bordered circle would put the
   close on the same footing as the drawer's own actions, and this is chrome
   for leaving the panel, not one of its choices. */
.global-drawer-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--spacing-smd);
    padding: var(--spacing-md) var(--spacing-mdlg);
    border-bottom: 1px solid var(--color-border-subtle);
    flex-shrink: 0;
}

/* Grows so the title hugs the left edge (or the back arrow) with the X pinned
   right — space-between alone would float it to the center once the head holds
   three children. min-width lets a long title truncate instead of pushing. */
.global-drawer-head__text {
    flex: 1 1 auto;
    min-width: 0;
}

/* An <h2>, so heading navigation finds the open drawer. That tag costs us two
   fixes. It brings its own big size and bottom margin, reset below. And main.css
   styles every h2 from the tenant's subheading tokens, which base/base.html sets
   per org — so without the four body-token lines here, a tenant whose brand sets
   subheading type (Fortis: Helvetica Neue, uppercase) would show every drawer
   title in that font, ALL CAPS. Those four only look redundant. They are what
   keeps this title plain for every tenant, so leave them in place. */
.global-drawer-head__title {
    margin: 0;
    font-family: var(--font-family);
    font-size: var(--font-size-md);
    font-style: var(--font-style-body);
    font-weight: var(--font-weight-medium);
    letter-spacing: var(--letter-spacing-body);
    line-height: 1.5;
    text-transform: var(--text-transform-body);
    color: var(--color-text);
}

.global-drawer-head__sub {
    margin: 0;
    font-size: var(--font-size-sm);
    color: var(--color-text-secondary);
}

/* The back arrow (drawer_header.html's back_click param) shares the X's whole
   recipe: two quiet icon buttons bracketing the title, one look. */
.global-drawer-head__back,
.global-drawer-close {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    padding: 0;
    font-size: 16px;
    color: var(--color-text-muted);
    background: transparent;
    border: 0;
    border-radius: var(--radius-md);
    cursor: pointer;
}

.global-drawer-head__back:hover,
.global-drawer-close:hover {
    color: var(--color-text);
    background: var(--color-surface-subtle);
}

.global-drawer-head__back:focus-visible,
.global-drawer-close:focus-visible {
    outline: 2px solid var(--color-focus-ring);
    outline-offset: 2px;
}

/* ── OVERLAY variant ───────────────────────────────────────────────────────
   For the persistent, teleported drawers that open ON TOP of another modal
   (e.g. the add-exception form launched from inside the settings modal). Unlike
   the push drawer above — ephemeral, content-level, pushes the page beside it —
   this one stays in the DOM and slides on .is-open, always overlays (never
   pushes, so it is safe outside .global-app-layout and never trips the JS reconcile),
   dims the whole viewport, and clears the modal rung. It reuses .global-drawer-panel's
   box (position/width/bg/shadow/slide) and the shared .global-drawer-body / -title /
   -sub; only the persistent + modal-level bits differ. The closed state is
   visibility:hidden so the off-screen fields stay out of the tab order. */
.global-drawer-overlay-scrim {
    position: fixed;
    inset: 0;
    background: rgba(17, 24, 39, .12);
    backdrop-filter: blur(2.5px);
    -webkit-backdrop-filter: blur(2.5px);
    z-index: var(--z-popover);
    opacity: 0;
    visibility: hidden;
    transition: opacity .2s ease, visibility .2s ease;
}

.global-drawer-overlay-scrim.is-open {
    opacity: 1;
    visibility: visible;
}

.global-drawer-panel--overlay {
    z-index: calc(var(--z-popover) + 1);
    visibility: hidden;
    transition: transform .2s ease, visibility .2s ease;
}

.global-drawer-panel--overlay.is-open {
    visibility: visible;
}

@media (prefers-reduced-motion: reduce) {
    .global-drawer-overlay-scrim,
    .global-drawer-panel--overlay {
        transition: none;
    }
}

/* ── Bootstrap-offcanvas skin ──────────────────────────────────────────────
   Some drawers stay on Bootstrap's .offcanvas — its JS owns show/hide, the
   backdrop, the focus trap, and the HTMX-loaded bodies, so they aren't worth
   rebuilding on the custom drawer. This skin gives them the shared drawer LOOK:
   the overlay-mode panel shadow (this family always overlays behind Bootstrap's
   backdrop, so it keeps the halo the push drawers drop at >=1200px), 1.25rem
   body padding, and a top edge that starts BELOW the main header instead of
   sliding up over it. A stock offcanvas is top:0/full-height, which would cover
   the sticky header (and hide its own title behind it); starting at the header's
   bottom edge keeps the header visible and clickable. It stays AT that edge —
   the extra --spacing-lg drop is a push-mode dress; this family always floats,
   like the push drawers under 1200px, and floating drawers stay flush.
   Behavior, header, and close button stay Bootstrap's; width does NOT — it
   reads the same --drawer-width token as the push drawers so the two families
   can't diverge.
   The 100vw floor is Bootstrap's own small-screen behavior, kept. */
.offcanvas.global-drawer-offcanvas {
    top: var(--drawer-top);
    height: auto;
    bottom: 0;
    width: min(var(--drawer-width), 100vw);
    box-shadow: var(--shadow-modal);
}

.global-drawer-offcanvas .offcanvas-body {
    padding: var(--spacing-mdlg);
}

/* ── DOCK marker ───────────────────────────────────────────────────────────
   No rule below sets it: .drawer-panel--dock styles nothing, it only marks a
   panel for drawer.js. It is documented here because this is where you look
   for it.

   A PERSISTENT push drawer that lives in the page full-time so its Alpine state
   survives htmx repaints of the content under it (the opportunities view-settings
   drawer). It looks and pushes exactly like the ephemeral push drawer, but two
   bits differ so a permanent panel doesn't jam the shared reconcile:
     • drawer.js panels() skips it (the :not() in its selector), so its
       always-present node never pins the ephemeral push on;
     • it rides its OWN push class (.is-dock-drawer-open, toggled by drawer.dock())
       instead of .is-drawer-open, so an ephemeral drawer opening/closing beside it
       can't clear its push, and vice-versa.
   It carries no width rule of its own: it inherits .global-drawer-panel's
   --drawer-width like every other drawer. It used to have a wider
   --dock-drawer-width token; that was the last per-family width and it went
   with the rest of the drift (2026-07-29). */

/* PUSH (>=1200px): the open drawer resizes the content beside it; no scrim. */
@media (min-width: 1200px) {
    .global-drawer-scrim {
        display: none;
    }

    /* A pushed-in drawer is not an overlay — it sits IN the page next to the
       cards, so it dresses like one: .global-card's hairline + resting shadow,
       and the page's own top gap (Nick, 08-17). Every container starts
       --page-gutter below the header — the .global-app-content padding — so the
       drawer does too: its top edge lands on the content line and is finished
       like a container's, full border and the card corner on the one corner it
       exposes. The right and bottom edges stay flush — it is still a drawer.
       The halo (and the flush top) survive only where the panel truly floats:
       under 1200px, and the --overlay variant, which opens above a modal
       behind its own scrim at every width.

       No inset re-dress for --inset here: that band sells content tucking
       under the header, and up here nothing tucks — the (0,2,0) selector below
       outweighs the variant's (0,1,0) base, so both wear the plain card skin. */
    .global-drawer-panel:not(.global-drawer-panel--overlay) {
        top: calc(var(--drawer-top) + var(--page-gutter));
        border-top: 1px solid var(--color-border);
        border-top-left-radius: var(--radius-card);
        border-left: 1px solid var(--color-border);
        box-shadow: var(--shadow-sm);
    }

    /* Resize only the CONTENT beside the drawer — NOT .global-app-main, which also holds
       the sticky .global-app-header. Pushing .global-app-main shifted the header sideways too;
       the header must stay full-width above the drawer (which opens below it at
       --drawer-top). width:100% + max-width:calc(100% - drawer) resolves to the
       shrunk width (border-box app-wide) and margin-right fills the gap.
       Both push classes get the same offset because both push the same distance —
       every drawer is --drawer-width wide, so the ephemeral and dock pushes are
       one rule, not two that have to be kept in step.

       The distance is named first and consumed second so that a rule elsewhere
       in the layout can read it: see --content-push at the top of this file.
       Only the header stays put — anything the header PAINTS over the content
       has to move with the content. */
    .global-app-layout.is-drawer-open,
    .global-app-layout.is-dock-drawer-open {
        --content-push: var(--drawer-width);
    }

    .global-app-layout.is-drawer-open .global-app-content,
    .global-app-layout.is-dock-drawer-open .global-app-content {
        margin-right: var(--content-push);
        max-width: calc(100% - var(--content-push));
    }

    /* Content stays scrollable in push mode — the lock only matters for overlay. */
    body.is-drawer-lock {
        overflow: visible;
    }
}

/* OVERLAY (<1200px): panel over full-width content, scrim visible. */
@media (max-width: 1199.98px) {
    .global-drawer-scrim {
        left: 0;
    }
}

/* ── Phone (<768px) ─────────────────────────────────────────────────────────
   Every drawer is a bottom sheet. drawer.js adds `global-drawer-panel--sheet`
   to each panel while this query matches, so no template carries a per-page
   class. The 1200px push/overlay split above is untouched. Stays LAST. */
@media (max-width: 767.98px) {
    .global-drawer-panel {
        /* The sheet stands on the bottom bar (tokens.css --bottom-bar-h). */
        inset: auto 0 var(--bottom-bar-h) 0;
        /* No width here: the base rule's min(var(--drawer-width), 100%) is
           already the phone width. max() picked the 500px token over 100% and
           the sheet ran off the right edge. Past 500px the sheet centres. */
        margin-inline: auto;
        height: auto;
        max-height: calc(100dvh - var(--header-bottom) - var(--bottom-bar-h));
        transform: translateY(100%);
        border-radius: var(--radius-lg) var(--radius-lg) var(--radius-none) var(--radius-none);
        /* translateY(100%) parks the closed sheet ON the bar (its bottom edge is
           the bar's top), so its head drew over the bottom bar. Hidden while
           closed, the same as the sidebar sheet; visibility rides the transform's
           transition so the slide still shows. */
        visibility: hidden;
        transition: transform .2s ease, visibility .2s ease;
    }

    .global-drawer-panel.is-open {
        transform: translateY(0);
        visibility: visible;
    }

    /* The sheet is its own scroller; overscroll-behavior-y sits beside
       overflow-y (test_overscroll_containment keys on file + selector). */
    .global-drawer-panel--sheet {
        overflow-y: auto;
        overscroll-behavior-y: contain;
        /* The bar under the sheet pays the home indicator. */
        padding-bottom: var(--spacing-lg);
    }

    .global-drawer-body {
        padding-bottom: var(--spacing-mdlg);
    }

    /* The bell's Bootstrap offcanvas is a bottom sheet on the bar too. Left
       and right at 0 with the token width and auto margins = centred, which
       under 500px is the whole screen (the same shape .global-drawer-panel
       takes above; test_drawer_width lets only the token size a drawer). */
    .offcanvas.global-drawer-offcanvas {
        top: auto;
        bottom: var(--bottom-bar-h);
        left: 0;
        right: 0;
        margin-inline: auto;
        height: auto;
        max-height: calc(100dvh - var(--header-bottom) - var(--bottom-bar-h));
        border: 0;
        border-radius: var(--radius-lg) var(--radius-lg) var(--radius-none) var(--radius-none);
    }

    /* The closed transform lives on a one-class selector on purpose: it
       outranks Bootstrap's `.offcanvas-end` only by source order, and stays
       UNDER Bootstrap's own `.offcanvas.showing` / `.offcanvas.show`, so the
       open and closing states stay Bootstrap's without this file spelling
       its state classes. */
    .global-drawer-offcanvas {
        transform: translateY(100%);
    }

    /* The drag-handle pill sits centred on the head's top edge. It hangs off
       the head, not the panel: test_drawer_width reads every `width` on a
       `global-drawer-panel` selector as the drawer's size, and a 36px pill
       is not that. The head grows at the top to make room for it. */
    .global-drawer-head {
        position: relative;
        padding-top: var(--spacing-mdlg);
    }

    .global-drawer-head::before {
        content: "";
        position: absolute;
        top: var(--spacing-sm);
        left: 50%;
        width: var(--sheet-handle-w);
        height: var(--sheet-handle-h);
        translate: -50%;
        border-radius: var(--radius-pill);
        background: var(--color-grey-300);
    }

    .global-drawer-close {
        width: var(--control-h-touch);
        height: var(--control-h-touch);
        color: var(--color-text-secondary);
    }

    .global-drawer-head__back {
        width: var(--control-h-touch);
        height: var(--control-h-touch);
        color: var(--color-text-secondary);
    }

    /* Below the header nothing is sticky, so the scrim starts at the top. */
    .global-drawer-scrim {
        top: 0;
    }

    /* A panel that opts out of the sheet (drawer.js skips it, so it never
       wears --sheet) keeps the overlay shape on a phone: the right edge, the
       slide in from the right, the phone's own width, clear of the bottom bar.
       No visibility rule here on purpose: the bare .global-drawer-panel rule
       above already hides it closed and .is-open shows it, the same way
       opportunity-cards.css layers its own side panel under that rule. */
    .global-drawer-panel--side {
        inset: var(--drawer-top) 0 var(--bottom-bar-h) auto;
        width: min(var(--drawer-width), 100%);
        margin-inline: 0;
        max-height: none;
        border-radius: var(--radius-none);
        transform: translateX(100%);
    }

    .global-drawer-panel--side.is-open {
        transform: translateX(0);
    }

    /* No drag handle: the panel slides in from the side, not up from the bar. */
    .global-drawer-panel--side .global-drawer-head {
        padding-top: var(--spacing-md);
    }

    .global-drawer-panel--side .global-drawer-head::before {
        display: none;
    }
}
