/*
 * The in-flow sections: the opening, the community media, the footer.
 *
 * The pinned scenes live in web/public/css/scenes.css. The split is by
 * behaviour, not by section: anything held while the scroll drives it belongs
 * there, anything that simply scrolls belongs here.
 */

/* ==================================================================== opening
 * The fold is NOT a scene. A scene holds the view still, and holding the view
 * still before the reader has decided to stay reads as a page that will not let
 * them leave - so the first thing they meet scrolls normally, and the first pin
 * is the scene below it.
 *
 * `100svh` rather than `100vh`: on mobile the large viewport unit includes
 * browser chrome that is about to collapse, so a `vh` fold is taller than the
 * screen it is sized to fill.
 */
.open {
  display: grid;
  align-content: center;
  min-height: calc(100svh - var(--banx-nav-h));
  padding: var(--banx-space-7) var(--banx-gutter);
}

/* Copy left, the ecosystem right. The diagram is not decoration here: it states
 * the page's argument before a word of the copy is read, which is what a fold is
 * for. It goes below the copy rather than beside it under 1024px, where a
 * two-column fold gives the headline half a viewport. */
.open-body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.92fr);
  gap: var(--banx-space-7);
  align-items: center;
  width: min(100%, var(--banx-content));
  margin-inline: auto;
}

.open-copy { min-width: 0; }

.open h1 { max-width: 21ch; }

.open h1 {
  max-width: 24ch;
  margin: var(--banx-space-5) 0 0;
  font-size: clamp(2.2rem, 4.6vw, 4.2rem);
  letter-spacing: var(--banx-tracking-display);
  line-height: var(--banx-leading-display);
}

/* The accent carries the second half of the sentence rather than a separate
 * line: one clause emphasised inside the phrase reads as a claim, where a
 * detached green line reads as a tagline bolted underneath. */
.open h1 em {
  /* Through the register, not the raw accent: a section that redefines its own
   * ground has to be able to invert this, and #00E788 is unreadable on anything
   * light. There is no exception to remember if there is no literal. */
  color: var(--banx-accent-text);
  font-style: normal;
}

.open-lede {
  max-width: 52ch;
  margin-top: var(--banx-space-5);
  color: var(--banx-muted);
  font-size: var(--banx-text-lg);
  line-height: 1.6;
}

.open-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--banx-space-4);
  margin-top: var(--banx-space-6);
}

.open-actions .ex-btn { min-height: 52px; padding-inline: 26px; }

/* ------------------------------------------------------------- the ecosystem
 * A fixed aspect ratio is what lets the labels be HTML.
 *
 * The orbits are an SVG viewBox, the labels are ordinary type positioned as
 * percentages of this box. Because the box keeps its ratio at every width, a
 * percentage lands on the same point of the same arrangement whether the block is
 * 380px or 640px wide - and the type stays at a real font size instead of being
 * scaled down with the viewBox, which is what makes SVG <text> unreadable on a
 * phone.
 */
.open-orbit {
  position: relative;
  aspect-ratio: 1000 / 640;
  width: 100%;
  min-width: 0;
}

/* The diagram's own box: the mark is centred on the point every feed runs to, so
 * it is positioned against this and not against `.open-orbit`, which under 700px
 * grows to hold the row of names underneath. */
.open-orbit-figure {
  position: relative;
  aspect-ratio: 1000 / 640;
}

.open-orbit-svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}

