/*
 * Pickle motion. Loads after components.css, which owns structure; this file
 * owns time. Nothing here is required for a page to be usable.
 *
 * THE CONTRACT, and it is the whole reason this file can be as busy as it is:
 *
 *   1. Every rule that hides something lives inside
 *      `@media (prefers-reduced-motion: no-preference)`. A reader who asked for
 *      less motion never matches it, so content is not hidden rather than hidden
 *      and never revealed.
 *   2. Scroll-driven rules additionally sit inside
 *      `@supports (animation-timeline: view())`. A browser without them matches
 *      nothing here at all.
 *   3. No JavaScript. These six UIs have no build step and load their scripts at
 *      the end of <body>; a script-driven reveal flashes on every page of every
 *      one of them, and leaves content invisible for as long as the bundle takes.
 *      Time-based entrances below run from the stylesheet, so they complete even
 *      with scripting disabled entirely.
 *   4. Print undoes all of it (bottom of the file). Paper has no scroll position,
 *      so a scroll-driven rule would otherwise commit half the page at opacity 0.
 *
 * TWO MECHANICS LEARNED BY MEASURING, both invisible in a stylesheet:
 *
 *   - A view() timeline resolves against the subject's nearest ANCESTOR SCROLL
 *     CONTAINER, and `overflow: hidden` makes one. The signature 1px-gap grid
 *     used it to clip its own radius, so timelines on its cells resolved against
 *     a box that never scrolls and sat permanently at their end state. Those
 *     grids now use `overflow: clip`, which clips WITHOUT becoming a scroll
 *     container: verified in a browser, both ways round.
 *   - A grid's cells must never animate their own opacity. The gap trick paints
 *     one --banx-line ground behind 1px gaps, so a faded cell shows that ground
 *     through them and an unrevealed strip renders as a solid grey block. The
 *     frame animates its own colours, and the cells animate their CONTENTS.
 */

/* ============================================================= the two moves */

/* Content arriving: up and in. */
@keyframes ex-in {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: none; }
}

/* Structure arriving: the hairline frame draws itself, and nothing moves.
 * Deliberately no transform here. A transform on the grid would shift its own
 * cells, which changes their view() progress while they are animating on it: the
 * two feed back into each other and the cascade stutters. */
@keyframes ex-frame {
  from { background-color: transparent; border-color: transparent; }
  to { background-color: var(--banx-line); border-color: var(--banx-line); }
}

