/* Benefits — the seven approved claims as a single column, with the claim
   nearest the centre of the viewport carrying the focus.
   Tokens only, per tokens.css: no literal colours. */

.bnf__head {
  max-width: var(--measure);
  margin-bottom: var(--sp-9);
}

.bnf__head > * + * {
  margin-top: var(--sp-4);
}

.bnf__figure {
  margin: 0 auto var(--sp-8);
  max-width: 22rem;
}

.bnf__figure img {
  display: block;
  width: 100%;
  height: auto;
}

/* ---- the claims --------------------------------------------------------
   One column. Seven rows read down the page in the client's approved order,
   which two columns could not do — a two-column grid reads across, so the
   order was only ever implied. */

.bnf__list {
  list-style: none;
  /* Left-aligned, not centred: the list shares the heading's left edge so the
     section reads as one column of content rather than a centred block that
     happens to sit under a left-aligned heading. max-width still caps the
     measure on a wide screen. */
  margin: 0;
  padding: 0;
  max-width: 46rem;
  display: flex;
  flex-direction: column;
}

/* Icon in its own gutter, text beside it, so the text keeps one left edge all
   the way down regardless of icon width. */
.bnf__item {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: var(--sp-5);
  align-items: center;
  padding: var(--sp-6) 0;
  border-top: 1px solid var(--rule);

  /* The focused claim grows from its left edge — scaling from the centre
     would slide the text sideways as it grew, which is what makes this kind
     of effect feel unsteady. */
  transform-origin: left center;
  transition:
    transform var(--dur, 320ms) var(--ease-out, ease),
    opacity var(--dur, 320ms) var(--ease-out, ease),
    border-color var(--dur, 320ms) var(--ease-out, ease);

  /* 0.75 is a contrast floor, not a taste call. Measured against the dark
     ground: the claim composites to 5.2:1 and the uppercase label — small text,
     so it needs the full 4.5:1 — to 4.69:1. At 0.7 the label drops to 4.25 and
     at 0.45 (where this started) the claim was 2.68, well into failing. The
     focused state does its work through colour, scale and the brighter rule
     instead of by pushing the resting state further down. */
  opacity: 0.75;
}

.bnf__item:last-child {
  border-bottom: 1px solid var(--rule);
}

.bnf__item.is-focus {
  opacity: 1;
  transform: scale(1.06);
  border-color: var(--rule-strong);
}

/* The icon spans the whole text block and centres on it (QA-05). Placed in
   row 1 only, it centred on the title alone and sat high. `span 3` covers
   name + claim + study link; without a study link the extra implicit row is
   empty and collapses to 0. (`1 / -1` would not work: -1 counts explicit
   rows only, and this grid has none.) */
.bnf__icon {
  display: block;
  grid-row: 1 / span 3;
  align-self: center;
  color: var(--gold-line);
  transition: transform var(--dur, 320ms) var(--ease-out, ease);
}

.bnf__icon .kicon {
  display: block;
  width: 2.25rem;
  height: 2.25rem;
}

.bnf__item.is-focus .bnf__icon {
  transform: scale(1.08);
}

.bnf__name,
.bnf__claim,
.bnf__study {
  grid-column: 2;
}

.bnf__name {
  margin: 0;
  text-transform: uppercase;
  line-height: var(--leading-snug);
}

.bnf__claim {
  margin: var(--sp-2) 0 0;
  color: var(--ink-muted);
  font-size: var(--step-1);
  line-height: var(--leading-snug);
}

.bnf__item.is-focus .bnf__claim {
  color: var(--ink);
}

.bnf__study {
  margin: var(--sp-3) 0 0;
  justify-self: start;
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 0.25em;
  text-decoration-color: var(--gold-line);
}

.bnf__study:hover {
  color: var(--gold-text);
}

/* A claim with no icon keeps its text on the same left edge as the rest. */
.bnf__item:not(:has(.bnf__icon)) :is(.bnf__name, .bnf__claim, .bnf__study) {
  grid-column: 1 / -1;
}

/* ---- tail -------------------------------------------------------------- */

