/* ════════════════════════════════════════════════════════════════════════
   THE CODES EXPERIENCE — ported from Codes Native.

   Built to BRANDING.md, not to taste:
     • PAINT, DON'T REFRACT (:6470) — no backdrop-filter on any static
       surface here. Every panel is a painted directional gradient in the
       Code's own colour. Glass survives only on things you tap.
     • NO PURE BLACK, NO PURE WHITE, EXCEPT TEXT (:7414).
     • Buttons are NEVER gradient-filled (:4305) — a gradient may only be a
       thin outline around the body.
     • One radius plus the capsule. No drop shadows anywhere.
     • Contrast is bought with LIGHT, never with black.
   Colour comes from src/codes-palette.js, which carries the app's real
   per-Code palettes.
   ════════════════════════════════════════════════════════════════════════ */

.page-code-next {
  --cn-r: 20px;                      /* THE radius. One. */
  --cn-hair: rgba(255, 255, 255, .12);
  --cn-hair-2: rgba(255, 255, 255, .22);
  --cn-surface: rgba(255, 255, 255, .05);
  --cn-dim: rgba(255, 255, 255, .55);
  --cn-fade: rgba(255, 255, 255, .28);
  background: var(--code-bg, #15151C);
  color: var(--code-ink, #fff);
}
/* A light Code (DETOX, HEAL) flips every alpha to ink, or the whole page
   turns to milk. The app does exactly this by luminance. */
.page-code-next.is-light {
  --cn-hair: rgba(10, 10, 14, .14);
  --cn-hair-2: rgba(10, 10, 14, .26);
  --cn-surface: rgba(10, 10, 14, .05);
  --cn-dim: rgba(10, 10, 14, .62);
  --cn-fade: rgba(10, 10, 14, .34);
}

/* ── The painted ground ───────────────────────────────────────────────── */
.cn-hero {
  position: relative; isolation: isolate; overflow: clip;
  padding: calc(var(--nav-h) + 2.2rem) 0 3.2rem;
  /* The diagonal. Contrast comes from the fall-off, not from more colour. */
  background:
    radial-gradient(120% 90% at 78% 6%, color-mix(in srgb, var(--code-accent) 26%, transparent), transparent 62%),
    linear-gradient(158deg, var(--code-bg) 0%, color-mix(in srgb, var(--code-bg) 72%, #0A0A0E) 68%, #0A0A0E 100%);
}
/* Centred on the page, not pinned to the top — Arian: "the shape needs to be
   more on the middle of the page not stuck to the top." It sits behind the
   card and breathes around it. */
.cn-hero__geo {
  position: absolute; inset: 0; z-index: 0;
  display: grid; place-items: center; pointer-events: none;
  opacity: .5;
}
.cn-hero__geo .geo { width: min(100%, 980px); max-width: 100%; }
/* Every form the deck can land on, one over the other (library.js,
   codeCardPage). code-deck.js sets how strongly each shows from where the
   swipe is, so the next Code's shape arrives with its card, not after a tap.
   Without scripts only the page's own form shows. A form at nothing is
   hidden and its motion paused. The lines take the new Code's colour over
   the same beat as the page does. */
.cn-hero__geo > .cn-geo { grid-area: 1 / 1; width: 100%; display: grid; place-items: center; }
.cn-geo:not(.is-on) { opacity: 0; }
.cn-geo.is-idle { visibility: hidden; }
.cn-geo.is-idle, .cn-geo.is-idle * { animation-play-state: paused; }
.cn-hero__geo .geo :is(path, circle, ellipse) { transition: stroke 1.1s var(--ease-io), fill 1.1s var(--ease-io); }

/* ── Living geometry ──────────────────────────────────────────────────── */
.geo { width: min(118%, 980px); height: auto; overflow: visible; }
.geo path, .geo circle, .geo ellipse {
  fill: none;
  stroke: var(--code-accent, #fff);
  stroke-width: 1;                    /* absolute hairline, never scaled */
  vector-effect: non-scaling-stroke;
  opacity: .30;
}
.geo .geo__web { opacity: .12; }
.geo .geo__seeds circle { opacity: .16; }
.geo .geo__rim { opacity: .42; }
.geo .geo__node { fill: var(--code-accent); stroke: none; opacity: .85; }

/* ~7s breath, ~70s turn, 4.5s travelling shimmer — the app's own tempos. */
.geo--breathe { animation: geoBreathe 7s cubic-bezier(.4, 0, .2, 1) infinite; transform-origin: 50% 50%; }
.geo--spin > * { animation: geoSpin 70s linear infinite; transform-origin: 50% 50%; }
.geo--shimmer .geo__rim, .geo--shimmer .geo__seeds { animation: geoShimmer 4.5s ease-in-out infinite; }
.geo .geo__node { animation: geoPulse 7s ease-in-out infinite; animation-delay: calc(var(--i, 0) * -1.4s); }

@keyframes geoBreathe { 0%, 100% { transform: scale(.965); } 50% { transform: scale(1.035); } }
@keyframes geoSpin    { to { transform: rotate(360deg); } }
@keyframes geoShimmer { 0%, 100% { opacity: .22; } 50% { opacity: .62; } }
@keyframes geoPulse   { 0%, 100% { opacity: .35; } 50% { opacity: .95; } }

/* Three colour blooms, drifting. */
.blooms { position: absolute; inset: 0; z-index: 0; pointer-events: none; overflow: clip; }
.blooms i {
  position: absolute; width: 62vmax; height: 62vmax; border-radius: 50%;
  background: radial-gradient(circle, color-mix(in srgb, var(--code-accent) 40%, transparent), transparent 66%);
  opacity: .07; filter: blur(42px);
  animation: bloomDrift 67s ease-in-out infinite; animation-delay: var(--d, 0s);
}
.blooms i:nth-child(2) { animation-duration: 83s; opacity: .06; }
.blooms i:nth-child(3) { animation-duration: 53s; opacity: .05; }
@keyframes bloomDrift {
  0%   { transform: translate(-18%, -24%) scale(1); }
  33%  { transform: translate(42%, 8%) scale(1.18); }
  66%  { transform: translate(6%, 38%) scale(.92); }
  100% { transform: translate(-18%, -24%) scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .geo--breathe, .geo--spin > *, .geo--shimmer .geo__rim,
  .geo--shimmer .geo__seeds, .geo .geo__node, .blooms i { animation: none; }
}

/* ── The card on its pad ──────────────────────────────────────────────── */
.cn-hero__inner {
  position: relative; z-index: 2;
  display: grid; gap: clamp(1.6rem, 4vw, 2.6rem); justify-items: center; text-align: center;
  /* The air above and below a card inside the deck — the column beside it
     on a wide screen lines up with the card by the same amounts. */
  --deck-pt: 1.2rem; --deck-pb: 1.6rem;
}
/* The words under the card are one block (library.js). On a phone it steps
   aside and its lines stack under the deck, each a row of this grid, with
   the same beat between them rhythm.css gave them when they were the
   grid's own children (`.wrap > * + *`, which no longer reaches them). */
.cn-copy { display: contents; }
@media (max-width: 859px) {
  .cn-hero__inner .cn-copy > * { margin-top: var(--gap-blocks); }
}
.cn-range {
  font-size: var(--fs-micro); letter-spacing: var(--track-hud); text-transform: uppercase;
  color: var(--cn-fade);
}
.cn-card { position: relative; width: min(74vw, 330px); }
.cn-card__img {
  display: block; width: 100%; height: auto; border-radius: var(--cn-r);
  /* A pad, not a shadow: a painted halo in the Code's own light. */
  outline: 1px solid var(--cn-hair);
  outline-offset: 0;
}
.cn-card::after {
  content: ''; position: absolute; inset: -9% -9% -13%; z-index: -1; border-radius: 50%;
  background: radial-gradient(circle, color-mix(in srgb, var(--code-accent) 34%, transparent), transparent 68%);
  filter: blur(34px); opacity: .55;
  animation: geoBreathe 7s cubic-bezier(.4, 0, .2, 1) infinite;
}
@media (prefers-reduced-motion: reduce) { .cn-card::after { animation: none; } }

/* ── The numeral, in Qubed, gradient-filled at 135° ───────────────────── */
.cn-num {
  font-family: var(--font-mark); line-height: .9;
  font-size: clamp(3.4rem, 13vw, 5.6rem); letter-spacing: .04em;
  background: linear-gradient(135deg, var(--code-grad-from), var(--code-grad-to));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.cn-title {
  font-family: var(--font-display); font-weight: 300;
  font-size: clamp(2rem, 8.5vw, 3.6rem); letter-spacing: .1em; text-transform: uppercase;
  margin-top: .25rem;
}
.cn-sub { color: var(--cn-dim); font-size: var(--fs-small); max-width: 32rem; line-height: 1.6; }
.cn-state {
  font-size: var(--fs-micro); letter-spacing: var(--track-hud); text-transform: uppercase;
  color: var(--code-accent);
}

/* ── Actions. Solid accent fill; the outline one is a hairline only. ──── */
.cn-actions { display: grid; gap: var(--s-2); width: min(100%, 30rem); margin-top: .4rem; }
/* The Vault button is a form now; the form steps aside so the button sits in the grid. */
.cn-actions .cn-save { display: contents; }
.cn-btn {
  display: flex; align-items: center; justify-content: center; gap: var(--s-2);
  min-height: 54px; padding: 0 1.6rem; border-radius: var(--r-pill);
  font-size: var(--fs-hud); letter-spacing: var(--track-wide); text-transform: uppercase;
  text-decoration: none; border: 1px solid transparent;
  transition: transform var(--t-fast), opacity var(--t-fast), background var(--t-fast);
}
.cn-btn--fill { background: var(--code-accent); color: var(--code-accent-ink); font-weight: 600; }
.cn-btn--line { background: transparent; color: inherit; border-color: var(--cn-hair-2); }
.cn-btn:active { transform: scale(.985); }
.cn-btn--fill:hover { opacity: .9; }
.cn-btn--line:hover { background: var(--cn-surface); border-color: var(--code-accent); }

/* ── Everything else folds away. Tap to open. ─────────────────────────── */
.cn-body { position: relative; z-index: 2; padding: 2.4rem 0 4.5rem; }
/* A second, slower form low on the page so the motion does not stop dead
   where the hero ends. */
/* ⚠️ NO NEGATIVE INSETS, and the parent must clip. Negative insets plus a
   130% width made this 2016px wide inside a 1440px page — it expanded the
   LAYOUT VIEWPORT itself to 473px on a 390px phone, which is the whole page
   rendering wider than the device. Decoration must never change layout. */
.cn-body { overflow: clip; }
.cn-body__geo {
  position: absolute; inset: auto 0 -6% 0; z-index: 0;
  display: grid; place-items: center; pointer-events: none; opacity: .22;
}
.cn-body__geo .geo { width: min(100%, 720px); max-width: 100%; }
.cn-body > .wrap { position: relative; z-index: 1; }
.cn-fold + .cn-fold { margin-top: .6rem; }
.cn-fold {
  border: 1px solid var(--cn-hair); border-radius: var(--cn-r);
  background: linear-gradient(158deg, var(--cn-surface), transparent 70%);
  overflow: hidden;
}
.cn-fold > summary {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
  padding: 1.05rem 1.25rem; cursor: pointer; list-style: none;
  font-size: var(--fs-micro); letter-spacing: var(--track-hud); text-transform: uppercase;
  color: var(--cn-dim); min-height: 52px;
}
.cn-fold > summary::-webkit-details-marker { display: none; }
.cn-fold > summary:hover { color: inherit; }
.cn-fold__mark { flex: none; width: 13px; height: 13px; position: relative; opacity: .6; }
.cn-fold__mark::before, .cn-fold__mark::after {
  content: ''; position: absolute; inset: 50% 0 auto; height: 1px; background: currentColor;
  transition: transform var(--t-fast);
}
.cn-fold__mark::after { transform: rotate(90deg); }
.cn-fold[open] .cn-fold__mark::after { transform: rotate(0deg); }
.cn-fold[open] > summary { color: inherit; }
.cn-fold__body { padding: 0 1.25rem 1.3rem; color: var(--cn-dim); font-size: var(--fs-small); line-height: 1.7; }
.cn-fold__body > * + * { margin-top: .9rem; }
.cn-fold__body strong, .cn-fold__body b { color: inherit; }

/* A readout row — label left, value right, hairline between. */
.cn-rows { display: grid; }
.cn-rows div { display: flex; justify-content: space-between; gap: var(--s-3); padding: .7rem 0; border-top: 1px solid var(--cn-hair); }
.cn-rows div:first-child { border-top: 0; }
.cn-rows dt, .cn-rows .k { color: var(--cn-fade); font-size: var(--fs-micro); letter-spacing: var(--track-hud); text-transform: uppercase; }
.cn-rows dd, .cn-rows .v { margin: 0; font-family: var(--font-mark); }

/* The affirmation is the one thing that gets to be loud. */
.cn-affirm {
  margin: 2rem 0 0; padding: 1.4rem 0 0; border-top: 1px solid var(--cn-hair);
  font-family: var(--font-display); font-weight: 300; line-height: 1.5;
  font-size: clamp(1rem, 3.6vw, 1.35rem); letter-spacing: .06em; text-transform: uppercase;
  color: var(--code-accent);
}

/* A WIDE SCREEN: THE WORDS SIT WITHIN THE CARD'S OWN EDGES. Arian,
   2026-09-25: "make sure the text and the numbers and all the contents sit
   perfectly within the boundaries of the card and next to it. Not so far
   off." They were seven lines on eight grid rows, a row-gap apart, so the
   number started above the card and the buttons ended wherever the rows
   ran out. Now: the card, and beside it one column exactly the card's
   height — the range level with the card's top edge, the buttons level with
   its bottom edge, a clear, fixed step to its right. */
@media (min-width: 860px) {
  .cn-hero__inner {
    grid-template-columns: 320px minmax(0, 1fr); grid-template-rows: auto 1fr;
    column-gap: clamp(2.25rem, 4vw, 3.5rem); row-gap: 0;
    align-items: stretch; justify-content: start; justify-items: start; text-align: left;
  }
  /* grid-column must be explicit: .cn-range comes first, so auto-placement
     dropped the card into COLUMN 2 and it rendered 618px wide instead of
     320. Caught by the flip-card build. */
  /* One card fills the column exactly, flush with it; the next is a swipe
     (or an arrow key) away. */
  /* rhythm.css (loaded last) gives every child of a .wrap a top margin and
     every .cn-range a chip's padding; here both would push the column off
     the card's edges, so the doubled classes opt out, as academy.css does. */
  .cn-hero__inner.wrap > * + * { margin-top: 0; }
  .cn-hero__inner > .cn-deck { grid-column: 1; grid-row: 1 / span 2; width: 100%; --deck-card: 320px; padding-inline: 0; }
  .cn-hero__inner > .cn-range { grid-column: 2; grid-row: 1; padding: var(--deck-pt) 0 0; margin-bottom: var(--s-3); line-height: 1; }
  .cn-copy {
    grid-column: 2; grid-row: 2;
    display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-3);
    padding-bottom: var(--deck-pb); min-width: 0;
  }
  .cn-copy .cn-title { margin-top: 0; }
  .cn-copy .cn-actions { margin-top: auto; grid-template-columns: 1fr 1fr; width: auto; }
}

/* ── The same living geometry behind an Academy hero ──────────────────── */
.aca-hero__geo {
  position: absolute; inset: 0; z-index: 0;
  display: grid; place-items: center; pointer-events: none; opacity: .34;
}
.aca-hero__geo .geo { width: min(120%, 900px); }
.aca-hero__geo .geo path, .aca-hero__geo .geo circle, .aca-hero__geo .geo ellipse {
  stroke: var(--accent, #fff);
}
.aca-lhero, .aca-shero, .aca-dhero { position: relative; isolation: isolate; }
.aca-lhero > .wrap, .aca-shero > .wrap, .aca-dhero > .wrap { position: relative; z-index: 1; }

/* ── THE DECK — swiping between Codes as cards ────────────────────────────
   Native scroll-snap, so the flick, the momentum and the rubber-band at the
   ends belong to the platform. Nothing re-implements physics; that is why it
   feels like cards instead of a carousel widget.

   BREATHE'S PROGRAMS, ON THE WEB. Arian, 2026-09-25: "make sure the cards are
   clean and not slightly turned. Kinda like the breathe app programs. Clean
   scroll and you see the other 2 skewed." — and then, "They're tilted out
   instead of tilted in." The card you are on stands dead straight; the one
   either side turns in to face it, hinged on its inner edge, its outer edge
   towards you, exactly as Breathe's coverflow does. code-deck.js sets that
   turn from where each card was laid out, every frame of the scroll, so it
   follows the finger and lands at exactly 0. (It was a CSS scroll animation
   that tilted every card up to 2.2° in the flat plane — the "slightly
   turned" — and nested a 3D layer inside the flip card's own, which Safari
   is known to stop repainting.) With scripts off the cards simply sit side
   by side, flat. */
.cn-deck {
  /* The space between two cards is this gap and only this: each card turns
     on its inner edge (public/js/code-deck.js), so nothing closes it up.
     Arian, 2026-09-25: "more distance between the cards". */
  position: relative;
  display: flex; gap: clamp(1.25rem, 5.5vw, 2rem);
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  /* A narrower card, so each neighbour shows a slice worth seeing. */
  --deck-card: min(66vw, 300px);
  padding-inline: max(var(--gutter), calc(50% - var(--deck-card) / 2));
  padding-block: var(--deck-pt, 1.2rem) var(--deck-pb, 1.6rem);
  width: 100%;
}
/* On a phone the deck runs to the glass, as Breathe's does: the neighbours
   are cut by the edge of the screen, not by the page's margin. */
@media (max-width: 859px) {
  .cn-deck { width: calc(100% + 2 * var(--gutter)); margin-inline: calc(-1 * var(--gutter)); }
}
.cn-deck::-webkit-scrollbar { display: none; }
.cn-deck:focus-visible { outline: 2px solid var(--code-accent); outline-offset: 4px; border-radius: var(--cn-r); }

.cn-deck__card {
  flex: 0 0 auto; width: var(--deck-card);
  scroll-snap-align: center; scroll-snap-stop: always;
  position: relative; display: block; text-decoration: none; color: inherit;
}
/* Turned by code-deck.js, and only the cards on screen. No will-change and
   no hidden backface here, on purpose: a layer for every card is every
   Code's picture held in a phone's graphics memory at once, and Safari's
   answer to that is to stop painting some of them — a blank card. */
.cn-deck__img { display: block; width: 100%; height: auto; border-radius: var(--cn-r); outline: 1px solid var(--cn-hair); }

/* The page itself crossfades to whichever card you land on. */
.page-code-next { transition: background-color 1.1s var(--ease-io), color .6s var(--ease-io); }
.cn-num, .cn-title, .cn-sub, .cn-state, .cn-btn { transition: color .6s var(--ease-io), background-color .6s var(--ease-io), border-color .6s var(--ease-io); }

@media (prefers-reduced-motion: reduce) {
  .cn-deck { scroll-behavior: auto; }
  .cn-deck__card { transition: none; animation: none; }
  .page-code-next, .cn-num, .cn-title, .cn-sub, .cn-state, .cn-btn { transition: none; }
}

/* ── The pay gate ────────────────────────────────────────────────────────── */
.gate {
  position: relative; isolation: isolate; overflow: clip;
  padding-block: var(--pad-section);
  border-radius: var(--cn-r, 20px);
  background:
    radial-gradient(110% 80% at 70% 8%, color-mix(in srgb, var(--code-accent) 20%, transparent), transparent 62%),
    linear-gradient(158deg, var(--code-bg) 0%, color-mix(in srgb, var(--code-bg) 74%, #0A0A0E) 70%, #0A0A0E 100%);
  color: var(--code-ink, #fff);
}
.gate__geo { position: absolute; inset: 0; z-index: 0; display: grid; place-items: center; pointer-events: none; opacity: .3; }
.gate__inner { position: relative; z-index: 1; display: grid; gap: var(--s-3); justify-items: start; max-width: 42rem; }
.gate__eyebrow { display: inline-flex; align-items: center; gap: var(--s-1); font-size: var(--fs-micro); letter-spacing: var(--track-hud); text-transform: uppercase; color: var(--code-accent); }
.gate__title { font-family: var(--font-display); font-weight: 300; font-size: var(--fs-h2); letter-spacing: .1em; text-transform: uppercase; }
.gate__line { font-size: var(--fs-body); line-height: 1.7; }
.gate__why { color: rgba(255,255,255,.55); font-size: var(--fs-small); line-height: 1.7; }
.gate__fold { width: 100%; margin-block: var(--s-2); }
.gate__price { font-family: var(--font-mark); font-size: var(--fs-h3); color: var(--code-accent); }
.gate__form { width: min(100%, 22rem); display: grid; gap: .75rem; }
.gate__fine { font-size: var(--fs-micro); color: rgba(255,255,255,.34); letter-spacing: .05em; }
.gate__instead { font-size: var(--fs-small); color: rgba(255,255,255,.6); margin-top: var(--s-2); }
.gate__instead a { color: var(--code-accent); text-decoration: underline; text-underline-offset: 3px; }
.gate .cn-btn { width: min(100%, 22rem); }
