/* =============================================================================
   Bottom-sheet presentation for Bootstrap modals.
   =============================================================================

   Applied via the .is-sheet class that sheet_controller.js adds on
   show.bs.modal when the pointer is coarse. Bootstrap keeps owning the modal's
   mechanics; these rules only change where it sits and how it arrives.

   This deliberately overrides mobile.css, which today pins .modal-dialog to the
   TOP edge with square corners so a mobile modal reads as a full-screen page.
   A sheet is the opposite gesture: it rises from the bottom edge, keeps the page
   visible above it, and can be thrown back down. The .modal.is-sheet selector
   (0,2,0) outranks mobile.css's bare .modal-dialog (0,1,0), so no !important is
   needed anywhere in this file.

   Colours and easing curves come from static/css/tokens/semantic.css and are
   enforced by tests/unit/test_design_token_ratchet.py, which fails on a raw hex
   or an unnamed cubic-bezier. It does NOT check lengths, weights or ratios, so
   the plain values below are exactly that: plain values this file owns, not
   tokens it forgot to use.

   JavaScript: static/js/controllers/esm/sheet_controller.js
   ========================================================================== */

.modal.is-sheet .modal-dialog {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    max-width: none;
    margin: 0;
    /* The dialog is the drag target, so the transform origin has to be the edge
       it is anchored to or a partial drag would scale rather than slide. */
    transform-origin: bottom center;
}

.modal.is-sheet .modal-content {
    /* Leave the page visible above the sheet. svh rather than vh so a mobile
       browser's collapsing URL bar does not change the sheet's height mid-drag. */
    max-height: 92svh;
    display: flex;
    flex-direction: column;
    border: 0;
    border-radius: var(--border-radius-lg) var(--border-radius-lg) 0 0;
    background-color: var(--bg-primary);
    box-shadow: var(--shadow-lg);
    /* themes/skin.css pads .modal by the safe-area insets so the sheet clears
       the status bar at the top; the home indicator needs the same clearance at
       the bottom, and only the content knows where its own bottom edge is. */
    padding-bottom: var(--safe-bottom);
}

/* svh is newer than the shell's floor, so keep a vh fallback. It has to be
   @supports-guarded rather than a plain preceding declaration: the value would
   otherwise fail at computed-value time and reset max-height to `none`. */
@supports not (height: 100svh) {
    .modal.is-sheet .modal-content {
        max-height: 92vh;
    }
}

/* The sheet's body scrolls inside it; the handle and header stay put. Without
   this the whole sheet would grow past its cap and push the footer off-screen. */
.modal.is-sheet .modal-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}

.modal.is-sheet .modal-footer {
    flex-shrink: 0;
}

/* ── Grab handle ───────────────────────────────────────────────────────────
   Injected by the controller, not present in any template. The visible bar is
   small, so the hit area is the padded wrapper — a 4px drag target would be
   unusable with a thumb. touch-action: none stops the browser claiming the
   vertical gesture for scrolling before the drag handler sees it. */
.modal.is-sheet .sheet-handle {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    /* ~44px of grabbable height around a 4px bar. */
    padding: var(--space-5) var(--space-4) var(--space-2);
    cursor: grab;
    touch-action: none;
}

.modal.is-sheet .sheet-handle:active {
    cursor: grabbing;
}

.modal.is-sheet .sheet-handle-bar {
    display: block;
    width: var(--space-10);
    height: var(--space-1);
    border-radius: var(--radius-pill);
    background-color: var(--border-dark);
}

/* A sheet with a handle does not need the header's top padding as well. */
.modal.is-sheet .sheet-handle + .modal-header {
    padding-top: var(--space-2);
    border-bottom: 0;
}

/* ── Motion ────────────────────────────────────────────────────────────────
   Rises from the bottom edge on --ease-emphasis, the same curve and duration as
   the mobile page push in motion.css, so a sheet and a navigation read as the
   same physical system rather than two unrelated animations.

   Bootstrap's own .modal.fade .modal-dialog transition is a translateY(-50px)
   drop from above; this replaces it wholesale rather than fighting it. */
.modal.is-sheet.fade .modal-dialog {
    transform: translateY(100%);
    transition: transform var(--dur-slow) var(--ease-emphasis);
}

.modal.is-sheet.show .modal-dialog {
    transform: none;
}

/* Reduced motion: the sheet still needs to arrive from somewhere the user can
   understand, but it should not travel. Fade in place instead. --dur-* are
   already zeroed by the token layer, so only the transform needs handling. */
@media (prefers-reduced-motion: reduce) {
    .modal.is-sheet.fade .modal-dialog {
        transform: none;
        opacity: 0;
        transition: opacity var(--dur-fast) linear;
    }
    .modal.is-sheet.show .modal-dialog {
        opacity: 1;
    }
}
