/* Hosts the console demo in the hero.

   The console is the real dashboard, served in an iframe: its stylesheet is a
   full Tailwind build with preflight, which would reset the marketing page if
   it were inlined. The pages are rendered on a 1440px screen, so the frame is
   pinned to that width and scaled down to whatever room the container has —
   scaling instead of reflowing keeps the interface identical to the product.
   The width is wider than the frame ever gets on purpose: it is what sets how
   small the interface reads. It must match WIDTH in console-embed.js, the
   aspect-ratio on .console-window-viewport below, and the width
   demo-console/render.py renders at in ECC, because the charts are
   snapshots taken at that width. The height keeps the 1280 x 840 ratio. */

/* Ambient light for the whole hero, on the lowest layer: below the map, below
   the headline, below the console. It is a property of the background, not of
   the window — the window merely sits in front of it and casts a shadow.
   --hero-glow is the one knob; 0 turns the light off. */
.hero {
    /* Strength of the light at its core. 0 turns it off. */
    --hero-glow: 0.82;

    /* Diameter of the box the light is painted into. The box is square and
       every colour stop is a percentage of it, so the light stays a circle at
       any size and its falloff scales with it. Raise this to widen the light
       and to carry it further down past the hero.

       One value, not a width and a height: vw and vh are not comparable, so
       a pair like 100vw / 100vh is a 1920x1080 rectangle on a normal laptop
       and paints an oval however round the intent was. */
    --hero-glow-size: 165vh;

    /* Where the centre of that circle sits in the section.

       console-embed.js overwrites --hero-glow-y on load and on resize with
       the console window's measured centre, because the window is scaled to
       fit and that point moves with the viewport. The value here is what
       stands if the script never runs, so it is the same place, guessed.

       To move the light off the window's centre, use the nudge — a positive
       length pushes it down. Editing --hero-glow-y itself will not hold: the
       measurement replaces it. */
    --hero-glow-x: 50%;
    --hero-glow-y: 72%;
    --hero-glow-y-nudge: 0px;
}

/* Two elements. The outer one spans the section and positions the light; the
   inner one carries the paint.

   Both are painted once and never touched again. An earlier version drifted
   the inner one with the scroll and leaned it towards the cursor, which meant
   a filtered layer of several megapixels had to be kept live and recomposited
   every frame. Measured on an Intel UHD laptop that cost most of the GPU on
   its own: the hero sat at 96% with the drift, and at 0-2% without it.

   The wrapper used to carry a mask that dissolved the light towards the
   section's bottom edge, and .hero used to clip to that edge. Both are gone.
   Between them they cut the bottom off the light, which is what made it read
   as an oval however square the box was, and what made the next section look
   like it was chopping it. Neither is needed: the gradient reaches full
   transparency inside its own box on every side, so there is no edge to hide
   and nothing to clip. */
.hero-glow {
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
}

.hero-glow i {
    position: absolute;
    left: var(--hero-glow-x);
    top: calc(var(--hero-glow-y) + var(--hero-glow-y-nudge));
    /* min() so a tall narrow window cannot push the circle wider than the page
       and raise a horizontal scrollbar — which is the only reason .hero used
       to clip. aspect-ratio keeps the box square, and square is what makes
       the light round. */
    width: min(var(--hero-glow-size), 100%);
    aspect-ratio: 1;
    /* Centring on the point set by left/top above. */
    translate: -50% -50%;
    /* Gathered rather than spread: the core holds its strength for longer and
       then falls away sooner, which is what gives the reference a light with
       a centre instead of an even wash.

       The nine stops are what make the ramp smooth. There used to be four,
       with a large blur() on top to dissolve the rings they banded into —
       but a blur over an area this size is one of the most expensive things
       a page can ask for, and the stops do the same job in paint. */
    background: radial-gradient(
        circle at center,
        rgba(var(--color-primary-rgb), var(--hero-glow)),
        rgba(var(--color-primary-rgb), calc(var(--hero-glow) * 0.94)) 9%,
        rgba(var(--color-primary-rgb), calc(var(--hero-glow) * 0.80)) 16%,
        rgba(var(--color-primary-rgb), calc(var(--hero-glow) * 0.55)) 26%,
        rgba(var(--color-primary-rgb), calc(var(--hero-glow) * 0.38)) 33%,
        rgba(var(--color-primary-rgb), calc(var(--hero-glow) * 0.25)) 39%,
        rgba(var(--color-primary-rgb), calc(var(--hero-glow) * 0.16)) 44%,
        rgba(var(--color-primary-rgb), calc(var(--hero-glow) * 0.09)) 50%,
        rgba(var(--color-primary-rgb), calc(var(--hero-glow) * 0.04)) 56%,
        transparent 66%
    );
}

.console-embed {
    position: relative;
    margin-top: var(--space-3xl);
}

/* The window is a sheet of glass with the screen inset into it, rather than a
   dark card sitting on the page. The mount around the screen is what does the
   work: the hero's light carries through it, so the window separates from the
   background by being *lighter* than it, and needs no drop shadow at all.

   The tint is the brand hue, not white — over a coloured glow a white film
   greys everything it covers, while a tint of the light's own hue reads as
   material catching that light. */