/* The dotted feeds, and the dots travel inward.
 *
 * `stroke-dashoffset` going NEGATIVE moves the pattern along the line's own
 * direction, which is from the label to the centre - the direction the page
 * argues value moves in. One full period per iteration (dash 2 plus gap 6 is 8)
 * so the loop has no seam.
 *
 * The delays are staggered per line, or nine identical animations pulse in
 * lockstep and the diagram reads as one blinking object rather than nine feeds.
 * They are fractions of ONE period, so shortening the duration means rescaling
 * them: a delay longer than the period still desynchronises, but it stops being
 * an even spread across it.
 *
 * THE DURATION IS THE SPEED, and it is measured rather than felt. One iteration
 * moves the pattern exactly one period, so a dot travels 8 units per iteration.
 * At the 1.4s this started on that is 5.7 units a second, or 3.2 px/s at a
 * 567px-wide figure - a dot took 26 seconds to cross a typical 150-unit feed,
 * which is why it read as not moving at all. At 0.25s it is 32 units a second,
 * 18 px/s, and the same crossing takes 4.7 seconds. */
.open-feed {
  fill: none;
  stroke: rgb(0 231 136 / 34%);
  stroke-width: 2;
  stroke-dasharray: 2 6;
  stroke-linecap: round;
}

@media (prefers-reduced-motion: no-preference) {
  .open-feed { animation: open-feed .25s linear infinite; }
  .open-feeds line:nth-child(2) { animation-delay: -.03s; }
  .open-feeds line:nth-child(3) { animation-delay: -.06s; }
  .open-feeds line:nth-child(4) { animation-delay: -.08s; }
  .open-feeds line:nth-child(5) { animation-delay: -.11s; }
  .open-feeds line:nth-child(6) { animation-delay: -.14s; }
  .open-feeds line:nth-child(7) { animation-delay: -.17s; }
  .open-feeds line:nth-child(8) { animation-delay: -.19s; }
  .open-feeds line:nth-child(9) { animation-delay: -.22s; }
}

@keyframes open-feed {
  from { stroke-dashoffset: 0; }
  to { stroke-dashoffset: -8; }
}

/* The three orbits and the arcs of light that travelled on them are gone: an arc
 * on an invisible ellipse is a line from nowhere to nowhere. The glow keeps its
 * breath, because it is the ground the feeds are drawn on and not a shape. */
@media (prefers-reduced-motion: no-preference) {
  .open-glow { animation: open-breathe 7s var(--banx-ease) infinite; }
}

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

/* The mark, on the centre every feed runs to. It sat above a $PKL label
 * until the ticker was removed from the fold, which is why the offset was -62%
 * rather than -50%. */
.open-orbit-mark {
  position: absolute;
  left: 50%;
  top: 50%;
  width: clamp(64px, 12%, 116px);
  height: auto;
  transform: translate(-50%, -50%);
}

/* One label per application, scattered around the mark.
 *
 * The offsets were computed from three ellipses - rx/ry 205/122, 325/195 and
 * 452/272 about a centre at 500,320 in a 1000x640 box - as
 * left = 50 + rx% * sin(t), top = 50 - ry% * cos(t), with t clockwise from
 * twelve, three labels per ellipse and each ring phase-shifted so no two sat on
 * one radius. The ellipses are no longer drawn; the arrangement they produced is
 * what these numbers are.
 *
 * The angles were chosen rather than evenly spaced: a long label near three or
 * nine o'clock hangs off the side of the box and into the copy column, so the top
 * and bottom - where there is horizontal room - take the long names.
 * `synthetic.pkl` is thirteen characters and sits at twelve o'clock for exactly
 * that reason.
 *
 * ORBIT_APPS in Opening.jsx holds the same numbers, because the feeds are drawn
 * from them, and tests/design-language.test.mjs compares the two. */
/* The labels' container, and the whole of the two-layout decision.
 *
 * On a desktop it is an overlay the exact size of the diagram, so a percentage
 * on a label lands where its feed is drawn to. Under 700px it is an ordinary wrapping
 * row underneath, and the labels stop being positioned.
 *
 * Absolutely positioned and inset rather than `display: contents`, because the
 * labels' containing block has to be a box of known size: with no box at all
 * their percentages would resolve against whatever positioned ancestor came
 * next, which is not this diagram. */