/* A shorter rise for things already on screen at load. */
@keyframes ex-in-sm {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: no-preference) {

  /* ======================================================== 1. load entrance
   * The first screen cannot use a scroll timeline: it is already in view, so a
   * view() animation is complete before the reader arrives. This is the one
   * time-based entrance in the system, and it is why rule 3 above matters -
   * a stylesheet animation runs whether or not any script does.
   *
   * The explorer and .pkl names render their heads into #view on every route
   * change, so the same rules double as a page transition there.
   */
  .ex-wrap .ex-crumb,
  .ex-wrap .ex-kicker,
  .ex-wrap .ex-h,
  .ex-wrap h1,
  .ex-wrap .ex-lede {
    animation: ex-in 620ms var(--banx-ease) both;
  }

  /* Read in order: eyebrow, headline, standfirst. 90ms is enough to be felt as
   * sequence and short enough that the whole head has landed in under half a
   * second. */
  .ex-wrap .ex-crumb { animation-delay: 0ms; }
  .ex-wrap .ex-kicker { animation-delay: 40ms; }
  .ex-wrap .ex-h,
  .ex-wrap h1 { animation-delay: 130ms; }
  .ex-wrap .ex-lede { animation-delay: 220ms; }

  /* A hero opens the same way, one step larger.
   *
   * .ex-hero is used by exactly one page, web/pages/testnet.jsx, and that page
   * mounts no GSAP at all: mountReveals runs only on the home page, so the
   * testnet page had no motion of any kind until this stylesheet reached it.
   * Structure-agnostic on purpose - the hero's column holds a pill, a headline,
   * a standfirst, a pair of actions and a status line, and nth-child means the
   * order comes from the markup rather than from a list here that would drift. */
  .ex-hero > div:first-child > * {
    animation: ex-in 660ms var(--banx-ease) both;
  }

  .ex-hero > div:first-child > :nth-child(1) { animation-delay: 60ms; }
  .ex-hero > div:first-child > :nth-child(2) { animation-delay: 140ms; }
  .ex-hero > div:first-child > :nth-child(3) { animation-delay: 220ms; }
  .ex-hero > div:first-child > :nth-child(4) { animation-delay: 300ms; }
  .ex-hero > div:first-child > :nth-child(5) { animation-delay: 360ms; }

  /* The panel beside it arrives last, as ONE block.
   *
   * :first-child on the rule above matters: without it this selector also caught
   * the panel's own children, so the panel faded from 260ms while its contents
   * had already finished fading at 220ms. Two fades over the same pixels, the
   * inner one wasted. The column staggers; the panel does not. */
  .ex-hero > div + div { animation: ex-in 700ms var(--banx-ease) 260ms both; }

  /* The bar slides down over the page rather than appearing on it. shell.js
   * injects it, so on four of the six apps there is no bar at all without
   * scripting and this animation has nothing to hide. */
  .ex-nav {
    animation: ex-nav-in 520ms var(--banx-ease) both;
  }

  @keyframes ex-nav-in {
    from { opacity: 0; transform: translateY(-100%); }
    to { opacity: 1; transform: none; }
  }

  /* The explorer's live strip follows the bar it sits under. */
  .ex-livebar {
    animation: ex-in-sm 560ms var(--banx-ease) 160ms both;
  }

  /* ====================================================== 2. state entrances
   * Not scroll, not load: a panel becoming the active one. `display: none` to
   * `block` restarts an animation, so adding .on is all the trigger needed.
   */
  .ex-tabpanes > .ex-panel.on,
  .ex-step.on {
    animation: ex-in-sm 380ms var(--banx-ease) both;
  }

  /* A freshly rendered result, wherever a site adds the class after writing
   * innerHTML. Cheap for app.js to opt into and harmless where it does not. */
  .ex-fresh {
    animation: ex-in-sm 420ms var(--banx-ease) both;
  }

  /* A value that just changed. components.css flashes the colour; this adds the
   * smallest possible lift, which is what makes a live figure read as arriving
   * rather than as being overwritten. */
  .flash {
    animation: banx-flash .55s var(--banx-ease), ex-tick .42s var(--banx-ease);
  }

  @keyframes ex-tick {
    0% { transform: translateY(4px); }
    100% { transform: none; }
  }

  /* ==================================================== 3. scroll arrival */
  @supports (animation-timeline: view()) {

    /* The section heads. Short blocks, safe to move as a whole. */
    .ex-sechead,
    .ex-pagehead,
    .ex-list-head,
    [data-arrive] {
      /* Longhands, not the `animation` shorthand: the shorthand resets
       * animation-timeline to auto, which silently turns this back into an
       * ordinary time-based animation that plays once on load. */
      animation-name: ex-in-sm;
      animation-duration: auto;
      animation-fill-mode: both;
      animation-timeline: view(block);
      /* Finish before the block has fully entered, so one cut off by the fold on
       * a barely-scrollable page is not left part-faded. */
      animation-range: entry 0% entry 70%;
    }

    /* The frame of every 1px-gap grid draws itself as the grid arrives. */
    .ex-stats,
    .ex-cells,
    .ex-apps,
    .ex-wlist {
      animation-name: ex-frame;
      animation-duration: auto;
      animation-fill-mode: both;
      animation-timeline: view(block);
      animation-range: entry 0% entry 40%;
    }

    /* Then the contents cascade into it.
     *
     * The CONTENTS, never the cell: see the note at the top of this file. And no
     * animation-delay, because a delay does nothing on a progress timeline. The
     * cascade comes from giving each cell a later range END, so cells sharing a
     * row share an entry phase but finish at different points along it.
     *
     * The cycle is four because CSS cannot know a grid's column count: .c2, .c3,
     * .c4, `dense`, `app` and a bare auto-fit strip all land here. Four reads as
     * a cascade at every one of those widths, which a per-column rule per variant
     * would only match exactly at one of them.
     */
    .ex-stat > *,
    .ex-cell > *,
    .ex-appcard > * {
      animation-name: ex-in-sm;
      animation-duration: auto;
      animation-fill-mode: both;
      animation-timeline: view(block);
      animation-range: entry 8% entry 58%;
    }

    .ex-stats > :nth-child(4n + 2) > *,
    .ex-cells > :nth-child(4n + 2) > *,
    .ex-apps > :nth-child(4n + 2) > * { animation-range: entry 8% entry 68%; }

    .ex-stats > :nth-child(4n + 3) > *,
    .ex-cells > :nth-child(4n + 3) > *,
    .ex-apps > :nth-child(4n + 3) > * { animation-range: entry 8% entry 78%; }

    .ex-stats > :nth-child(4n + 4) > *,
    .ex-cells > :nth-child(4n + 4) > *,
    .ex-apps > :nth-child(4n + 4) > * { animation-range: entry 8% entry 88%; }

    /* Rows of a list, each on its own timeline. Only the ROW moves: these are
     * not gap-grid cells, they carry their own border-top, so fading one cannot
     * expose a container ground. */
    .ex-block-item,
    .ex-nft-card {
      animation-name: ex-in-sm;
      animation-duration: auto;
      animation-fill-mode: both;
      animation-timeline: view(block);
      animation-range: entry 0% entry 55%;
    }
  }
}

