/* ========================================
   MODALS - VistaFly

   All modals share one open/closed mechanism. Both .is-open
   (booking, driven by global.js) and .active (video + form
   modals, driven by portfolio.js / contact-form.js) are
   honoured so every script keeps working.
   ======================================== */

.book-now-modal,
#video-modal,
.success-modal,
.error-modal {
    position: fixed;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--gutter);
    z-index: var(--z-modal);
    background: rgba(4, 4, 4, 0.88);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition:
        opacity var(--dur-mid) var(--ease-out),
        visibility var(--dur-mid);
}

.book-now-modal.is-open,
.book-now-modal.active,
#video-modal.is-open,
#video-modal.active,
.success-modal.is-open,
.success-modal.active,
.error-modal.is-open,
.error-modal.active {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
}

body.modal-open {
    overflow: hidden;
}


/* ============================================================
   BOOKING MODAL
   ============================================================ */
.book-now-modal .modal-container {
    position: relative;
    width: 100%;
    max-width: 900px;
    height: min(86vh, 900px);
    background: var(--text-hi);
    border-radius: var(--radius-lg);
    overflow: hidden;
    box-shadow: var(--shadow-xl);
    transform: translateY(12px) scale(0.985);
    transition: transform var(--dur-mid) var(--ease-out);
}

.book-now-modal.is-open .modal-container,
.book-now-modal.active .modal-container {
    transform: translateY(0) scale(1);
}

.book-now-modal .modal-content,
.booking-iframe {
    width: 100%;
    height: 100%;
    border: none;
}

.modal-close,
.close-modal {
    position: absolute;
    top: var(--space-4);
    right: var(--space-4);
    display: grid;
    place-items: center;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: rgba(6, 6, 6, 0.72);
    border: 1px solid var(--edge-soft);
    color: var(--text-hi);
    font-size: var(--fs-xl);
    line-height: 1;
    z-index: var(--z-raised);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    transition:
        background-color var(--dur-mid) var(--ease-out),
        color var(--dur-mid) var(--ease-out),
        border-color var(--dur-mid) var(--ease-out),
        transform var(--dur-mid) var(--ease-out);
}

.modal-close:hover,
.close-modal:hover {
    background: var(--bone-300);
    border-color: var(--bone-300);
    color: var(--text-on-bone);
    transform: scale(1.06);
}

@media (max-width: 720px) {
    .book-now-modal {
        padding: 0;
    }

    .book-now-modal .modal-container {
        max-width: none;
        height: 100svh;
        border-radius: 0;
    }
}


/* ============================================================
   VIDEO MODAL
   ============================================================ */
#video-modal {
    cursor: pointer;
}

/* ---- Size the player to the screen ----

   The modal used to be a flat 1400px. That was costing twice over:
   YouTube picks its stream from the player's width, and the smaller
   the real container, the harder js/portfolio.js has to downscale its
   oversized render — which is what shrinks YouTube's own controls.
   A bigger container improves both at once.

   So the width is now taken from the height available, converted back
   through the 16:9 the aspect box applies, and capped at 2200px.
   Everything that is not the player — the caption below, the padding
   — is subtracted first.

   This also fixes the overflow that used to push the close button off
   the top of a laptop screen. An earlier attempt at the same cap
   produced a 644px modal, under the 700px floor in portfolio.js,
   which switched the quality trick off entirely. The difference now
   is that the chrome being subtracted is far smaller: the close
   button moved onto the player and the caption was tightened, so
   ~112px comes off instead of ~240px. At a 682px viewport this lands
   near 950px — comfortably above the floor.

   Padding is --space-4 rather than --gutter for the same reason. On a
   wide screen --gutter is 5rem a side, and 160px of horizontal air
   around a video is 160px the video could have had.

   svh, not vh: on mobile vh is measured with the browser chrome
   retracted, so a vh-based cap still overflows behind a visible
   address bar. */
#video-modal {
    padding: var(--space-4);
}

