/* How it works, as a hub: the center node, six step nodes, a line from the center to each, pulses along the lines (hub.js builds
   the lines and pulses; the nodes are the old step buttons, .how-step in inside.css, so their done / active look is unchanged).
   Entrance = the page's "gather and settle" (motion.css): the center arrives, the nodes fly out from it one after another, the
   lines draw out to them, then the pulses begin. The finished diagram is the default; every start state lives inside the
   @supports / .mo-in rules, so Reduce Motion and browsers without scroll-driven animation show it complete. */

.hub {
  --core-w: clamp(380px, 46%, 700px);
  --hub-gap: clamp(48px, 4.6vw, 72px);
  position: relative; display: grid; grid-template-columns: minmax(0, 1fr); margin-top: 6px;
  view-timeline: --hub block;
}

/* the center node: IcedCoffee itself, a live window in the app's own light mode (hubwin.js), the resume in the middle and the robot's chat
   on the right. It is the page's .win (window frame, bar) wearing the --g-* tokens; as large as the row allows, about half the hub's width,
   the steps' cards (about a quarter each) on either side. Its stage is the crop of the app the window shows (hubwin.js CROP, 1328 x 900). */
.hub-core {
  grid-area: 1 / 1; place-self: center; position: relative; z-index: 2; width: var(--core-w);
  border-color: var(--g-line); box-shadow: 0 1px 0 rgba(255, 255, 255, .06) inset, 0 28px 70px -30px var(--g-shade);
}
.hub .hub-core .win-bar { background: var(--g-bar); border-bottom: 1px solid var(--g-line); }
.hub .hub-core .win-bar span { color: var(--g-ink-3); }
.hub .hub-core .win-stage { aspect-ratio: 1328 / 900; }
.hub .hub-core .win-stage img.still { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: top left; display: block; z-index: 2; }
/* hover: an amber hairline and a slight lift on the page's spring; the layout never moves (the lines are measured from layout boxes) */
@media (hover: hover) {
  .hub .hub-core:hover { border-color: color-mix(in srgb, var(--how-acc) 75%, transparent); box-shadow: 0 1px 0 rgba(255, 255, 255, .06) inset, 0 34px 80px -28px var(--g-shade); }
}
/* (the lift is the `translate` property, not transform: the entrance's animation holds `transform`, so a transform lift would never show) */
.hub .hub-core, .hub .hub-core:hover { transform: none; }
.hub-core { transition: translate 460ms var(--spring), box-shadow 420ms ease, border-color 300ms ease; }
@media (hover: hover) and (prefers-reduced-motion: no-preference) { .hub .hub-core:hover { translate: 0 -5px; } }

/* the nodes: three down each side, 1 to 3 on the left, 4 to 6 on the right */
.hub .how-steps {
  grid-area: 1 / 1; position: relative; z-index: 1; margin: 0; padding: 0; list-style: none;
  display: grid; grid-template-columns: minmax(0, 1fr) var(--core-w) minmax(0, 1fr); column-gap: var(--hub-gap);
  grid-auto-rows: 1fr; row-gap: 28px;
}
.hub .how-steps > li { grid-column: var(--c, 1); grid-row: var(--r, auto); display: flex; min-width: 0; }
.hub .how-step {
  padding: 22px 24px 24px; gap: 6px; border-radius: 20px; justify-content: center; cursor: default;
  border-color: var(--g-line);
  box-shadow: 0 1px 0 rgba(255, 255, 255, .05) inset, 0 24px 60px -28px var(--g-shade);
}
/* On the light ground the step cards recede (Q, Oct 8): the ground darkened about 6% with a soft inset and a slightly darker hairline, so the
   bright center window leads. They are set through the tokens (--g-card, --g-line) so the done, active (amber ring and tint, mixed over
   this darker base) and hover rules above and below keep working unchanged. */
