/* grid.css — home §2 "Everything it does": the capability pane.
   ---------------------------------------------------------------------------
   Everything in this section that CANNOT be an inline style, and nothing else.
   The page is 100% inline `style={{…}}` objects (see LandingPage/CLAUDE.md), so
   only four kinds of rule live here: keyframes, `:hover`, the `.is-live`
   descendant selectors that drive the section's one authored moment, and the
   reduced-motion safety net. Layout, surface and type stay inline in
   hifi/sections/home-grid.jsx, and responsiveness stays JS-driven through
   useIsMobile() — there is deliberately no @media breakpoint in this file.

   This file cannot read GW, so every token it needs is handed down as a --gx-*
   custom property set inline on the section from GW. GW stays the single source
   of truth; nothing below hardcodes a brand colour.

   THE MOMENT, in order (see the header comment in home-grid.jsx for why):
     0.30s  the seams draw outward from the pane's centre   one form -> nine
     0.26s+ the cells arrive 60ms apart in reading order    nine, read in order
     0.68s+ each glyph wakes 0.42s after its OWN cell       one current, nine
                                                            responses
   It runs ONCE, when the pane scrolls to the shared reveal point, and rests. The
   resting state of every element below is its FINISHED shape, so no-JS and
   reduced motion both land on a complete, static credential rather than on a
   half-drawn one.

   `--cd` and `--wake` are set per cell and both DERIVED from the tile's index
   (GRID_CELL_* in home-grid.jsx), so the nine share one ramp and cannot drift
   apart; `--gd` is the per-element offset inside a glyph. A glyph delay is
   `--wake` + `--gd`, which is what lets one ramp cross nine different drawings.
   --------------------------------------------------------------------------- */

/* ---------------------------------------------------------------------------
   Cells
   --------------------------------------------------------------------------- */

/* Background is owned here, not inline, because an inline value cannot be
   overridden by :hover. The tint warms a REGION of the pane rather than lifting
   a card out of it: lifting would contradict the one-surface argument. */
.cd-cell {
    background: transparent;
    transition: background 0.34s cubic-bezier(0.22, 1, 0.36, 1);
}
@media (hover: hover) {
    .cd-cell:hover {
        background: var(--gx-hover);
    }
}

/* THE ARRIVAL. Nine cells on ONE derived ramp (--cd, set from the tile's index
   in home-grid.jsx), so the pane populates as a single read rather than as nine
   independent pop-ins.

   `both` is load-bearing: it holds the cell on the 0% frame through its own
   delay. Without it a cell sits at its finished state until its turn and then
   snaps, which is what made some tiles look like they were "already there" and
   others like they arrived late. Nothing here runs before `is-live`, so a
   reduced-motion or no-JS visitor gets the finished pane with no delay at all. */
.cd-grid.is-live .cd-cell {
    animation: cdGxCell 0.5s var(--cd, 0s) var(--gx-ease) both;
}
@keyframes cdGxCell {
    0% {
        opacity: 0;
        transform: translate3d(0, 12px, 0);
    }
    100% {
        opacity: 1;
        transform: none;
    }
}

/* ---------------------------------------------------------------------------
   Seams — the pane dividing into its nine cells
   --------------------------------------------------------------------------- */

.cd-seam {
    position: absolute;
    z-index: 2;
    pointer-events: none;
    background: var(--gx-line);
    transform-origin: center;
}
.cd-grid.is-live .cd-seam--v {
    animation: cdGxSeamV 0.62s var(--sd, 0s) cubic-bezier(0.22, 1, 0.36, 1) both;
}
.cd-grid.is-live .cd-seam--h {
    animation: cdGxSeamH 0.62s var(--sd, 0s) cubic-bezier(0.22, 1, 0.36, 1) both;
}
@keyframes cdGxSeamV {
    from {
        opacity: 0;
        transform: scaleY(0);
    }
    to {
        opacity: 1;
        transform: scaleY(1);
    }
}
@keyframes cdGxSeamH {
    from {
        opacity: 0;
        transform: scaleX(0);
    }
    to {
        opacity: 1;
        transform: scaleX(1);
    }
}

