/* ============================================================
   gtag.editing
   Palette sampled from the server mark: navy #03005C, periwinkle #9494F7.
   Committed dark, single theme, every colour painted explicitly.
   ============================================================ */

:root{
  --void:    #030308;
  --peri:    #9494f7;
  --peri-lo: #45457f;

  --chalk:   #e7e7f3;
  --mist:    #84849f;
  --slate:   #5e5e76;

  /* Surfaces, darkest to lightest. The page is --void, and everything
     that sits on it is one of these: solid, edged with a hairline, never a
     gradient. Colour comes from the products and the accent, not from the
     panels they sit on. */
  --s1:      #0d0d15;
  --s2:      #14141e;
  --s3:      #1b1b27;
  --line:    rgba(255,255,255,.075);
  --line-2:  rgba(255,255,255,.13);
  --hair:    var(--line);
  --hair-lit:var(--line-2);
  --spec:    rgba(255,255,255,.06);

  /* The one accent. --peri is it as light, for text, icons and lines;
     --accent is it as a fill, deep enough to carry white text. */
  --accent:    #5b5bdf;
  --accent-hi: #6767e8;
  --ok:        #4cc38a;
  --ok-ink:    #8ae3b3;
  --bad:       #e08aa6;

  /* Three radii: panels, cards, and the small things inside them. The
     product tiles are the one exception. They are icons, and keep an
     icon's corners. */
  --r-lg: 16px;
  --r:    12px;
  --r-sm: 8px;

  --ease:      cubic-bezier(.16,1,.3,1);       /* entrances */
  --ease-soft: cubic-bezier(.4,0,.1,1);        /* ambient loops */
  --ease-hover:cubic-bezier(.32,.16,.24,1);    /* interaction, eased both ends */
  --ease-out:  cubic-bezier(.22,.61,.36,1);    /* starts at once, no dead zone, soft landing */
  --t-hover:   .52s;
  --t:         1.9s;

  --shell:  1120px;
  --gutter: clamp(20px, 5vw, 52px);

  --f-display:"Geist","Helvetica Neue",Arial,sans-serif;
  --f-body:   "IBM Plex Sans",system-ui,-apple-system,sans-serif;
  --f-mono:   "IBM Plex Mono",ui-monospace,Consolas,monospace;
}

*,*::before,*::after{ box-sizing:border-box }
html{ scroll-behavior:smooth; -webkit-text-size-adjust:100% }
/* Decoration may run past the sides of the screen (the logo's halo, the
   store's rings), and body hiding it is not enough on a phone: the
   browser still widens the page to fit, the fixed header stretches with
   it and everything can be nudged sideways. clip on both keeps the page
   exactly as wide as the screen. */
html,body{ overflow-x:clip }

body{
  margin:0;
  background:var(--void);
  color:var(--chalk);
  font-family:var(--f-body);
  font-size:16px; line-height:1.65;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;   /* older browsers; clip above wins where it works */
  overflow-x:clip;
}
img{ max-width:100%; display:block }
a{ color:inherit; text-decoration:none }
:focus-visible{ outline:2px solid var(--peri); outline-offset:3px; border-radius:5px }

/* ============================================================
   AMBIENT
   ============================================================ */

/* The ambient layer is anchored to the top of the document and is only
   as tall as the lit zone, so it scrolls away with the hero instead of
   following the viewport down the page. JS sets --litH from the hero. */
.field{
  position:absolute; top:0; left:0; right:0;
  height:var(--litH, 100svh);
  overflow:hidden; pointer-events:none; z-index:0;
  /* pointer parallax. app.js writes the offset straight onto this one
     element and the transition does the easing, so a mouse move restyles
     one box, not the page. */
  transform:translate3d(0,0,0);
  transition:transform 1.6s var(--ease-hover);
}
.motes{
  transform:translate3d(0,0,0);        /* app.js writes the parallax here */
  transition:transform 1.9s var(--ease-hover);
}
.field.is-hidden{ visibility:hidden }
/* hidden is not stopped: without this the browser keeps sampling every
   animation in here on every frame, long after the hero has gone by */
.field.is-hidden .pool,
.field.is-hidden .frames,
.field.is-hidden .strata__lane{ animation-play-state:paused }

/* The feather between the lit zone and the black page below it.

   It has its own height. It must NOT be sized with bottom:0: on an
   absolutely positioned child of a static <body>, bottom is measured
   from the bottom of the viewport, not of the page, so the panel was
   only its full height when the lit zone happened to be exactly one
   screen tall. On the store, where the zone is two screens, it came out
   0px tall and the ambient layer just ended in a flat line.

   Below the feather nothing is needed: the page background is the same
   colour the feather ends on. The extra 40px of solid colour is cover
   for the ambient layer, which leans a few pixels with the pointer. */