/* ==================================================== 4. interaction, always
 * Hover and press are not entrances: they hide nothing, need no scroll and have
 * nothing to degrade, so they sit outside the reduced-motion gate. base.css
 * already collapses every transition duration under that preference.
 */

/* A cell that navigates rises very slightly. Two pixels, because a cell in a
 * 1px-gap grid that moves any further opens a visible seam in the frame. */
a.ex-cell,
.ex-cell.hov,
.ex-appcard {
  transition:
    background var(--banx-fast) var(--banx-ease),
    transform var(--banx-base) var(--banx-ease);
}

a.ex-cell:hover,
.ex-cell.hov:hover,
.ex-appcard:hover { transform: translateY(-2px); }

/* The icon on a green hairline leans in with it. */
.ex-cell .ex-ico,
.ex-appcard .ex-ico {
  transition: transform var(--banx-base) var(--banx-ease);
}

a.ex-cell:hover .ex-ico,
.ex-cell.hov:hover .ex-ico,
.ex-appcard:hover .ex-ico { transform: scale(1.12); }

/* Buttons acknowledge the press. */
.ex-btn,
.ex-iconbtn {
  transition:
    background var(--banx-fast) var(--banx-ease),
    border-color var(--banx-fast) var(--banx-ease),
    color var(--banx-fast) var(--banx-ease),
    transform 90ms var(--banx-ease);
}

.ex-btn:active,
.ex-iconbtn:active { transform: scale(.97); }

/* A field's border already turns mint on focus, in components.css. It snapped.
 * Easing it is this file's business: components.css owns which colour, this owns
 * how long it takes to get there. */
.ex-form input,
.ex-form textarea,
.ex-form select,
.ex-meta input,
.ex-meta textarea {
  transition:
    border-color var(--banx-fast) var(--banx-ease),
    background var(--banx-fast) var(--banx-ease);
}

/* Tabs slide their active marker rather than snapping it. */
.ex-tabs button { transition: color var(--banx-fast) var(--banx-ease), border-color var(--banx-base) var(--banx-ease); }

/* Table rows and list rows warm on hover. */
.ex-table tbody tr,
.ex-block-item,
.ex-nft-card { transition: background var(--banx-fast) var(--banx-ease); }

/* An NFT card's art fills its frame a little on hover. The card clips it. */
.ex-nft-card { overflow: clip; }
.ex-nft-card img { transition: transform var(--banx-slow) var(--banx-ease); }
.ex-nft-card:hover img { transform: scale(1.04); }

/* ================================================ 6. the app pages, in motion
 * Everything above was written for the six tools when they had almost no motion
 * of their own. This section is the second pass: the parts a reader actually
 * looks at first on a tool page - the eyebrow, the accent in the headline, the
 * form, the stat tiles, the steps - and the atmosphere the main page has and
 * these did not.
 *
 * Same contract as the rest of the file. Every rule that hides anything is
 * inside the reduced-motion gate; none of it needs JavaScript; print undoes it.
 */

