/*
Block 10 "Four steps to your baseline". Spec: docs/blocks/10-steps.md.
Every value comes from tokens.css.

Two columns: an accordion of steps with a rail down its left, and a cycle diagram. On the
mobile frame the diagram leaves the row and goes underneath.

--- The rail is drawn, not stated -----------------------------------------------------------

The design gives the connectors as 173 after the open step and 52.5 between the closed ones.
Neither number appears here, and that is the point of this block: both are consequences of how
tall the steps are, and the steps are as tall as the client's copy makes them. Written down,
the rail would come apart the first time anyone edits a paragraph.

What is stated instead is the rule the design actually follows — a dot 44.2 below its step's
top edge, and a connector that clears the dots above and below it by 16 — and the connector
runs from its own dot to the next one wherever that happens to be. At the design's own copy it
comes out at 173 and 52.5.

--- The heading of a step is one animation, not two states ----------------------------------

Opening a step takes its heading from 22 at 67% to 36 at full. Both ends are tokens and the
transition sits on the element itself, so the two are one movement rather than a swap between
two rules.
*/
.steps {
  padding-block: var(--steps-top) var(--steps-bottom);
}

.steps__inner {
  width: min(var(--page-max), 100% - var(--page-inset) * 2);
  margin-inline: auto;
}

/*
The row. The accordion takes the width the design gives it and the diagram takes what is left,
which is how the diagram gives way first when the frame narrows — it is decorative, and the
steps are not.
*/
.steps__row {
  display: grid;
  align-items: start;
  gap: var(--steps-col-gap);
  grid-template-columns: minmax(0, var(--steps-list-w)) minmax(0, 1fr);
}

/* The shared two-tone heading at this section's size. */
.steps__title {
  max-width: var(--steps-title-w);

  --heading-fs: var(--fs-steps-title);
  --heading-ls: var(--ls-steps-title);
  --heading-lh: var(--lh-steps-title);
}

.steps__list {
  margin: var(--steps-list-gap) 0 0;
  padding: 0;
  list-style: none;
}

/*
A step. Two columns — the rail and everything else — so the dots and the copy are laid out by
one grid rather than by an absolute layer over the text.

The wash on the open one is a background on the item and spans its full width, which is what
the design draws: transparent at both ends, --accent at 32% through the middle.
*/
.steps__item {
  display: grid;
  column-gap: var(--steps-rail-gap);
  grid-template-columns: var(--steps-rail-w) minmax(0, 1fr);
}

.steps__item[data-open="true"] {
  background-image: var(--steps-item-wash);
}

/*
The rail cell. position: relative for the connector, which is a pseudo-element that reaches
past the bottom of its own step to the next step's dot — nothing between here and the section
clips, so the overhang paints.
*/
.steps__rail {
  position: relative;
}

.steps__dot {
  position: absolute;
  top: var(--steps-dot-top);
  left: 50%;
  width: var(--steps-dot);
  height: var(--steps-dot);
  border-radius: 50%;
  background: var(--steps-dot-color);
  translate: -50% -50%;
  transition: width var(--steps-dur) var(--steps-ease), height var(--steps-dur) var(--steps-ease),
    background-color var(--steps-dur) var(--steps-ease);
}

.steps__item[data-open="true"] .steps__dot {
  width: var(--steps-dot-active);
  height: var(--steps-dot-active);
  background: var(--steps-dot-active-color);
}

/*
The connector, from this step's dot to the next one. The bottom is negative by exactly as much
as a dot sits below a step's top edge, less the dot's own radius and the clearance — so the
line ends 16 short of the next dot whatever the height of the step in between.

The last step has none: :not(:last-child) rather than a class, so a fifth step added by the
client takes the line and gives it up again with nothing edited.
*/
.steps__item:not(:last-child) .steps__rail::after {
  position: absolute;
  top: calc(var(--steps-dot-top) + var(--steps-dot) / 2 + var(--steps-dot-clear));
  right: 0;
  bottom: calc(
    -1 * (var(--steps-dot-top) - var(--steps-dot) / 2 - var(--steps-dot-clear))
  );
  left: 0;
  width: var(--steps-connector-w);
  margin-inline: auto;
  border-radius: var(--steps-connector-w);
  background: var(--rule-color);
  content: "";
}

