/* =========================================================================
   SlizeLink — features page stylesheet
   Loaded after base.css. Tokens, bands, buttons, navbar, footer and the
   shared phone-mockup component all come from base.css; this file is only
   what's specific to features.html.
   ========================================================================= */

/* -------------------------------------------------------------------------
   1. HERO — hub-and-spokes visual
   ------------------------------------------------------------------------- */
.fx-hero { padding-block: var(--s-9) var(--s-8); }

.fx-hero__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s-8);
  align-items: center;
}
@media (min-width: 992px) {
  .fx-hero__grid { grid-template-columns: 1.05fr 1fr; gap: var(--s-9); }
}

.fx-hero__kicker { color: #fff; margin-bottom: var(--s-5); }
.fx-hero__kicker::before { background: currentColor; }

.fx-hero__title {
  font-size: clamp(2rem, 5.5vw, 3.25rem);
  font-weight: 700;
  line-height: 1.12;
  letter-spacing: -0.03em;
  color: #fff;
  margin-bottom: var(--s-4);
}

.fx-hero__sub {
  font-size: 1.0625rem;
  color: rgba(255, 255, 255, 0.85);
  max-width: 34rem;
  margin-bottom: var(--s-6);
}

.fx-hero .btn-on-brand { padding-inline: 2rem; }

/* the hero copy is the first thing anyone sees, so it gets its own
   staggered entrance instead of the plain .reveal fade: kicker, then
   headline, then sub-copy, then button, each settling in just after the
   last. The un-transitioned start state also carries its own
   revealFallback (the keyframe base.css defines for .reveal itself) so a
   base.js failure — which has taken this exact page down before — can
   never leave the headline stuck invisible. */
.js .fx-hero__copy .fx-hero__kicker,
.js .fx-hero__copy .fx-hero__title,
.js .fx-hero__copy .fx-hero__sub,
.js .fx-hero__copy .btn {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 620ms var(--ease-soft), transform 620ms var(--ease-soft);
  animation: revealFallback 1ms 2.5s forwards;
}
.js .fx-hero__copy.is-visible .fx-hero__kicker { opacity: 1; transform: none; transition-delay: 60ms; }
.js .fx-hero__copy.is-visible .fx-hero__title  { opacity: 1; transform: none; transition-delay: 170ms; }
.js .fx-hero__copy.is-visible .fx-hero__sub    { opacity: 1; transform: none; transition-delay: 290ms; }
.js .fx-hero__copy.is-visible .btn             { opacity: 1; transform: none; transition-delay: 410ms; }

/* --- the fanned card stack: four small previews, one per feature, dealt
   out from a stacked start rather than presented as icon badges. Each
   card's rotation/offset comes from --r/--tx/--ty set inline in the
   markup, so the fan geometry lives with the content, not duplicated
   across four CSS rules. --- */
.fx-stack {
  position: relative;
  height: 210px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.fx-stack__card {
  position: absolute;
  width: 130px;
  height: 92px;
  padding: 0.7rem 0.8rem;
  background: #fff;
  border-radius: var(--r-md);
  border-top: 3px solid var(--tone, var(--violet));
  box-shadow: 0 20px 40px -18px rgba(0, 0, 0, 0.4);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.fx-stack__card:hover {
  transform: translate(var(--tx), calc(var(--ty) - 10px)) rotate(var(--r)) !important;
  box-shadow: 0 28px 52px -18px rgba(0, 0, 0, 0.48);
  z-index: 5;
}

/* bio card: avatar dot + three link bars */
.fx-stack__avatar { width: 16px; height: 16px; border-radius: 50%; background: var(--tone, var(--violet)); margin-bottom: 2px; }
.fx-stack__bar { display: block; height: 6px; border-radius: 3px; background: var(--surface-2); width: var(--w, 60%); }

/* shorten card: struck-through long URL settling into the short one */
.fx-stack__card--mono { justify-content: center; align-items: flex-start; gap: 7px; }
.fx-stack__mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.5625rem; line-height: 1.3; }
.fx-stack__mono--old { color: var(--ink-soft); text-decoration: line-through; opacity: 0.55; }
.fx-stack__mono--new { color: var(--tone, var(--violet)); font-weight: 700; font-size: 0.75rem; }

/* qr card: a stylised 3x3 module swatch, not a real code (the real one is
   further down the page) */
.fx-stack__card--grid { align-items: center; justify-content: center; }
.fx-stack__qr { display: grid; grid-template-columns: repeat(3, 1fr); gap: 3px; width: 46px; height: 46px; }
.fx-stack__qr i {
  display: block;
  border-radius: 1px;
  background: var(--ink);
  font-style: normal;
}
.fx-stack__qr i:nth-child(2), .fx-stack__qr i:nth-child(4),
.fx-stack__qr i:nth-child(6), .fx-stack__qr i:nth-child(8) { background: var(--tone, var(--pink)); }

/* analytics card: a tiny four-bar chart on its own baseline */
.fx-stack__card--chart { flex-direction: row; align-items: flex-end; justify-content: center; gap: 6px; padding-bottom: 0.85rem; }
.fx-stack__col { display: inline-block; width: 9px; height: var(--h, 40%); background: var(--tone, var(--violet)); border-radius: 2px 2px 0 0; }

/* staggered entrance: cards start stacked dead-centre and fan out to their
   own offset once .fx-stack (a .reveal target) becomes visible. */
.js .fx-stack__card {
  opacity: 0;
  transform: translate(0, 0) rotate(0deg) scale(0.8);
  transition: opacity 520ms var(--ease-soft), transform 620ms var(--ease-soft);
}
.js .fx-stack.is-visible .fx-stack__card {
  opacity: 1;
  transform: translate(var(--tx), var(--ty)) rotate(var(--r));
}
.js .fx-stack.is-visible .fx-stack__card:nth-child(1) { transition-delay: 60ms; }
.js .fx-stack.is-visible .fx-stack__card:nth-child(2) { transition-delay: 150ms; }
.js .fx-stack.is-visible .fx-stack__card:nth-child(3) { transition-delay: 240ms; }
.js .fx-stack.is-visible .fx-stack__card:nth-child(4) { transition-delay: 330ms; }

@media (max-width: 420px) {
  .fx-stack { transform: scale(0.8); }
}

@media (max-width: 991.98px) {
  .fx-hero__grid { text-align: center; }
  .fx-hero__sub { margin-inline: auto; }
  /* was --s-9 top / --s-8 bottom (a 2rem mismatch) unconditionally; below
     desktop that reads as uneven, so both sides match here specifically. */
  .fx-hero { padding-block: var(--s-8); }
  .fx-hero__kicker { margin-bottom: var(--s-6); }
}

/* -------------------------------------------------------------------------
   2. FEATURE SECTIONS — alternating two-column layout
   ------------------------------------------------------------------------- */
.fx-feature__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s-7);
  align-items: center;
}
@media (min-width: 992px) {
  .fx-feature__grid { grid-template-columns: 1fr 1fr; gap: var(--s-9); }
  /* Grid order follows DOM order by default, so which side each section's
     visual lands on is set by whichever of .fx-feature__copy/__visual comes
     first in that section's markup — that's what actually gives the four
     sections below their alternating copy/visual/copy/visual layout, not
     this class. --reverse flips a section's visual to the left without
     needing to reorder its markup; none of the four need it right now, but
     it's here for whichever section wants that flip next. */
  .fx-feature--reverse .fx-feature__copy { order: 2; }
  .fx-feature--reverse .fx-feature__visual { order: 1; }
}