.open-chips {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.open-chip {
  position: absolute;
  padding: 5px 11px;
  color: var(--banx-text);
  font-family: var(--banx-font-mono);
  /* 1.05vw never reaches the floor on a phone, so the floor is what a phone
   * gets, and at .62rem that was 9.9px of mono. Raised until the labels are
   * legible and re-measured for collisions at 390px, which is what bounds it:
   * nine chips in a 343px box. */
  font-size: clamp(.72rem, 1.05vw, .8rem);
  white-space: nowrap;
  background: var(--banx-bg);
  border: 1px solid rgb(0 231 136 / 34%);
  border-radius: 999px;
  transform: translate(-50%, -50%);
}

/* the inner three: dex placed by hand, the other two from rx 20.5% ry 19.1% at
 * t = 120 and 240.
 *
 * `dex.pkl` was at t = 0, which put it at left 50%, directly under
 * `synthetic.pkl` - the two shared a vertical axis and read as one stacked pair.
 * Sliding it round its own arc traded that for the opposite problem: at any angle
 * small enough to stay clear of `lend.pkl` it landed at the same HEIGHT as
 * `yield.pkl`, 16px from it, which is a horizontal pair instead of a vertical one.
 *
 * With the ellipses no longer drawn, nothing requires dex to sit on that arc at
 * all, so it is placed between the two rows: 57px off `synthetic.pkl`'s axis, and
 * at the midpoint between the labels above and below it - 45px under
 * `synthetic.pkl` and 45px over `yield.pkl`, measured in the rendered page.
 *
 * It went to 43%/22% first, at 40px off the axis, and further out was asked for.
 * The tightest gap between any two labels is now 12px, dex to `yield.pkl`, which
 * is measured rather than predicted: a width estimate put it at 19px and the page
 * says otherwise. Nothing overlaps, and that 12px is the budget any further move
 * left has to come out of. */
.open-chip.is-dex { left: 40%; top: 20%; }
.open-chip.is-perp { left: 67.8%; top: 59.5%; }
.open-chip.is-lend { left: 32.2%; top: 59.5%; }

/* the middle three, from rx 32.5% ry 30.5% at t = 55, 180, 305 */
.open-chip.is-stake { left: 76.6%; top: 32.5%; }
.open-chip.is-stocks { left: 50%; top: 80.5%; }
.open-chip.is-yield { left: 23.4%; top: 32.5%; }

/* the outer three, from rx 45.2% ry 42.5% at t = 0, 145, 215 */
.open-chip.is-synthetic { left: 50%; top: 7.5%; }
.open-chip.is-stable { left: 75.9%; top: 84.8%; }
.open-chip.is-bridge { left: 24.1%; top: 84.8%; }

@media (max-width: 1023px) {
  .open-body { grid-template-columns: minmax(0, 1fr); }
  .open h1 { max-width: 24ch; }
  /* Below the copy, and narrower than the column: at full width the rings are
   * wider than they are tall by so much that they read as lines. */
  .open-orbit { width: min(100%, 34rem); margin-inline: auto; }
}

/* ---- the labels come off the diagram ------------------------------------
 * Nine labels measure 795px here, in a box 343px wide and 219px tall, with each
 * label 31px of that height. A solver over every arrangement of three evenly
 * spaced labels per ellipse found NO placement that clears a 6px gap - not a
 * tight one, none - and the hand-placed arrangement it replaced had
 * `stocks.pkl` overlapping `stable.pkl` and `bridge.pkl` by 4 by 22px.
 *
 * So at this width the diagram keeps its glow and its mark, the feeds are hidden
 * because they would point at nothing, and the names become a row underneath. It is the same answer the scenes reach
 * at the same breakpoint, for the same reason: a label that has to sit inside a
 * shape cannot be fixed by making it smaller. */
@media (max-width: 699px) {
  /* `.open-orbit` stops being the diagram's box and becomes the diagram plus the
   * names. `.open-orbit-figure` is what keeps the ratio, and the mark stays
   * centred on the diagram because it is positioned against that. */
  .open-orbit { aspect-ratio: auto; }

  /* The labels leave the diagram at this width, so a line drawn to where one used
   * to be points at nothing. */
  .open-feeds { display: none; }

  .open-chips {
    position: static;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 6px;
    margin-top: var(--banx-space-4);
    pointer-events: auto;
  }

  .open-chip {
    position: static;
    transform: none;
  }
}


/* ================================================================== community
 * Two clips above a photo strip. `preload="none"` with a poster is what keeps
 * this close to free: nothing but two stills is fetched until a visitor presses
 * play, so the clips cost nothing to anyone who scrolls past.
 */
.home-vids {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--banx-space-4);
  margin-top: var(--banx-space-6);
}