/*
The head is the control. A button rather than a div with a handler: it is focusable, it
announces its state, and it works from the keyboard without a line of script.

Three columns — number, heading, toggle — and the items are aligned to the START of the row
rather than to its centre. That is what keeps the number and the toggle beside the heading's
FIRST line when it wraps, which every heading does on the mobile frame; centred, they would
drift to the middle of a two-line block. Each of the two is then nudged onto that first line by
half the difference between the line and its own height.
*/
.steps__head {
  position: relative;
  display: grid;
  width: 100%;
  padding: var(--steps-head-pad-t) 0 var(--steps-head-pad-b);
  border: 0;
  background: none;
  color: inherit;
  cursor: pointer;
  font: inherit;
  gap: 0;
  grid-template-columns: var(--steps-item-text-x) minmax(0, 1fr) auto;
  align-items: start;
  text-align: left;
}

/*
The step's heading element exists for the outline — a section of four steps is four headings —
and carries nothing of its own. The button inside it is what is styled.
*/
.steps__heading {
  margin: 0;
  font: inherit;
}

/*
The whole row is the target, not the words in it.

The head is inside a cell that is inset from the item by the item's own padding, and to the
left of it sits the rail — so a reader aiming at a step could miss it above, below and to the
left of the text. This reaches back out over all three: up by the item's top padding, left
across the rail and its gap, and — when the step is collapsed — down by the bottom padding as
well, which is the whole of a collapsed row.

Not down when the step is open: below an open head is its own panel, and a reader selecting a
line of that text would be closing the step instead.
*/
.steps__head::before {
  position: absolute;
  top: calc(-1 * var(--steps-item-pad-t));
  right: 0;
  bottom: 0;
  left: calc(-1 * (var(--steps-rail-w) + var(--steps-rail-gap)));
  content: "";
}

.steps__item:not([data-open="true"]) .steps__head::before {
  bottom: calc(-1 * var(--steps-item-pad-b));
}

.steps__num {
  padding-top: calc((var(--lh-steps-item) - var(--lh-steps-num)) / 2);
  color: var(--teal-deep);
  font-size: var(--fs-steps-num);
  font-weight: var(--fw-regular);
  letter-spacing: var(--ls-steps-num);
  line-height: var(--lh-steps-num);
}

/*
The heading of a step. Both the size and the opacity move, and the transition is written once
here rather than on a modifier, so the two ends are one movement.

The colour never changes: what the design dims is the whole heading, which is an opacity and
not a second colour — at 67% of --ink over this background it samples as the render does.
*/
.steps__label {
  margin: 0;
  color: var(--ink);
  font-size: var(--fs-steps-item);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-steps-item);
  line-height: var(--lh-steps-item);
  opacity: var(--steps-item-dim);
  transition: font-size var(--steps-dur) var(--steps-ease),
    opacity var(--steps-dur) var(--steps-ease);
}

.steps__item[data-open="true"] .steps__label {
  font-size: var(--fs-steps-item-open);
  line-height: var(--lh-steps-item-open);
  opacity: 1;
}

/*
The toggle. Two bars: one always, the second crossed over it and rotated away when the step
opens — so a plus becomes a minus by turning, which is one property to animate and no glyph to
load. Drawn rather than set in a font, like the arrow and the pin.
*/
.steps__toggle {
  position: relative;
  width: var(--steps-toggle-bar-w);
  height: var(--steps-toggle-bar-w);
  margin-top: calc((var(--lh-steps-item) - var(--steps-toggle-bar-w)) / 2);
}

.steps__toggle::before,
.steps__toggle::after {
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  height: var(--steps-toggle-bar-h);
  border-radius: var(--steps-toggle-bar-h);
  background: var(--steps-toggle-color);
  content: "";
  translate: 0 -50%;
  transition: rotate var(--steps-dur) var(--steps-ease),
    background-color var(--steps-dur) var(--steps-ease);
}

.steps__toggle::after {
  rotate: 90deg;
}

.steps__item[data-open="true"] .steps__toggle::before,
.steps__item[data-open="true"] .steps__toggle::after {
  background: var(--steps-toggle-color-open);
}

.steps__item[data-open="true"] .steps__toggle::after {
  rotate: 0deg;
}

/*
The panel.

The height is animated with a grid row rather than with max-height: a max-height large enough
for the client's longest paragraph is a guess, and every value that is not the real height
makes the movement wrong at one end or the other. A 0fr-to-1fr row is the content's own height
at both ends, whatever it turns out to be.

The inner element is what carries overflow: hidden — a grid item cannot be clipped by its
track, so without it the text spills out of a closed step.
*/
.steps__panel {
  display: grid;
  grid-template-rows: 1fr;
  transition: grid-template-rows var(--steps-dur) var(--steps-ease);
}