.curtain{
  position:absolute; z-index:1; pointer-events:none;
  left:0; right:0;
  top:var(--curtainTop, calc(100svh - 190px));
  height:230px;
  background:linear-gradient(180deg,
    rgba(3,3,8,0)    0px,
    rgba(3,3,8,.30)  60px,
    rgba(3,3,8,.72) 125px,
    rgba(3,3,8,.94) 170px,
    #030308         190px,
    #030308         230px);
}
.below{ position:relative; z-index:2 }

.pool{ position:absolute; border-radius:50%; will-change:transform }
.pool--a{
  width:78vw; height:78vw; max-width:1100px; max-height:1100px;
  left:-20vw; top:-30vw;
  background:radial-gradient(circle at 50% 50%,
    rgba(28,20,130,.26) 0%, rgba(12,8,70,.12) 34%, rgba(0,0,0,0) 70%);
  animation:poolA 90s var(--ease-soft) infinite alternate;
}
.pool--b{
  width:62vw; height:62vw; max-width:880px; max-height:880px;
  right:-16vw; top:6vh;
  background:radial-gradient(circle at 50% 50%,
    rgba(20,14,100,.20) 0%, rgba(10,6,58,.09) 38%, rgba(0,0,0,0) 72%);
  animation:poolB 118s var(--ease-soft) infinite alternate;
}
@keyframes poolA{
  from{ transform:translate3d(0,0,0) scale(1) }
  to  { transform:translate3d(8vw,10vh,0) scale(1.12) }
}
@keyframes poolB{
  from{ transform:translate3d(0,0,0) scale(1.08) }
  to  { transform:translate3d(-9vw,-5vh,0) scale(.94) }
}

/* frame ticks: fine vertical lines, like the marks on a ruler,
   sliding by so slowly you only notice if you watch for it */
.frames{
  position:absolute; inset:0 -140px 0 0;
  background-image:repeating-linear-gradient(90deg,
    rgba(148,148,247,.055) 0 1px, transparent 1px 78px);
  -webkit-mask-image:linear-gradient(180deg, rgba(0,0,0,.9) 0%, rgba(0,0,0,.28) 52%, transparent 88%);
  mask-image:linear-gradient(180deg, rgba(0,0,0,.9) 0%, rgba(0,0,0,.28) 52%, transparent 88%);
  animation:frameDrift 150s linear infinite;
  will-change:transform;
}
@keyframes frameDrift{ to{ transform:translateX(-78px) } }

/* clip bars along the floor only */
.strata{
  --gap:12px;
  position:absolute; left:0; right:0; bottom:0;
  height:min(56vh,480px);
  /* Every row moves at the same speed, so every row going one way moves as
     one rigid piece. app.js builds two lanes, left-going and right-going,
     each holding every row (a row sits in its own direction's lane and
     leaves an empty slot of its height in the other), and each lane is one
     animated layer. The lanes share this one grid cell, so their rows line
     up exactly. */
  display:grid; grid-template-columns:100%; grid-template-rows:100%;
  gap:clamp(9px,1.5vh,17px);
  padding-bottom:5vh;
  overflow:hidden;
  -webkit-mask-image:linear-gradient(180deg,transparent 0%,#000 34%,#000 80%,transparent 100%);
  mask-image:linear-gradient(180deg,transparent 0%,#000 34%,#000 80%,transparent 100%);
}
/* Each row holds its clip set twice, so translateX(-50%) loops with no
   seam. Linear timing and a duration derived from the row's own width,
   so every row moves at exactly the same speed and never slows down. */
.strata__lane{
  grid-area:1 / 1; height:100%;
  display:flex; flex-direction:column; justify-content:flex-end;
  gap:inherit;
  animation:bars var(--dur,160s) linear infinite;
  animation-delay:var(--delay,0s);
  will-change:transform;
}
.strata__lane--rev{ animation-direction:reverse }
.strata__row{
  display:flex; width:max-content; flex:none;
  opacity:var(--o,.5);
}
/* the course panel's small stack still moves row by row */
.solo__rows .strata__row{
  animation:bars var(--dur,160s) linear infinite;
  animation-delay:var(--delay,0s);
  will-change:transform;
}
.solo__rows .strata__row--rev{ animation-direction:reverse }
.strata__row i{
  flex:0 0 var(--w);
  margin-right:var(--gap);
  height:var(--h,20px);
  border-radius:5px;
  background:linear-gradient(180deg, rgba(148,148,247,.115), rgba(148,148,247,.03));
  border:1px solid rgba(148,148,247,.11);
  box-shadow:inset 0 1px 0 rgba(205,205,255,.10);
}
.strata__row i.hi{
  background:linear-gradient(180deg, rgba(148,148,247,.20), rgba(120,120,225,.06));
  border-color:rgba(148,148,247,.20);
}
@keyframes bars{ to{ transform:translate3d(-50%,0,0) } }

.grain{
  position:absolute; inset:0; opacity:.12;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.55'/%3E%3C/svg%3E");
  background-repeat:repeat; background-size:160px 160px;
}

/* ============================================================
   NAV
   ============================================================ */

.navwrap{
  position:fixed; inset:0 0 auto; z-index:60;
  display:flex; justify-content:center;
  padding:18px var(--gutter);
  pointer-events:none;
}
/* The nav is the one thing that floats over the page, so it is the one
   surface with a shadow. Solid, a hairline, and the blur behind it. */
.nav{
  pointer-events:auto;
  display:flex; align-items:center; justify-content:center;
  gap:clamp(16px,3.4vw,34px);
  padding:6px 6px 6px 20px; border-radius:14px;
  background:rgba(10,10,17,.74);
  backdrop-filter:blur(16px) saturate(130%);
  -webkit-backdrop-filter:blur(16px) saturate(130%);
  border:1px solid var(--line);
  box-shadow:0 12px 32px -18px rgba(0,0,0,.9);
  transition:padding .9s var(--ease), background-color .9s var(--ease), border-color .9s var(--ease);
}
.nav.is-stuck{
  padding:5px 5px 5px 18px;
  background:rgba(10,10,17,.9);
  border-color:var(--line-2);
}

.brand{ display:flex; align-items:center; gap:10px }
.brand__name{
  font-family:var(--f-display); font-weight:600; font-size:16px;
  letter-spacing:-.025em; color:#fff; white-space:nowrap;
}
.brand__dot{ color:var(--peri); animation:dotPulse 5.5s var(--ease-soft) infinite }
@keyframes dotPulse{ 0%,100%{ opacity:.68 } 50%{ opacity:1 } }

.nav__links{ display:flex; align-items:center; gap:clamp(14px,2.2vw,26px); list-style:none; margin:0; padding:0 }
.nav__links a{
  position:relative; display:block; padding:6px 1px;
  font-size:14px; color:var(--mist); transition:color .8s var(--ease);
}
.nav__links a::after{
  content:""; position:absolute; left:0; right:0; bottom:2px; height:1px;
  background:var(--peri); opacity:0; transform:scaleX(.4);
  transition:opacity .8s var(--ease), transform .8s var(--ease);
}
.nav__links a:hover{ color:var(--chalk) }
.nav__links a:hover::after,
.nav__links a[aria-current="page"]::after{ opacity:.85; transform:scaleX(1) }
.nav__links a[aria-current="page"]{ color:var(--chalk) }
/* On a phone the nav is a full-width bar: the links on the left, a little
   smaller and closer together, and the sign-in button or the avatar on
   the right. Only on the narrowest screens does the button drop its word
   and keep the Discord mark. */
@media (max-width:780px){
  .navwrap{ padding:12px 14px }
  .nav{ width:100%; justify-content:space-between; gap:12px; padding:5px 5px 5px 16px }
  .nav__links{ gap:16px }
  .nav__links a{ font-size:14px }
}
@media (max-width:400px){
  .navwrap{ padding:10px 10px }
  .nav{ padding-left:13px }
  .nav__links{ gap:12px }
  .nav__links a{ font-size:13.5px }
}
@media (max-width:350px){
  .nav .discord--sm span{ display:none }
}

/* ============================================================
   BUTTONS
   ============================================================ */

/* Two buttons, and no more. The primary is the accent, solid, with one
   hairline of light along its top edge. The secondary is a hairline on
   whatever it sits on. Hover eases the colour; nothing moves, sweeps or
   glows. */
.discord,.btn{
  position:relative;
  display:inline-flex; align-items:center; justify-content:center; gap:9px;
  padding:12px 20px; border-radius:var(--r-sm);
  font-size:14.5px; font-weight:500; line-height:1.25;
  border:1px solid transparent;
  transition:background-color var(--t-hover) var(--ease-hover),
             border-color var(--t-hover) var(--ease-hover),
             color var(--t-hover) var(--ease-hover);
}
.discord svg{ width:18px; height:auto; flex:none }

.discord{
  color:#fff; background:var(--accent);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.16);
}
.discord:hover{ background:var(--accent-hi) }
.discord:active{ background:#5252d4 }

.discord--sm{ padding:8px 14px; font-size:13.5px }
.discord--sm svg{ width:16px }

.discord--quiet,.btn{
  color:var(--chalk);
  background:rgba(255,255,255,.03);
  border-color:var(--line-2);
  box-shadow:none;
}
.discord--quiet:hover,.btn:hover{ background:rgba(255,255,255,.07); border-color:rgba(255,255,255,.2) }
.btn:active{ background:rgba(255,255,255,.05) }

.row{ display:flex; flex-wrap:wrap; gap:12px; align-items:center }
.row--center{ justify-content:center }

/* ============================================================
   TYPE
   ============================================================ */

.shell{ width:100%; max-width:var(--shell); margin-inline:auto; padding-inline:var(--gutter) }

h1,h2,h3,h4{ font-family:var(--f-display); font-weight:600; letter-spacing:-.025em; line-height:1.05; margin:0; text-wrap:balance }
.lede{ margin:16px 0 0; color:var(--mist); font-size:16px; line-height:1.72; max-width:56ch }

.section{ position:relative; z-index:2; padding-block:clamp(72px,9vw,120px) }
/* a plain rule between sections, exactly as wide as the content */
.section::before{
  content:""; position:absolute; top:0; height:1px;
  left:max(var(--gutter), calc(50% - var(--shell) / 2 + var(--gutter)));
  right:max(var(--gutter), calc(50% - var(--shell) / 2 + var(--gutter)));
  background:var(--line);
}
.section--flush::before{ display:none }

.pagehead h1,.sechead h2{ margin-top:0 }

/* ============================================================
   HERO — centred, the logo carries it
   ============================================================ */

.hero{
  position:relative; z-index:2;
  min-height:100svh;
  display:flex; align-items:center;
  padding:clamp(130px,16vh,180px) 0 clamp(70px,10vh,110px);
  text-align:center;
}
.hero__in{
  position:relative; z-index:2;
  display:flex; flex-direction:column; align-items:center;
}

/* particles: slow motes rising through the hero. One composited
   transform each, no script loop. */
.hero__fx{
  position:absolute; inset:0; z-index:1;
  overflow:hidden; pointer-events:none;
  will-change:transform;
  container-type:size;
}
.motes{ position:absolute; inset:0 }
/* On the tall pages the particles start their rise above the feather, not
   at the very bottom of the zone. Started down there they were appearing
   in the black part of the page. 340px is the feather plus the most the
   layer can drift with the scroll. */
.storetop .motes,.dashtop .motes{ bottom:340px }
.motes i{
  position:absolute;
  left:var(--x); bottom:-4%;
  width:var(--s); height:var(--s);
  border-radius:50%;
  background:var(--peri);
  box-shadow:0 0 var(--b) rgba(148,148,247,.75);
  opacity:0;
  animation:rise var(--dur) linear infinite;
  animation-delay:var(--delay);
}
@keyframes rise{
  0%  { transform:translate3d(0,0,0);                      opacity:0 }
  9%  { opacity:var(--o) }
  45% { transform:translate3d(var(--sway),-46cqh,0) }
  88% { opacity:var(--o) }
  100%{ transform:translate3d(0,-104cqh,0);                opacity:0 }
}

/* The artwork carries its own glow, and that glow is a big transparent
   margin: the letterforms are only 93.2% of the image width and 70% of
   its height. Laying that out as-is pushed the copy far below the logo.

   So the glow stays in the file, and the dead space is pulled back out
   of the flow with negative margins on the image. Percentage margins
   resolve against the containing block's WIDTH, which is why these are
   expressed against the image width:
     top    120px of 1920 wide = 6.250% of width
     bottom 117px of 1920 wide = 6.094% of width
   The glow then overflows the box instead of reserving space in it.
   If the artwork is ever re-exported, these three numbers change. */
.hero__logo{
  position:relative;
  display:flex; flex-direction:column;   /* stops the margins collapsing out */
  width:min(100%, 860px);
  margin:0 0 clamp(16px,2.1vw,26px);
  animation:logoIn 2.2s var(--ease) both;
}
.hero__logo img{
  display:block; width:100%; height:auto;
  margin-block:0;
}
.hero__logo::before{
  content:""; position:absolute; z-index:-1; pointer-events:none;
  left:50%; top:50%; translate:-50% -50%;
  width:118%; aspect-ratio:3.4/1;
  background:radial-gradient(closest-side,
    rgba(130,130,248,.165) 0%, rgba(86,86,205,.0675) 48%, rgba(0,0,0,0) 78%);
  animation:halo 8.5s var(--ease-soft) infinite alternate;
}
@keyframes logoIn{
  from{ opacity:0; transform:translate3d(0,22px,0) scale(.97) }
  to  { opacity:1; transform:none }
}
@keyframes halo{
  from{ opacity:.45; transform:scale(.9) }
  to  { opacity:1;   transform:scale(1.1) }
}
.hero__desc{
  margin:0 auto clamp(26px,3.2vw,36px);
  max-width:52ch; font-size:clamp(15.5px,1.6vw,17.5px);
  line-height:1.74; color:var(--mist);
}
.hero__price{
  margin:clamp(20px,2.4vw,28px) 0 0;
  font-size:14px; color:var(--slate);
}

/* ============================================================
   TICKER — what is actually in the packs, scrolling past
   ============================================================ */

.ticker{
  position:relative; z-index:2;
  display:flex; flex-direction:column; gap:9px;
  padding-block:13px;
  border-top:1px solid var(--hair);
  border-bottom:1px solid var(--hair);
  overflow:hidden;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent);
}
.ticker__row{
  display:flex; width:max-content;
  animation:marq var(--dur,120s) linear infinite;
  will-change:transform;
}
.ticker__row--rev{ animation-direction:reverse }
.ticker.is-off .ticker__row{ animation-play-state:paused }   /* app.js, while off screen */
@keyframes marq{ to{ transform:translateX(-50%) } }
.ticker__row b{
  font-family:var(--f-mono); font-weight:400; font-size:12px;
  letter-spacing:.02em; color:var(--slate);
  padding-inline:15px; white-space:nowrap;
}
.ticker__row b::after{
  content:""; display:inline-block; vertical-align:middle;
  width:3px; height:3px; border-radius:50%;
  background:var(--peri); opacity:.45; margin-left:30px;
}

/* ============================================================
   SECTION HEADINGS
   ============================================================ */

.sechead{ max-width:640px; margin-bottom:clamp(32px,4vw,52px) }
.sechead h2{ font-size:clamp(30px,4.4vw,52px); line-height:1.02 }
.sechead .lede{ margin-top:18px }

/* Keyframes that lived with the old preview rig and are still used
   elsewhere: the store icons' flowing dashes, the pacing bars, and the
   playhead on the dashboard's timeline. */
@keyframes flow{ to{ stroke-dashoffset:-36 } }
/* ============================================================
   THE JOURNEY — a rail that fills as you scroll past the pillars
   ============================================================ */

.journey{ position:relative; padding-left:clamp(0px,4vw,54px) }
@media (max-width:760px){ .journey{ padding-left:26px } }

.journey__rail{
  position:absolute; left:clamp(0px,1.4vw,18px); top:10px; bottom:10px; width:2px;
  border-radius:2px; background:var(--line);
  overflow:hidden;
}
@media (max-width:760px){ .journey__rail{ left:3px } }
.journey__fill{
  position:absolute; inset:0;
  background:var(--peri);
  /* app.js writes scaleY() here as you scroll */
  transform:scaleY(0); transform-origin:top;
  /* It follows the scroll rather than being welded to it. A scroll handler
     runs after the page has already been scrolled and painted, so anything
     positioned from one is a frame behind the thing it is meant to track,
     and every stutter in the wheel shows up in it. Easing toward the new
     value instead turns that into a smooth trail: the bar is still behind,
     but on purpose, and it glides. The transition restarts each time the
     value changes, which settles into a gentle chase.

     It is `transform` and not the custom property, so no @property
     registration is needed and the browser keeps this on the compositor. */
  transition:transform .55s var(--ease-out);
}

.stations{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:clamp(34px,5vw,70px) }
.station{ position:relative }
.station__dot{
  position:absolute; left:calc(clamp(0px,1.4vw,18px) - clamp(0px,4vw,54px) - 5px);
  top:12px; width:12px; height:12px; border-radius:50%;
  background:var(--void); border:2px solid var(--line-2);
  transition:background-color .9s var(--ease-hover), border-color .9s var(--ease-hover);
}
@media (max-width:760px){ .station__dot{ left:-27px } }
.station.is-live .station__dot{ background:var(--peri); border-color:var(--peri) }
.station__no{ margin:0 0 6px; font-family:var(--f-mono); font-size:13px; color:var(--slate) }
.station__no:empty{ display:none }
.station h3{ font-size:clamp(23px,2.8vw,32px); letter-spacing:-.025em; margin-bottom:8px }
.station__sub{ margin:0 0 clamp(20px,2.4vw,26px); font-size:15.5px; color:var(--mist) }
.station .feats{ margin-top:clamp(20px,2.4vw,26px) }

/* --- the drawings ------------------------------------------------- */
.viz{
  margin:0; padding:clamp(18px,2.2vw,24px);
  border-radius:var(--r); border:1px solid var(--line);
  background:var(--s1);
}
.viz svg{ width:100%; height:auto; display:block; overflow:visible }
.viz__grid path{ stroke:rgba(255,255,255,.04); stroke-width:1 }
.viz__bad,.viz__good{ fill:none; stroke-width:2.2; stroke-linecap:round;
  stroke-dasharray:400; stroke-dashoffset:400 }
.viz__bad{ stroke:#8a5a7a }
.viz__good{ stroke:var(--peri) }
.station.is-live .viz__bad{ transition:stroke-dashoffset 2.1s var(--ease-soft) .15s; stroke-dashoffset:0 }
.station.is-live .viz__good{ transition:stroke-dashoffset 2.3s var(--ease-soft) .5s; stroke-dashoffset:0 }
.viz__pip{ fill:#8a5a7a; opacity:0 }
.station.is-live .viz__pip{ transition:opacity .8s var(--ease) 1.6s; opacity:1 }

/* per-drawing tuning */
.station__body{ min-width:0 }
.viz--pace{ padding-block:clamp(22px,2.6vw,30px) }

/* ============================================================
   PILLAR 2 — pacing
   A timeline. A ruler, one track, five clips on it. The clips pull
   apart at the cuts so you can see where the blade went, and the
   mistimed one tears rather than separating cleanly. A playhead
   crosses at its own speed and never hurries.
   ============================================================ */

/* Each clip and each cut mark runs its own copy of the same 10s cycle.
   They start together when the pillar comes into view, so they stay in
   step, and since they animate only transform and opacity the compositor
   plays them without the page having to restyle every frame. */
.pace{
  position:relative;
  --edge:24px;   /* the track runs this far past the clips at each end */
  overflow-x:clip;   /* the playhead's box travels past the right edge */
}
@keyframes paceSeg{
  0%,8%   { translate:0 0 }
  38%,62% { translate:calc(var(--mul) * 11px) 0 }
  92%,100%{ translate:0 0 }
}
@keyframes paceCut{
  0%,8%   { translate:0 0; opacity:.22 }
  38%,62% { translate:calc(var(--mul) * 11px) 0; opacity:1 }
  92%,100%{ translate:0 0; opacity:.22 }
}

/* the ruler: a fine tick every 12px, a taller one every 60px, on a hairline */
.pace__ruler{
  display:block; height:14px; margin-inline:var(--edge);
  border-bottom:1px solid rgba(255,255,255,.1);
  background:
    repeating-linear-gradient(90deg, rgba(255,255,255,.192) 0 1px, transparent 1px 60px) left bottom / 100% 9px no-repeat,
    repeating-linear-gradient(90deg, rgba(255,255,255,.078) 0 1px, transparent 1px 12px) left bottom / 100% 4px no-repeat;
}

/* the track: a shade lighter than the frame, a hairline top and bottom */
.pace__lane{
  position:relative; margin-top:10px;
  height:clamp(62px,9vw,84px); border-radius:6px;
  background:rgba(255,255,255,.03);
}
/* the clips and the cut marks share one box, so seams and marks agree */
.pace__track{
  position:relative; height:100%; margin-inline:var(--edge);
  display:flex; align-items:stretch; padding-block:4px;
}

/* a clip: its share of the track less a 2px seam, a name bar along the
   top, and the faint frame lines a video clip shows down its body */
.pace__seg{
  position:relative; flex:0 0 calc(var(--w) - 2px); margin-inline:1px;
  border-radius:4px; overflow:hidden;
  background:
    repeating-linear-gradient(90deg, transparent 0 25px, rgba(255,255,255,.035) 25px 26px),
    rgba(148,148,247,.17);
  border:1px solid rgba(148,148,247,.32);
}
.station.is-live .pace__seg{ animation:paceSeg 10s var(--ease-soft) infinite }
.pace__seg i{
  position:absolute; left:0; right:0; top:0; height:6px;
  background:rgba(148,148,247,.72);
}

/* the mistimed clip: tinted, torn, and never quite steady */
.pace__seg--bad{
  transform:translateX(0);
  background:
    repeating-linear-gradient(90deg, transparent 0 25px, rgba(255,255,255,.03) 25px 26px),
    rgba(215,143,174,.15);
  border-color:rgba(215,143,174,.4);
}
.pace__seg--bad i{ background:rgba(215,143,174,.72) }
.station.is-live .pace__seg--bad{ animation:paceSeg 10s var(--ease-soft) infinite, tear 10s var(--ease-soft) infinite }
@keyframes tear{
  0%,36%  { transform:translate(0,0) }
  40%     { transform:translate(2px,-3px) }
  44%     { transform:translate(-2px,2px) }
  48%     { transform:translate(1px,-1px) }
  56%,100%{ transform:translate(0,0) }
}

/* the cut marks reach a little past the track top and bottom */
.pace__cut{
  position:absolute; top:-8px; bottom:-8px; left:var(--x); width:2px;
  margin-left:-1px; border-radius:2px; background:var(--peri);
  opacity:.22;
}
.station.is-live .pace__cut{ animation:paceCut 10s var(--ease-soft) infinite }
.pace__cut--bad{
  background:repeating-linear-gradient(180deg, #d78fae 0 5px, transparent 5px 9px);
}
.pace__warn{
  position:absolute; left:50%; top:-2px; translate:-50% -100%;
  width:15px; height:15px; border-radius:50%;
  display:grid; place-items:center;
  font-style:normal; font-size:10px; font-weight:700; line-height:1;
  color:#2a0f1c; background:#d78fae;
  opacity:0;
}
.station.is-live .pace__warn{ animation:warn 10s var(--ease-soft) infinite }
@keyframes warn{
  0%,30%  { opacity:0; scale:.5 }
  40%,80% { opacity:1; scale:1 }
  92%,100%{ opacity:0; scale:.5 }
}

/* the playhead: a hairline down the whole height with its head on the
   ruler. It runs the track at a steady speed, fades at the far end and
   comes back in at the start. */
.pace__head{
  position:absolute; top:0; bottom:0; left:var(--edge); right:var(--edge);
  pointer-events:none; opacity:0;
}
.pace__head::before{
  content:""; position:absolute; top:0; bottom:0; left:0;
  width:1px; background:rgba(255,255,255,.6);
}
.pace__head::after{
  content:""; position:absolute; top:0; left:0; translate:-50% 0;
  width:9px; height:10px; background:#fff;
  clip-path:polygon(0 0, 100% 0, 100% 50%, 50% 100%, 0 50%);
}
.station.is-live .pace__head{ animation:playhead 16s linear infinite }
/* the head's box is the track, so moving it by its own width carries the
   line from the start of the track to the end */
@keyframes playhead{
  0%      { transform:translateX(0);    opacity:0;   animation-timing-function:var(--ease-soft) }
  5%      { opacity:.85;                             animation-timing-function:linear }
  91%     { transform:translateX(100%); opacity:.85; animation-timing-function:var(--ease-soft) }
  96%,100%{ transform:translateX(100%); opacity:0 }
}

@media (max-width:620px){
  .pace{ --edge:14px }
}

/* ============================================================
   PILLAR 3 — sound design
   One continuous waveform, drawn by app.js on a single canvas.
   ============================================================ */
.viz--wave{ padding-block:clamp(26px,3vw,34px) }
.wave{ position:relative; height:clamp(86px,12vw,116px) }
.wave canvas{ position:absolute; inset:0; width:100%; height:100%; display:block }

/* ============================================================
   The status dot (the "Online now" figure uses it) and the button the
   join bar on the home page wears.
   ============================================================ */

.dot{ width:7px; height:7px; border-radius:50%; background:var(--slate); flex:none }
.dot--on{ background:var(--ok) }

/* The bar's button is the primary button in everything but name. Its
   hover belongs to the whole bar, because the whole bar is the link. */
.invite__cta{
  display:inline-flex; align-items:center; gap:9px;
  margin-top:10px; padding:12px 20px; border-radius:var(--r-sm);
  font-size:14.5px; font-weight:500; line-height:1.25; color:#fff; white-space:nowrap;
  background:var(--accent);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.16);
  transition:background-color var(--t-hover) var(--ease-hover);
}
.invite__cta svg{ width:18px; height:auto; flex:none; transition:transform var(--t-hover) var(--ease-hover) }

/* ============================================================
   FEATURE LISTS (shared by pillars and pricing)
   ============================================================ */

.feats{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:12px }
.feats li{
  display:flex; align-items:flex-start; gap:11px;
  font-size:14.5px; line-height:1.5; color:var(--mist);
}
.feats li::before{
  content:""; flex:none;
  width:16px; height:16px; margin-top:3px;
  background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.4 6.3 4.8 8.6 9.6 3.6' fill='none' stroke='%239494f7' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 14px 14px no-repeat;
}

/* ============================================================
   STORE PAGE
   One centre axis: title, the shelf of four, the bundle, how buying
   works, the questions.
   ============================================================ */

.page{ padding:0 }

.shell--narrow{ max-width:calc(820px + var(--gutter) * 2) }
.sechead--center{ max-width:none; text-align:center }
.sechead--center .lede{ margin-inline:auto }

/* the lit zone. The bottom padding is room for the clip bars and for the
   black panel's feathered edge to arrive without touching the bundle. */
.storetop{
  --headTop:clamp(140px,18vh,200px);
  /* the laid-out height of the wordmark, which the orbit rings centre on */
  --titleSize:clamp(112px,23vw,252px);
  position:relative; z-index:2;
  padding-bottom:clamp(170px,19vw,235px);
}

.pagehead{
  position:relative; z-index:2; text-align:center;
  padding:var(--headTop) 0 clamp(42px,5vw,62px);
}
/* The wordmark is artwork and carries its own glow. That glow is a wide
   transparent margin: the letterforms are only 87.6% of the image width
   and 71.6% of its height. Laid out as-is it would push the copy far
   below the title, so the dead bands are pulled back out of the flow
   with negative margins on the image. Percentage margins resolve against
   the containing block's WIDTH, hence these being expressed that way:
     top    0.1414 of height x 0.50284 aspect = 7.112% of width
     bottom 0.1429 of height x 0.50284 aspect = 7.183% of width
   The glow then overflows the box instead of reserving space in it.
   Re-export the artwork and these three numbers change. */
.pagehead__title{
  position:relative;
  display:flex; justify-content:center;
  /* the negative margin above trims the glow out of the layout, which
     sits the copy tight under the letterforms. This puts the breathing
     room back so it clears the glow rather than overlapping it. */
  margin:0 0 clamp(32px,3.6vw,50px);
}
.pagehead__title img{
  display:block; width:min(100%,600px); height:auto;
  /* the same overlap the old logo had, so the copy sits where it did */
  margin-block:-2.829% -2.92%;
}
/* a halo of its own behind the mark, breathing, so the baked-in glow
   never reads as flat */
.pagehead__title::before{
  content:""; position:absolute; z-index:-1; pointer-events:none;
  left:50%; top:50%; translate:-50% -50%;
  width:min(112%,790px); aspect-ratio:2.6/1;
  background:radial-gradient(closest-side,
    rgba(130,130,248,.22) 0%, rgba(86,86,205,.08) 48%, rgba(0,0,0,0) 78%);
  animation:halo 9.5s var(--ease-soft) infinite alternate;
}
.pagehead .lede{
  margin:0 auto; max-width:46ch;
  font-size:clamp(15.5px,1.5vw,17px); line-height:1.72;
}

/* --- product accents -----------------------------------------------
   Four hues from the same violet-to-blue family as the brand, close
   enough to belong together and far enough apart to tell at a glance. */
.acc-pr { --a1:#ab95ff; --a2:#4a31b6; --accRGB:171,149,255 }
.acc-dr { --a1:#86b4ff; --a2:#2946b2; --accRGB:134,180,255 }
.acc-sfx{ --a1:#9c9cfa; --a2:#3d3aae; --accRGB:156,156,250 }
.acc-crs{ --a1:#c8a0ff; --a2:#5a2eae; --accRGB:200,160,255 }

/* --- the shelf ------------------------------------------------------ */
.shelf{ position:relative; z-index:2 }
/* "Launch" is said once, here, rather than in front of all three names.
   It sits on the grid's left edge, the way a shelf in a shop is labelled. */
.shelfhead{
  margin:0 0 clamp(16px,2vw,22px);
  font-size:clamp(20px,2vw,24px); letter-spacing:-.022em; color:#fff;
}
.shop{
  display:grid; grid-template-columns:repeat(3,minmax(0,1fr));
  gap:clamp(14px,1.6vw,20px); align-items:stretch;
}
/* three cards do not split into two columns without leaving one alone
   on a row, so below this width they stack */
@media (max-width:860px){ .shop{ grid-template-columns:1fr; max-width:400px; margin-inline:auto } }

.prod{
  position:relative;
  display:flex; flex-direction:column;
  border-radius:var(--r);
  border:1px solid var(--line);
  background:var(--s1);
  transition:border-color var(--t-hover) var(--ease-hover);
}
.prod:hover{ border-color:var(--line-2) }

/* the cover: the product's own colour, barely, behind its tile. That tint
   is how the three cards tell themselves apart at a glance. */
.prod__cover{
  position:relative; overflow:hidden;
  aspect-ratio:16/10;
  display:grid; place-items:center;
  border-radius:calc(var(--r) - 1px) calc(var(--r) - 1px) 0 0;
  border-bottom:1px solid var(--line);
  background-color:color-mix(in srgb, rgb(var(--accRGB)) 6%, var(--s1));
  transition:background-color var(--t-hover) var(--ease-hover);
}
.prod:hover .prod__cover{ background-color:color-mix(in srgb, rgb(var(--accRGB)) 10%, var(--s1)) }

.prod__chip{
  position:absolute; top:12px; right:12px; z-index:3;
  padding:4px 9px; border-radius:6px;
  font-size:12.5px; font-weight:500; color:var(--chalk);
  background:rgba(3,3,8,.45);
  border:1px solid var(--line);
}

/* the tilt, then the tile: nested transforms, so the pointer lean and
   the hover scale never fight over one property */
.prod__float{ position:relative; z-index:2 }

/* The tiles are the products, and the one place a gradient lives outside
   the heroes: each is its product's colour, like an app icon. */
.ptile{
  position:relative; display:grid; place-items:center;
  width:clamp(92px,8.6vw,112px); aspect-ratio:1;
  border-radius:26%;
  background:linear-gradient(152deg, var(--a1) 0%, var(--a2) 80%);
  border:1px solid rgba(255,255,255,.18);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.22),
    0 12px 24px -16px rgba(0,0,0,.9);
  transform:perspective(620px)
            rotateX(calc(var(--rx,0) * 1deg)) rotateY(calc(var(--ry,0) * 1deg))
            scale(var(--ts,1));
  transition:transform .6s var(--ease-out);
}
.prod:hover .ptile{ --ts:1.05 }
.ptile svg{ position:relative; z-index:1; width:66%; height:auto; overflow:visible }

/* --- icon drawing ----------------------------------------------------
   The drawing inside a tile only moves while its card is hovered. The
   tile itself still bobs, leans and carries its glare at idle.

   Whether a drawing is playing is decided HERE, by one class the script
   puts on the card, and nowhere else. It used to be declared `paused` in
   each animation as well, which meant the stylesheet said "paused" on
   every style recalculation while the script was easing the playback rate
   up: the two fought, the animation clock sat at zero for the whole ramp,
   and the sound pack looked like it took half a second to wake up before
   snapping into motion. One owner, no argument.

   Every loop below is declared paused. app.js starts it on pointerenter
   and eases its playback RATE up to 1, then back down to 0 on leave, so
   the drawing winds up and coasts to a stop wherever it is. Nothing
   snaps back to a first frame, and hovering again carries on from there.

   The negative delays pick the frame each icon rests on before it has
   ever been played: a bead part way along its curve, a ring mostly
   drawn. Timing functions inside keyframes are written out literally,
   because var() does not resolve there. */
.ic-grid{ stroke:rgba(255,255,255,.16); stroke-width:1 }
.ic-dash{ stroke:rgba(255,255,255,.6); stroke-width:1.6; stroke-dasharray:3 3.4 }
.ic-line{ stroke:#fff; stroke-width:4.4; stroke-linecap:round }
.ic-kf{ fill:#fff }
.ic-bead{
  fill:#fff;
  offset-path:path("M18 74 C 46 74 48 24 78 22"); offset-rotate:0deg;
  /* the delay picks the frame it rests on. -1.75s parked it on top of
     the end marker; this sits it on the rise, clear of both markers. */
  animation:icBead 4.6s var(--ease-soft) -1.1s infinite;
}
@keyframes icBead{
  0%{ offset-distance:0%; opacity:0 } 10%{ opacity:1 }
  68%{ offset-distance:100%; opacity:1 } 80%,100%{ offset-distance:100%; opacity:0 }
}

.ic-wire{ stroke:rgba(255,255,255,.4); stroke-width:2.2 }
.ic-flow{
  stroke:#fff; stroke-width:2.6; stroke-linecap:round;
  stroke-dasharray:5 13; animation:flow 2.4s linear infinite;
}
.ic-node{ fill:rgba(255,255,255,.18); stroke:#fff; stroke-width:2.6 }
.ic-node--lit{ fill:#fff }

/* Every bar leaves from its drawn height and comes back to it, so the
   rest pose is the waveform as drawn. Two moves per loop rather than one,
   and the first is early, so the wave is alive almost as soon as you
   arrive: it used to be most of a second before anything shifted.
   Neighbours run opposite shapes, one squashing while the next stretches,
   instead of the whole row leaning the same way at the start. */
.ic-mid{ stroke:rgba(255,255,255,.3); stroke-width:1.2 }
.ic-bar{
  fill:#fff; transform-box:fill-box; transform-origin:center;
  animation:icBar calc(var(--d,1.4s) * 1.6) linear infinite;
}
.ic-bar:nth-of-type(even){ animation-name:icBarUp }
@keyframes icBar{
  /* the one move that is not eased in: the wave should be going by the
     time your eye lands on it, not a moment after */
  0%  { transform:scaleY(1);    animation-timing-function:cubic-bezier(.26,.72,.42,1) }
  13% { transform:scaleY(.52);  animation-timing-function:cubic-bezier(.45,0,.55,1) }
  42% { transform:scaleY(1.12); animation-timing-function:cubic-bezier(.45,0,.55,1) }
  71% { transform:scaleY(.62);  animation-timing-function:cubic-bezier(.45,0,.55,1) }
  100%{ transform:scaleY(1) }
}
@keyframes icBarUp{
  /* the one move that is not eased in: the wave should be going by the
     time your eye lands on it, not a moment after */
  0%  { transform:scaleY(1);    animation-timing-function:cubic-bezier(.26,.72,.42,1) }
  13% { transform:scaleY(1.14); animation-timing-function:cubic-bezier(.45,0,.55,1) }
  42% { transform:scaleY(.55);  animation-timing-function:cubic-bezier(.45,0,.55,1) }
  71% { transform:scaleY(1.08); animation-timing-function:cubic-bezier(.45,0,.55,1) }
  100%{ transform:scaleY(1) }
}

/* the ring draws round, holds, then empties in the same direction, so
   the loop point is an empty ring on both sides and there is no cut */
.ic-ring-bg{ stroke:rgba(255,255,255,.24); stroke-width:5.5 }
.ic-ring{
  stroke:#fff; stroke-width:5.5; stroke-linecap:round;
  stroke-dasharray:188.5 188.5; stroke-dashoffset:188.5;
  transform:rotate(-90deg); transform-origin:48px 48px;
  animation:icRing 6.5s linear -1.85s infinite;
}
@keyframes icRing{
  0%  { stroke-dashoffset:188.5;  animation-timing-function:cubic-bezier(.45,0,.55,1) }
  46% { stroke-dashoffset:0 }
  58% { stroke-dashoffset:0;      animation-timing-function:cubic-bezier(.45,0,.55,1) }
  100%{ stroke-dashoffset:-188.5 }
}
.ic-play{ fill:#fff }

/* parked until the card is hovered; the script adds .is-drawing */
.ptile svg *{ animation-play-state:paused }
.is-drawing .ptile svg *{ animation-play-state:running }

/* --- the owned tick ---------------------------------------------------
   The tick is the whole of how a tile shows it is owned: the tile keeps
   its full colour. It belongs to the tile, not to the cover, so it leans with the tilt
   and grows with the hover scale instead of being left behind. Sized
   and placed in percentages of the tile: its centre sits on the middle
   of the corner curve, the way a badge sits on an app icon, and the
   same rule then fits the smaller tiles in the bundle. */
.prod .ptile::before,
.mini .ptile::before,
.solo .ptile::before{
  content:""; position:absolute; z-index:3; pointer-events:none;
  right:-6.6%; bottom:-6.6%; width:29%; aspect-ratio:1; border-radius:50%;
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.6 6.2 4.8 8.4 9.4 3.7' fill='none' stroke='%23071a10' stroke-width='2.1' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 58% no-repeat,
    var(--ok);
  box-shadow:0 0 0 2.5px var(--s1);
  opacity:0; transform:scale(.4);
  transition:opacity .45s var(--ease-out), transform .7s var(--ease);
}
.prod.is-owned .ptile::before,
.mini.is-owned .ptile::before,
.is-owned .solo .ptile::before{ opacity:1; transform:none }
/* in the fan the middle tile sits on top of both its neighbours, so a
   tick on the right of the first tile would be hidden under it. That one
   carries it on the left, which also keeps the hand symmetrical. */
.mini:nth-child(1) .ptile::before{ right:auto; left:-6.6% }

/* --- the words and the price ------------------------------------------ */
.prod__body{
  display:flex; flex-direction:column; flex:1;
  padding:clamp(18px,1.8vw,22px);
}
.prod__name{ font-size:clamp(16.5px,1.5vw,18px); letter-spacing:-.025em; line-height:1.2; margin-bottom:9px }
.prod__desc{ margin:0; flex:1; font-size:14px; line-height:1.62; color:var(--mist) }
.prod__buy{
  display:flex; align-items:center; justify-content:space-between; gap:10px;
  margin-top:clamp(16px,1.8vw,20px); padding-top:clamp(14px,1.6vw,18px);
  border-top:1px solid var(--hair);
}
.prod__price{
  margin:0; display:flex; align-items:baseline;
  font-family:var(--f-display); font-weight:700;
  font-size:clamp(24px,2.3vw,28px); letter-spacing:-.035em; line-height:1;
  color:#fff; font-variant-numeric:tabular-nums;
}
.prod__frac{ font-size:.56em; font-weight:700; letter-spacing:-.02em }
.prod__btn{ padding:9px 16px; font-size:14px }

/* --- the bundle --------------------------------------------------------- */
/* One panel, two uses: the course on its own, and the bundle. Everything
   that gives the panel its colour is a variable here, so the course panel
   is the same build piece for piece and only these values differ. */
.bundle{
  --tint:148,148,247;
  position:relative;
  display:grid; grid-template-columns:minmax(0,1.1fr) minmax(0,.9fr);
  align-items:center; gap:clamp(16px,3vw,44px);
  margin-top:clamp(16px,1.8vw,22px);
  padding:clamp(28px,3.8vw,48px);
  border-radius:var(--r-lg);
  border:1px solid var(--line);
  background-color:color-mix(in srgb, rgb(var(--tint)) 4%, var(--s1));
  transition:border-color var(--t-hover) var(--ease-hover);
}
@media (max-width:820px){ .bundle{ grid-template-columns:1fr } }
.bundle:hover{ border-color:var(--line-2) }

.bundle__copy{ position:relative; z-index:2; display:flex; flex-direction:column; align-items:flex-start }
.bundle__name{
  font-size:clamp(28px,3.4vw,42px); letter-spacing:-.03em; margin-bottom:12px;
}
.bundle__desc{ margin:0 0 clamp(20px,2.4vw,26px); max-width:40ch; font-size:15.5px; line-height:1.66; color:var(--mist) }
.bundle__price{ display:flex; flex-wrap:wrap; align-items:baseline; gap:6px 14px; margin-bottom:clamp(20px,2.4vw,26px) }
.bundle__now{
  margin:0; display:flex; align-items:baseline;
  font-family:var(--f-display); font-weight:700;
  font-size:clamp(42px,4.8vw,58px); letter-spacing:-.04em; line-height:1;
  color:#fff; font-variant-numeric:tabular-nums;
}
.bundle__was{
  margin:0; font-size:17px; color:var(--slate);
  text-decoration:line-through; text-decoration-thickness:1.5px;
  font-variant-numeric:tabular-nums;
}
.bundle__save{
  margin:0; padding:4px 9px; border-radius:6px;
  font-size:13px; font-weight:500; color:var(--ok-ink);
  background:rgba(76,195,138,.1);
  font-variant-numeric:tabular-nums;
}
.bundle__price{ transition:opacity .9s var(--ease-out) }
.bundle.is-complete .bundle__price{ opacity:.4 }
.bundle .discord{ padding-inline:24px }

/* --- the course panel: the same panel, in the course's purple ------------ */
.bundle--crs{ --tint:200,160,255 }

/* Where the bundle has its hand of tiles, the course has one large tile
   standing on a small stack of timeline tracks: the same clip bars that
   run along the floor of every page, in the course's purple, drifting
   slowly past behind it in alternating directions. They are feathered
   out on every side so they never meet the edge of anything, and they
   come up a little when the panel is hovered. The tile itself is
   untouched: it bobs, and its drawing plays on hover, like every other. */
.solo{ position:relative; width:min(100%,420px); height:clamp(170px,19vw,230px) }
.solo__tile{ position:absolute; left:50%; top:50%; translate:-50% -50%; z-index:2 }
.solo .ptile{
  width:clamp(112px,11.5vw,146px);
  transform:none; transition:none;
}
.solo__rows{
  position:absolute; inset:0; z-index:1; overflow:hidden;
  display:flex; flex-direction:column; justify-content:center; gap:9px;
  opacity:.62; transition:opacity .9s var(--ease-hover);
  -webkit-mask-image:radial-gradient(47% 62% at 50% 50%, #000 24%, transparent 96%);
  mask-image:radial-gradient(47% 62% at 50% 50%, #000 24%, transparent 96%);
}
.bundle:hover .solo__rows{ opacity:1 }
.solo__rows .strata__row i{
  background:rgba(200,160,255,.13);
  border:1px solid rgba(214,182,255,.24);
  box-shadow:none;
}

/* owned: the price is struck and the button goes quiet, like an owned
   card. The tile and the art behind it keep their full colour. */
.bundle--crs.is-owned .bundle__now{
  color:var(--slate); text-shadow:none;
  text-decoration:line-through; text-decoration-thickness:2px;
}
.bundle--crs.is-owned .discord{
  color:var(--chalk); background:rgba(255,255,255,.03);
  border-color:var(--line-2); box-shadow:none;
}
.bundle--crs.is-owned .discord:hover{ background:rgba(255,255,255,.07); border-color:rgba(255,255,255,.2) }

/* the four tiles fanned like a hand of cards; they spread on hover */
.bundle__art{ position:relative; z-index:2; display:grid; place-items:center; min-height:clamp(170px,19vw,230px) }
.fan{ position:relative; width:min(100%,440px); height:clamp(170px,19vw,230px) }
.mini{
  position:absolute; left:50%; top:50%;
  translate:calc(-50% + var(--x)) calc(-50% + var(--y));
  rotate:var(--r);
  transition:translate .9s var(--ease-hover), rotate .9s var(--ease-hover);
}
.mini:nth-child(1){ --x:-100px; --y:12px;  --r:-11deg; z-index:1 }
.mini:nth-child(2){ --x:0px;    --y:-12px; --r:0deg;   z-index:3 }
.mini:nth-child(3){ --x:100px;  --y:12px;  --r:11deg;  z-index:2 }
/* on hover the hand opens a little, not far enough to come apart */
.bundle:hover .mini:nth-child(1){ --x:-110px; --y:15px;  --r:-13deg }
.bundle:hover .mini:nth-child(2){ --x:0px;    --y:-16px; --r:0deg }
.bundle:hover .mini:nth-child(3){ --x:110px;  --y:15px;  --r:13deg }
@media (max-width:520px){
  .mini:nth-child(1){ --x:-74px } .mini:nth-child(3){ --x:74px }
  .bundle:hover .mini:nth-child(1){ --x:-80px } .bundle:hover .mini:nth-child(3){ --x:80px }
}
.mini .ptile{
  width:clamp(98px,10.6vw,138px);
  transform:none; transition:none;
}

/* ============================================================
   SESSION STATE
   <html data-session="in|out"> is set by a one-line script in <head>
   before the first paint, and these rules are in the render-blocking
   stylesheet, so the right variant is the only one that ever appears.
   There is no frame where a signed-in visitor sees "Sign in".

   The nav chip is filled in by that same head script, called again the
   moment the nav's markup has been parsed (data-filled), so it is
   complete on the first frame too. Everything else that is signed-in
   only waits, invisible, until app.js has run (data-ready). If that ever
   takes a frame, the space is simply empty for that frame.
   ============================================================ */

[hidden]{ display:none !important }

:root:not([data-session="in"]) [data-auth="in"],
:root[data-session="in"] [data-auth="out"]{ display:none !important }
:root:not([data-ready]) [data-auth="in"]:not([data-filled]){ visibility:hidden }

/* labels that depend on owning the thing: both are in the markup and
   the state picks one, so no script ever rewrites button text */
.when-own{ display:none }
.is-owned .when-buy,.is-complete .when-buy{ display:none }
.is-owned .when-own,.is-complete .when-own{ display:inline }

/* ============================================================
   THE ACCOUNT CHIP
   Replaces the nav's Sign in button once you are signed in. Same
   height and rhythm as the button it stands in for, so the pill does
   not change shape between the two states.
   ============================================================ */

.acct{ position:relative }

.acct__btn{
  display:flex; align-items:center; gap:9px;
  padding:4px 12px 4px 4px; border-radius:var(--r-sm);
  font:inherit; font-size:13.5px; font-weight:500; color:var(--chalk);
  cursor:pointer;
  background:var(--s2);
  border:1px solid var(--line);
  transition:border-color var(--t-hover) var(--ease-hover),
             background-color var(--t-hover) var(--ease-hover);
}
.acct__btn:hover,
.acct__btn[aria-expanded="true"]{ background:var(--s3); border-color:var(--line-2) }

.acct__av{
  display:grid; place-items:center; overflow:hidden;
  width:26px; height:26px; flex:none; border-radius:50%;
  font-family:var(--f-display); font-weight:600; font-size:12.5px; color:#fff;
  background:var(--accent);
}
.acct__av img{ width:100%; height:100%; object-fit:cover }
.acct__name{ white-space:nowrap; letter-spacing:-.01em }
.acct__chev{
  width:10px; height:auto; flex:none; color:var(--slate);
  transition:transform var(--t-hover) var(--ease-hover);
}
.acct__btn[aria-expanded="true"] .acct__chev{ transform:rotate(180deg) }

.acct__menu{
  position:absolute; top:calc(100% + 10px); right:0; z-index:5;
  min-width:196px; padding:6px;
  display:flex; flex-direction:column; gap:1px;
  border-radius:var(--r);
  background:var(--s2);
  border:1px solid var(--line-2);
  box-shadow:0 18px 40px -18px rgba(0,0,0,.95);
  animation:acctIn .34s var(--ease-out) both;
}
@keyframes acctIn{
  from{ opacity:0; transform:translateY(-7px) scale(.98) }
  to  { opacity:1; transform:none }
}
.acct__who{
  margin:0; padding:9px 10px 10px;
  font-size:12.5px; color:var(--slate);
  border-bottom:1px solid var(--line);
}
.acct__who b{ color:var(--chalk); font-weight:600 }
.acct__item{
  display:block; width:100%; text-align:left;
  padding:9px 10px; border-radius:6px;
  font:inherit; font-size:13.5px; color:var(--mist);
  background:none; border:0; cursor:pointer;
  transition:background-color var(--t-hover) var(--ease-hover),
             color var(--t-hover) var(--ease-hover);
}
.acct__item:hover{ background:var(--s3); color:#fff }
.acct__item--out{ margin-top:3px; border-top:1px solid var(--line); border-radius:0 0 6px 6px; color:var(--slate) }
.acct__item--out:hover{ background:rgba(224,138,166,.1); color:#f0c2d4 }

@media (max-width:780px){ .acct__name,.acct__chev{ display:none } .acct__btn{ padding:5px } }

/* ============================================================
   OWNED PRODUCTS
   The artwork of something you own does not change. What marks it is
   the tick on its tile, the chip saying so, the struck price and the
   button going quiet, so the ones you can still buy lead.
   ============================================================ */

.prod.is-owned .prod__chip{ color:var(--ok-ink); border-color:rgba(76,195,138,.3) }
.prod.is-owned .prod__btn{
  color:var(--chalk); background:rgba(255,255,255,.03);
  border-color:var(--line-2); box-shadow:none;
}
.prod.is-owned .prod__btn:hover{ background:rgba(255,255,255,.07); border-color:rgba(255,255,255,.2) }
.prod.is-owned .prod__price{ color:var(--slate); text-decoration:line-through; text-decoration-thickness:1.5px }

/* ============================================================
   FOOTER
   ============================================================ */

.foot{
  position:relative; z-index:2;
  border-top:1px solid var(--hair);
}
.foot__bar{
  display:flex; flex-wrap:wrap; align-items:baseline;
  gap:8px clamp(20px,4vw,44px);
  padding-block:clamp(24px,2.8vw,34px);
}
.foot__copy{ margin:0; font-size:12.5px; color:var(--mist); white-space:nowrap }
/* the policies, which Stripe wants a click away from every page */
.foot__links{ display:flex; flex-wrap:wrap; gap:6px 18px; font-size:12.5px }
.foot__links a{ color:var(--mist); transition:color var(--t-hover) var(--ease-hover) }
.foot__links a:hover,.foot__links a:focus-visible{ color:var(--chalk) }
.foot__legal{ margin:0; font-size:12.5px; line-height:1.6; color:var(--slate); max-width:62ch }

/* ============================================================
   FAQ — one list on the page's centre axis, the questions on hairlines
   ============================================================ */

.faq{ display:flex; flex-direction:column; border-top:1px solid var(--line) }
.qa{ border-bottom:1px solid var(--line) }
.qa summary{
  display:flex; align-items:center; justify-content:space-between; gap:18px;
  padding:22px 4px; cursor:pointer; list-style:none;
  font-family:var(--f-display); font-weight:500; font-size:17px; letter-spacing:-.02em;
  color:var(--chalk);
  transition:color var(--t-hover) var(--ease-hover);
}
.qa summary::-webkit-details-marker{ display:none }
.qa summary:hover,.qa.is-open summary{ color:#fff }
.qa summary i{ position:relative; flex:none; width:12px; height:12px; transition:transform .52s var(--ease-out) }
.qa summary i::before,.qa summary i::after{
  content:""; position:absolute; inset:50% 0 auto; height:1.5px; margin-top:-.75px;
  background:var(--mist); border-radius:2px;
  transition:background-color var(--t-hover) var(--ease-hover), transform .52s var(--ease-out);
}
.qa summary:hover i::before,.qa summary:hover i::after,
.qa.is-open summary i::before,.qa.is-open summary i::after{ background:var(--peri) }
.qa summary i::after{ transform:rotate(90deg) }
.qa.is-open summary i{ transform:rotate(180deg) }
.qa.is-open summary i::after{ transform:rotate(0deg) }
/* The answer. app.js folds it open and shut (height on the wrapper,
   opacity on the text), because a bare <details> snaps shut: the
   browser stops drawing the answer the moment it closes. Without the
   script the answers still open and close, just without the fold. */
.qa > div{ overflow:hidden }
.qa > div p{
  margin:0; padding:0 48px 24px 4px;
  color:var(--mist); font-size:15px; line-height:1.7;
}

/* ============================================================
   REVEAL
   ============================================================ */

.reveal{
  opacity:0; transform:translateY(20px);
  transition:opacity var(--t) var(--ease), transform var(--t) var(--ease);
}
.reveal.in{ opacity:1; transform:none }

/* ============================================================
   REDUCED MOTION
   ============================================================ */

@media (prefers-reduced-motion:reduce){
  html{ scroll-behavior:auto }
  *,*::before,*::after{
    animation-duration:.001ms !important; animation-iteration-count:1 !important;
    transition-duration:.001ms !important;
  }
  .reveal,.hero__logo{ opacity:1 !important; transform:none !important; filter:none !important }
  .motes{ display:none }
  .hero__fx{ transform:none !important }
}

/* ============================================================
   STORE PAGE AMBIENT
   The same ingredients as the home hero, arranged differently so the
   page is recognisably somewhere else without changing the palette.
   ============================================================ */

.p-store .pool--a{
  left:auto; right:-24vw; top:-34vw;
  background:radial-gradient(circle at 50% 50%,
    rgba(24,18,120,.24) 0%, rgba(11,8,64,.11) 34%, rgba(0,0,0,0) 70%);
}
.p-store .pool--b{
  right:auto; left:-14vw; top:14vh;
  background:radial-gradient(circle at 50% 50%,
    rgba(30,22,136,.18) 0%, rgba(12,8,66,.08) 38%, rgba(0,0,0,0) 72%);
}
/* fewer clip bars, sitting lower and quieter */
.p-store .strata{ height:min(40vh,320px); padding-bottom:3vh }

/* orbit rings centred on the title, with two beads riding them */
.orbits{
  position:absolute; left:50%;
  top:calc(var(--headTop) + var(--titleSize) * .5);
  width:0; height:0;
}
.orbits span,.orbits i{
  position:absolute; left:0; top:0; translate:-50% -50%;
  border-radius:50%; aspect-ratio:1;
}
.orbits span:nth-child(1){ width:min(66vw,540px);  border:1px dashed rgba(148,148,247,.2);  animation:spin 80s linear infinite }
.orbits span:nth-child(2){ width:min(96vw,800px);  border:1px solid  rgba(148,148,247,.075) }
.orbits span:nth-child(3){ width:min(128vw,1080px); border:1px dotted rgba(148,148,247,.15); animation:spin 150s linear infinite reverse }
.orbits i:nth-of-type(1){ width:min(66vw,540px); animation:spin 38s linear infinite }
.orbits i:nth-of-type(2){ width:min(96vw,800px); animation:spin 61s linear infinite reverse }
.orbits i::before{
  content:""; position:absolute; left:50%; top:-3.5px; translate:-50% 0;
  width:7px; height:7px; border-radius:50%;
  background:var(--peri); box-shadow:0 0 14px rgba(148,148,247,1);
}
.orbits i:nth-of-type(2)::before{ width:5px; height:5px; top:-2.5px; background:#d9d9ff }

/* nothing in the lit zone keeps animating once it has scrolled away */
[data-lit].is-idle .motes i,
[data-lit].is-idle .orbits span,
[data-lit].is-idle .orbits i,
[data-lit].is-idle .hero__logo::before,
[data-lit].is-idle .pagehead__title::before,
[data-lit].is-idle .solo__rows .strata__row{ animation-play-state:paused }

/* ------------------------------------------------------------
   Drawn on the CPU
   With hardware acceleration off there is no graphics card compositing
   the page, and every moving layer behind a hero has to be redrawn by
   the processor, full screen, every frame. The home page managed 8
   frames a second like that. The head script marks the page
   data-gpu="off" when it finds no acceleration, and then:
   - the drift stops, and the pieces stop being layers of their own, so
     the whole background is painted once and after that only scrolled;
   - each piece keeps a frame it would have passed through anyway: the
     second pool at its starting size, the motes partway up their rise
     (app.js places them), the halos halfway through a breath, and the
     beads apart on their rings;
   - the nav drops its blur, which would be redone for every frame of a
     scroll, and is a shade more solid instead.
   Hovers, reveals and the course drawings are small enough to keep.
   ------------------------------------------------------------ */
:root[data-gpu="off"] :is(.pool, .frames, .strata__lane, .orbits span, .orbits i){
  animation:none; will-change:auto;
}
:root[data-gpu="off"] .pool--b{ transform:scale(1.08) }
:root[data-gpu="off"] .motes i{
  animation:none;
  transform:translate(var(--tx,0px), var(--ty,0px));
  opacity:var(--op,0);
}
:root[data-gpu="off"] :is(.hero__logo, .pagehead__title)::before{ animation:none; opacity:.72 }
:root[data-gpu="off"] .orbits i:nth-of-type(1){ rotate:-38deg }
:root[data-gpu="off"] .orbits i:nth-of-type(2){ rotate:52deg }
:root[data-gpu="off"] .nav{
  -webkit-backdrop-filter:none; backdrop-filter:none;
  background:rgba(10,10,17,.9);
}

/* ============================================================
   DASHBOARD
   You, top left. Under that a sidebar of three views: downloads, past
   purchases, settings. The right-hand side is whichever one is open.
   Signed out, the whole page is one centred way in.
   ============================================================ */

/* --- ambient: the same palette as the other pages, laid out its own
   way. A dot grid drifting on the diagonal instead of ruler lines, and
   the main pool parked top left, behind the profile. */
.p-dash .frames{
  inset:-68px;
  background-image:radial-gradient(circle, rgba(148,148,247,.085) 0 1px, rgba(0,0,0,0) 1.7px);
  background-size:34px 34px;
  animation:dotDrift 110s linear infinite;
}
@keyframes dotDrift{ to{ transform:translate3d(34px,-34px,0) } }
.p-dash .pool--a{
  width:90vw; height:90vw; max-width:1180px; max-height:1180px;
  left:-26vw; top:-38vw;
  background:radial-gradient(circle at 50% 50%,
    rgba(40,32,160,.135) 0%, rgba(16,12,84,.055) 36%, rgba(0,0,0,0) 68%);
}
.p-dash .pool--b{
  left:auto; right:-20vw; top:30vh;
  background:radial-gradient(circle at 50% 50%,
    rgba(30,22,136,.09) 0%, rgba(12,8,66,.04) 38%, rgba(0,0,0,0) 72%);
}
.p-dash .strata{ height:min(40vh,320px); padding-bottom:3vh }

/* the lit zone. Bottom padding is room for the clip bars and for the
   black panel's feathered edge, the same as the store. */
.dashtop{
  position:relative; z-index:2;
  padding-bottom:clamp(190px,21vw,260px);
}

/* --- the identity ring ------------------------------------------------
   Four arcs round the avatar with a spark riding them, so it is never
   completely still. Only the signed-out page uses this now: the arcs used
   to light up one per product on the sidebar profile, and that came out in
   favour of a plain avatar in an outlined box. */
.idring{
  position:relative; isolation:isolate;
  width:clamp(150px,17vw,196px); aspect-ratio:1;
  margin:0 auto clamp(24px,2.8vw,34px);
}
.idring > svg{ position:absolute; inset:0; width:100%; height:100%; overflow:visible }
.seg{ transform:rotate(var(--a)); transform-origin:100px 100px }
.seg circle{ stroke-width:5; stroke-linecap:round; stroke-dasharray:76 284 }
.seg__base{ stroke:rgba(255,255,255,.1) }
.idring__av{
  position:absolute; inset:13%; border-radius:50%; overflow:hidden;
  display:grid; place-items:center;
  font-family:var(--f-display); font-weight:700;
  font-size:clamp(42px,5vw,60px); line-height:1; color:#fff;
  background:var(--accent);
}
.idring__av img{ width:100%; height:100%; object-fit:cover }
.idring__av--discord{ background:var(--accent) }
.idring__av--discord svg{ width:48%; height:auto }

/* --- signed out ------------------------------------------------------ */
.gate{ text-align:center; padding:clamp(140px,19vh,200px) 0 clamp(20px,4vw,50px) }
.gate__title{ font-size:clamp(30px,4.6vw,54px) }
.gate .lede{ margin:18px auto clamp(26px,3.2vw,36px); max-width:46ch }

/* --- the frame: sidebar and the open view ------------------------------ */
.dash{ padding-top:clamp(112px,14vh,142px) }
/* wider than the rest of the site: this is a place you work in, not a
   page you read. The sidebar is a full-length column, so the two sides
   are stretched to the same height, and that height is never less than
   the screen. */
.dash__grid{
  --dgap:clamp(20px,2.4vw,36px);
  max-width:calc(1430px + var(--gutter) * 2);
  display:grid; grid-template-columns:300px minmax(0,1fr);
  gap:var(--dgap); align-items:stretch;
  min-height:calc(100svh - 170px);
}

.side{
  position:relative; z-index:2;   /* in front of the page */
  display:flex; flex-direction:column;
  padding:20px 14px 16px;
  border-radius:var(--r-lg); border:1px solid var(--line);
  background:var(--s1);
  /* the one shadow on this page: the sidebar's, falling on the page
     that comes out from behind it */
  box-shadow:14px 0 36px -24px rgba(0,0,0,.95);
}
/* the panel is as long as the page, so the part you use rides along
   inside it as you scroll, and the store link stays at its foot */
.side__top{ position:sticky; top:96px; display:flex; flex-direction:column; gap:20px }
/* The profile: who you are, in a box of its own at the top of the
   sidebar, with the same plain hairline as every other box. */
.side__me{
  position:relative;
  display:flex; align-items:center; gap:14px;
  padding:14px;
  border-radius:var(--r);
  border:1px solid var(--line);
  background:var(--s2);
  transition:border-color var(--t-hover) var(--ease-hover);
}
.side__me:hover{ border-color:var(--line-2) }
@keyframes spin{ from{ rotate:0deg } to{ rotate:360deg } }
.side__av{
  position:relative; flex:none; display:grid; place-items:center; overflow:hidden;
  width:52px; height:52px; border-radius:50%;
  font-family:var(--f-display); font-weight:700; font-size:21px; line-height:1; color:#fff;
  background:var(--accent);
}
.side__av img{ width:100%; height:100%; object-fit:cover }
.side__who{ min-width:0 }
.side__name{
  margin:0; font-family:var(--f-display); font-weight:700;
  font-size:21px; letter-spacing:-.025em; line-height:1.1; color:#fff;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.side__handle{ position:relative; margin:5px 0 0; font-size:13.5px; color:var(--mist); overflow:hidden; text-overflow:ellipsis; white-space:nowrap }

/* the tabs. One lit bar sits behind whichever is open and slides to the
   next one, the same piece as the selection bar in the course player. */
.side__nav{ position:relative; display:flex; flex-direction:column; gap:3px }
.side__glide{
  position:absolute; left:0; top:0; z-index:0; pointer-events:none;
  width:var(--w,0); height:var(--h,0);
  transform:translate(var(--x,0), var(--y,0));
  border-radius:var(--r-sm);
  background:var(--s3);
  border-left:2px solid var(--peri);
  transition:transform .62s var(--ease-hover), width .62s var(--ease-hover), height .62s var(--ease-hover);
}
.tab{
  position:relative; z-index:1;
  display:flex; align-items:center; gap:11px;
  padding:11px 12px 11px 14px; border-radius:var(--r-sm);
  font-size:14.5px; font-weight:500; color:var(--mist);
  transition:color var(--t-hover) var(--ease-hover);
}
.tab svg{ width:18px; height:18px; flex:none }
.tab span{ flex:1; min-width:0 }
.tab:hover{ color:var(--chalk) }
.tab[aria-current="page"]{ color:#fff }
.tab__n{
  flex:none; min-width:24px; padding:2px 7px; border-radius:6px; text-align:center;
  font-family:var(--f-mono); font-size:11.5px; font-weight:500; color:var(--mist);
  background:var(--s3);
  font-variant-numeric:tabular-nums;
  transition:color var(--t-hover) var(--ease-hover), background-color var(--t-hover) var(--ease-hover);
}
.tab[aria-current="page"] .tab__n{ color:var(--chalk); background:rgba(255,255,255,.08) }
.side__more{ margin:auto 0 0; padding:16px 6px 2px; border-top:1px solid var(--hair) }
.side__more .btn{ width:100% }
#dash[data-have="3"] .side__more{ display:none }

/* the open view. Switching fades the panel out, swaps, and fades it
   back, so one view never cuts to the next. */
/* The page. Everything right of the sidebar sits on one slab: the same
   radius and hairline as the sidebar, but darker, flatter and quieter.
   It reaches back under the sidebar, nearly to its far edge, and stands
   a little shorter, so it comes out from behind the sidebar the way a
   page comes out from a book's cover. The sidebar's shadow falls on it.
   The views swap inside it; the slab itself never moves. */
.dpage{
  --pad-x:clamp(22px,3vw,40px);
  --tuck:290px;                       /* how far back under the sidebar */
  position:relative; z-index:1; min-width:0; min-height:430px;
  margin:10px 0 10px calc(-1 * (var(--dgap) + var(--tuck)));
  padding:clamp(22px,2.8vw,36px) var(--pad-x) clamp(26px,3vw,40px) calc(var(--pad-x) + var(--dgap) + var(--tuck));
  border-radius:var(--r-lg); border:1px solid var(--line);
  background:#08080e;
}

.dpanel{
  --travel:10px;                      /* how far a view moves each way */
  min-width:0;
  transition:opacity .6s var(--ease-out), transform .7s var(--ease-out);
}
/* leaving: the same fade as before, but the direction now follows the
   list. Going down, the old view moves up and out; going up, down and out. */
.dpanel.is-swapping{
  opacity:0;
  transition:opacity .22s var(--ease-soft), transform .22s var(--ease-soft);
}
.dpanel.is-swapping[data-dir="down"]{ transform:translateY(calc(var(--travel) * -1)) }
.dpanel.is-swapping[data-dir="up"]  { transform:translateY(var(--travel)) }
/* arriving: parked on the far side without a transition, so that when the
   class comes off it eases in from there, continuing the same direction */
.dpanel.is-entering{ opacity:0; transition:none }
.dpanel.is-entering[data-dir="down"]{ transform:translateY(var(--travel)) }
.dpanel.is-entering[data-dir="up"]  { transform:translateY(calc(var(--travel) * -1)) }
/* and a row added to a list while you are looking at it comes the same way */
.dpanel[data-dir="up"] .ledger__row{ --from:-8px }
.view{ display:none }
.view.is-on{ display:block }
.view__head{
  display:flex; flex-wrap:wrap; align-items:baseline; justify-content:space-between; gap:8px 20px;
  margin:4px 0 clamp(20px,2.4vw,28px);
}
.view__head h1{
  font-size:clamp(26px,2.6vw,34px); font-weight:600; letter-spacing:-.025em; line-height:1.1;
}
.view__sub{ margin:0; font-size:15.5px; color:var(--mist) }
.view__sub b{ color:#fff; font-weight:600; font-variant-numeric:tabular-nums }

/* one of three lines, picked by how much of the four you own */
[data-if]{ display:none }
#dash[data-have="0"] span[data-if="none"],
#dash[data-have="3"] span[data-if="all"],
#dash:not([data-have="0"]):not([data-have="3"]) span[data-if="some"]{ display:inline }

/* --- downloads: four rows ---------------------------------------------- */
.cases{ display:flex; flex-direction:column; gap:12px }

.case{
  position:relative;
  display:grid; grid-template-columns:clamp(104px,17%,134px) minmax(0,1fr) auto;
  align-items:stretch;
  border-radius:var(--r);
  border:1px dashed rgba(255,255,255,.12);
  transition:border-color var(--t-hover) var(--ease-hover), background-color var(--t-hover) var(--ease-hover);
}
.case.is-owned{ border-style:solid; border-color:var(--line); background:var(--s1) }
.case.is-owned:hover{ border-color:var(--line-2) }

/* the stage the tile stands on: empty until the slot is filled, then the
   product's colour, barely, the same as its card in the store */
.case__stage{
  position:relative; overflow:hidden;
  display:grid; place-items:center;
  min-height:120px;
  border-radius:calc(var(--r) - 1px) 0 0 calc(var(--r) - 1px);
  border-right:1px solid var(--line);
  transition:background-color 1s var(--ease-out);
}
.case.is-owned .case__stage{ background-color:color-mix(in srgb, rgb(var(--accRGB)) 6%, var(--s1)) }

/* The tile. Empty, it is a dashed outline with the drawing ghosted
   inside. The glossy body is a layer on top that fades in when the slot
   fills, so going from one to the other is a cross-fade, never a swap. */
.case .ptile{
  width:clamp(62px,6vw,74px);
  background:none; box-shadow:none;
  border:1.5px dashed rgba(255,255,255,.16);
}
.case .ptile::before{
  content:""; position:absolute; inset:-1.5px; z-index:0;
  border-radius:inherit; pointer-events:none;
  background:linear-gradient(152deg, var(--a1) 0%, var(--a2) 80%);
  border:1px solid rgba(255,255,255,.18);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.22), 0 10px 20px -14px rgba(0,0,0,.9);
  opacity:0; transition:opacity 1s var(--ease-out);
}
.case .ptile::after{ opacity:0; transition:opacity 1s var(--ease-out) }
.case .ptile svg{ opacity:.36; transition:opacity 1s var(--ease-out) }
.case.is-owned .ptile::before,
.case.is-owned .ptile::after,
.case.is-owned .ptile svg{ opacity:1 }
.case.is-owned:hover .ptile{ --ts:1.07 }
.case__body{
  display:flex; flex-direction:column; justify-content:center; min-width:0;
  padding:16px clamp(16px,2vw,24px);
}
.case__name{
  font-size:clamp(16.5px,1.5vw,18.5px); letter-spacing:-.025em; line-height:1.2;
  color:var(--mist); transition:color .8s var(--ease-out);
}
.case.is-owned .case__name{ color:#fff }
.case__act{
  display:grid; align-items:center; justify-items:end;
  padding:16px clamp(16px,2vw,24px) 16px 0;
}

.case__spec{ list-style:none; margin:10px 0 9px; padding:0; display:flex; flex-wrap:wrap; gap:6px }
.case__spec li{
  padding:3px 8px; border-radius:6px;
  font-family:var(--f-mono); font-size:12px; color:var(--mist);
  background:var(--s2);
  border:1px solid var(--line);
}
.case__meta{ margin:0; font-size:13.5px; color:var(--mist) }
.case__order{ margin-left:9px; font-family:var(--f-mono); font-size:12.5px; color:var(--slate); white-space:nowrap }
.case__btn{ padding:9px 16px; font-size:14px; white-space:nowrap }
.case__btn svg{ width:16px }

@media (max-width:640px){
  .case{ grid-template-columns:96px minmax(0,1fr) }
  .case__stage{ grid-row:1 / span 2; border-radius:calc(var(--r) - 1px) 0 0 calc(var(--r) - 1px) }
  .case__body{ padding-bottom:6px }
  .case__act{ grid-column:2; justify-items:start; padding:0 16px 16px }
}

/* nothing bought yet: the same dashed outline a row used to wear when it
   was empty, once, in the middle, instead of four times over */
.lib__none{
  display:none; flex-direction:column; align-items:center; text-align:center;
  gap:clamp(16px,2vw,22px);
  padding:clamp(40px,5.4vw,68px) clamp(20px,3vw,34px);
  border-radius:var(--r); border:1px dashed rgba(255,255,255,.12);
}
#dash[data-have="0"] .lib__none{ display:flex }
#dash[data-have="0"] .cases{ display:none }
.lib__none p{ margin:0; max-width:40ch; font-size:15px; line-height:1.7; color:var(--mist) }
.lib__none-mark{
  position:relative; width:60px; height:60px; border-radius:14px;
  border:1.5px dashed rgba(255,255,255,.16);
}
/* the same arrow the download buttons carry, waiting */
.lib__none-mark::before{
  content:""; position:absolute; left:50%; top:50%; translate:-50% -50%;
  width:24px; height:24px;
  background:var(--mist);
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M10 3v10m0 0 4-4m-4 4-4-4M4 16.5h12' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M10 3v10m0 0 4-4m-4 4-4-4M4 16.5h12' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* --- past purchases: a ledger --------------------------------------------
   One row per order, newest first: the product's own tile, the date, the
   name, the amount. A bundle shows its three tiles stacked. Rows sit on
   hairlines and light up faintly under the pointer. The amount is set in
   the display face, as on a receipt. */
.ledger{ min-width:0 }
.ledger__rows{ list-style:none; margin:0; padding:0 }
.ledger__row{
  position:relative;
  display:grid; grid-template-columns:auto 8em minmax(0,1fr) auto;
  gap:4px 20px; align-items:center;
  padding:13px 12px; border-radius:var(--r-sm);
  transition:background-color var(--t-hover) var(--ease-hover);
}
.ledger__row::after{
  content:""; position:absolute; left:12px; right:12px; bottom:0; height:1px;
  background:var(--hair);
}
.ledger__row:hover{ background:rgba(255,255,255,.03) }

/* the tile: the same art as the download rows, at a fifth of the size */
.ledger__art{ position:relative; display:block; width:46px; height:46px }
.ledger__tile{ display:block }
.ledger__art .ptile{
  width:46px; border-radius:13px;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.2), 0 6px 14px -10px rgba(0,0,0,.9);
  transition:transform .8s var(--ease-hover);
}
.ledger__art .ptile svg{ width:60% }
/* three packs in one order: stacked, each showing an edge of the next */
.ledger__art--set{ width:78px }
.ledger__art--set .ledger__tile{ position:absolute; top:0; left:calc(var(--i) * 16px); z-index:var(--i) }
.ledger__art--set .ptile{ width:40px; margin-top:3px }
.ledger__row:hover .ledger__art .ptile{ transform:translateY(-2px) }

.ledger__date{ font-size:13.5px; color:var(--mist); font-variant-numeric:tabular-nums; white-space:nowrap }
.ledger__what{ min-width:0; display:flex; flex-direction:column; gap:3px }
.ledger__name{ font-size:15px; font-weight:500; color:var(--chalk) }
.ledger__items{ font-size:13px; line-height:1.55; color:var(--mist) }
.ledger__id{ font-family:var(--f-mono); font-size:12px; color:var(--slate); margin-top:1px }
.ledger__amount{
  font-family:var(--f-display); font-weight:600; font-size:18px; letter-spacing:-.02em;
  color:#fff; font-variant-numeric:tabular-nums; text-align:right; white-space:nowrap;
}

/* nothing yet: the same dashed outline the download rows use for a
   product you do not own yet */
.ledger__empty{
  display:none; margin:0; padding:36px 20px; text-align:center;
  font-size:15px; color:var(--mist);
  border:1px dashed rgba(255,255,255,.12); border-radius:var(--r);
}
#history.is-empty .ledger__empty{ display:block }
#history.is-empty .ledger__rows,
#history.is-empty .ledger__foot{ display:none }

.ledger__foot{
  display:flex; justify-content:space-between; align-items:baseline;
  margin:0; padding:18px 12px 0;
  font-size:13.5px; color:var(--mist);
}
.ledger__foot b{
  margin-left:8px; font-family:var(--f-display); font-weight:600; font-size:20px;
  letter-spacing:-.02em; color:#fff; font-variant-numeric:tabular-nums;
}
@media (max-width:640px){
  .ledger__row{ grid-template-columns:auto minmax(0,1fr) auto; gap:2px 14px; padding:12px 8px }
  .ledger__row::after{ left:8px; right:8px }
  .ledger__date{ grid-column:2 / -1; font-size:12.5px; order:1 }
  .ledger__art{ grid-row:span 2 }
  .ledger__amount{ font-size:16px }
}
/* an order placed while you are looking at the list arrives in the
   direction the page itself came in (see .dpanel[data-dir]) */
.ledger__row.is-new{ animation:rowIn .9s var(--ease-out) both }
@keyframes rowIn{ from{ opacity:0; transform:translateY(var(--from,8px)) } to{ opacity:1; transform:none } }

/* --- settings ---------------------------------------------------------------
   Three groups, each a heading and a list: rows on hairlines, the words on
   the left and the control on the right. A row warms a shade under the
   pointer and its icon takes the accent. The controls are the site's own
   secondary button, and a switch. */
.prefs{ display:flex; flex-direction:column; gap:28px }
.prefs__group{
  display:flex; align-items:center; gap:14px;
  margin:0 0 12px; padding:0 2px;
  font-family:var(--f-body); font-size:13.5px; font-weight:600; letter-spacing:0;
  color:var(--chalk);
}
.prefs__group::after{ content:""; flex:1; height:1px; background:var(--line) }

.prefs__list{
  border-radius:var(--r); border:1px solid var(--line);
  background:var(--s1); overflow:hidden;
}

.pref{
  display:flex; align-items:center; gap:16px;
  padding:16px 18px;
  transition:background-color var(--t-hover) var(--ease-hover);
}
.pref + .pref{ border-top:1px solid var(--line) }
.pref:hover{ background:rgba(255,255,255,.02) }
.pref__txt{ flex:1; min-width:0 }
.pref__label{ margin:0; font-size:15px; font-weight:500; color:var(--chalk) }
.pref__desc{ margin:4px 0 0; font-size:13.5px; line-height:1.55; color:var(--mist) }

.pref__ic{
  flex:none; width:38px; height:38px; border-radius:var(--r-sm);
  display:grid; place-items:center; color:var(--mist);
  background:var(--s2); border:1px solid var(--line);
  transition:color var(--t-hover) var(--ease-hover), border-color var(--t-hover) var(--ease-hover);
}
.pref__ic svg{ width:19px; height:19px }
.pref:hover .pref__ic{ color:var(--peri); border-color:var(--line-2) }

.pref--who{ padding:18px }
.pref__av{
  flex:none; width:52px; height:52px; border-radius:50%; overflow:hidden;
  display:grid; place-items:center;
  font-family:var(--f-display); font-weight:600; font-size:20px; line-height:1; color:#fff;
  background:var(--accent);
}
.pref__av img{ width:100%; height:100%; object-fit:cover }
.pref--who .pref__label{
  font-family:var(--f-display); font-weight:600; font-size:19px; letter-spacing:-.02em;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.pref--who .pref__desc{ display:flex; align-items:flex-start; gap:7px; margin-top:4px }
.pref--who .pref__desc svg{ width:15px; height:auto; flex:none; margin-top:4px; opacity:.8 }

/* the row buttons are the site's secondary button */
.pbtn{
  flex:none;
  display:inline-flex; align-items:center; justify-content:center; gap:8px;
  margin:0; padding:9px 16px; border-radius:var(--r-sm); cursor:pointer;
  font:inherit; font-size:13.5px; font-weight:500; color:var(--chalk);
  background:rgba(255,255,255,.03);
  border:1px solid var(--line-2);
  transition:background-color var(--t-hover) var(--ease-hover),
             border-color var(--t-hover) var(--ease-hover),
             color var(--t-hover) var(--ease-hover);
}
.pbtn:hover{ background:rgba(255,255,255,.07); border-color:rgba(255,255,255,.2); color:#fff }
.pbtn--danger{ color:var(--bad); border-color:rgba(224,138,166,.28) }
.pbtn--danger:hover{ color:#f3bccd; background:rgba(224,138,166,.08); border-color:rgba(224,138,166,.5) }
.prefs__set--danger .prefs__list{ border-color:rgba(224,138,166,.16) }
.prefs__set--danger .pref__ic{ color:var(--bad) }
.prefs__set--danger .pref:hover .pref__ic{ color:#f3bccd; border-color:rgba(224,138,166,.35) }

/* the switch: a track and a knob. Switched on, the track takes the
   accent and one thin ring leaves the knob and fades. */
.switch{
  position:relative; flex:none; width:44px; height:26px; padding:0; border-radius:999px; cursor:pointer;
  background:var(--s3);
  border:1px solid var(--line-2);
  transition:background-color .5s var(--ease-hover), border-color .5s var(--ease-hover);
}
.pref:hover .switch{ border-color:rgba(255,255,255,.2) }
.switch[aria-checked="true"]{ background:var(--accent); border-color:var(--accent) }
.switch i{
  position:absolute; left:2px; top:2px; width:20px; height:20px; border-radius:50%;
  background:#a9a9bd;
  box-shadow:0 1px 2px rgba(0,0,0,.4);
  transition:transform .55s var(--ease-hover), background-color .55s var(--ease-hover);
}
.switch[aria-checked="true"] i{ transform:translateX(18px); background:#fff }
.switch::after{
  content:""; position:absolute; top:50%; left:calc(100% - 12px); width:16px; height:16px;
  border-radius:50%; translate:-50% -50%; pointer-events:none;
  border:1.5px solid rgba(148,148,247,.8);
  opacity:0;
}
.switch.is-lit::after{ animation:switchRing .9s var(--ease-out) forwards }
@keyframes switchRing{
  from{ opacity:.9; scale:.6 }
  to  { opacity:0;  scale:3 }
}

@media (max-width:560px){
  .pref{ flex-wrap:wrap; gap:12px 14px; padding:15px 14px }
  .pref__txt{ flex:1 1 calc(100% - 52px) }
  .pref > .switch,.pref > .pbtn{ margin-left:auto }
  .pref--who .pref__txt{ flex:1 1 calc(100% - 66px) }
}

/* --- narrower screens: the sidebar becomes a strip across the top -------- */
@media (max-width:900px){
  .dash__grid{ grid-template-columns:minmax(0,1fr) }
  .dash__grid{ min-height:0 }
  .side{ position:static; min-width:0; padding:16px 12px 12px }
  .side__top{ position:static; gap:14px }
  .dpage{ margin:0; padding:20px 16px 24px; border-radius:var(--r-lg); min-height:0 }
  /* the three tabs share the strip equally, so all three are always in
     view (a strip that scrolled hid Settings off the right edge) */
  .side__nav{ flex-direction:row; gap:2px }
  .tab{ flex:1 1 0; min-width:0; justify-content:center; padding:10px 12px }
  .tab span{ flex:none }
  .side__glide{ border-left:0; border-bottom:2px solid var(--peri) }
  .side__more{ display:none }
}
/* on a phone the tabs keep only their words, so all three fit across */
@media (max-width:560px){
  .tab{ padding:10px 6px; font-size:14px }
  .tab svg,.tab__n{ display:none }
}

/* ============================================================
   HOME
   The ticker's colours, the two cards at the foot of the page, and the
   sections under the hero. Everything here is scoped to body.p-home,
   which only index.html carries.
   ============================================================ */

/* each file name's extension takes its pack's colour, the same ones the
   store uses, so the strip reads as what it is: three packs going past */
.p-home .ticker{ border-color:var(--line) }
.p-home .ticker__row b i{ font-style:normal }
.p-home .ticker__row .x-pr { color:rgba(171,149,255,.72) }
.p-home .ticker__row .x-dr { color:rgba(134,180,255,.72) }
.p-home .ticker__row .x-sfx{ color:rgba(156,156,250,.76) }

/* The first section comes straight out of the hero, with no rule between them and about half the usual space
   above, so the page gets going sooner as you scroll. */
.p-home #about{ padding-top:clamp(32px,4vw,56px); scroll-margin-top:clamp(40px,6vw,76px) }
/* the menu's About lands with the heading clear of the header, not under it */

/* the join bar: the server's way in, one line long, under the
   "Trusted by editors" box. It fades in from a wrapper, as every card
   does: the bar's own transition, for its hover, would otherwise replace
   the fade and it would just appear. The menu's Discord link lands on
   it, clear of the header. */
.p-home .joinbar-wrap{ margin-top:clamp(16px,2vw,24px); scroll-margin-top:120px }
.p-home .joinbar{
  display:flex; align-items:center; gap:clamp(14px,1.8vw,20px);
  padding:clamp(16px,1.8vw,20px) clamp(18px,2vw,24px);
  border-radius:var(--r-lg); border:1px solid var(--line); background:var(--s1);
  transition:border-color var(--t-hover) var(--ease-hover);
}
.p-home .joinbar:hover{ border-color:var(--line-2) }
.p-home .joinbar__icon{
  flex:none; display:grid; place-items:center;
  width:clamp(48px,4.6vw,56px); aspect-ratio:1; border-radius:14px;
  color:#fff; background:var(--accent);
}
.p-home .joinbar__icon svg{ width:52%; height:auto }
.p-home .joinbar__txt{ min-width:0; display:flex; flex-direction:column; gap:3px }
.p-home .joinbar__title{
  font-family:var(--f-display); font-weight:600;
  font-size:clamp(18px,1.8vw,21px); letter-spacing:-.02em; line-height:1.2; color:#fff;
}
.p-home .joinbar__sub{ font-size:14.5px; line-height:1.5; color:var(--mist) }
.p-home .joinbar__cta{ margin:0 0 0 auto; flex:none }
.p-home .joinbar__go{ width:17px }
.p-home .joinbar:hover .joinbar__cta{ background:var(--accent-hi) }
.p-home .joinbar:hover .joinbar__go{ transform:translateX(3px) }
@media (max-width:620px){
  .p-home .joinbar{ flex-wrap:wrap }
  .p-home .joinbar__txt{ flex:1 }
  .p-home .joinbar__cta{ margin:4px 0 0; width:100%; justify-content:center }
}

/* --- What this is: the heading on the left, and on the right a short
   directory of where things are. Each row is a link; hovering it lifts
   the name and slides the arrow, and its rule brightens. */
.p-home .about{
  display:grid; grid-template-columns:minmax(0,5fr) minmax(0,6fr);
  column-gap:clamp(28px,6vw,96px); row-gap:clamp(28px,3.6vw,48px); align-items:start;
}
.p-home .about .sechead{ margin-bottom:0 }
@media (max-width:860px){ .p-home .about{ grid-template-columns:1fr } }

.p-home .hubs{ list-style:none; margin:0; padding:0; border-top:1px solid var(--line) }
.p-home .hub{
  display:grid; grid-template-columns:minmax(0,1fr) auto; align-items:center;
  column-gap:24px; row-gap:4px;
  padding:clamp(18px,2vw,24px) 2px;
  border-bottom:1px solid var(--line);
  transition:border-color var(--t-hover) var(--ease-hover);
}
.p-home .hub__name{
  font-family:var(--f-display); font-weight:600;
  font-size:clamp(20px,2vw,24px); letter-spacing:-.022em; line-height:1.15;
  color:var(--chalk);
  transition:color var(--t-hover) var(--ease-hover);
}
.p-home .hub__desc{ grid-column:1; font-size:15px; line-height:1.55; color:var(--mist) }
.p-home .hub__go{
  grid-column:2; grid-row:1 / span 2;
  width:20px; height:20px; color:var(--slate);
  transition:color var(--t-hover) var(--ease-hover), transform var(--t-hover) var(--ease-hover);
}
.p-home .hub:hover{ border-bottom-color:var(--line-2) }
.p-home .hub:hover .hub__name{ color:#fff }
.p-home .hub:hover .hub__go{ color:var(--peri); transform:translateX(4px) }

/* "The Editing Store" wants the click. The words are periwinkle with a
   soft glow round them. Every few seconds a pale band of light sweeps
   across them, and then three small glints light up and fade one after
   another. Everything happens INSIDE the letters: it is all one
   background clipped to the text (the glints are three tiny radial
   gradients that grow and shrink), so nothing ever shows outside the
   words. Hovering the row brightens the glow. It repaints only these few
   words, and it stops for anyone who has asked for less motion. */
.p-home .hub__name--shine{
  justify-self:start;
  filter:drop-shadow(0 0 10px rgba(148,148,247,.5));
  transition:filter var(--t-hover) var(--ease-hover);
}
.p-home .hub:hover .hub__name--shine{ filter:drop-shadow(0 0 14px rgba(148,148,247,.85)) }
.p-home .shine{
  background-image:
    radial-gradient(closest-side, rgba(255,255,255,.95), rgba(255,255,255,0)),
    radial-gradient(closest-side, rgba(255,255,255,.95), rgba(255,255,255,0)),
    radial-gradient(closest-side, rgba(255,255,255,.95), rgba(255,255,255,0)),
    linear-gradient(100deg,
      #b4b4ff 0%, #b4b4ff 40%, #d6d6ff 48%, #dedeff 50%, #d6d6ff 52%, #b4b4ff 60%, #b4b4ff 100%);
  background-repeat:no-repeat;
  background-position:14% 42%, 50% 60%, 84% 36%, 100% 0;
  background-size:0 0, 0 0, 0 0, 260% 100%;
  -webkit-background-clip:text; background-clip:text;
  color:transparent; -webkit-text-fill-color:transparent;
  animation:shineSweep 5.4s cubic-bezier(.45,0,.55,1) infinite;
}
@keyframes shineSweep{
  0%   { background-position:14% 42%, 50% 60%, 84% 36%, 100% 0; background-size:0 0, 0 0, 0 0, 260% 100% }
  38%  { background-position:14% 42%, 50% 60%, 84% 36%, 0% 0;   background-size:0 0, 0 0, 0 0, 260% 100% }
  48%  { background-size:18px 18px, 0 0, 0 0, 260% 100% }
  56%  { background-size:0 0, 0 0, 0 0, 260% 100% }
  62%  { background-size:0 0, 16px 16px, 0 0, 260% 100% }
  70%  { background-size:0 0, 0 0, 0 0, 260% 100% }
  76%  { background-size:0 0, 0 0, 18px 18px, 260% 100% }
  84%,100% { background-position:14% 42%, 50% 60%, 84% 36%, 0% 0; background-size:0 0, 0 0, 0 0, 260% 100% }
}

/* --- Trusted by: one large panel round the whole section, and inside
   it the heading and three smaller panels a shade lighter, all set a
   little larger than the page's other sections. */
.p-home .statsbox{
  padding:clamp(32px,5vw,72px) clamp(20px,4vw,60px);
  border-radius:calc(var(--r-lg) + 4px); border:1px solid var(--line); background:var(--s1);
}
.p-home .statsbox .sechead{ margin-bottom:clamp(26px,3.4vw,46px) }
.p-home .statsbox h2{ font-size:clamp(32px,4.8vw,58px) }
/* the three: plain panels in a row, a label with its icon, the number
   large, and a line under it saying what it counts */
.p-home .stats{
  list-style:none; margin:0; padding:0;
  display:grid; grid-template-columns:repeat(3,minmax(0,1fr));
  gap:clamp(12px,1.8vw,24px);
}
@media (max-width:760px){ .p-home .stats{ grid-template-columns:1fr; max-width:440px; margin-inline:auto } }
.p-home .stat{
  display:flex; flex-direction:column; align-items:center; gap:12px;
  padding:clamp(28px,3vw,40px) 22px;
  border-radius:var(--r-lg); border:1px solid var(--line); background:var(--s2);
  text-align:center;
}
.p-home .stat__label{
  display:inline-flex; align-items:center; gap:8px;
  font-size:15px; font-weight:500; color:var(--mist);
}
.p-home .stat__label svg{ width:18px; height:18px; color:var(--slate) }
.p-home .stat__num{
  font-family:var(--f-display); font-weight:600;
  font-size:clamp(44px,5.2vw,64px); letter-spacing:-.03em; line-height:1;
  color:#fff; font-variant-numeric:tabular-nums;
}
.p-home .stat__sub{ font-size:14.5px; color:var(--slate) }
/* The live numbers are drawn as reels (app.js): each digit is a window
   one digit tall onto a strip of digits that slides past it when the
   number changes, like a slot machine. Only transform moves. */
.p-home .stat__num{ display:inline-flex }
.reel{ display:inline-block; height:1em; overflow:hidden }
.reel__strip{ display:block }
.reel__strip > span{ display:block; height:1em; line-height:1 }

/* ============================================================
   THE TOAST, and the owner's X
   A short line at the foot of the screen when Buy now or the X has done
   something, gone again after three seconds. The X sits in the corner of
   an owned download for owners only (html[data-owner], set by app.js),
   quiet until you point at it.
   ============================================================ */
.toast{
  position:fixed; left:50%; bottom:22px; z-index:80;
  translate:-50% 12px; opacity:0; pointer-events:none;
  max-width:min(520px, calc(100vw - 40px));
  padding:11px 16px; border-radius:var(--r);
  font-size:14px; line-height:1.45; text-align:center; color:var(--chalk);
  background:var(--s2); border:1px solid var(--line-2);
  box-shadow:0 14px 40px -18px rgba(0,0,0,.9);
  transition:opacity .4s var(--ease-out), translate .5s var(--ease-out);
}
.toast.is-on{ opacity:1; translate:-50% 0 }

.case__x{
  position:absolute; top:8px; right:8px; z-index:3;
  display:none; place-items:center; width:24px; height:24px; padding:0;
  border-radius:50%; cursor:pointer;
  color:var(--mist); background:var(--s2); border:1px solid var(--line);
  opacity:.55; transition:opacity var(--t-hover) var(--ease-hover), color var(--t-hover) var(--ease-hover), border-color var(--t-hover) var(--ease-hover);
}
.case__x svg{ width:11px; height:11px }
:root[data-owner="1"] .case.is-owned .case__x{ display:grid }
.case__x:hover,.case__x:focus-visible{ opacity:1; color:#fff; border-color:var(--line-2) }

/* ============================================================
   PHONES
   The small-screen sizing in one place, so a phone gets a page laid
   out for it rather than the desktop one squeezed.
   ============================================================ */
@media (max-width:620px){
  /* the header: the four links share the bar evenly up to the avatar or
     Sign in, instead of bunching at the left and leaving a hole */
  .nav{ gap:14px }
  .nav__links{ flex:1; justify-content:space-around; gap:0 }

  /* the hero starts just under the header rather than floating in the
     middle of the screen, and the copy under the logo is set small and
     even so it reads as a caption, not a page of text */
  .hero{ min-height:0; padding:112px 0 60px }
  .hero__logo{ margin-bottom:12px }
  .hero__desc{
    max-width:31ch; margin-bottom:24px;
    font-size:15px; line-height:1.6; text-wrap:balance;
  }
  .hero .row{ width:100%; max-width:360px; gap:10px }
  .hero .row > *{ flex:1 1 0; min-width:0; justify-content:center; padding-inline:12px }
  .hero__price{ margin-top:16px; max-width:32ch; font-size:13px; line-height:1.5; text-wrap:balance }

  /* less air between sections on a small screen */
  .section{ padding-block:52px }
  .p-home #about{ padding-top:24px }
  .sechead{ margin-bottom:24px }
  .lede{ font-size:15px; line-height:1.65 }

  /* the three figures side by side, one compact row */
  .p-home .statsbox{ padding:28px 14px 16px }
  .p-home .statsbox .sechead{ margin-bottom:20px }
  .p-home .stats{ grid-template-columns:repeat(3,minmax(0,1fr)); max-width:none; gap:8px }
  .p-home .stat{ padding:16px 6px; gap:6px }
  .p-home .stat__label{ gap:5px; font-size:12px }
  .p-home .stat__label svg{ width:14px; height:14px }
  .p-home .stat__num{ font-size:26px }
  .p-home .stat__sub{ font-size:11.5px; line-height:1.35 }

  /* the store: shorter pictures, and the header closer to the top */
  .prod__cover{ aspect-ratio:16/8 }
  .storetop{ --headTop:118px; padding-bottom:120px }

  /* the home page: the numbers box is its own frame, so no rule above
     it doubling up with the line under the list */
  .p-home #stats::before{ display:none }

  /* the dashboard: the three tabs get their room, and a download card's
     picture column gives the words more of the width */
  .tab{ padding:10px 4px; font-size:13.5px }
  .case{ grid-template-columns:80px minmax(0,1fr) }
}

/* ============================================================
   THE POLICY PAGES (terms.html, privacy.html, refunds.html)
   Flat, like the rest of the site outside the heroes. A heading, a
   switch between the three, then the text at a reading width.
   ============================================================ */
.legal{ position:relative; z-index:2; padding:clamp(124px,15vh,156px) 0 clamp(72px,9vw,112px) }
.legal h1{ font-size:clamp(36px,5.2vw,58px) }
.legal__meta{ margin:14px 0 0; font-size:14.5px; color:var(--mist) }

.legal__tabs{ display:flex; flex-wrap:wrap; gap:8px; margin-top:28px }
.legal__tabs a{
  padding:8px 16px; border-radius:var(--r-sm);
  font-family:var(--f-display); font-weight:500; font-size:14.5px; color:var(--mist);
  border:1px solid var(--line);
  transition:color var(--t-hover) var(--ease-hover), border-color var(--t-hover) var(--ease-hover), background-color var(--t-hover) var(--ease-hover);
}
.legal__tabs a:hover{ color:var(--chalk); border-color:var(--line-2) }
.legal__tabs a[aria-current="page"]{ color:#fff; background:var(--s2); border-color:var(--line-2) }

.legal__body{ max-width:70ch; margin-top:8px }
.legal__body h2{ margin-top:48px; font-size:23px }
.legal__body p,.legal__body ul{ margin:14px 0 0; color:#c3c3d6 }
.legal__body ul{ padding-left:20px }
.legal__body li{ margin-top:8px }
.legal__body li::marker{ color:var(--slate) }
.legal__body b{ color:var(--chalk); font-weight:600 }
.legal a:not(.legal__tabs a){
  color:var(--peri);
  text-decoration:underline; text-decoration-color:rgba(148,148,247,.35); text-underline-offset:3px;
  transition:text-decoration-color var(--t-hover) var(--ease-hover);
}
.legal a:not(.legal__tabs a):hover{ text-decoration-color:var(--peri) }

@media (max-width:620px){
  .legal{ padding-top:112px }
  .legal__body h2{ margin-top:40px; font-size:21px }
}

/* ============================================================
   HOW TO GET PRODUCTS (the store, under the packs)
   No panel round it: the copy and the store button on the left, four steps
   on the right joined by a thin rail. A step is todo (an empty ring),
   done (filled, the tick drawing itself, the rail below it filling down)
   or now (an accent ring with a dot): app.js moves through them one at a
   time when the panel comes into view.
   ============================================================ */
#howto{ padding-top:clamp(30px,5vw,70px) }
.howto{
  display:grid; grid-template-columns:minmax(0,.9fr) minmax(0,1.1fr);
  gap:clamp(28px,5vw,72px); align-items:center;
}
.howto h2{ font-size:clamp(30px,4vw,46px); line-height:1.04 }
.howto .lede{ margin-top:14px; max-width:40ch }
.howto__cta{ margin-top:26px }
.howto__go{ width:17px; height:17px; transition:transform var(--t-hover) var(--ease-hover) }
.howto__cta:hover .howto__go{ transform:translateX(3px) }

.howto__steps{ list-style:none; margin:0; padding:0 }
.hstep{ position:relative; padding:0 0 26px 50px }
.hstep:last-child{ padding-bottom:0 }
/* the rail to the next step, and its fill */
.hstep:not(:last-child)::before,
.hstep:not(:last-child)::after{
  content:""; position:absolute; left:14px; top:36px; bottom:6px; width:2px; border-radius:1px;
}
.hstep:not(:last-child)::before{ background:var(--line-2) }
.hstep:not(:last-child)::after{
  background:var(--accent); transform:scaleY(0); transform-origin:top;
  transition:transform .75s var(--ease-out) .22s;
}
.hstep.is-done::after{ transform:none }

.hstep__dot{
  position:absolute; left:0; top:0;
  display:grid; place-items:center; width:30px; height:30px; border-radius:50%;
  background:var(--s2); box-shadow:inset 0 0 0 2px var(--line-2);
  transition:background-color .5s var(--ease-out), box-shadow .5s var(--ease-out);
}
.hstep__dot svg{ width:15px; height:15px; position:absolute; opacity:0; transition:opacity .2s linear .1s }
.hstep.is-done .hstep__dot svg{ opacity:1 }
/* (hidden, not just undrawn: a round line cap still paints a speck) */
.hstep__dot path{
  stroke:#fff; stroke-width:2.6; stroke-linecap:round; stroke-linejoin:round;
  stroke-dasharray:1; stroke-dashoffset:1;
  transition:stroke-dashoffset .5s var(--ease-out) .12s;
}
.hstep__dot::after{
  content:""; width:10px; height:10px; border-radius:50%; background:var(--peri);
  transform:scale(0); transition:transform .5s var(--ease-out);
}
.hstep.is-done .hstep__dot{ background:var(--accent); box-shadow:inset 0 0 0 2px var(--accent) }
.hstep.is-done .hstep__dot path{ stroke-dashoffset:0 }
.hstep.is-now .hstep__dot{ box-shadow:inset 0 0 0 2px var(--peri), 0 0 0 6px rgba(148,148,247,.12) }
.hstep.is-now .hstep__dot::after{ transform:none }

.hstep b{
  display:block; padding-top:3px;
  font-family:var(--f-display); font-weight:600; font-size:18px; letter-spacing:-.015em;
  color:var(--mist); transition:color .6s var(--ease-out);
}
.hstep.is-done b,.hstep.is-now b{ color:var(--chalk) }
.hstep p{ margin:4px 0 0; font-size:14.5px; line-height:1.55; color:var(--slate) }

@media (max-width:760px){
  .howto{ grid-template-columns:1fr; gap:30px }
}
@media (max-width:620px){
  .howto__cta{ margin-top:20px }
  .hstep{ padding-left:44px; padding-bottom:22px }
  .hstep b{ font-size:16.5px }
  .hstep p{ font-size:13.5px }
}

/* ============================================================
   HOME HERO, toned down
   The same pieces, made to read like a real editing timeline rather
   than decoration: the two navy pools hold still, the clip bars keep to
   the floor in five flat tracks (no gloss, no glow, a few clips picked
   out in the accent), the logo's halo holds still, and there is no grain
   and no floating motes.
   ============================================================ */
.p-home .pool{ animation:none }
.p-home .hero__logo::before{ animation:none; opacity:.8 }
.p-home .strata{
  inset:auto 0 0 0; height:min(32vh,290px); padding:0 0 4vh;
  -webkit-mask-image:linear-gradient(180deg, transparent 0%, #000 45%, #000 85%, transparent 100%);
  mask-image:linear-gradient(180deg, transparent 0%, #000 45%, #000 85%, transparent 100%);
}
.p-home .strata__row i{
  border-radius:4px; box-shadow:none;
  background:rgba(148,148,247,.065); border:1px solid rgba(148,148,247,.10);
}
.p-home .strata__row i.hi{ background:rgba(91,91,223,.2); border-color:rgba(148,148,247,.26) }
/* a phone's hero is all logo and copy, with no floor left for the tracks */
@media (max-width:720px){ .p-home .strata{ display:none } }

/* ============================================================
   YOUR DOWNLOADS (home, between the directory and the numbers)
   The user's feature-with-art element: a tinted panel holding a small
   download card and a badge, then the copy with a ticked list.
   ============================================================ */
.p-home .fa{
  display:grid; grid-template-columns:repeat(auto-fit,minmax(300px,1fr));
  gap:clamp(28px,4.4vw,64px); align-items:center;
}
.p-home .fa__art{
  position:relative; display:grid; place-items:center;
  height:clamp(260px,26vw,340px); border-radius:20px;
  background:linear-gradient(135deg, rgba(91,91,223,.14), rgba(255,107,168,.06)), var(--s1);
  border:1px solid var(--line);
}
.p-home .fa__card{
  width:78%; max-width:360px; padding:16px; border-radius:14px;
  background:var(--s2); border:1px solid var(--line-2);
  box-shadow:0 22px 40px -20px rgba(0,0,0,.9);
}
.p-home .fa__row{ display:flex; align-items:center; gap:11px }
.p-home .fa__ic{
  display:grid; place-items:center; flex:none; width:30px; height:30px; border-radius:9px;
  color:#5b9dff; background:rgba(91,157,255,.14);
}
.p-home .fa__row b{ display:block; font-size:13.5px; font-weight:600; color:var(--chalk) }
.p-home .fa__row small{ font-size:12px; color:var(--mist) }
.p-home .fa__row em{ margin-left:auto; font-size:12px; font-style:normal; color:var(--slate) }
.p-home .fa__files{
  display:flex; flex-direction:column; gap:5px; margin:14px 0;
  padding:10px; border-radius:9px; background:var(--s1);
}
.p-home .fa__files i{ height:7px; border-radius:3px; background:var(--s3) }
.p-home .fa__f1{ width:72%; background:rgba(148,148,247,.32)!important }
.p-home .fa__f2{ width:86%; background:rgba(148,148,247,.22)!important }
.p-home .fa__f3{ width:56%; background:rgba(148,148,247,.22)!important }
.p-home .fa__acts{ display:flex; gap:7px }
.p-home .fa__acts > *{
  display:inline-flex; align-items:center; gap:6px; padding:7px 11px; border-radius:8px;
  font-size:12.5px; font-weight:600; color:var(--mist); background:var(--s3);
}
.p-home .fa__acts .fa__ok{ background:var(--accent); color:#fff }
.p-home .fa__badge{
  position:absolute; right:7%; bottom:12%;
  display:inline-flex; align-items:center; gap:6px; padding:8px 12px; border-radius:999px;
  font-size:12.5px; font-weight:600; color:#fff; background:var(--accent);
  box-shadow:0 12px 24px -12px rgba(0,0,0,.9);
}
@media (max-width:620px){ .p-home .fa__badge{ right:auto; left:50%; bottom:5%; translate:-50% 0 } .p-home .fa__card{ margin-bottom:34px } }
.p-home .fa__copy h2{ margin:0; font-size:clamp(30px,4.2vw,50px); line-height:1.04 }
.p-home .fa__copy > p{ margin:14px 0 0; max-width:42ch; color:var(--mist); font-size:16px; line-height:1.7 }
.p-home .fa__copy ul{ display:flex; flex-direction:column; gap:11px; margin:22px 0 0; padding:0; list-style:none; font-size:15px; color:var(--chalk) }
.p-home .fa__copy li{ display:flex; align-items:center; gap:11px }
.p-home .fa__copy .fa__tick{
  display:grid; place-items:center; flex:none; width:22px; height:22px; border-radius:50%;
  background:rgba(148,148,247,.16); color:var(--peri);
}

/* --- store cards, toned down -------------------------------------------
   Same cards, fewer tells: the count sits top left as plain text rather
   than a pill in the corner, the tile is flatter (no gloss line, a
   gentler gradient, squarer corners), and each card says what it runs
   on in a quiet spec line under the description. */
.shop .prod__chip{
  top:14px; left:16px; right:auto; padding:0;
  background:none; border:0; font-size:13px; color:var(--mist);
}
.shop .prod.is-owned .prod__chip{ color:var(--ok-ink) }
.shop .ptile{
  border-radius:22%;
  background:linear-gradient(160deg, var(--a1) 0%, color-mix(in srgb, var(--a2) 75%, var(--a1)) 100%);
  border:0;
  box-shadow:0 10px 22px -16px rgba(0,0,0,.9);
}
.shop .prod__desc{ flex:0 1 auto }
.prod__spec{ margin:10px 0 0; flex:1; font-size:13px; color:var(--slate) }

/* the stats section ends the page, so less empty room under the Discord bar */
.p-home #stats{ padding-bottom:clamp(32px,4vw,56px) }

/* --- the editors' own marks on the two preset packs --------------------
   Premiere Pro's real "Pr" letterforms and DaVinci Resolve's real mark
   (the round plate, its ring, three teardrop petals lighter where they
   meet), traced from the official artwork and drawn in white on each
   pack's own tile instead of the apps' colours. Sized the way the apps'
   own icons size them: the Pr letters span 57% of the tile's width, the
   Resolve disc 78%. */
.ptile svg.mk-pr{ width:57.5% }
/* Resolve: the app's own icon, tinted to the pack's blues (an image).
   Lifted above the tile's own layers like the drawn icons are, and on
   the dashboard ghosted until owned the same way. */
.ptile--img{ background:none; border:0 }
.ptile--img img{ position:relative; z-index:1; display:block; width:100%; height:100%; border-radius:inherit; pointer-events:none }
.case .ptile--img{ border:0 }
.case .ptile--img img{ opacity:.36; transition:opacity 1s var(--ease-out) }
.case.is-owned .ptile--img img{ opacity:1 }
.ic-pr{ fill:#fff }

/* the sound pack's waveform: larger, still, no centre line */
.ptile svg.mk-sfx{ width:80% }
.mk-sfx .ic-bar{ animation:none }

/* no count or "Owned" label in the card corner, and no tick on owned tiles */
.shop .prod__chip{ display:none }
.prod .ptile::before,.mini .ptile::before,.solo .ptile::before{ display:none }


/* --- owned: the price stays as it is, and the button goes grey ------- */
.prod.is-owned .prod__price{ color:#fff; text-decoration:none; opacity:.4 }
.bundle--crs.is-owned .bundle__now{ color:#fff; text-decoration:none; text-shadow:none; opacity:.4 }
.prod.is-owned .prod__btn,
.bundle--crs.is-owned .discord{
  color:var(--mist); background:var(--s3); border-color:transparent; box-shadow:none;
}
.prod.is-owned .prod__btn:hover,
.bundle--crs.is-owned .discord:hover{ color:var(--chalk); background:#232331; border-color:transparent }

/* the bundle with one pack left: still on the shelf, greyed out */
.bundle.is-unavailable .bundle__price{ opacity:.4 }
.bundle.is-unavailable [data-bundle-cta]{
  color:var(--mist); background:var(--s3); box-shadow:none; cursor:default; pointer-events:none;
}

/* past purchases: an order of several packs shows them side by side
   instead of stacked, so no tile looks cut off */
.ledger__art--set{ width:auto; height:auto; display:flex; gap:4px }
.ledger__art--set .ledger__tile{ position:static }
.ledger__art--set .ptile{ width:32px; margin:0; border-radius:9px }

/* the course's ring, complete and still (the icons no longer animate) */
.ic-ring{ animation:none; stroke-dasharray:none; stroke-dashoffset:0 }

/* the dashboard switches: no ring flashing out when one is turned on */
.switch::after,.switch.is-lit::after{ display:none; animation:none }

/* the download mock answers the pointer a little: the card lifts and the
   file lines fill out, the badge settles, and its two buttons are real
   links (Download goes to the dashboard, View files to the store) */
.p-home .fa__card{ transition:transform .6s var(--ease-hover), border-color .6s var(--ease-hover) }
.p-home .fa__art:hover .fa__card{ transform:translateY(-4px); border-color:rgba(255,255,255,.2) }
.p-home .fa__files i{ transition:width .7s var(--ease-hover), background-color .7s var(--ease-hover) }
.p-home .fa__art:hover .fa__f1{ width:88% }
.p-home .fa__art:hover .fa__f2{ width:96% }
.p-home .fa__art:hover .fa__f3{ width:74% }
.p-home .fa__badge{ transition:transform .6s var(--ease-hover) }
.p-home .fa__art:hover .fa__badge{ transform:translateY(-3px) }
.p-home .fa__acts > a{ text-decoration:none; transition:background-color .35s var(--ease-hover), color .35s var(--ease-hover) }
.p-home .fa__acts > a:hover{ background:#2a2a3a; color:var(--chalk) }
.p-home .fa__acts > a.fa__ok:hover{ background:var(--accent-hi); color:#fff }

/* ============================================================
   DOWNLOAD CARDS, CALMER (dashboard)
   The picture sits straight on the card instead of on its own tinted
   panel with a divider, and the facts are one plain line of text
   instead of a row of little code-styled chips.
   ============================================================ */
.case__stage,
.case.is-owned .case__stage{ border-right:0; background-color:transparent; min-height:104px }
.case .ptile{ width:clamp(54px,5vw,62px) }
.case__body{ padding-left:4px }
.case__spec{ margin:6px 0 8px; gap:0 }
.case__spec li{ padding:0; border:0; background:none; border-radius:0; font-family:var(--f-body); font-size:14px; color:var(--mist) }
.case__spec li + li::before{ content:"\00b7"; margin:0 5px; color:var(--slate) }
@media (min-width:641px){ .case{ grid-template-columns:clamp(88px,12%,104px) minmax(0,1fr) auto } }
.case__body{ padding-left:0 }
.case__meta{ margin-top:6px; font-size:13.5px; color:var(--mist) }
.case__order{ margin:0; font-family:var(--f-body); font-size:inherit; color:inherit }
@media (max-width:640px){ .case__act{ padding-left:0 } }

/* ============================================================
   PAYMENT COMPLETE (dashboard, back from Stripe; app.js)
   The user's own element: the page dimmed, confetti falling, a card with
   a green tick and one button. Sizes are the reference's, scaled up for
   a full screen. While the server checks the payment the circle holds
   a spinner instead of the tick, and there is no confetti yet.
   ============================================================ */
.paid{
  position:fixed; inset:0; z-index:90;
  display:grid; place-items:center; padding:16px; overflow:hidden;
  color:#ededed; background:rgba(0,0,0,.62);
  opacity:0; transition:opacity .45s var(--ease-out);
}
.paid.is-in{ opacity:1 }
.paid__f{ position:absolute; inset:0; pointer-events:none; opacity:0; transition:opacity .6s var(--ease-out) }
.paid[data-state="done"] .paid__f{ opacity:1 }
.paid__f i{
  position:absolute; top:-16px; width:10px; height:15px; border-radius:3px;
  animation:paidFall 3s linear infinite;
}
.paid__card{
  position:relative; display:grid; justify-items:center;
  width:100%; max-width:320px; padding:30px 24px 24px;
  border-radius:26px; background:#151517;
  box-shadow:0 30px 60px rgba(0,0,0,.7);
  text-align:center;
  transform:translateY(14px) scale(.97);
  transition:transform .55s var(--ease-out);
}
.paid.is-in .paid__card{ transform:none }
.paid__mark{
  position:relative; display:grid; place-items:center;
  width:72px; height:72px; border-radius:50%;
  background:#26262a; color:#8e8e8e;
  transition:background-color .5s var(--ease-out), color .5s var(--ease-out), box-shadow .5s var(--ease-out);
}
.paid__mark svg{ position:absolute; width:34px; height:34px; transition:opacity .4s var(--ease-out), transform .5s var(--ease-out) }
.paid__spin{ animation:paidSpin 1s linear infinite }
.paid__tick{ opacity:0; transform:scale(.6) }
.paid[data-state] .paid__spin{ opacity:0 }
.paid[data-state] .paid__tick{ opacity:1; transform:none }
.paid[data-state="done"] .paid__mark{
  background:#3ecf8e; color:#04140c;
  box-shadow:0 0 0 10px rgba(62,207,142,.15);
  animation:paidPulse 2.4s ease-in-out .5s infinite;
}
.paid__card b{ margin-top:20px; font-family:var(--f-display); font-weight:600; font-size:21px; letter-spacing:-.02em }
.paid__card p{ margin:8px 0 20px; font-size:15px; line-height:1.5; color:#8e8e8e; overflow-wrap:anywhere }
.paid__btn{
  border:0; cursor:pointer;
  font:500 15px/1 var(--f-body);
  width:100%; height:48px; border-radius:14px;
  background:#fff; color:#000;
  transition:opacity .3s var(--ease-hover), background-color .25s var(--ease-hover);
}
.paid__btn:hover{ background:#e6e6e6 }
.paid__btn:disabled{ opacity:.35; cursor:default }
@keyframes paidFall{ 0%{ transform:translateY(0) rotate(0) } 100%{ transform:translateY(calc(100vh + 32px)) rotate(600deg) } }
@keyframes paidPulse{ 50%{ box-shadow:0 0 0 16px rgba(62,207,142,.06) } }
@keyframes paidSpin{ to{ transform:rotate(360deg) } }
@media (prefers-reduced-motion: reduce){
  .paid__f{ display:none }
  .paid__spin,.paid[data-state="done"] .paid__mark{ animation:none }
}

/* ============================================================
   STORE CARDS, REBUILT (store shelf)
   Built like the bundle panel under them, at card size: the pack's tile
   leaning beside its name, one short line, the price big and the button
   under it. The tile leans a little further on hover, the way the
   bundle's hand of tiles opens, and the card takes the faintest wash of
   its product's colour, as the bundle does of its own.
   ============================================================ */
.shop .prod{
  transition:opacity var(--t) var(--ease), transform .6s var(--ease-out), border-color var(--t-hover) var(--ease-hover);
  display:grid; grid-template-columns:auto minmax(0,1fr); grid-template-rows:auto 1fr auto;
  column-gap:18px;
  padding:clamp(22px,2.2vw,28px);
  border-radius:var(--r-lg);
  background-color:color-mix(in srgb, rgb(var(--accRGB)) 4%, var(--s1));
}
.shop .prod__body{ display:contents }
.shop .prod__art{ grid-column:1; grid-row:1; align-self:center }
.shop .prod__art .prod__float{
  transform:rotate(var(--lean,-4deg));
  transition:transform .7s var(--ease-out);
}
.shop .prod:hover{ transform:scale(1.012); border-color:var(--line-2) }
/* the bundle grows on hover the way the cards used to, a touch more */
#bundle{ transition:opacity var(--t) var(--ease), transform .6s var(--ease-out), border-color var(--t-hover) var(--ease-hover) }
#bundle:hover{ transform:scale(1.025) }
.shop .prod:hover .prod__art .prod__float{ --lean:-6deg }
/* owned: the price greys, but Download stays a real, lit button */
.shop .prod.is-owned .prod__btn{ color:#fff; background:var(--accent); box-shadow:inset 0 1px 0 rgba(255,255,255,.16) }
.shop .prod.is-owned .prod__btn:hover{ color:#fff; background:var(--accent-hi) }
.shop .prod__art .ptile{ width:clamp(62px,5.4vw,70px) }
.shop .prod__name{
  grid-column:2; grid-row:1; align-self:center; margin:0;
  font-size:clamp(20px,1.9vw,24px); line-height:1.12; letter-spacing:-.03em;
}
.shop .prod__desc{ grid-column:1 / -1; grid-row:2; margin-top:20px; font-size:15px; line-height:1.6 }
.shop .prod__buy{
  grid-column:1 / -1; grid-row:3;
  flex-direction:column; align-items:stretch; gap:16px;
  margin-top:24px; padding-top:0; border-top:0;
}
.shop .prod__price{ font-size:clamp(34px,3.2vw,42px); letter-spacing:-.04em }
.shop .prod__btn{ justify-content:center; padding:12px 18px; font-size:15px }

/* ============================================================
   HEADER MARK AND THE SHINY SIGN-IN (from the user's test redesign)
   The logo and "GTAG Editing" open the nav. Every "Sign in with
   Discord" button gets the glossy look, a shade darker than the test
   site so it sits with the rest of this one: a violet to blue gradient,
   a light top edge and a soft coloured shadow. Only the sign-in buttons;
   every other button stays flat.
   ============================================================ */
.nav .brand{ flex:none; gap:9px; margin-left:-10px }
.brand__logo{ width:30px; height:30px; border-radius:8px; box-shadow:0 0 0 1px rgba(255,255,255,.12) }
.nav .brand__name{ font-size:15px }

.discord[data-signin]{
  background:linear-gradient(135deg,#7a6cf0,#4a7de6);
  border:1px solid rgba(255,255,255,.2);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.3), 0 6px 18px -6px rgba(96,104,240,.55);
  transition:transform .3s var(--ease-hover), box-shadow .35s var(--ease-hover), filter .35s var(--ease-hover);
}
.discord[data-signin]:hover{
  background:linear-gradient(135deg,#7a6cf0,#4a7de6);
  filter:brightness(1.1); transform:translateY(-1px);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.36), 0 10px 26px -6px rgba(96,104,240,.7);
}
.discord[data-signin]:active{ transform:translateY(1px); filter:brightness(.97) }

/* on a phone the bar is full, so the mark keeps its logo and drops its name */
@media (max-width:780px){ .nav .brand__name{ display:none } .nav .brand{ margin-left:-6px } }

/* ============================================================
   MEET THE OWNERS: live Discord profile cards (home, #owners)
   Ported from the GTAG Editing Store source (every .dprof / .dp- rule,
   plus its role pills, activity row and emoji). Its colours live under
   --dp- names and its classes start with dp- so nothing here touches the
   rest of the site. The script is assets/owners.js.
   ============================================================ */
.dp-owners{
  --dp-text:#f2f3f5; --dp-dim:#b5bac1; --dp-mute:#8a8f98;
  --dp-s2:#313338; --dp-s3:#3a3c42; --dp-s4:#44464d;
  --dp-line-2:rgba(255,255,255,.09); --dp-accent:#979bfa;
  max-width:760px; margin:0 auto; text-align:left;
}
.dprof [hidden]{ display:none !important }
.dp-i{ width:18px; height:18px; flex:none }
.dp-name{ color:var(--dp-text) }
.dp-roles{ color:var(--dp-text) }
.dp-btn{
  display:flex; align-items:center; justify-content:center; gap:8px; width:100%;
  height:46px; padding:0 20px; border-radius:999px;
  background:var(--dp-s3); color:var(--dp-text);
  font-size:15px; font-weight:600; white-space:nowrap;
  transition:background .2s, transform .2s var(--ease-hover);
}
.dp-btn:hover{ background:var(--dp-s4) }
.dp-btn:active{ transform:scale(.97) }
@keyframes dpFade{ from{ opacity:0 } }
.dp-owners {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(290px, 1fr));
  align-items: start;
  gap: 18px;
}
.dprof {
  border-radius: 22px;
  overflow: hidden;
  background: #111214;
  box-shadow: inset 0 0 0 1px var(--dp-line-2), 0 24px 60px rgba(0, 0, 0, 0.3);
}
.dp-banner {
  position: relative;
  aspect-ratio: 2.5;
  overflow: hidden;
  background: var(--dp-s3);
}
.dp-banner img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  animation: dpFade 0.4s ease;
}
.dp-top {
  position: relative;
  height: 58px;
}
.dp-av {
  position: absolute;
  left: 14px;
  top: -58px;
  width: 108px;
  height: 108px;
}
.dp-av-img {
  position: absolute;
  inset: 9px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  overflow: hidden;
  background: var(--dp-s4);
  color: var(--dp-dim);
  box-shadow: 0 0 0 6px #111214;
}
.dp-av-img img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  animation: dpFade 0.4s ease;
}
.dp-av-img .dp-i {
  width: 40%;
  height: 40%;
}
.dp-deco {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.dp-deco img {
  width: 100%;
  height: 100%;
}
.dp-body {
  padding: 4px 18px 18px;
}
.dp-name {
  margin: 0;
  font-size: 22px;
  font-weight: 700;
  letter-spacing: -0.01em;
}
.dp-user {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 2px 0 14px;
  font-size: 14px;
  color: var(--dp-dim);
}
.dp-tag {
  height: 20px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 0 6px;
  border-radius: 6px;
  background: var(--dp-s3);
  font-size: 12px;
  font-weight: 700;
  color: var(--dp-text);
}
.dp-tag img {
  width: 14px;
  height: 14px;
}
.dp-card {
  margin-bottom: 14px;
  padding: 14px;
  border-radius: 12px;
  background: #1d1e21;
}
.dp-card h5 {
  margin: 0 0 6px;
  font-size: 12px;
  font-weight: 700;
  color: var(--dp-text);
}
.dp-card p {
  margin: 0 0 14px;
  font-size: 14px;
  color: var(--dp-dim);
}
.dp-since span {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}
.dp-since em {
  font-style: normal;
}
.dp-since .dp-i {
  width: 15px;
  height: 15px;
  color: var(--dp-mute);
}
.dp-roles {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.dp-role {
  height: 24px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0 8px;
  border-radius: 6px;
  background: var(--dp-s2);
  font-size: 12px;
  font-weight: 500;
}
.dp-role i {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--dp-mute);
}
.dp-role i.acc {
  background: var(--dp-accent);
}
.dp-inner {
  position: relative;
  border-radius: 18px;
  overflow: hidden;
}
.dprof.themed {
  padding: 4px;
  background: linear-gradient(180deg, var(--tp), var(--ta));
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.35);
}
.dprof.themed .dp-inner {
  background: linear-gradient(rgba(17, 18, 20, 0.6), rgba(17, 18, 20, 0.6)), linear-gradient(180deg, var(--tp), var(--ta));
}
.dprof.themed .dp-av-img {
  box-shadow: 0 0 0 6px color-mix(in srgb, var(--tp) 40%, #111214);
}
.dprof.themed .dp-status {
  box-shadow: 0 0 0 5px color-mix(in srgb, var(--tp) 40%, #111214);
}
.dprof.themed .dp-card,
.dprof.themed .dp-role,
.dprof.themed .dp-tag {
  background: rgba(0, 0, 0, 0.3);
}
.dprof.themed .dp-btn {
  background: rgba(255, 255, 255, 0.12);
}
.dprof.themed .dp-btn:hover {
  background: rgba(255, 255, 255, 0.2);
}
.dprof.themed .dp-user,
.dprof.themed .dp-card p {
  color: rgba(255, 255, 255, 0.78);
}
.dp-status {
  position: absolute;
  right: 12px;
  bottom: 12px;
  z-index: 2;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: #80848e;
  box-shadow: 0 0 0 5px #111214;
}
.dp-status.s-online {
  background: #23a55a;
}
.dp-status.s-idle {
  background: #f0b232;
}
.dp-status.s-dnd {
  background: #f23f43;
}
.dp-badges {
  position: absolute;
  right: 14px;
  top: 12px;
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 2px;
  max-width: calc(100% - 140px);
  padding: 4px;
  border-radius: 9px;
  background: rgba(0, 0, 0, 0.35);
}
.dp-badges img {
  width: 22px;
  height: 22px;
}
.dp-name.grad {
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
}
.dp-pron::before {
  content: "·";
  margin-right: 8px;
  opacity: 0.6;
}
.dp-custom {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: -6px 0 14px;
  font-size: 14px;
  color: var(--dp-text);
}
.dp-act {
  margin-bottom: 14px;
}
.dp-actrow {
  display: flex;
  align-items: center;
  gap: 12px;
}
.dp-actrow img,
.dp-act-ic {
  width: 52px;
  height: 52px;
  flex: none;
  border-radius: 8px;
  object-fit: cover;
}
.dp-act-ic {
  display: grid;
  place-items: center;
  background: rgba(255, 255, 255, 0.08);
  color: var(--dp-dim);
}
.dp-actrow b {
  display: block;
  font-size: 14px;
}
.dp-actrow span {
  font-size: 13px;
  color: var(--dp-dim);
}
.dp-emo {
  display: inline-block;
  width: 18px;
  height: 18px;
  vertical-align: -4px;
}
.dp-emo-t {
  font-size: 16px;
  line-height: 1;
}
.dp-about[hidden] {
  display: none;
}

/* the four owners side by side, a little wider than the page column so
   each card keeps its room; two by two on tablets, one under another on
   phones */
.dp-owners{
  width:min(1280px, calc(100vw - 40px)); max-width:none;
  margin-left:50%; transform:translateX(-50%);
  grid-template-columns:repeat(4, minmax(0, 1fr)); gap:16px;
}
@media (max-width:1100px){ .dp-owners{ width:min(760px, calc(100vw - 40px)); grid-template-columns:repeat(2, minmax(0, 1fr)) } }
@media (max-width:640px){ .dp-owners{ grid-template-columns:1fr } }
/* the four cards the same height, their buttons on one line */
.dp-owners{ align-items:stretch }
.dprof,.dp-inner,.dp-body{ display:flex; flex-direction:column }
.dp-inner,.dp-body{ flex:1 }
.dp-btn{ margin-top:auto }
/* each card sits in a slot that also holds its short description, which
   slides down from under the card on hover (or keyboard focus). It lies
   over whatever is below rather than pushing it, so every card keeps the
   same height. Phones have no hover, so there it simply sits under the card. */
.dp-slot{ position:relative; display:flex; flex-direction:column; outline:none }
.dp-slot > .dprof{ flex:1; position:relative; z-index:2 }
.dp-slot:hover,.dp-slot:focus-within{ z-index:6 }
.dp-drop{
  position:absolute; left:12px; right:12px; top:calc(100% - 14px); z-index:1;
  padding:26px 18px 16px;
  border-radius:0 0 18px 18px;
  background:#16171b;
  border:1px solid rgba(255,255,255,.1); border-top:0;
  box-shadow:0 22px 44px -16px rgba(0,0,0,.85);
  opacity:0; transform:translateY(-16px); pointer-events:none;
  transition:opacity .4s var(--ease-out), transform .55s var(--ease-out);
}
.dp-drop p{ margin:0; font-size:14.5px; line-height:1.55; color:#d6d8de }
.dp-slot:hover .dp-drop,.dp-slot:focus-within .dp-drop{ opacity:1; transform:none; pointer-events:auto }
@media (hover:none){
  .dp-drop{ position:relative; left:auto; right:auto; top:auto; margin:-14px 12px 0; opacity:1; transform:none }
}