/* ---------------------------------------------------------------------------
   Glyph sizing
   ---------------------------------------------------------------------------
   THE GLYPH FILLS ITS CELL. Every drawing is authored at the real proportions
   of the cell it ships in (sm 324x104, wide 696x104, lg 696x264, band 1068x168),
   so the honest sizing is simply "fill the box and let the viewBox scale".

   The earlier px caps here are gone. They were sized for an older set of small
   icons, and against these drawings they pinned a 324-wide composition to 96px
   and left the cell four-fifths empty.

   `height: 100%` plus `preserveAspectRatio` on each svg means a cell that is
   proportionally taller or shorter than its drawing letterboxes rather than
   distorting, and `min-height: 0` on the flex parent is what allows the svg to
   shrink inside a fixed-height cell at all.
   --------------------------------------------------------------------------- */

.cd-gx {
    display: block;
    width: 100%;
    height: 100%;
    /* Clip to the glyph's OWN box. The Kiosk walker deliberately travels past
       the right edge of its viewBox, and the pane's own overflow is hidden, so
       without this it would walk across the neighbouring tile instead of
       leaving the frame. */
    overflow: hidden;
}

/* Every svg here inherits SVG's default preserveAspectRatio of `xMidYMid meet`,
   which is what centres the drawing inside the box and letterboxes rather than
   distorting when a cell's proportions differ from the drawing's. It is the
   default, but it is load-bearing, so do not set `none` on these. */

/* Transforms are per-shape inside an SVG, so every animated part gets its own
   box to transform within. */
/* ---------------------------------------------------------------------------
   TILE MOTION. One rule for all nine drawn visuals (hifi/mocks/grid-glyphs.jsx).

   Every animation is gated on `.is-live` and delayed by the cell's `--wake`, so
   nothing moves until the wave reaches that cell, and nothing runs before the
   pane is on screen. `--gd` is the per-element offset inside a glyph.
   Under reduced motion `is-live` is never added and every glyph rests on its
   finished frame, which is why each resting state below is the finished picture.

   WHERE A SET MUST CLEAR TOGETHER, the stagger is written into the keyframes
   rather than into animation-delay: delay gives each element its own cycle
   boundary, so the last one lingers into the next scene. That is why the belt
   stripes and the app scenes have numbered keyframes instead of a shared one.
   --------------------------------------------------------------------------- */

.cd-gx [class^="gx-"],
.cd-gx [class*=" gx-"] { will-change: transform, opacity; }

/* resting states — what a no-JS or reduced-motion visitor sees */
.gx-p, .gx-coin, .gx-tot, .gx-blk, .gx-walk, .gx-ok, .gx-stamp,
.gx-scene, .gx-it, .gx-lbl, .gx-pnl, .gx-stack, .gx-out, .gx-up { opacity: 1; }
.gx-tot:not(.gx-tot--last), .gx-scene:not(.gx-scene--last),
.gx-belt:not(.gx-belt--last), .gx-coin, .gx-stack { opacity: 0; }
.gx-sig, .gx-okdraw { stroke-dashoffset: 0; }

/* MEMBERS · gather */
.cd-grid.is-live .gx-p {
    opacity: 0;
    animation: gxGather 5.4s var(--gx-ease) infinite;
    animation-delay: calc(var(--wake, 0s) + var(--gd, 0s));
}
@keyframes gxGather {
    0%       { transform: translate(var(--dx), var(--dy)) scale(0.8); opacity: 0; }
    14%      { transform: translate(var(--dx), var(--dy)) scale(0.8); opacity: 0.5; }
    46%, 86% { transform: none; opacity: 1; }
    100%     { transform: translate(var(--dx), var(--dy)) scale(0.8); opacity: 0; }
}

/* BILLING · flow. One 3.2s period shared by the payments and the counter. */
.cd-grid.is-live .gx-coin {
    animation: gxRun 3.2s linear infinite;
    animation-delay: calc(var(--wake, 0s) + var(--gd, 0s));
}
@keyframes gxRun {
    0%   { transform: translateX(0); opacity: 0; }
    8%   { opacity: 1; }
    88%  { opacity: 1; }
    100% { transform: translateX(var(--run)); opacity: 0; }
}
.cd-grid.is-live .gx-tot {
    opacity: 0;
    animation: gxTot 3.2s linear infinite;
    animation-delay: calc(var(--wake, 0s) + var(--gd, 0s));
}
@keyframes gxTot { 0%, 24.9% { opacity: 1; } 25%, 100% { opacity: 0; } }
.cd-grid.is-live .gx-pulse {
    animation: gxPulse 0.8s var(--gx-ease) infinite;
    animation-delay: calc(var(--wake, 0s) + 0.8s);
}
@keyframes gxPulse { 0% { transform: scale(1.035); } 34%, 100% { transform: scale(1); } }