@media (prefers-reduced-motion: no-preference) {
  /* ---- the eyebrow's rule draws --------------------------------------------
   * The signature section header opens with a 26px mint dash. It was simply
   * there; now it is drawn, left to right, just ahead of the index and the
   * label it introduces. `scaleX` and not `width`, because a width animation on
   * a pseudo-element relays out its parent on every frame. */
  .ex-wrap .ex-kicker::before {
    animation: ex-rule 420ms var(--banx-ease) 60ms both;
    transform-origin: left center;
  }

  @keyframes ex-rule {
    from { transform: scaleX(0); }
    to { transform: scaleX(1); }
  }

  /* ---- the accent lands last -----------------------------------------------
   * Every one of these headlines is a sentence with its last clause in mint:
   * "Deploy, inspect, stake." The mint half is the page's claim, so it arrives
   * after the words that set it up rather than with them - 190ms after the
   * headline's own entrance, which is long enough to read as a second beat and
   * short enough not to feel like a fault.
   *
   * The headline is already fading as a whole, so this multiplies two opacities
   * on the way in. Both end at 1, and the compounding is what makes the accent
   * hold back while the rest of the line is already legible. */
  .ex-wrap .ex-h .accent,
  .ex-wrap h1 .accent {
    animation: ex-in 520ms var(--banx-ease) 320ms both;
  }

  /* ---- the form arrives in reading order -----------------------------------
   * Eleven of these twelve pages are a form, and the form was the one thing on
   * them that appeared all at once. Its children come in in the order they are
   * read: the heading, the standfirst, each labelled field, the buttons, the
   * hint, then the output.
   *
   * `.ex-panel` is the same box on pages that are not forms, and it is left
   * alone: a panel of live data should not stagger every time it re-renders. */
  .ex-wrap .ex-form > * {
    animation: ex-in-sm 480ms var(--banx-ease) both;
  }

  .ex-wrap .ex-form > :nth-child(1) { animation-delay: 260ms; }
  .ex-wrap .ex-form > :nth-child(2) { animation-delay: 320ms; }
  .ex-wrap .ex-form > :nth-child(3) { animation-delay: 375ms; }
  .ex-wrap .ex-form > :nth-child(4) { animation-delay: 425ms; }
  .ex-wrap .ex-form > :nth-child(5) { animation-delay: 470ms; }
  .ex-wrap .ex-form > :nth-child(6) { animation-delay: 510ms; }
  .ex-wrap .ex-form > :nth-child(n + 7) { animation-delay: 545ms; }

  /* The output is the one child that must never be hidden on arrival: a script
   * writes a result into it and a reader may be sent straight back to it. It
   * fades with the rest but starts from opacity 1 the moment it has content. */
  .ex-wrap .ex-form > .ex-out:not(:empty) { animation: none; }

  /* ---- the stat tiles, on load rather than on scroll -----------------------
   * These sit at the top of a tool page, inside the first viewport, and a
   * scroll-driven cascade never runs for something already on screen: the entry
   * range is complete before the reader arrives, so the tiles render finished.
   * That is safe and it is also nothing. A timed entrance is what gives them a
   * beat, and the trade is stated rather than hidden - a stats block far down a
   * long page now animates before it is seen. On these pages there is no such
   * block; the explorer's long pages use `.ex-cells`, which keeps its
   * scroll-driven cascade below.
   *
   * The CELLS never animate their own opacity - the 1px-gap grid paints one
   * line-coloured ground behind its gaps, and a faded cell shows that ground
   * through them, so an unrevealed strip renders as a solid grey block. The
   * contents animate. */
  .ex-wrap .ex-stats .ex-stat > * {
    animation: ex-in-sm 460ms var(--banx-ease) both;
  }

  .ex-wrap .ex-stats .ex-stat:nth-child(1) > * { animation-delay: 300ms; }
  .ex-wrap .ex-stats .ex-stat:nth-child(2) > * { animation-delay: 350ms; }
  .ex-wrap .ex-stats .ex-stat:nth-child(3) > * { animation-delay: 400ms; }
  .ex-wrap .ex-stats .ex-stat:nth-child(4) > * { animation-delay: 445ms; }
  .ex-wrap .ex-stats .ex-stat:nth-child(n + 5) > * { animation-delay: 485ms; }

  /* ---- the numbered steps -------------------------------------------------- */
  .ex-wrap .ex-step {
    animation: ex-in-sm 480ms var(--banx-ease) both;
  }

  .ex-wrap .ex-step:nth-child(1) { animation-delay: 300ms; }
  .ex-wrap .ex-step:nth-child(2) { animation-delay: 360ms; }
  .ex-wrap .ex-step:nth-child(3) { animation-delay: 415ms; }
  .ex-wrap .ex-step:nth-child(4) { animation-delay: 465ms; }
  .ex-wrap .ex-step:nth-child(n + 5) { animation-delay: 505ms; }

  /* ---- the atmosphere ------------------------------------------------------
   * The one thing that made these pages read as a different site from the main
   * one: the fold there sits in a soft mint glow that breathes, and a tool page
   * sat on flat black.
   *
   * A pseudo-element on the page container, so all twelve pages get it without a
   * line of markup changing - there is no build step here and no component to
   * edit once. It is behind the content, it takes no pointer, and it is the same
   * hue and roughly the same weight as `.open-glow` on the fold.
   */
  .ex-wrap::before {
    animation: ex-breathe 7s var(--banx-ease) infinite;
  }
}