/* Disclaimer tight to the list it qualifies, then the call to action. */
.bnf__disclaimer {
  margin-top: var(--sp-5);
  max-width: var(--measure);
}

.bnf__cta {
  margin-top: var(--sp-7);
}

/* ---- narrow ------------------------------------------------------------ */

@media (max-width: 48rem) {
  .bnf__item {
    column-gap: var(--sp-4);
    padding: var(--sp-5) 0;
  }

  /* A 6% scale on a narrow screen pushes the claim against the right edge,
     so the focused state leans on contrast alone here. */
  .bnf__item.is-focus {
    transform: none;
  }

  .bnf__icon .kicon {
    width: 1.875rem;
    height: 1.875rem;
  }

  .bnf__claim {
    font-size: var(--step-0);
  }
}

/* Reduced motion: no scaling, and every claim at full weight. The dimmed
   resting state is part of the effect, not the design, so it goes too — the
   JS also stops applying is-focus. */
@media (prefers-reduced-motion: reduce) {
  .bnf__item {
    opacity: 1;
    transform: none;
    transition: none;
  }

  .bnf__item.is-focus {
    transform: none;
  }

  .bnf__icon {
    transition: none;
  }
}

/* ==========================================================================
   The body figure — "7 ways to restore vitality" (Justin, 29 Sep)

   The designer's graphic, rebuilt so it can move: the mesh is an image, the
   icons, lines and points are inline SVG, and the names and claims are the
   same list as above, placed around the figure. The coordinates below come
   from the artwork; prototype/tools/extract-benefits-figure.py prints them.

   Three states:
     static        no script, or reduced motion: the whole graphic lit.
     .is-live      the figure holds in view and benefits.js lights one benefit
                   at a time, head to foot. Each stays lit once reached.
     .is-done      past the last one: all seven at full strength.
   ========================================================================== */

/* The glow's centre travels between points instead of jumping. Without
   @property support it jumps, which is acceptable. */
@property --bnf-gx { syntax: "<percentage>"; inherits: true; initial-value: 50%; }
@property --bnf-gy { syntax: "<percentage>"; inherits: true; initial-value: 8%; }

.bnf__track {
  /* The figure is sized by height so it always fits the screen it holds in;
     its width follows the artwork's 384 : 604 box. */
  --bnf-h: min(34rem, 58svh);
  --bnf-w: calc(var(--bnf-h) * 384 / 604);
  /* Scroll distance per benefit while the figure holds. */
  --bnf-step: 30svh;
  --bnf-ground: var(--ground);
}

.band-dark .bnf__track {
  --bnf-ground: var(--band-dark);
}

.bnf__stage {
  position: relative;
  width: var(--bnf-w);
  height: var(--bnf-h);
  margin-inline: auto;
}