/* SCHEDULING · board. Each class settles at its OWN fullness. */
.cd-grid.is-live .gx-blk {
    opacity: 0;
    animation: gxBlk 4.8s var(--gx-ease) infinite;
    animation-delay: calc(var(--wake, 0s) + var(--gd, 0s));
}
@keyframes gxBlk {
    0%       { opacity: 0; transform: translateY(7px) scale(0.9); }
    22%, 86% { opacity: 1; transform: none; }
    100%     { opacity: 0; transform: translateY(-4px) scale(0.96); }
}
.gx-lv { fill: var(--gx-accent); fill-opacity: var(--i); }
.cd-grid.is-live .gx-lv {
    fill-opacity: 0.1;
    animation: gxLv 4.8s var(--gx-ease) infinite;
    animation-delay: calc(var(--wake, 0s) + var(--gd, 0s));
}
@keyframes gxLv { 0% { fill-opacity: 0.1; } 34%, 86% { fill-opacity: var(--i); } 100% { fill-opacity: 0.1; } }

/* KIOSK · walk-by. `linear`, not eased: an eased walk reads as skating. */
.cd-grid.is-live .gx-walk {
    animation: gxWalk 4.5s linear infinite;
    animation-delay: calc(var(--wake, 0s) + var(--gd, 0s));
}
@keyframes gxWalk {
    0%   { transform: translate(-64px, 0); opacity: 0; }
    6%   { transform: translate(-40px, -1.5px); opacity: 1; }
    14%  { transform: translate(6px, 0); }
    21%  { transform: translate(50px, -1.5px); }
    29%  { transform: translate(96px, 0); }
    33%  { transform: translate(104px, 0); }
    46%  { transform: translate(104px, 0); }
    54%  { transform: translate(146px, -1.5px); }
    64%  { transform: translate(196px, 0); }
    74%  { transform: translate(240px, -1.5px); }
    86%  { transform: translate(286px, 0); opacity: 1; }
    100% { transform: translate(330px, 0); opacity: 0; }
}
/* the tick lands INSIDE the pause, verified against the walk's own timing */
.cd-grid.is-live .gx-idle {
    animation: gxIdle 2.25s var(--gx-ease) infinite;
    animation-delay: calc(var(--wake, 0s) + 1.14s);
}
@keyframes gxIdle { 0%, 42% { opacity: 0; } 58%, 100% { opacity: 1; } }
.cd-grid.is-live .gx-ok {
    opacity: 0;
    animation: gxOk 2.25s var(--gx-ease) infinite;
    animation-delay: calc(var(--wake, 0s) + 1.14s);
}
@keyframes gxOk {
    0%       { opacity: 0; transform: scale(0.7); }
    16%, 40% { opacity: 1; transform: none; }
    56%,100% { opacity: 0; transform: scale(1); }
}
.cd-grid.is-live .gx-okdraw {
    stroke-dashoffset: 26;
    animation: gxOkDraw 2.25s var(--gx-ease) infinite;
    animation-delay: calc(var(--wake, 0s) + 1.14s);
}
@keyframes gxOkDraw { 0%, 6% { stroke-dashoffset: 26; } 24%, 40% { stroke-dashoffset: 0; } 56%, 100% { stroke-dashoffset: 26; } }