/*
Open is the default and closed is the exception, gated on the flag steps.js puts on <html>.
Without JavaScript the flag never arrives, nothing is ever collapsed, and all four steps read
as plain text — which is the requirement, held by construction rather than by a fallback that
has to be remembered.
*/
:root[data-accordion-js="on"] .steps__item:not([data-open="true"]) .steps__panel {
  grid-template-rows: 0fr;
}

.steps__panel-inner {
  overflow: hidden;
  padding-left: var(--steps-item-text-x);
}

/*
The gap between a step's heading and its text. On the panel's first child rather than on the
panel, and not on the head at all.

Not on the head because the head is shared with the collapsed steps, and the mobile frame's 16
would make every one of them 16 taller. Not on the panel itself because padding is not content:
a box clipped to nothing still has its padding, and putting it there grew the three collapsed
steps by exactly that 16 — which is what the measurement showed. A margin on the child is
inside what the clip removes.
*/
.steps__panel-inner > :first-child {
  margin-top: var(--steps-panel-pad-t);
}

.steps__body {
  max-width: var(--steps-body-w);
  margin: 0;
  color: var(--ink-muted);
  font-size: var(--fs-steps-body);
  font-weight: var(--fw-regular);
  line-height: var(--lh-steps-body);
}

.steps__links {
  display: flex;
  flex-wrap: wrap;
  margin: var(--steps-links-top) 0 0;
  padding: 0 0 var(--steps-panel-pad-b);
  gap: var(--steps-links-gap);
  list-style: none;
  white-space: normal;
}

.steps__link {
  display: inline-flex;
  align-items: center;
  gap: var(--steps-link-arrow-gap);
  color: var(--teal-deep);
  font-size: var(--fs-steps-link);
  font-weight: var(--fw-bold);
  line-height: var(--lh-steps-link);
  text-decoration: none;
}

.steps__link .arrow {
  --arrow-size: var(--fs-steps-link);
}

/*
The hairline under a step. On the content column only — the design runs it from the number to
the toggle and leaves the rail clear, which is why it is not a border on the item.
*/
.steps__content {
  border-bottom: var(--rule-w) solid var(--rule-color);
  padding-block: var(--steps-item-pad-t) var(--steps-item-pad-b);
}

/*
--- The diagram ---------------------------------------------------------------------------

Held static. Nothing here is driven by the accordion, because what it should do when a step
opens has not been decided; what is prepared is the shape of that decision. Which node is
active is an attribute on this element — data-active — so switching it is one attribute and
not a class moved between six nodes. The arc's extent is one number, --steps-arc, in degrees,
because the circle it is drawn on declares pathLength="360".

The box IS the disc: the ring is concentric with it in the design to within half a pixel, so
one box positions both, and everything else hangs off the centre. Labels are allowed out of it
— "MONITORING" starts 22.6 left of the disc's edge in the design — which is why nothing here
clips.
*/
.steps__diagram {
  position: relative;
  justify-self: end;
  margin-top: var(--steps-disc-top);
  width: var(--steps-disc);
  max-width: 100%;
  margin-inline-end: var(--steps-disc-inset);
  aspect-ratio: 1;
  border-radius: 50%;
  background: linear-gradient(
    to top,
    rgb(var(--accent-rgb) / 0%) 1%,
    rgb(var(--accent-rgb) / 32%) 51.4%,
    rgb(var(--accent-rgb) / 0%) 100%
  );
}

.steps__ring {
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--steps-ring);
  height: var(--steps-ring);
  overflow: visible;
  translate: -50% -50%;
}

/*
Both strokes are declared non-scaling, so their widths are the CSS pixels the design gives them
rather than units of a 100-wide viewBox that is drawn at 382.8.
*/
.steps__ring-track {
  fill: none;
  stroke: var(--surface);
  stroke-width: var(--steps-ring-w);
  vector-effect: non-scaling-stroke;
}

/*
The arc: one dash as long as --steps-arc degrees, and a gap as long as everything else, so the
token stays a number of degrees and is the one thing that moves the day this is wired to the
accordion.

The lengths are worked out from the ring's own diameter rather than from a pathLength on the
circle, and that is not a preference. With vector-effect: non-scaling-stroke the dashes are
measured on SCREEN while pathLength rescales distances in USER units, and the two disagree by
whatever the viewBox is scaled by — the first version of this had pathLength="360" and drew the
60-degree arc three and a half times around the circle. Measuring both in the same space is
what fixes it.

3.14159265 is pi, not a design value.
*/
.steps__ring-arc {
  fill: none;
  stroke: var(--accent);
  stroke-dasharray:
    calc(var(--steps-ring) * 3.14159265 * var(--steps-arc) / 360)
    calc(var(--steps-ring) * 3.14159265 * (360 - var(--steps-arc)) / 360);
  stroke-linecap: round;
  stroke-width: var(--steps-arc-w);
  vector-effect: non-scaling-stroke;
  transition: stroke-dasharray var(--steps-dur) var(--steps-ease);
}

