/**
 * Application start-up loader.
 *
 * Everything is scoped to #cys-boot-loader and the cbl- prefix. This file must not reuse
 * any selector from loader.css: that file styles <app-loader-component>, which is used on
 * 80+ screens, and the two loaders have to stay independent.
 *
 * Only opacity, transform and colour are animated, so the compositor can do the work
 * without the main thread — which is busy parsing the bundle and bootstrapping Angular at
 * exactly this moment.
 *
 * Neutral tones only. The portal's own colours are not known at first paint (they arrive
 * with the portal settings), and guessing would risk showing one tenant another tenant's
 * brand.
 */

#cys-boot-loader {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 2000;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    background: #FFFFFF;
    /* A system stack, deliberately: character positions are measured once and animated
       from those numbers, so a webfont arriving later would misalign the whole line. */
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
    opacity: 0;
    transition: opacity 260ms ease;
    /* Its own compositor layer, and paint contained. Angular is filling <app-root> with the
       whole application underneath while this animates; without isolation every DOM
       insertion repaints the loader too, which showed as a stutter on a real boot. */
    will-change: opacity;
    transform: translateZ(0);
    contain: layout paint;
}

#cys-boot-loader[hidden] {
    display: none;
}

#cys-boot-loader.cbl-shown {
    opacity: 1;
}

/* Secondary loader (respondent-facing routes). The sentence's furniture is dropped and the
   mark stands alone, at the size the in-app mark uses for a full-screen area. A CSS animation,
   not a frame loop: this plays during bundle evaluation and bootstrap, and only the compositor
   keeps running through a blocked main thread. The two halves are the same keyframes, half a
   cycle apart. Timing and faintest tone match mark-loader.ts, so the two never drift. */
#cys-boot-loader.cbl-secondary .cbl-stage,
#cys-boot-loader.cbl-secondary .cbl-status,
#cys-boot-loader.cbl-secondary .cbl-rule {
    display: none;
}

.cbl-mark {
    display: block;
    width: 88px;
    height: 88px;
    fill: #263746;
}

.cbl-mark[hidden] {
    display: none;
}

.cbl-mark path {
    animation: cbl-beat 1600ms ease-in-out infinite;
}

.cbl-mark .cbl-mark-b {
    animation-delay: -800ms;
}

@keyframes cbl-beat {
    0%, 100% { opacity: 1; }
    50% { opacity: .30; }
}

.cbl-stage {
    position: relative;
    width: 100%;
    height: 64px;
}

.cbl-line {
    position: absolute;
    left: 0;
    right: 0;
    top: 14px;
    padding: 0 20px;
    text-align: center;
    white-space: nowrap;
    will-change: transform, opacity;   /* the two lines slide; keep them on their own layers */
}

.cbl-line.cbl-slide {
    transition: transform 460ms cubic-bezier(.32, 0, .24, 1), opacity 380ms ease;
}

.cbl-ch {
    display: inline-block;
    white-space: pre;
    opacity: 0;
    color: #7A858E;
    font-weight: 600;
    font-size: 25px;
    font-size: clamp(18px, 4.2vw, 25px);
    line-height: 1.25;
    transform-origin: left top;
    /* A short fade per character. The stagger is owned by the script, not by
       transition-delay, so that a half-written sentence can be accelerated. */
    transition: opacity 90ms linear;
}

.cbl-ch.cbl-on {
    opacity: 1;
}

/* Every sentence is revealed this way — by the compositor rather than by a script loop,
   because act one plays while the main thread is at its busiest (bundle evaluation,
   bootstrap) and a frame loop is starved exactly then. Opacity over 90ms, linear; the
   stagger is the per-character animation-delay the script sets before adding this class. */
.cbl-ch.cbl-write {
    animation: cbl-write-in 90ms linear both;
}

@keyframes cbl-write-in {
    from { opacity: 0; }
    to { opacity: 1; }
}

/* The word that survives, and the words appended after it. */
.cbl-ch.cbl-strong {
    color: #263746;
}

.cbl-ch.cbl-abs {
    position: absolute;
    left: 0;
    top: 0;
}

/* Travels into place and deepens in tone on the way, so the finished line never has to
   change colour after it has settled. */
.cbl-ch.cbl-keep {
    transition: transform 720ms cubic-bezier(.32, 0, .24, 1), color 320ms ease 160ms;
}

.cbl-ch.cbl-move {
    transition: transform 720ms cubic-bezier(.32, 0, .24, 1), opacity 340ms linear;
}

/* Three classes, so this wins over .cbl-ch.cbl-on regardless of declaration order. */
.cbl-ch.cbl-on.cbl-faded {
    opacity: 0;
}

/* Set while act one is fast-forwarded, because the app reported ready before both sentences
   were written. Only the hand-off between sentences speeds up: the writing pace and the
   whole of act two keep their normal durations, so nothing the user is reading is hurried. */
#cys-boot-loader.cbl-fast .cbl-line.cbl-slide {
    transition-duration: 220ms;
}

.cbl-status {
    margin: 22px 0 0;
    min-height: 18px;
    font-size: 13.5px;
    color: #505151;
}

.cbl-rule {
    position: relative;
    margin-top: 14px;
    width: 240px;
    max-width: 70vw;
    height: 3px;
    overflow: hidden;
    border-radius: 2px;
    background: #D7DBDE;
}

/* Set once the app is ready, so the finished line resolves on a clean screen: the progress
   furniture belongs to the wait, not to the result. Faded rather than hidden, because
   removing it from the layout would shift the stage — and every character's position was
   measured before this point. */
#cys-boot-loader.cbl-done .cbl-status,
#cys-boot-loader.cbl-done .cbl-rule {
    opacity: 0;
    transition: opacity 240ms ease;
}

/* Scaled, not resized: a width transition lays out on every frame, on the main thread, while
   the app is bootstrapping. A transform is composited. The script sets the scale. */
.cbl-rule-fill {
    display: block;
    width: 100%;
    height: 100%;
    border-radius: 0 2px 2px 0;
    background: #263746;
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform 480ms cubic-bezier(.3, 0, .3, 1);
    will-change: transform;
}

/* Both sentences are written but the app is not ready yet — rare, and only on a slow boot.
   The fill stays where it honestly got to and this sweep takes over the message: still
   working, no estimate left. Drawn over the track rather than by resizing the fill, so the
   determinate value underneath is never overwritten and only a transform animates. */
#cys-boot-loader.cbl-waiting .cbl-rule::after {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 35%;
    background: linear-gradient(90deg, rgba(38, 55, 70, 0), rgba(38, 55, 70, .5), rgba(38, 55, 70, 0));
    animation: cbl-sweep 1200ms ease-in-out infinite;
}

@keyframes cbl-sweep {
    from { transform: translate3d(-100%, 0, 0); }
    to { transform: translate3d(286%, 0, 0); }
}

@media (prefers-reduced-motion: reduce) {

    #cys-boot-loader,
    .cbl-line,
    .cbl-ch,
    .cbl-status,
    .cbl-rule,
    .cbl-rule-fill {
        transition: none !important;
    }

    #cys-boot-loader.cbl-waiting .cbl-rule::after {
        animation: none;
    }

    /* the mark is simply there, both halves at full tone */
    .cbl-mark path {
        animation: none;
    }
}
