/* The app's ONE modal chrome (M-459) — the presentation behind `<x-platform::modal>`. Moved verbatim out of
 * quick-add.css, where it had grown up welded to the picker quick-add (M-104 → M-107); the values are
 * unchanged, only the ownership is. Every modal in the app — the picker quick-add, the todos jotter — draws
 * the same box, so a change to "what a modal looks like" is a change to this file (coding-standards §3).
 *
 * Token-styled throughout, so the dialog re-skins with the theme (incl. dark mode); the native top-layer and
 * `::backdrop` give the overlay for free — no hand-rolled scrim, no z-index arms race. Presentation only:
 * open/close lives in the shared `DialogController` base (§4). */

.platform-modal {
    width: min(38rem, calc(100vw - 2 * var(--platform-space-lg)));
    max-height: calc(100vh - 2 * var(--platform-space-lg));
    padding: 0;
    color: var(--platform-color-text);
    background: var(--platform-color-surface);
    border: 1px solid var(--platform-color-border);
    border-radius: var(--platform-radius-lg);
    box-shadow: var(--platform-shadow-lg, var(--platform-shadow-md));
    overflow: hidden;
}

.platform-modal::backdrop {
    background: rgba(0, 0, 0, 0.45);
}

.platform-modal__panel {
    display: flex;
    flex-direction: column;
    max-height: inherit;
}

.platform-modal__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--platform-space-md);
    padding: var(--platform-space-md) var(--platform-space-lg);
    border-bottom: 1px solid var(--platform-color-border);
}

/* A DIALOG THE USER HAS DRAGGED (M-713). Set by `movable_dialog_controller.js`, which writes only `left`/`top`
   inline — those are values, these are rules, and rules belong here (§4). Naming the class for the BEHAVIOUR
   rather than for `.platform-modal` is deliberate: the controller is generic and the four hand-rolled dialogs
   that opt in are not the shared chrome.

   Removing the class is also the whole of the reset. Setting `margin: 0` inline and removing it again does not
   round-trip — the CSSOM expands the shorthand into longhands that `removeProperty("margin")` leaves behind,
   so the dialog would come back stuck to the top-left on its next open. */
.platform-dialog-moved {
    position: fixed;
    margin: 0;
}

/* THE DRAG HANDLE (M-713) — keyed off the Stimulus TARGET, not off `.platform-modal__header`.
   Deliberate, and the reason is the three hand-rolled dialogs that opt in: dev-tools, the groups import and
   the schedule editor each have their own header class, so a class-based rule would need every opt-in to
   remember a second thing, and a forgotten one gives a draggable header with no cursor to say so. The
   attribute IS the definition of "this element is the handle"; one selector therefore covers every dialog
   that opts in, now and later.

   `touch-action: none` is what makes a touch drag possible at all — without it the browser claims the gesture
   for scrolling before a single `pointermove` is delivered. Scoped to the handle: the body keeps its own
   touch scrolling, which a modal taller than the screen needs.

   `user-select` belongs here rather than being toggled from JS: the controller calls `preventDefault()` on a
   drag it has accepted, and this covers the rest — a double-click on the title selecting the heading. The
   cursor is the whole affordance, and it is scoped to the desktop side of the split because that is the only
   side where the modal is draggable (see the controller). */
[data-movable-dialog-target="handle"] {
    user-select: none;
    touch-action: none;
}

@media (min-width: 48rem) {
    [data-movable-dialog-target="handle"] {
        cursor: grab;
    }

    [data-movable-dialog-target="handle"]:active {
        cursor: grabbing;
    }

    /* The controls inside a handle are not handles — the controller refuses to drag from them, and the cursor
       has to say the same thing or the ✕ looks like part of the grip. */
    [data-movable-dialog-target="handle"] button,
    [data-movable-dialog-target="handle"] a,
    [data-movable-dialog-target="handle"] input,
    [data-movable-dialog-target="handle"] select,
    [data-movable-dialog-target="handle"] textarea,
    [data-movable-dialog-target="handle"] label {
        cursor: pointer;
    }
}

.platform-modal__title {
    margin: 0;
    font-size: var(--platform-font-size-lg);
    font-weight: 600;
}

.platform-modal__close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    color: var(--platform-color-text-muted, var(--platform-color-text));
    background: none;
    border: 0;
    border-radius: var(--platform-radius-md);
    cursor: pointer;
}

.platform-modal__close:hover,
.platform-modal__close:focus-visible {
    background: var(--platform-color-surface-muted);
}

.platform-modal__close-icon {
    width: 1.25rem;
    height: 1.25rem;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.75;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* The body scrolls inside the fixed-height dialog, so a long field set never grows the modal past the
   viewport. The caller owns this element (a <div> for a quick-add nested in a host form, a real <form> for a
   standalone modal) and only adds the class — see the component's docblock for why. */
.platform-modal__body {
    padding: var(--platform-space-lg);
    overflow-y: auto;
}

/* Server validation summary (a flat message list); hidden until the owning controller fills it on a 422. */
.platform-modal__errors {
    margin: 0 0 var(--platform-space-md);
    padding-left: var(--platform-space-lg);
}

.platform-modal__errors[hidden] {
    display: none;
}

.platform-modal__actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--platform-space-sm);
    margin-top: var(--platform-space-lg);
}