/* WAIVERS · sign */
.cd-grid.is-live .gx-ln {
    transform-origin: left center;
    animation: gxGrowX 5s var(--gx-ease) infinite;
    animation-delay: calc(var(--wake, 0s) + var(--gd, 0s));
}
@keyframes gxGrowX { 0% { transform: scaleX(0); } 16%, 90% { transform: scaleX(1); } 100% { transform: scaleX(0); } }
.cd-grid.is-live .gx-sig {
    stroke-dashoffset: 96;
    animation: gxSig 5s var(--gx-ease) infinite;
    animation-delay: var(--wake, 0s);
}
@keyframes gxSig { 0%, 12% { stroke-dashoffset: 96; } 40%, 90% { stroke-dashoffset: 0; } 100% { stroke-dashoffset: 96; } }
.cd-grid.is-live .gx-stamp {
    opacity: 0;
    animation: gxStamp 5s var(--gx-ease) infinite;
    animation-delay: var(--wake, 0s);
}
@keyframes gxStamp {
    0%, 44%  { opacity: 0; transform: scale(1.25) rotate(-4deg); }
    56%, 90% { opacity: 1; transform: none; }
    100%     { opacity: 0; }
}

/* RANKS · belts. Five belts share an 8s loop; four stripes land inside each
   1.6s window and CLEAR on the frame the belt advances on. */
.cd-grid.is-live .gx-belt {
    opacity: 0;
    animation: gxBelt 8s linear infinite;
    animation-delay: calc(var(--wake, 0s) + var(--gd, 0s));
}
@keyframes gxBelt { 0%, 19.9% { opacity: 1; } 20%, 100% { opacity: 0; } }
.cd-grid.is-live .gx-stripe {
    opacity: 0;
    animation-duration: 1.6s;
    animation-timing-function: var(--gx-ease);
    animation-iteration-count: infinite;
    animation-delay: var(--wake, 0s);
}
.cd-grid.is-live .gx-stripe--0 { animation-name: gxS0; }
.cd-grid.is-live .gx-stripe--1 { animation-name: gxS1; }
.cd-grid.is-live .gx-stripe--2 { animation-name: gxS2; }
.cd-grid.is-live .gx-stripe--3 { animation-name: gxS3; }
@keyframes gxS0 { 0%, 4%  { opacity: 0; transform: scaleY(0.3); } 11%, 100% { opacity: 1; transform: none; } }
@keyframes gxS1 { 0%, 23% { opacity: 0; transform: scaleY(0.3); } 30%, 100% { opacity: 1; transform: none; } }
@keyframes gxS2 { 0%, 42% { opacity: 0; transform: scaleY(0.3); } 49%, 100% { opacity: 1; transform: none; } }
@keyframes gxS3 { 0%, 61% { opacity: 0; transform: scaleY(0.3); } 68%, 100% { opacity: 1; transform: none; } }
.cd-grid.is-live .gx-up {
    opacity: 0;
    animation: gxUp 1.6s var(--gx-ease) infinite;
    animation-delay: var(--wake, 0s);
}
@keyframes gxUp {
    0%, 74%  { opacity: 0; transform: translateY(7px) scale(0.85); }
    84%, 96% { opacity: 1; transform: none; }
    100%     { opacity: 0; transform: none; }
}

/* APP · everything. Five scenes on 13s; the item cycle divides the scene
   window exactly, so items reset on the scene change, never mid-scene. */
.cd-grid.is-live .gx-scene {
    opacity: 0;
    animation: gxScene 13s linear infinite;
    animation-delay: calc(var(--wake, 0s) + var(--gd, 0s));
}
@keyframes gxScene { 0%, 19.9% { opacity: 1; } 20%, 100% { opacity: 0; } }
.cd-grid.is-live .gx-it {
    opacity: 0;
    animation-duration: 2.6s;
    animation-timing-function: var(--gx-ease);
    animation-iteration-count: infinite;
    animation-delay: var(--wake, 0s);
}
.cd-grid.is-live .gx-it--0 { animation-name: gxI0; }
.cd-grid.is-live .gx-it--1 { animation-name: gxI1; }
.cd-grid.is-live .gx-it--2 { animation-name: gxI2; }
.cd-grid.is-live .gx-it--3 { animation-name: gxI3; }
@keyframes gxI0 { 0%,2%  { opacity: 0; transform: translateY(22px) scale(0.96); } 12%,84% { opacity: 1; transform: none; } 100% { opacity: 0; transform: translateY(-10px); } }
@keyframes gxI1 { 0%,14% { opacity: 0; transform: translateY(22px) scale(0.96); } 24%,84% { opacity: 1; transform: none; } 100% { opacity: 0; transform: translateY(-10px); } }
@keyframes gxI2 { 0%,26% { opacity: 0; transform: translateY(22px) scale(0.96); } 36%,84% { opacity: 1; transform: none; } 100% { opacity: 0; transform: translateY(-10px); } }
@keyframes gxI3 { 0%,38% { opacity: 0; transform: translateY(22px) scale(0.96); } 48%,84% { opacity: 1; transform: none; } 100% { opacity: 0; transform: translateY(-10px); } }
.cd-grid.is-live .gx-lbl {
    opacity: 0;
    animation: gxLbl 2.6s var(--gx-ease) infinite;
    animation-delay: var(--wake, 0s);
}
@keyframes gxLbl { 0%,4% { opacity: 0; transform: translateX(-6px); } 14%,84% { opacity: 1; transform: none; } 100% { opacity: 0; } }