#video-modal .modal-content {
    /* caption block + its margin */
    --vm-chrome: 7rem;

    position: relative;
    width: 100%;

    /* The max() floor is not optional: on a very short viewport the
       inner calc goes to zero or negative, and a negative max-width
       collapses the modal to nothing. */
    max-width: min(
        2200px,
        max(320px, calc((100svh - 2 * var(--space-4) - var(--vm-chrome)) * 16 / 9))
    );

    cursor: default;
    transform: scale(0.96);
    transition: transform var(--dur-mid) var(--ease-out);
}

/* A browser without svh drops the whole declaration above, including
   the 2200px cap, leaving the modal unbounded. The @supports guard
   means this only applies where that happens, so order is irrelevant. */
@supports not (height: 100svh) {
    #video-modal .modal-content {
        max-width: min(
            2200px,
            max(320px, calc((100vh - 2 * var(--space-4) - var(--vm-chrome)) * 16 / 9))
        );
    }
}

#video-modal.is-open .modal-content,
#video-modal.active .modal-content {
    transform: scale(1);
}

/* ---- Close control ----

   This used to sit --space-9 (64px) ABOVE the player, which put it in
   the vertical stack: the modal was then the player's height plus
   64px plus the caption, and on a laptop-height viewport the button
   was the part that fell off the top edge. A close control you cannot
   reach is worse than one that is slightly in the way.

   It now sits inside the player's top-right corner, so it costs no
   vertical space and stays visually part of the frame rather than
   floating in the backdrop beside it.

   It does share that corner with YouTube's own volume, captions and
   settings icons. Sitting outside in the gutter was tried to avoid
   exactly that and read as detached. The mitigation is inset rather
   than separation: ours is pushed to the extreme corner where
   YouTube's row ends, and YouTube's icons are only painted while its
   controls are showing, so for most of a playthrough this button is
   alone up there. */
#video-modal .close-modal {
    top: var(--space-3);
    right: var(--space-3);
    width: 40px;
    height: 40px;
}

#youtube-player,
#native-player {
    position: relative;
    padding-bottom: 56.25%;
    height: 0;
    border-radius: var(--radius-lg);
    overflow: hidden;
    background: #000;
    box-shadow: var(--shadow-xl);
}

/* ---- The oversized player box ----

   YouTube picks its stream from the player's own window.innerWidth,
   and at the modal's 1400px it was serving 360p. .yt-scale is laid
   out at 1920px and transformed down to fit; the iframe inside fills
   it, so the player believes it is 1920 wide while being painted
   smaller. js/portfolio.js owns the width/height/transform values —
   they are measured, so they cannot live here.

   All that is needed from the stylesheet is the positioning context
   and a guarantee the overflow is clipped: an unscaled 1920px box
   inside a 1400px modal would otherwise paint over the page for the
   frame before the transform lands. */
#youtube-player .yt-scale {
    position: absolute;
    top: 0;
    left: 0;
    transform-origin: top left;
}

#youtube-player {
    /* Overflow is already hidden below for the radius; stated again
       here because the scale box depends on it rather than merely
       benefiting from it. */
    overflow: hidden;
}

#youtube-player iframe,
#native-player video {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: none;
}

/* object-fit matters here: a rendition is 16:9 but the padding box is
   exactly 16:9 too, so this only ever bites on a source that is not —
   an ultrawide grade, say. Contain letterboxes it rather than cropping
   the frame the shot was composed for. */
#native-player video {
    object-fit: contain;
    background: #000;
}

/* [hidden] loses to the display value above without this. */
#youtube-player[hidden],
#native-player[hidden] {
    display: none;
}

/* Tightened from --space-5. The caption is the other half of the
   modal's vertical budget, and 24px of air above a three-line block
   is space the player would rather have on a short screen. */
.video-info {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    margin-block-start: var(--space-3);
    text-align: center;
    font-size: var(--fs-base);
    color: var(--text-mid);
}