/* band-sub's ink/violet contrast now lives in base.css, generically, for
   any page. band-title needs no override here: it has no explicit colour
   of its own, so it already inherits white correctly from .band--ink /
   .band--violet on the section. (The previous rules for both of these
   used ".band--X .fx-feature .foo" — but .band--X and .fx-feature are
   both classes on the same <section>, never in an ancestor/descendant
   relationship, so neither selector could ever match anything.) */
.fx-feature .kicker--accent { color: var(--accent); }

.fx-points { list-style: none; padding: 0; margin: var(--s-5) 0 var(--s-6); display: grid; gap: var(--s-3); }
.fx-points li {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  font-size: 0.9375rem;
}
.fx-points svg {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  color: var(--violet-deep);
}
.band--ink .fx-points svg,
.band--violet .fx-points svg { color: var(--accent); }
.band--violet .fx-points svg { color: #fff; }

.fx-link-cta {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  font-size: 0.9375rem;
  font-weight: 700;
  color: var(--violet-deep);
  text-decoration: none;
}
.fx-link-cta svg { width: 17px; height: 17px; transition: transform var(--dur) var(--ease); }
.fx-link-cta:hover svg, .fx-link-cta:focus-visible svg { transform: translateX(4px); }
.fx-link-cta--dark { color: #fff; }

.fx-feature__visual { display: flex; justify-content: center; }

/* --- bio: a loose card, no device chrome at all — deliberately not the
   phone mockup used throughout home.html, so this page has its own visual
   identity instead of repeating that component. --- */
.fx-bio-stage { display: flex; flex-direction: column; align-items: center; gap: var(--s-5); }

.fx-bio-card {
  width: 100%;
  max-width: 21rem;
  padding: var(--s-5);
  background: var(--white);
  border-radius: var(--r-lg);
  box-shadow: 0 26px 50px -22px rgba(36, 16, 70, 0.3);
}

.fx-bio-card__head { display: flex; align-items: center; gap: var(--s-3); margin-bottom: var(--s-5); }
.fx-bio-card__avatar {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--violet);
  color: #fff;
  font-size: 0.8125rem;
  font-weight: 700;
}
.fx-bio-card__name { font-size: 0.9375rem; font-weight: 700; color: var(--ink); line-height: 1.3; }
.fx-bio-card__handle { font-size: 0.75rem; font-weight: 600; color: var(--violet-deep); }

.fx-bio-card__links { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--s-2); }
.fx-bio-card__link {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--white);
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--ink);
  transition: transform 600ms var(--ease-soft), box-shadow 600ms var(--ease-soft), border-color 600ms var(--ease-soft);
}