/* ANALYTICS · dashboards */
.cd-grid.is-live .gx-pnl {
    opacity: 0;
    animation: gxPnl 5.4s var(--gx-ease) infinite;
    animation-delay: calc(var(--wake, 0s) + var(--gd, 0s));
}
@keyframes gxPnl {
    0%       { opacity: 0; transform: translateY(8px) scale(0.94); }
    16%, 86% { opacity: 1; transform: none; }
    100%     { opacity: 0; transform: translateY(-5px); }
}

/* AI · once. One approval releases the whole queue.

   THE TWO HALVES OVERLAP ON PURPOSE. A strictly sequential loop (drafts, then
   approve, then outputs, then empty) reads as one thing happening slowly. Here
   the NEXT batch of drafts arrives at 82% while the current outputs are still
   completing (they hold to 96%), so there is always work on both sides of the
   button and the tile reads as constantly busy.

   The draft keyframes start and end in the SAME state, so the loop is seamless:
   the arrival happens mid-cycle rather than at the wrap, where a jump would be
   visible. Period is 4.4s, down from 7s, because the point is pace. */
.cd-grid.is-live .gx-stack {
    animation: gxStack 4.4s var(--gx-ease) infinite;
    animation-delay: calc(var(--wake, 0s) + var(--gd, 0s));
}
@keyframes gxStack {
    0%       { opacity: 1; transform: none; }
    40%      { opacity: 1; transform: none; }
    50%      { opacity: 0; transform: translateX(12px); }
    72%      { opacity: 0; transform: translateX(-16px); }
    82%      { opacity: 1; transform: none; }
    100%     { opacity: 1; transform: none; }
}
.cd-grid.is-live .gx-go {
    animation: gxGo 4.4s var(--gx-ease) infinite;
    animation-delay: var(--wake, 0s);
}
@keyframes gxGo { 0%, 38% { transform: scale(1); } 44% { transform: scale(0.9); } 56%, 100% { transform: scale(1); } }
.cd-grid.is-live .gx-out {
    opacity: 0;
    animation-duration: 4.4s;
    animation-timing-function: var(--gx-ease);
    animation-iteration-count: infinite;
    animation-delay: var(--wake, 0s);
}
.cd-grid.is-live .gx-out--0 { animation-name: gxO0; }
.cd-grid.is-live .gx-out--1 { animation-name: gxO1; }
.cd-grid.is-live .gx-out--2 { animation-name: gxO2; }
.cd-grid.is-live .gx-out--3 { animation-name: gxO3; }
@keyframes gxO0 { 0%,42% { opacity:0; transform: translateX(-26px);} 48%,96% { opacity:1; transform:none;} 100% { opacity:0; } }
@keyframes gxO1 { 0%,46% { opacity:0; transform: translateX(-26px);} 52%,96% { opacity:1; transform:none;} 100% { opacity:0; } }
@keyframes gxO2 { 0%,50% { opacity:0; transform: translateX(-26px);} 56%,96% { opacity:1; transform:none;} 100% { opacity:0; } }
@keyframes gxO3 { 0%,54% { opacity:0; transform: translateX(-26px);} 60%,96% { opacity:1; transform:none;} 100% { opacity:0; } }

@media (prefers-reduced-motion: reduce) {
    .cd-grid *,
    .cd-grid *::before,
    .cd-grid *::after { animation: none !important; transition: none !important; }
}