.video-info-title {
    font-size: var(--fs-lg);
    font-weight: var(--weight-semi);
    letter-spacing: var(--track-tight);
    color: var(--text-hi);
}

.video-info-meta {
    font-family: var(--font-mono);
    font-size: var(--fs-2xs);
    letter-spacing: var(--track-wide);
    text-transform: uppercase;
    color: var(--text-lo);
}

/* Always present on YouTube entries, quiet enough to ignore. A video
   can be public and still refuse to play in an iframe — embedding
   turned off, or a music claim restricting it to youtube.com — and the
   iframe is cross-origin, so there is no error we can catch and react
   to. This is the fallback for a failure we cannot detect. */
.video-info-out {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    align-self: center;
    margin-block-start: var(--space-2);
    font-size: var(--fs-2xs);
    letter-spacing: var(--track-wide);
    color: var(--text-faint);
    transition: color var(--dur-fast) var(--ease-out);
}

.video-info-out:hover,
.video-info-out:focus-visible {
    color: var(--bone-300);
}

.video-info-out i {
    font-size: 0.8em;
}

@media (max-width: 800px) {
    #video-modal .close-modal {
        top: var(--space-2);
        right: var(--space-2);
        width: 36px;
        height: 36px;
    }
}


/* ============================================================
   FEEDBACK MODALS — success / error
   ============================================================ */
.success-modal-content,
.error-modal-content {
    width: 100%;
    max-width: 400px;
    padding: var(--space-9) var(--space-7);
    text-align: center;
    background: var(--ground-200);
    border: 1px solid var(--edge-soft);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-xl);
    transform: translateY(14px);
    transition: transform var(--dur-mid) var(--ease-out);
}

.success-modal.is-open .success-modal-content,
.success-modal.active .success-modal-content,
.error-modal.is-open .error-modal-content,
.error-modal.active .error-modal-content {
    transform: translateY(0);
}

.success-modal-content {
    border-color: var(--edge-bone);
}

.error-modal-content {
    border-color: var(--signal-live-edge);
}

.success-icon,
.error-icon {
    font-size: 2.75rem;
    line-height: 1;
    margin-block-end: var(--space-5);
    animation: icon-pop var(--dur-slow) var(--ease-spring);
}

.success-icon { color: var(--bone-300); }
.error-icon   { color: var(--signal-live); }

.success-message h3,
.error-message h3 {
    font-family: var(--font-display);
    font-variation-settings: var(--display-axes);
    font-size: var(--fs-xl);
    letter-spacing: var(--track-tighter);
    margin-block-end: var(--space-3);
}

.success-message h3 { color: var(--bone-300); }
.error-message h3   { color: var(--signal-live); }

.success-message p,
.error-message p {
    font-size: var(--fs-base);
    color: var(--text-mid);
    line-height: var(--leading-body);
}

/* Secondary to the confirmation itself. The modal auto-dismisses
   after 4s (see js/contact-form.js), so this is a signpost the
   visitor may catch rather than something to read -- it must not
   compete with the line above it. */
.success-message .success-aside {
    margin-block-start: var(--space-3);
    font-size: var(--fs-xs);
    color: var(--text-lo);
}

.success-message .success-aside a {
    color: var(--bone-300);
    text-decoration: underline;
    text-underline-offset: 2px;
}

.error-close-btn {
    margin-block-start: var(--space-6);
    padding: var(--space-3) var(--space-7);
    border-radius: var(--radius-pill);
    background: var(--signal-live);
    color: var(--text-hi);
    font-size: var(--fs-base);
    font-weight: var(--weight-semi);
    letter-spacing: var(--track-wide);
    transition: transform var(--dur-mid) var(--ease-out), opacity var(--dur-mid) var(--ease-out);
}

.error-close-btn:hover {
    transform: translateY(-2px);
    opacity: 0.9;
}

@keyframes icon-pop {
    0%   { transform: scale(0.7); opacity: 0; }
    60%  { transform: scale(1.08); opacity: 1; }
    100% { transform: scale(1); }
}