.bnf__body,
.bnf__wires {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

/* The mesh file is one flat gold; the artwork's quieter tan is this opacity. */
.bnf__body {
  opacity: 0.6;
}

.bnf__body--lit {
  opacity: 0;
  /* Mask values are alpha, not colour. */
  -webkit-mask-image: radial-gradient(circle calc(var(--bnf-w) * 0.4) at var(--bnf-gx) var(--bnf-gy), black 12%, transparent 100%);
  mask-image: radial-gradient(circle calc(var(--bnf-w) * 0.4) at var(--bnf-gx) var(--bnf-gy), black 12%, transparent 100%);
  transition:
    opacity var(--dur-slow) var(--ease-out),
    --bnf-gx var(--dur-slow) var(--ease-in-out),
    --bnf-gy var(--dur-slow) var(--ease-in-out);
}

.bnf__wires {
  overflow: visible;
}

.bnf__wire {
  fill: none;
  stroke: var(--gold-line);
  stroke-width: 2.2;
  stroke-linejoin: round;
}

.bnf__dot,
.bnf__glyph {
  fill: var(--gold-line);
}

/* The disc hides the mesh behind the icon, so it takes the section's ground. */
.bnf__disc-bg,
.bnf__glyph--cut {
  fill: var(--bnf-ground);
}

.bnf__ring {
  fill: none;
  stroke: var(--gold-line);
  stroke-width: 2.4;
}

/* The heart is the artwork's one oxblood mark. */
.bnf__glyph--accent {
  fill: var(--accent-surface);
}

.bnf__dot {
  transform-box: fill-box;
  transform-origin: center;
}

/* Static, narrow: the figure, then the claims as the rows above. The dimmed
   resting state belongs to the scroll effect, so it does not apply here. */
.bnf__track .bnf__list {
  margin-top: var(--sp-7);
}

.bnf__track .bnf__item {
  opacity: 1;
}

/* ---- wide: the claims sit around the figure, where the artwork has them -- */

.bnf__item[data-point="01-vitality"]            { --bnf-y: 5.79%;  --bnf-edge: 88.07%; --bnf-fx: 50.07%; --bnf-fy: 7.50%; }
.bnf__item[data-point="04-mental-focus"]        { --bnf-y: 12.71%; --bnf-edge: 89.99%; --bnf-fx: 56.27%; --bnf-fy: 12.71%; }
.bnf__item[data-point="03-antioxidant-defense"] { --bnf-y: 24.47%; --bnf-edge: 88.07%; --bnf-fx: 44.54%; --bnf-fy: 24.47%; }
.bnf__item[data-point="02-healthy-circulation"] { --bnf-y: 30.50%; --bnf-edge: 89.99%; --bnf-fx: 52.48%; --bnf-fy: 28.51%; }
.bnf__item[data-point="06-gut-balance"]         { --bnf-y: 43.14%; --bnf-edge: 88.07%; --bnf-fx: 49.73%; --bnf-fy: 43.18%; }
.bnf__item[data-point="05-physical-endurance"]  { --bnf-y: 62.35%; --bnf-edge: 87.05%; --bnf-fx: 58.91%; --bnf-fy: 57.03%; }
.bnf__item[data-point="07-immune-support"]      { --bnf-y: 65.98%; --bnf-edge: 88.07%; --bnf-fx: 40.16%; --bnf-fy: 66.05%; }

@media (min-width: 64rem) {
  .bnf__track {
    --bnf-h: clamp(26rem, calc(100svh - var(--header-h) - 5rem), 42rem);
    --bnf-step: 28svh;
    /* How far a claim may run outward from the figure before the gutter. */
    --bnf-label-w: min(15rem, calc((100vw - var(--bnf-w)) / 2 + var(--bnf-w) * 0.1 - var(--gutter) - var(--sp-5)));
  }

  .bnf__track .bnf__pin {
    position: relative;
    width: var(--bnf-w);
    height: var(--bnf-h);
    margin-inline: auto;
  }

  .bnf__track .bnf__stage,
  .bnf__track .bnf__list {
    position: absolute;
    inset: 0;
    margin: 0;
  }

  .bnf__track .bnf__list {
    display: block;
    max-width: none;
  }

  .bnf__track .bnf__item {
    position: absolute;
    top: var(--bnf-y);
    display: block;
    width: max-content;
    max-width: var(--bnf-label-w);
    padding: 0;
    border: 0;
    transform: translateY(calc(-50% + var(--bnf-lift, 0rem)));
  }

  /* The artwork sets its claims in the body colour, not the muted one; the
     resting state is carried by the row's opacity instead. */
  .bnf__track .bnf__claim {
    color: var(--ink);
    text-wrap: balance;
  }

  /* The left hand comes up under this claim's second line. */
  .bnf__track .bnf__item[data-point="06-gut-balance"] {
    --bnf-lift: -0.875rem;
  }

  /* Left column: text runs up to the icon from the left. */
  .bnf__track .bnf__item:is([data-point="01-vitality"], [data-point="03-antioxidant-defense"], [data-point="06-gut-balance"], [data-point="07-immune-support"]) {
    right: calc(var(--bnf-edge) + var(--sp-3));
    text-align: right;
    --bnf-from: -0.75rem;
  }

  .bnf__track .bnf__item:is([data-point="04-mental-focus"], [data-point="02-healthy-circulation"], [data-point="05-physical-endurance"]) {
    left: calc(var(--bnf-edge) + var(--sp-3));
    --bnf-from: 0.75rem;
  }

  /* The figure draws the icon, so the list's own copy stands down. A benefit
     with no point on the figure has nowhere to sit. */
  .bnf__track .bnf__icon,
  .bnf__track .bnf__item[data-point="none"] {
    display: none;
  }

  .bnf__track .bnf__study {
    display: inline-block;
  }

  /* -- live -- */

  .bnf__track.is-live .bnf__pin {
    position: sticky;
    /* Centred in the space under the header. */
    top: calc(var(--header-h) + (100svh - var(--header-h) - var(--bnf-h)) / 2);
  }

  .bnf__track.is-live .bnf__item {
    opacity: 0;
    transform: translateY(calc(-50% + var(--bnf-lift, 0rem))) translateX(var(--bnf-from, 0));
    transition:
      opacity var(--dur-slow) var(--ease-out),
      transform var(--dur-slow) var(--ease-out);
  }

  /* 0.75 is the contrast floor measured for this list (see above). */
  .bnf__track.is-live .bnf__item.is-on {
    opacity: 0.75;
    transform: translateY(calc(-50% + var(--bnf-lift, 0rem)));
  }

  .bnf__track.is-live .bnf__item.is-focus,
  .bnf__track.is-live.is-done .bnf__item {
    opacity: 1;
  }
}

/* ---- live, both widths: the figure holds and the scroll steps through ---- */

/* The extra scroll the steps need: the first lights on the way in, the last
   holds for a little before the figure lets go. It is a box, not padding,
   because a sticky element only travels inside its parent's content box. */
.bnf__track.is-live::after {
  content: "";
  display: block;
  height: calc((var(--bnf-count, 7) - 0.6) * var(--bnf-step));
}

.bnf__track.is-live .bnf__disc {
  opacity: 0.3;
  transition: opacity var(--dur-slow) var(--ease-out);
}

.bnf__track.is-live .bnf__pt.is-on .bnf__disc {
  opacity: 0.8;
}

.bnf__track.is-live .bnf__pt.is-focus .bnf__disc,
.bnf__track.is-live.is-done .bnf__disc {
  opacity: 1;
}

/* pathLength is 1 on every line, so one dash is the whole line. */
.bnf__track.is-live .bnf__wire {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset var(--dur-slow) var(--ease-in-out);
}

.bnf__track.is-live .bnf__pt.is-on .bnf__wire {
  stroke-dashoffset: 0;
}

/* The point appears as the line reaches it. */
.bnf__track.is-live .bnf__dot {
  opacity: 0;
  transition:
    opacity var(--dur-fast) var(--ease-out),
    transform var(--dur) var(--ease-out);
}

.bnf__track.is-live .bnf__pt.is-on .bnf__dot {
  opacity: 1;
  transition-delay: calc(var(--dur-slow) * 0.7), 0s;
}

.bnf__track.is-live .bnf__pt.is-focus .bnf__dot {
  transform: scale(1.5);
}

.bnf__track.is-live.is-lit .bnf__body--lit {
  opacity: 1;
}

/* ---- live, narrow: the figure on top, one claim under it ----------------- */

@media (max-width: 63.999rem) {
  .bnf__track.is-live .bnf__pin {
    position: sticky;
    top: calc(var(--header-h) + var(--sp-3));
  }

  /* Every claim in one cell, so the card is as tall as the tallest and the
     figure never moves when the claim changes. */
  .bnf__track.is-live .bnf__list {
    display: grid;
    max-width: 24rem;
    margin: var(--sp-5) auto 0;
  }

  .bnf__track.is-live .bnf__item {
    grid-area: 1 / 1;
    display: block;
    padding: 0;
    border: 0;
    text-align: center;
    opacity: 0;
    visibility: hidden;
    transform: translateY(0.5rem);
    transition:
      opacity var(--dur) var(--ease-out),
      transform var(--dur) var(--ease-out),
      visibility 0s linear var(--dur);
  }

  .bnf__track.is-live .bnf__item.is-focus {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition-delay: 0s;
  }

  .bnf__track.is-live .bnf__icon {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .bnf__body--lit,
  .bnf__track .bnf__item {
    transition: none;
  }
}