/* the top row reads as mid-reorder: lifted, tilted, its own shadow */
.fx-bio-card__link--lifted {
  border-color: var(--violet);
  transform: translateY(-3px) rotate(-1.1deg);
  box-shadow: 0 16px 30px -14px rgba(36, 16, 70, 0.4);
}
.fx-bio-card__grip { flex-shrink: 0; color: var(--ink-soft); }
.fx-bio-card__grip svg { width: 9px; height: 16px; }
.fx-bio-card__link--lifted .fx-bio-card__grip { color: var(--violet); }

.js .fx-bio-card__link--lifted {
  transform: translateY(0) rotate(0deg);
}
.js .reveal.is-visible .fx-bio-card__link--lifted {
  transform: translateY(-3px) rotate(-1.1deg);
  transition-delay: 380ms;
}

.fx-bio-themes {
  --bio-step: 31.6px;
  position: relative;
  display: flex;
  gap: 0.6rem;
}
.fx-bio-theme {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--tone, var(--violet));
  border: 2px solid var(--white);
  box-shadow: 0 0 0 1px var(--line);
}
.fx-bio-theme.is-active { box-shadow: 0 0 0 1px var(--line); }

/* --- theme cycling: the card and its swatches take turns showing each
   colour, in sync, forever — showing off "five themes, one click to
   switch" without waiting for a click. Four separate single-property
   keyframes (never one combined one) so each element only animates the
   property it actually uses — sharing background+border+color on one
   keyframe would, e.g., also paint a background onto the lifted link row,
   which should only ever pick up a border colour.
   Gated behind .fx-feature__visual.is-visible rather than running from
   page load: background-color/border-color/box-shadow are paint-triggering
   properties, not compositor-only ones like transform/opacity, and eight
   of them animating continuously underneath an unrevealed section was
   competing with that section's own reveal fade, which is what was making
   it fall back to base.css's slow 2.5s CSS-only fallback instead of its
   normal fast one. Starting the loop only once revealed removes that
   contention entirely, and means nothing here animates somewhere the
   visitor can't even see yet.
   --bio-cycle reuses --rotate — already the site's "how long one state
   holds" token — so one full 5-colour loop takes 5x as long as a single
   rotation elsewhere, same pacing philosophy, no new token invented. Each
   swatch runs the identical bioThemeActive keyframe, offset by a negative
   delay per nth-child so the five take turns automatically — nth-child(2)
   gets the LARGEST offset (-4 rotations), not the smallest: a negative
   delay moves an element's own clock forward, so the swatch that needs to
   activate soonest after the first one needs to already be furthest into
   its cycle, not least far in. Verified against a full-cycle simulation
   before shipping, since this is exactly the kind of relationship that's
   easy to get backwards. */