/* Outside the gate: the glow is not an entrance and hides nothing, so a reader
 * who asked for less motion still gets the atmosphere - it simply holds still.
 * `z-index: -1` with a positioned parent is what puts it behind the content: a
 * positioned pseudo-element otherwise paints OVER the in-flow text it sits
 * under, which is how this first went in and covered every headline. */
.ex-wrap {
  position: relative;
  isolation: isolate;
}

.ex-wrap::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 0;
  z-index: -1;
  width: min(130%, 1180px);
  aspect-ratio: 2 / 1;
  transform: translate(-50%, -34%);
  pointer-events: none;
  background: radial-gradient(
    ellipse at center,
    rgb(0 231 136 / 10%),
    rgb(0 231 136 / 3%) 46%,
    transparent 72%
  );
}

@keyframes ex-breathe {
  0%, 100% { opacity: .7; }
  50% { opacity: 1; }
}

/* ============================================================== 7. on paper
 * Every animation off, and every animated property pinned to its end state.
 * `animation: none` rather than `animation-name: none` so the timeline and the
 * range go with it and nothing is left half-applied.
 *
 * Found by taking a full-page screenshot and noticing three panels had lost
 * their headings: a full-page capture, like a sheet of paper, has no scroll
 * position, so every scroll-driven block below the first fold was still at
 * opacity 0.
 */
@media print {
  .ex-nav,
  .ex-livebar,
  .ex-crumb,
  .ex-kicker,
  .ex-h,
  h1,
  .ex-lede,
  .ex-sechead,
  .ex-pagehead,
  .ex-list-head,
  .ex-stats,
  .ex-cells,
  .ex-apps,
  .ex-wlist,
  .ex-stat > *,
  .ex-cell > *,
  .ex-appcard > *,
  .ex-block-item,
  .ex-nft-card,
  .ex-hero > div:first-child > *,
  .ex-hero > div + div,
  .ex-tabpanes > .ex-panel.on,
  .ex-step.on,
  .ex-step,
  .ex-fresh,
  .flash,
  .ex-wrap .ex-kicker::before,
  .ex-wrap .ex-h .accent,
  .ex-wrap h1 .accent,
  .ex-wrap .ex-form > *,
  .ex-wrap::before,
  [data-arrive] {
    animation: none;
    opacity: 1;
    transform: none;
  }

  /* The glow is ink nobody asked for on paper, and a radial gradient prints as a
   * grey wash. `transform: none` above would also undo the centring translate,
   * so it goes entirely rather than being reset. */
  .ex-wrap::before { display: none; }

  .ex-stats,
  .ex-cells,
  .ex-apps,
  .ex-wlist {
    background-color: var(--banx-line);
    border-color: var(--banx-line);
  }
}