/*
A node, placed by its own sine and cosine rather than by a rotation. The template stamps the
two as plain numbers, so the node's box is never turned — which matters for the label inside
it: rotated, the label would have to be turned back, and its POSITION would still be in the
rotated frame, putting "ANALYSIS" outward along the radius instead of to the right of its node.
*/
.steps__node {
  position: absolute;
  top: calc(50% - var(--steps-node-y) * var(--steps-ring) / 2);
  left: calc(50% + var(--steps-node-x) * var(--steps-ring) / 2);
  width: var(--steps-node);
  height: var(--steps-node);
  border: var(--steps-node-w) solid var(--steps-node-ring);
  border-radius: 50%;
  background: var(--surface);
  translate: -50% -50%;
  transition: width var(--steps-dur) var(--steps-ease),
    height var(--steps-dur) var(--steps-ease),
    background-color var(--steps-dur) var(--steps-ease),
    border-color var(--steps-dur) var(--steps-ease);
}

.steps__node[data-active="true"] {
  width: var(--steps-node-active);
  height: var(--steps-node-active);
  border-color: var(--accent);
  background: var(--accent);
}

/*
The label rides with its node and is un-rotated inside it, so it reads horizontally wherever
the node sits. Which side it sits on follows from where the node is: above at the top, below at
the bottom, outside the ring on either flank.
*/
.steps__node-label {
  position: absolute;
  color: var(--ink);
  font-size: var(--fs-steps-label);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-steps-label);
  line-height: var(--lh-steps-label);
  text-transform: uppercase;
  white-space: nowrap;
}

.steps__node[data-side="top"] .steps__node-label {
  bottom: calc(100% + var(--steps-label-gap));
  left: 50%;
  translate: -50% 0;
}

.steps__node[data-side="bottom"] .steps__node-label {
  top: calc(100% + var(--steps-label-gap));
  left: 50%;
  translate: -50% 0;
}

.steps__node[data-side="right"] .steps__node-label {
  top: 50%;
  left: calc(100% + var(--steps-label-gap));
  translate: 0 -50%;
}

.steps__node[data-side="left"] .steps__node-label {
  top: 50%;
  right: calc(100% + var(--steps-label-gap));
  translate: 0 -50%;
}

/*
Mobile: one column, the diagram underneath. --bp-sm

The mobile frame has NO section heading — it begins at the first step. The heading is carried
down from the desktop at 40px, which is an ASSUMPTION and not a measurement: there is nothing
in the design to check it against. Marked here rather than buried in a token so that whoever
compares this to the mobile frame knows why it is there.
*/
/*
The two columns need 1324 between them — 707 for the steps, 32 of gap and the disc's 585 — and
the content band is only that wide from about 1404 of viewport. Below --bp-lg the diagram goes
under the list rather than squeezing beside it: the design describes a 1440 frame and a 390 one
and nothing in between, and of the two arrangements it does describe, the stacked one fits.

The list track is minmax(0, ...) rather than a flat 707 so that between --bp-lg and 1404 the
steps give way instead of pushing the page sideways. Written as a fixed track it added 176px of
horizontal scroll at 1168 — measured with this block hidden and shown, so it was this block's
and not the one block 05 already has.
*/
@media (max-width: 1300px) {
  .steps__row {
    grid-template-columns: minmax(0, 1fr);
  }

  .steps__diagram {
    justify-self: center;
    margin-top: var(--steps-col-gap);
    margin-inline-end: 0;
  }
}

@media (max-width: 700px) {
  /* The two links stack, which is the one change to a step's inside that is not a size. */
  .steps__links {
    flex-direction: column;
  }
}

/*
Reduced motion: the accordion still opens, it simply arrives. Everything animated in this block
is a size, an opacity or a grid row, so there is nothing to disable beyond the durations.
*/
@media (prefers-reduced-motion: reduce) {
  .steps__dot,
  .steps__label,
  .steps__panel,
  .steps__toggle::before,
  .steps__toggle::after,
  .steps__ring-arc {
    transition-duration: 0s;
  }
}