.fx-bio-stage { --bio-cycle: calc(var(--rotate) * 5); }

@keyframes bioToneCycleBg {
  0%, 16%  { background-color: var(--violet); }
  20%, 36% { background-color: var(--indigo); }
  40%, 56% { background-color: var(--mint); }
  60%, 76% { background-color: #c01a6b; }
  80%, 96% { background-color: #b31eb0; }
  100%     { background-color: var(--violet); }
}
@keyframes bioToneCycleBorder {
  0%, 16%  { border-color: var(--violet); }
  20%, 36% { border-color: var(--indigo); }
  40%, 56% { border-color: var(--mint); }
  60%, 76% { border-color: #c01a6b; }
  80%, 96% { border-color: #b31eb0; }
  100%     { border-color: var(--violet); }
}
@keyframes bioToneCycleFg {
  0%, 16%  { color: var(--violet); }
  20%, 36% { color: var(--indigo); }
  40%, 56% { color: var(--mint); }
  60%, 76% { color: #c01a6b; }
  80%, 96% { color: #b31eb0; }
  100%     { color: var(--violet); }
}
@keyframes bioToneCycleFgDeep {
  0%, 16%  { color: var(--violet-deep); }
  20%, 36% { color: #2a1bb0; }
  40%, 56% { color: #0e7a4b; }
  60%, 76% { color: #92104f; }
  80%, 96% { color: #8a1489; }
  100%     { color: var(--violet-deep); }
}
@keyframes bioThemeSwitch {
  0%, 16%   { transform: translateX(0); }
  20%, 36%  { transform: translateX(var(--bio-step)); }
  40%, 56%  { transform: translateX(calc(var(--bio-step) * 2)); }
  60%, 76%  { transform: translateX(calc(var(--bio-step) * 3)); }
  80%, 96%  { transform: translateX(calc(var(--bio-step) * 4)); }
  100%      { transform: translateX(0); }
}

.fx-feature__visual.is-visible .fx-bio-card__avatar { animation: bioToneCycleBg var(--bio-cycle) linear infinite; }
.fx-feature__visual.is-visible .fx-bio-card__link--lifted { animation: bioToneCycleBorder var(--bio-cycle) linear infinite; }
.fx-feature__visual.is-visible .fx-bio-card__link--lifted .fx-bio-card__grip { animation: bioToneCycleFg var(--bio-cycle) linear infinite; }
.fx-feature__visual.is-visible .fx-bio-card__handle { animation: bioToneCycleFgDeep var(--bio-cycle) linear infinite; }

.fx-bio-themes::after {
  content: "";
  position: absolute;
  inset: -4px auto auto -4px;
  width: 30px;
  height: 30px;
  border: 2px solid var(--ink-soft);
  border-radius: 50%;
  pointer-events: none;
}
.fx-feature__visual.is-visible .fx-bio-themes::after { animation: bioThemeSwitch var(--bio-cycle) var(--ease-soft) infinite; }

/* -------------------------------------------------------------------------
   3. URL SHORTENER — the morph card
   ------------------------------------------------------------------------- */
.fx-morph {
  width: 100%;
  max-width: 26rem;
  padding: var(--s-6);
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: var(--r-lg);
  text-align: center;
}

.fx-morph__label {
  margin-bottom: var(--s-5);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.4);
}

.fx-morph__stage { display: flex; flex-direction: column; align-items: center; gap: var(--s-3); }

.fx-morph__long {
  font-size: 0.8125rem;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--body-on-dark);
  line-height: 1.5;
  word-break: break-all;
}

.fx-morph__arrow {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.08);
  color: var(--accent);
}
.fx-morph__arrow svg { width: 17px; height: 17px; }

.fx-morph__short {
  font-size: 1.5rem;
  font-weight: 700;
  color: #fff;
  letter-spacing: -0.01em;
}

.fx-morph__stat {
  margin-top: var(--s-6);
  padding-top: var(--s-5);
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  font-size: 0.875rem;
  color: var(--body-on-dark);
}
.fx-morph__num {
  font-size: 1.125rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--accent);
}

/* the long URL fades out and the short one settles in once this section's
   .reveal target becomes visible — same trigger as everything else, just a
   different visual outcome. */
.js .fx-morph__long { transition: opacity 500ms var(--ease-soft); }
.js .fx-morph__arrow,
.js .fx-morph__short {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 460ms var(--ease-soft), transform 460ms var(--ease-soft);
}
.js .reveal:not(.is-visible) .fx-morph__long { opacity: 0.35; }
.js .reveal.is-visible .fx-morph__arrow  { opacity: 1; transform: none; transition-delay: 260ms; }
.js .reveal.is-visible .fx-morph__short  { opacity: 1; transform: none; transition-delay: 380ms; }

/* -------------------------------------------------------------------------
   4. QR CODES
   ------------------------------------------------------------------------- */
.fx-qr-stage { display: flex; flex-direction: column; align-items: center; gap: var(--s-4); }

.fx-qr-card {
  width: 100%;
  max-width: 17rem;
  padding: var(--s-5);
  background: var(--white);
  border-radius: var(--r-lg);
  box-shadow: 0 26px 50px -24px rgba(0, 0, 0, 0.5);
  text-align: center;
  transition: transform var(--dur) var(--ease-soft), box-shadow var(--dur) var(--ease-soft);
}
.fx-qr-card:hover { transform: translateY(-8px) rotate(-1.5deg); box-shadow: 0 30px 56px -22px rgba(0, 0, 0, 0.62); }

.fx-qr-code {
  position: relative;
  aspect-ratio: 1;
  color: var(--ink);
  overflow: hidden;
  border-radius: var(--r-sm);
}

/* materialises via clip-path once revealed, instead of animating 200
   individual modules — same visual idea, a fraction of the DOM/JS cost. */
.js .fx-qr-code svg {
  clip-path: circle(0% at 50% 50%);
  transition: clip-path 780ms var(--ease-soft);
}
.js .reveal.is-visible .fx-qr-code svg { clip-path: circle(75% at 50% 50%); }

.fx-qr-logo {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: grid;
  place-items: center;
  width: 30%;
  height: 30%;
  border-radius: 10px;
  background: var(--violet);
}
.fx-qr-logo svg { width: 92%; height: 92%; }
.fx-qr-card:hover .fx-qr-logo { background: var(--ink); }

.fx-qr-scan {
  position: absolute;
  inset-inline: 0;
  top: -18%;
  height: 2px;
  background: rgba(203, 184, 238, 0.78);
  box-shadow: 0 0 10px rgba(203, 184, 238, 0.45);
  animation: fxQrScan 3.2s ease-in-out infinite;
}
@keyframes fxQrScan {
  0%, 100% { top: -18%; }
  50% { top: 100%; }
}

.fx-qr-card__url {
  margin-top: var(--s-4);
  font-size: 0.8125rem;
  font-weight: 700;
  color: var(--violet-deep);
  word-break: break-all;
}

.fx-qr-formats { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--s-2); }
.fx-qr-format {
  padding: 0.3rem 0.7rem;
  border: 1px solid rgba(255, 255, 255, 0.4);
  border-radius: var(--r-sm);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: #fff;
  transition: var(--transition);
}
.fx-qr-format:hover { background: rgba(255, 255, 255, 0.16); transform: translateY(-2px); }
.fx-qr-format--note { border-style: dashed; border-color: rgba(255, 255, 255, 0.3); color: rgba(255, 255, 255, 0.75); letter-spacing: 0.02em; }

/* -------------------------------------------------------------------------
   5. SMART ANALYTICS — dashboard card
   ------------------------------------------------------------------------- */
.fx-dash {
  width: 100%;
  max-width: 26rem;
  padding: var(--s-6);
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: var(--r-lg);
  transition: transform var(--dur) var(--ease-soft), background var(--dur) var(--ease), border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.fx-dash:hover { transform: translateY(-8px); background: rgba(255, 255, 255, 0.08); border-color: rgba(255, 255, 255, 0.32); box-shadow: var(--lift-dark); }
.fx-dash:hover .fx-dash__num { animation: fxDashPulse 540ms var(--ease-soft); }
@keyframes fxDashPulse { 50% { color: var(--accent); text-shadow: 0 0 22px rgba(203, 184, 238, 0.5); } }

.fx-dash__label {
  margin-bottom: var(--s-4);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.4);
}

.fx-dash__value { font-size: clamp(2.25rem, 6vw, 2.75rem); font-weight: 700; color: #fff; line-height: 1; }
.fx-dash__num { font-variant-numeric: tabular-nums; }
.fx-dash__meta { margin-top: var(--s-2); font-size: 0.8125rem; color: var(--body-on-dark); }
.fx-dash__delta { color: var(--accent); font-weight: 700; }

.fx-dash__chart {
  display: flex;
  align-items: flex-end;
  gap: var(--s-2);
  height: 80px;
  margin: var(--s-6) 0 var(--s-3);
  padding-bottom: 1.25rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
  position: relative;
}
.fx-dash__col {
  position: relative;
  flex: 1;
  height: var(--h, 40%);
  border-radius: 3px 3px 0 0;
  background: rgba(255, 255, 255, 0.22);
  transform-origin: bottom;
  transition: transform 560ms var(--ease-soft);
}
.fx-dash__col--peak { background: var(--accent); }
.js .reveal:not(.is-visible) .fx-dash__col { transform: scaleY(0); }
.fx-dash__col:nth-child(1) { transition-delay: 40ms; }
.fx-dash__col:nth-child(2) { transition-delay: 90ms; }
.fx-dash__col:nth-child(3) { transition-delay: 140ms; }
.fx-dash__col:nth-child(4) { transition-delay: 190ms; }
.fx-dash__col:nth-child(5) { transition-delay: 240ms; }
.fx-dash__col:nth-child(6) { transition-delay: 290ms; }
.fx-dash__col:nth-child(7) { transition-delay: 340ms; }
.fx-dash:hover .fx-dash__col { transform: scaleY(1.08); }
.fx-dash:hover .fx-dash__col:nth-child(even) { transform: scaleY(0.9); }
.fx-dash__col span {
  position: absolute;
  left: 0; right: 0;
  bottom: -1.25rem;
  text-align: center;
  font-size: 0.625rem;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.4);
}

.fx-dash__split { list-style: none; padding: 0; margin: var(--s-5) 0 0; display: grid; gap: var(--s-3); }
.fx-dash__split li {
  display: grid;
  grid-template-columns: 4.25rem 1fr 2.25rem;
  align-items: center;
  gap: var(--s-3);
  font-size: 0.75rem;
}
.fx-dash__split li > span:first-child { font-weight: 600; color: #fff; text-align: left; }
.fx-dash__split li > span:last-child { font-weight: 700; text-align: right; color: var(--body-on-dark); font-variant-numeric: tabular-nums; }
.fx-dash__track { height: 5px; border-radius: 3px; background: rgba(255, 255, 255, 0.14); overflow: hidden; }
.fx-dash__fill {
  display: block;
  width: var(--w, 0%);
  height: 100%;
  border-radius: 3px;
  background: var(--violet);
  transition: width 700ms var(--ease-soft);
}
.js .reveal:not(.is-visible) .fx-dash__fill { width: 0; }

/* -------------------------------------------------------------------------
   6. SECONDARY GRID
   ------------------------------------------------------------------------- */
.fx-more__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s-4);
}
@media (min-width: 640px) { .fx-more__grid { grid-template-columns: repeat(2, 1fr); gap: var(--s-5); } }
@media (min-width: 992px) { .fx-more__grid { grid-template-columns: repeat(3, 1fr); } }

.fx-more__card {
  position: relative;
  overflow: hidden;
  padding: var(--s-5);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--white);
  transition: var(--transition);
}
.fx-more__card::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 4px;
  background: var(--tone, var(--violet));
  transform: scaleY(0);
  transform-origin: bottom;
  transition: transform var(--dur) var(--ease-soft);
}
.fx-more__card:hover {
  border-color: var(--tone, var(--violet));
  transform: translateY(-7px);
  box-shadow: var(--lift);
}
.fx-more__card:hover::before { transform: scaleY(1); }

.fx-more__icon {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  margin-bottom: var(--s-3);
  border-radius: var(--r-md);
  background: var(--tone, var(--violet));
  color: #fff;
}
.fx-more__icon svg { width: 20px; height: 20px; transition: transform var(--dur) var(--ease-soft); }
.fx-more__card:hover .fx-more__icon { box-shadow: 0 10px 20px -12px rgba(36, 16, 70, 0.7); }
.fx-more__card:hover .fx-more__icon svg { transform: rotate(-8deg) scale(1.12); }

.fx-more__card h3 { font-size: 1rem; font-weight: 700; letter-spacing: -0.01em; margin-bottom: var(--s-2); }
.fx-more__card p { font-size: 0.875rem; color: var(--ink-soft); }

/* -------------------------------------------------------------------------
   7. REDUCED MOTION / PRINT
   ------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .js .fx-hero__copy .fx-hero__kicker,
  .js .fx-hero__copy .fx-hero__title,
  .js .fx-hero__copy .fx-hero__sub,
  .js .fx-hero__copy .btn,
  .js .fx-stack__card,
  .js .fx-bio-card__link--lifted,
  .js .fx-morph__arrow, .js .fx-morph__short,
  .js .fx-dash__col, .js .fx-dash__fill {
    transition: none !important;
  }
  .js .fx-hero__copy:not(.is-visible) .fx-hero__kicker,
  .js .fx-hero__copy:not(.is-visible) .fx-hero__title,
  .js .fx-hero__copy:not(.is-visible) .fx-hero__sub,
  .js .fx-hero__copy:not(.is-visible) .btn { opacity: 1; transform: none; }
  .js .reveal:not(.is-visible) .fx-dash__col,
  .js .reveal:not(.is-visible) .fx-dash__fill { transform: none; width: var(--w, 0%); }
  .js .fx-qr-code svg { transition: none !important; clip-path: circle(75% at 50% 50%) !important; }
  /* infinite loops, not one-shot transitions: the site-wide rule crushing
     animation-duration to 0.01ms would turn these into a rapid flicker
     through all five colours instead of stopping them, so they need an
     explicit off-switch. Falls back to each element's static default
     (violet avatar/border/handle, indigo swatch marked active). */
  .fx-bio-card__avatar,
  .fx-bio-card__link--lifted,
  .fx-bio-card__link--lifted .fx-bio-card__grip,
  .fx-bio-card__handle,
  .fx-bio-themes::after,
  .fx-qr-scan {
    animation: none !important;
  }
  .fx-dash, .fx-dash:hover, .fx-more__card, .fx-more__card:hover, .fx-qr-card, .fx-qr-card:hover { transform: none; transition: none; }
}

@media print {
  .fx-stack { display: none !important; }
  .js .reveal { opacity: 1 !important; transform: none !important; }
  .js .fx-hero__copy .fx-hero__kicker,
  .js .fx-hero__copy .fx-hero__title,
  .js .fx-hero__copy .fx-hero__sub,
  .js .fx-hero__copy .btn { opacity: 1 !important; transform: none !important; }
  .fx-qr-code svg { clip-path: none !important; }
}
