/* ================================================================
   breach — the five-part intrusion animation and its enforcement reversal.

   Deliberately kept in its own file while hf-example is being reworked, so
   nothing here collides with hf-example.css. Fold it in when the page around
   it is settled — there are no selectors in here that clash.

   All styling lives here; breach.js only toggles data-attributes on the host
   ([data-breach]) and draws to the canvas.
   ================================================================ */

.breach {
    position: relative;
    width: 100%;
    height: auto;
}

/* sitewide.css pins EVERY <canvas> — `canvas { position: fixed; inset: 0 }` —
   for the full-bleed page backgrounds (#c, #space, #grain). So no canvas on
   this site is ever in flow, and the site's contract is that the CONTAINER
   carries the size while the canvas fills it absolutely; see .panel-anim /
   .panel-canvas in index.css and explainer.css.

   This file used to size .breach-stage itself and let it occupy flow. It never
   could: the canvas stayed fixed, .breach collapsed to the height of the button
   alone, and — because a positioned canvas paints above static in-flow content —
   the button was rendered UNDERNEATH the artwork. It turned visible on schedule
   at the end of the run and was simply buried. Hence the frame. */
/* aspect-ratio here is a DEFAULT, not a requirement. The composition now lays
   out against the live box, so the frame can be any shape the page wants — set
   a `height` on it (an explicit height beats aspect-ratio) and the drawing
   fills that instead, no letterboxing. 1300/440 is simply the proportion it
   was composed at and the one it reads best in.

   Do not put a fixed height clamp back on it. That is what capped the script's
   scale at 640/440 = 1.45, so the drawing could never exceed ~1890px wide
   however large the display, and on a big screen it sat marooned in the middle
   of an empty frame. */
.breach-frame {
    position: relative;
    width: 100%;
    aspect-ratio: 1300 / 300;
    max-height: 85vh;
}
.breach-stage {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
}

/* ── caption ── the line for each part. The script sets data-stage on the host;
   the copy itself is markup, so it stays editable without touching JS. */
.breach-copy {
    display: grid;
    justify-items: start;
    min-height: 5.5rem;
    max-width: 46rem;
    margin-top: 1rem;
}
.breach-line {
    grid-area: 1 / 1;
    font-family: "IBM Plex Sans", sans-serif;
    font-size: clamp(1rem, 0.4167vw + 0.9167rem, 1.25rem);
    font-weight: 300;
    line-height: 1.5;
    color: var(--fore);
    opacity: 0;
    visibility: hidden;
    transform: translateY(0.375rem);
    transition:
        opacity 0.3s ease,
        transform 0.3s ease,
        visibility 0s linear 0.3s;
}
.breach-line.is-active {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    transition:
        opacity 0.34s ease,
        transform 0.34s ease,
        visibility 0s;
}
.breach-line .breach-n {
    display: block;
    font-family: "Oxanium", sans-serif;
    font-size: 0.625rem;
    font-weight: 700;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--muted);
    margin-bottom: 0.35rem;
}
.breach-line .breach-k {
    color: var(--accent);
    font-weight: 600;
}

/* ── the button ── offered only once the run has played out, and retired once
   the architecture has been applied. */
/* The apply button is NOT owned by this component. It lives in the page markup
   and the page styles it. All breach.js needs is the data-breach-apply
   attribute — its click handler is bound on document, so the button can sit
   anywhere. Nothing here sizes, hides, or positions it, which is also why the
   canvas box no longer carries dead space underneath it. */

/* The resolution line replaces the last part's copy in place. */
.breach-resolve {
    grid-area: 1 / 1;
    font-family: "IBM Plex Sans", sans-serif;
    font-size: clamp(1rem, 0.4167vw + 0.9167rem, 1.25rem);
    font-weight: 300;
    line-height: 1.5;
    color: var(--fore);
    opacity: 0;
    visibility: hidden;
    transition:
        opacity 0.4s ease 0.35s,
        visibility 0s linear 0.75s;
}
[data-breach-applied] .breach-resolve {
    opacity: 1;
    visibility: visible;
    transition:
        opacity 0.5s ease 0.35s,
        visibility 0s;
}
[data-breach-applied] .breach-line {
    opacity: 0;
    visibility: hidden;
    transition:
        opacity 0.3s ease,
        visibility 0s linear 0.3s;
}

@media (prefers-reduced-motion: reduce) {
    .breach-line,
    .breach-resolve {
        transition: none;
    }
}

/* 769px, matching sitewide's mobile breakpoint. It was 760, which left a
   9px band where the page had reflowed to the stacked mobile layout but the
   frame was still asking for the 4.33:1 desktop band. */
@media (max-width: 769px) {
    /* Narrow screens take an explicit height instead of the aspect — at this
       width 1300:300 is ~80px tall and the composition is a smear. An explicit
       height beats aspect-ratio, so the frame becomes a portrait-ish MAP of the
       same run: the script lays the positions out against the live box, and
       (since S is the geometric mean of the two ratios) the marks come back up
       to roughly the size they are on a desktop instead of collapsing to a
       quarter of it.

       Height is what buys that scale back, so it is generous — but capped in vh
       so it never eats a whole phone screen on a short device. */
    .breach-frame {
        aspect-ratio: auto;
        height: clamp(20rem, 50vh, 26rem);
        max-height: none;
    }
    .breach-copy {
        min-height: 7rem;
    }
}