html[data-tone="light"] .hub .how-step { --g-card: rgba(61, 41, 21, .065); --g-line: rgba(61, 41, 21, .24); }
html[data-tone="light"] .hub .how-step.active { background-color: color-mix(in srgb, var(--how-acc) 11%, var(--g-card)); }
html[data-tone="light"] .hub .how-step.active .how-n { color: #7A3A00; }   /* (a deeper amber on the tinted card: 6+ : 1) */
html[data-tone="light"] .hub .how-step:not(.active) { box-shadow: inset 0 2px 7px rgba(61, 41, 21, .1), 0 1px 0 rgba(255, 255, 255, .5); }
.hub .how-step.done { border-color: color-mix(in srgb, var(--how-acc) 42%, transparent); }
.hub .how-step.active { transform: none; border-color: var(--how-acc); }   /* lit nodes glow in place, so a line never drifts off its node */
/* hover: an amber hairline and a slight lift, on the spring; no layout shift (a transform), none at all with Reduce Motion. It only decorates: the cycle never notices */
@media (hover: hover) {
  .hub .how-step:hover { border-color: color-mix(in srgb, var(--how-acc) 75%, transparent); }
  .hub .how-step.active:hover { border-color: var(--how-acc); }
}
@media (hover: hover) and (prefers-reduced-motion: no-preference) { .hub .how-step:hover, .hub .how-step.active:hover { transform: translateY(-4px); } }
.hub .how-text { gap: 6px; }
.hub .how-n { color: var(--g-ink-3); }
.hub .how-step.done .how-n, .hub .how-step.active .how-n { color: var(--g-yellow); }
.hub .how-title { font-size: clamp(17px, 1.35vw, 20px); }
.hub .how-desc { font-size: clamp(15px, 1.1vw, 16px); text-wrap: pretty; }
.hub .how-check { top: 18px; right: 20px; }

/* the lines and pulses sit under the nodes; they end exactly on the nodes' edges */
.hub-lines, .hub-pulses { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; overflow: visible; }
.hub-lines { z-index: 0; }
.hub-pulses { z-index: 1; }
.hub-line {
  fill: none; stroke: color-mix(in srgb, var(--g-ink) 24%, transparent); stroke-width: 1.5; stroke-linecap: butt;
  stroke-dasharray: 1; stroke-dashoffset: 0;   /* pathLength is 1: the draw-in animates the offset from 1 to 0 */
  transition: stroke 500ms ease;
}
.hub-line.done { stroke: color-mix(in srgb, var(--how-acc) 48%, transparent); }
.hub-line.active { stroke: var(--how-acc); stroke-width: 2; }

/* a pulse: a bright head with a short tail, riding its line (offset-path is set by hub.js to the same path as the line) */
.hub-pulse {
  position: absolute; left: 0; top: 0; width: 40px; height: 6px; display: block;
  offset-rotate: auto; offset-anchor: 100% 50%; offset-distance: 0%; opacity: 0;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--g-pulse) 70%, transparent));
  border-radius: 6px 0 0 6px;
  animation: hub-pulse var(--dur, 3.4s) var(--pd, 0s) linear infinite;
}
.hub-pulse::after {
  content: ""; position: absolute; right: -1px; top: 50%; width: 8px; height: 8px; border-radius: 50%; transform: translateY(-50%);
  background: var(--g-pulse); box-shadow: 0 0 10px 3px color-mix(in srgb, var(--g-pulse) 55%, transparent);
}
.hub[data-run="off"] .hub-pulse { animation-play-state: paused; }
@keyframes hub-pulse {
  0%   { offset-distance: 0%;   opacity: 0; }
  6%   { opacity: 1; }
  54%  { offset-distance: 100%; opacity: 1; }
  58%  { offset-distance: 100%; opacity: 0; }
  100% { offset-distance: 100%; opacity: 0; }
}
@media (prefers-reduced-motion: reduce) { .hub-pulses { display: none; } }