.console-window {
    --console-mount: 18px;
    /* The window is narrower than the text column on purpose: it reads as an
       object on the page rather than a full-bleed band, and since the frame is
       scaled to its own width, a narrower window is also what makes the
       interface inside it smaller. One knob for both. */
    max-width: 1000px;
    margin-inline: auto;

    position: relative;
    z-index: 1;
    border-radius: 20px;
    padding: 0 var(--console-mount) var(--console-mount);
    /* A painted tint rather than a backdrop-filter. The filter had to
       re-snapshot and re-blur this whole region on every frame the light
       drifted behind it, because a backdrop is by definition uncacheable —
       and the backdrop here is a smooth gradient, so the blur handed back
       almost the pixels it was given. The tint carries the look on its own.
       The top wash stands in for the saturate(): a highlight along the upper
       edge is what read as glass catching the light. */
    background:
        linear-gradient(to bottom, rgba(255, 255, 255, 0.055), transparent 38%),
        rgba(var(--color-primary-rgb), 0.26);
    box-shadow:
        inset 0 0 0 1px rgba(255, 255, 255, 0.09),
        inset 0 1px 0 rgba(255, 255, 255, 0.13);
}

/* The bar is part of the glass now, so it carries no fill of its own — one
   sheet, not two stacked panes. A nested backdrop-filter here would also cut
   the window off as its own backdrop root and blur nothing but its fill. */
.console-window-bar {
    display: flex;
    align-items: center;
    gap: var(--space-md);
    height: 46px;
    /* The window's own mount already provides most of the inset. */
    padding: 0 calc(var(--space-lg) - var(--console-mount));
    background: none;
}

.console-window-dots {
    display: flex;
    gap: 6px;
    flex-shrink: 0;
}

.console-window-dots i {
    width: 11px;
    height: 11px;
    border-radius: 50%;
}

.console-window-dots i:nth-child(1) { background: #ef4444; }
.console-window-dots i:nth-child(2) { background: #eab308; }
.console-window-dots i:nth-child(3) { background: #3d916f; }

/* Mirrors the dots so the url stays centred in the bar rather than in the
   space left of them. */
.console-window-bar::after {
    content: "";
    flex-shrink: 0;
    width: 45px;
}

.console-window-url {
    flex: 1;
    min-width: 0;
    text-align: center;
    font-family: var(--font-mono);
    font-size: var(--font-sm);
    color: var(--color-text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The screen: seated into the mount, with its own smaller radius. Concentric
   radii are impossible once the mount is this wide, so the inner corner is
   chosen to look right rather than to be outer-minus-mount. */
.console-window-viewport {
    position: relative;
    overflow: hidden;
    /* The screen's shape, 1440 x 945, fixed in CSS so the box has its final
       height on the first layout. Deriving it from --console-scale waited for
       console-embed.js: the frame painted at full height and snapped about
       300px shorter once the script measured it, dragging the hero's light
       with it. The script still scales the iframe inside, which moves nothing
       around it. overflow: hidden keeps the unscaled iframe from stretching
       the box, and the window is capped below 1440px wide, so the scale and
       this ratio always agree. */
    aspect-ratio: 1440 / 945;
    border-radius: 10px;
    background: #0f1419;
    /* A dark line seats the screen into the glass instead of letting the two
       surfaces meet on a bare edge. */
    box-shadow:
        0 0 0 1px rgba(0, 0, 0, 0.45),
        0 1px 0 rgba(255, 255, 255, 0.05);
}

/* Browsers without aspect-ratio (Safari before 15) would size the box to
   the unscaled 945px iframe; they keep the height the script sets. */
@supports not (aspect-ratio: 1) {
    .console-window-viewport {
        height: calc(var(--console-height, 945px) * var(--console-scale, 1));
    }
}

/* The demo does not take the pointer until it is clicked, so a wheel over the
   hero scrolls the page rather than the dashboard inside it. The click that
   wakes it lands on the viewport, which is why the cursor advertises it.
   console-embed.js adds is-live on that click and drops it on a click
   anywhere outside the window. */
.console-window-viewport {
    cursor: pointer;
}

.console-embed.is-live .console-window-viewport {
    cursor: default;
}

.console-window-viewport iframe {
    pointer-events: none;
    width: var(--console-width, 1440px);
    height: var(--console-height, 945px);
    border: 0;
    display: block;
    transform: scale(var(--console-scale, 1));
    transform-origin: top left;
}

.console-embed.is-live .console-window-viewport iframe {
    pointer-events: auto;
}

@media (max-width: 768px) {
    .console-embed { margin-top: var(--space-2xl); }
    /* A mount sized for a 1200px window swallows a phone-width one. */
    .console-window {
        --console-mount: 9px;
        border-radius: 14px;
    }

    .console-window-bar { height: 38px; }
    .console-window-viewport { border-radius: 7px; }
}

/* The demo behind the prompt. Blurred rather than dimmed: the interface stays
   recognisable as the thing the visitor was just using, and nothing in it can
   be read closely enough to be mistaken for their own data. */
.console-embed.is-locked .console-window-viewport iframe {
    filter: blur(7px);
    pointer-events: none;
}

.console-locked {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-lg);
    border-radius: 20px;
    background: rgba(9, 12, 17, 0.55);
}

.console-locked[hidden] {
    display: none;
}

.console-locked-card {
    max-width: 42ch;
    padding: var(--space-lg);
    text-align: center;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    background: var(--color-bg-elevated, #12161d);
}

.console-locked-title {
    margin: 0;
    font-size: var(--font-lg);
    color: var(--color-text);
}

.console-locked-text {
    margin: var(--space-sm) 0 var(--space-lg);
    font-size: var(--font-sm);
    color: var(--color-text-muted);
}

.console-locked-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-sm);
    justify-content: center;
}