.home-vid {
  margin: 0;
  overflow: hidden;
  background: var(--banx-surface-1);
  border: 1px solid var(--banx-line);
  border-radius: var(--banx-radius);
}

/* The poster sets the box: 16/9 stops the panel jumping between the poster
 * loading and the video's own metadata arriving. */
/* No pointer reaches the clip. There are no controls to reveal on hover, and
 * this is also what removes the native context menu, which is the one remaining
 * way to put them back. See the note in Community.jsx for what that costs. */
.home-vid video { pointer-events: none; }

.home-vid video {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  background: var(--banx-bg-deep);
  border-bottom: 1px solid var(--banx-line);
}


/* One ratio for every cell, so five landscape photographs and one tall polaroid
 * form a grid instead of six different heights. */
/* Four photographs in one strip. At three columns the fourth was an orphan on a
 * second row; four across reads as a strip under the two clips rather than as a
 * gallery competing with them, and every image is 3:2 now that the tall polaroid
 * is gone. */
.home-photos {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--banx-space-3);
  margin-top: var(--banx-space-4);
}

.home-photos img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  border: 1px solid var(--banx-line);
  border-radius: var(--banx-radius-sm);
}

@media (max-width: 860px) {
  .home-vids { grid-template-columns: minmax(0, 1fr); }
  /* Four across a tablet is 180px a photograph, which is a contact sheet. */
  .home-photos { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ===================================================================== footer */

.foot {
  padding: var(--banx-section) var(--banx-gutter) var(--banx-space-6);
  border-top: 1px solid var(--banx-line);
}

.foot-body,
.foot-disc,
.foot-run {
  width: min(100%, var(--banx-content));
  margin-inline: auto;
}

/* Centred, now that it is one line and not a run of four facts - and in a rule of
 * its own, because these three share the block above and putting the
 * `text-align` in there centred the brand, the application list and the whole
 * disclaimer with it. */
.foot-run { text-align: center; }

.foot-body {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
  gap: var(--banx-space-6);
}

.foot-blurb {
  max-width: 34ch;
  margin-top: var(--banx-space-4);
  color: var(--banx-muted);
  line-height: 1.6;
}

.foot-h {
  margin: 0 0 var(--banx-space-4);
  color: var(--banx-subtle);
  font-family: var(--banx-font-mono);
  font-size: var(--banx-text-2xs);
  font-weight: 400;
  letter-spacing: var(--banx-tracking-caps);
  text-transform: uppercase;
}

.foot-apps ul {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--banx-space-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

.foot-apps a { color: var(--banx-muted); }
.foot-apps a:hover { color: var(--banx-text); }

.foot-disc {
  margin-top: var(--banx-space-7);
  padding-top: var(--banx-space-5);
  color: var(--banx-subtle);
  font-size: var(--banx-text-xs);
  line-height: 1.7;
  border-top: 1px solid var(--banx-line);
}

.foot-run {
  margin-top: var(--banx-space-5);
  color: var(--banx-subtle);
  font-family: var(--banx-font-mono);
  font-size: var(--banx-text-2xs);
}

@media (max-width: 719px) {
  .foot-body { grid-template-columns: minmax(0, 1fr); }
}