/* ---------- narrow: the center at the top, the steps in a column, one spine down the left ---------- */
@media (max-width: 899px) {
  .hub { display: flex; flex-direction: column; gap: 30px; }
  .hub-core { width: 100%; align-self: stretch; }
  .hub .hub-core .win-stage { aspect-ratio: 1044 / 1000; }   /* (the still: img/phone/hub-light.webp, the app at 1100 wide, sidebar cut) */
  .hub .how-steps { display: flex; flex-direction: column; gap: 16px; margin-left: 56px; }
  .hub .how-step { padding: 18px 18px 20px; }
  .hub .how-check { top: 14px; right: 14px; }
  .hub .how-title { font-size: 17px; }
  .hub .how-desc { font-size: 15px; }
}

/* ---------- the entrance ---------- */
@keyframes hub-core  { from { opacity: 0; transform: scale(.72); } to { opacity: 1; transform: none; } }
@keyframes hub-node  { from { opacity: 0; transform: translate(var(--fx, 0px), var(--fy, 0px)) scale(.55); } 45% { opacity: 1; } to { opacity: 1; transform: none; } }
@keyframes hub-draw  { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
@keyframes hub-fade  { from { opacity: 0; } to { opacity: 1; } }

@media (prefers-reduced-motion: no-preference) {
  /* scrolled in: one timeline (the hub's own view), staged by index. Wide: center 4% to 14%, node i from 10% + 3i for 10%, its line
     from 16% + 3i for 9%, pulses fade in 40% to 46%. Everything is done by about the time the hub is a third of the way up. */
  @supports (animation-timeline: view()) {
    .hub-core { animation: hub-core var(--mo-ease) both; animation-timeline: --hub; animation-range: cover 4% cover 14%; }
    .hub .how-steps > li { animation: hub-node var(--mo-ease) both; animation-timeline: --hub; animation-range: cover calc(10% + var(--i, 0) * 3%) cover calc(20% + var(--i, 0) * 3%); }
    .hub-line { animation: hub-draw linear both; animation-timeline: --hub; animation-range: cover calc(16% + var(--i, 0) * 3%) cover calc(25% + var(--i, 0) * 3%); }
    .hub-spine { animation-range: cover 12% cover 60%; }
    .hub-pulses { animation: hub-fade linear both; animation-timeline: --hub; animation-range: cover 40% cover 46%; }
    @media (max-width: 899px) {
      /* narrow: the hub is tall, so each node arrives as the scroll reaches it, 3% + 8i; the spine draws alongside */
      .hub-core { animation-range: cover 0% cover 8%; }
      .hub .how-steps > li { animation-range: cover calc(3% + var(--i, 0) * 8%) cover calc(12% + var(--i, 0) * 8%); }
      .hub-line { animation-range: cover calc(6% + var(--i, 0) * 8%) cover calc(13% + var(--i, 0) * 8%); }
      .hub-spine { animation-range: cover 3% cover 52%; }
      .hub-pulses { animation-range: cover 50% cover 58%; }
    }
  }
  /* no scroll-driven animation (Firefox today): motion.js adds .mo-in the first time the hub enters, and it plays once */
  .mo-once .hub.mo-in .hub-core { animation: hub-core var(--mo-dur) var(--mo-ease) both; }
  .mo-once .hub.mo-in .how-steps > li { animation: hub-node var(--mo-dur) var(--mo-ease) calc(250ms + var(--i, 0) * var(--mo-stagger)) both; }
  .mo-once .hub.mo-in .hub-line { animation: hub-draw calc(var(--mo-dur) * .8) var(--mo-ease-soft) calc(550ms + var(--i, 0) * var(--mo-stagger)) both; }
  .mo-once .hub.mo-in .hub-spine { animation-duration: calc(var(--mo-dur) * 1.5); animation-delay: 300ms; }
  .mo-once .hub.mo-in .hub-pulses { animation: hub-fade 600ms ease 1.9s both; }
}
