/* ==========================================================
   MAIN STREET FITNESS — SERVICE ORBIT
   Static hub-and-spoke selector. NO continuous rotation, NO autoplay,
   no timed state change. Nodes never change position; only the ACTIVE
   selection moves between them (amendment B18).

   Pattern adapted from CodeFronts "Responsive Dashboard Radial Hub"
   (mission-hub, demo 06). CodeFronts demos are MIT licensed and free for
   commercial use. Trigonometric/vector positioning is mathematics and
   carries no restriction. No CodeFronts branding, colour (#7c3aed),
   copy or icon artwork is reused — icons here are a local SVG set.

   Index tokens, per-node unit vectors, active/focus selectors and panel
   reveal selectors are GENERATED into this file by the build from
   src/data/capabilities.json. Never inline style attributes: the deployed
   CSP is style-src 'self' (amendment B3).
   ========================================================== */

.msf-orb {
  position: relative;
  padding-block: clamp(1.25rem, 3.5vw, 2.5rem);

  /* ONE TOKEN DRIVES THE WHOLE CONSTELLATION (amendment B5's intent).
     Container-query units were the first choice, but on this page the
     cqi-sized stage produced a zero-size layout box in Chrome (measured:
     0 client rects, offsetParent null, checkVisibility() false) while the
     same declarations rendered correctly in isolation. An explicit length
     removes the whole class of resolution problem and stays deterministic. */
  /* PURE LENGTH — no percentage. A percentage inside a custom property
     resolves at the USE site, so `width: var(--x)` would resolve against the
     containing block's width while `height: var(--x)` resolved against its
     (auto) height, collapsing the stage to zero. vw has no such ambiguity. */
  --stage-size: min(34rem, calc(100vw - 3rem));
}

/* The query container is the STAGE WRAPPER, not the full-width section.
   Radius must be bounded by the stage's own box or the ring overflows it
   vertically and collides with the header and the panel below. */
.msf-orb__stagewrap {
  width: var(--stage-size);
  margin-inline: auto;
}

.msf-orb__group {
  border: 0;
  margin: 0;
  padding: 0;
  min-inline-size: 0;
}

/* The fieldset's flow content is wrapped in a plain div. All radios live inside
   this wrapper too, so the generated "#cap-N:checked ~ ..." sibling selectors
   remain valid. (This wrapper was first added while chasing the zero-size stage;
   that turned out to be a container-query resolution failure, not a fieldset
   issue — see --stage-size above. The wrapper is kept because it makes the
   sibling-selector scope explicit.) */
.msf-orb__inner { display: block; }

/* ---------- ring stage ---------- */
.msf-orb__stage {
  position: relative;
  width: var(--stage-size);
  height: var(--stage-size);          /* square: the ring stays circular */
  /* S3.3 geometry, all fractions of --stage-size:
       hub d 0.340  (r 0.170)      node d 0.210  (r 0.105)
       ring r 0.380
       hub<->node gap = .380 - .170 - .105 = 0.105  (was 0.050)
       neighbour gap  = 2(.380)sin36 - 2(.105) = 0.237
       outer extent   = .380 + .105 = 0.485 < 0.5, so the ring never
                        escapes the square stage.
       hub / node diameter = 0.340 / 0.210 = 1.62  (S3.3 floor is 1.6) */
  --r: calc(var(--stage-size) * 0.380);
}

/* ---------- central hub ---------- */
.msf-orb__hub {
  position: absolute;
  top: 50%;
  left: 50%;
  translate: -50% -50%;
  width: calc(var(--stage-size) * 0.340);
  aspect-ratio: 1;
  border-radius: 50%;
  overflow: hidden;
  border: 3px solid rgba(226, 232, 238, .92);
  box-shadow:
    0 0 0 6px rgba(24, 26, 30, .95),
    0 0 0 7px rgba(196, 206, 216, .42),
    0 0 90px -16px rgba(200, 16, 46, .5),   /* the ONE glow in the composition */
    var(--elev-3);
  z-index: 3;
  display: grid;
}
.msf-orb__hub > * { grid-area: 1 / 1; }
.msf-orb__hub picture, .msf-orb__hub img {
  display: block; width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 58%;
}
/* S3.1 — the hub holds the photograph and NOTHING else. The veil, the text
   plate, the name and the sub-line are all removed: identity is already stated
   by the header brand, and repeating it inside the hub was the single largest
   source of visual noise in the composition. Nothing here needs a scrim,
   because nothing here sits on top of the photograph. */

/* ---------- radios: visually hidden, still focusable and operable ---------- */
.msf-orb__radio {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; /* optical: standard clip-hide idiom */
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------- nodes ---------- */
.msf-orb__node {
  position: absolute;
  top: 50%;
  left: 50%;
  /* --x/--y are UNIT VECTORS generated per node from capabilities.json
     (amendment B16 fallback). Number x length in calc() — no runtime
     trigonometry, so no engine can discard the declaration as invalid.
     -50% on each axis centres the node on its own midpoint. */
  translate:
    calc(var(--x) * var(--r) - 50%)
    calc(var(--y) * var(--r) - 50%);
  scale: var(--node-scale, 1);
  z-index: 2;

  display: flex;
  align-items: center; justify-content: center;
  width: calc(var(--stage-size) * 0.210);
  min-width: 44px; min-height: 44px;
  aspect-ratio: 1;
  padding: var(--space-2) var(--space-1);
  border-radius: 50%;

  background: var(--msf-surface-raised);
  border: 1px solid rgba(226, 232, 238, .38);
  color: var(--msf-white);
  text-align: center;
  cursor: pointer;
  /* S3.2: nothing is painted inside the disc except the glyph, so the disc no
     longer needs to clip. It must not clip, because the label now sits BELOW
     it — see .msf-orb__nodelabel. */
  overflow: visible;
  isolation: isolate;
  transition: scale var(--motion-base) var(--ease-out),
              border-color var(--motion-fast) var(--ease-out);
}

/* Faint spoke back to the hub. Decorative; --spoke-rot is generated. */
.msf-orb__node::before {
  content: "";
  position: absolute; left: 50%; top: 50%;
  width: var(--r); height: 1px;
  margin-top: 0; /* optical: a 1px bar needs no half-pixel correction */
  transform-origin: 0 50%;
  rotate: var(--spoke-rot);
  border-radius: 2px;
  /* S3.3 — a whisper. One hairline, fading out well before it reaches the
     node, so the eye reads a relationship rather than a drawn line. */
  background: linear-gradient(90deg,
    rgba(196,206,216,.16), rgba(196,206,216,.04) 52%, transparent 82%);
  z-index: -1; pointer-events: none;
}

/* S3.2 — nodes carry a local SVG glyph and a text label on a solid surface.
   The photographic medallion layer (.msf-orb__art) and the scrim that existed
   only to rescue a label from that photograph are both gone. A label on a
   solid surface needs no plate, so the plate is gone too. */

/* status marker — shape carries the meaning, not colour alone */
.msf-orb__mark {
  position: absolute; top: 12%; right: 16%;
  width: .46rem; height: .46rem; border-radius: 50%;
  border: 2px solid currentColor;
}
.msf-orb__mark--now { color: #6FE39B; background: currentColor; }   /* solid */
.msf-orb__mark--approval { color: var(--msf-steel); }                /* hollow */
.msf-orb__mark--confirm { color: var(--msf-steel); border-style: dashed; }

.msf-orb__node { box-shadow: var(--elev-2); }
@media (hover: hover) {
  .msf-orb__node:hover { border-color: rgba(226,232,238,.72); }
}
.msf-orb__node:active { transform: scale(.985); }

.msf-orb__nodeglyph {
  display: flex; align-items: center; justify-content: center; flex: none;
}
.msf-orb__nodeicon { width: 28px; height: 28px; color: var(--msf-steel); flex: none; }

/* THE LABEL SITS BELOW THE DISC, NOT INSIDE IT.
   Measured, at the 14px floor: the widest word on the ring, "Memberships",
   needs 96px. Inside the disc the label box is 90px at 1440 and 66px at 1024,
   so the text could only survive by being clipped, broken mid-word, or set
   below the legibility floor — all three are defects. Moving the label out
   removes the constraint instead of trading something away for it, and it
   leaves the disc as a single clean glyph, which is what S3.2 is after.
   The label is still inside the <label> element, so it remains part of the
   click target and the accessible name. */
.msf-orb__nodelabel {
  position: absolute;
  top: calc(100% + var(--space-2));
  left: 50%;
  translate: -50% 0;
  width: max-content;
  max-width: calc(var(--stage-size) * 0.30);
  font-size: var(--text-xs);
  letter-spacing: .01em;
  line-height: 1.2; font-weight: 700;
  text-align: center;
  color: var(--msf-white);
  text-shadow: 0 1px 3px rgba(9, 9, 10, .9);
  pointer-events: none;
}

/* ACTIVE + FOCUS + PANEL REVEAL selectors are GENERATED (see end of file). */

/* ---------- information panels (pre-rendered, one per capability) ---------- */
.msf-orb__details { margin-top: clamp(1.1rem, 3vw, 1.8rem); }

.msf-orb__panel {
  display: none;                     /* generated :checked rule reveals one */
  background: var(--msf-surface);
  border: 1px solid var(--msf-border);
  border-radius: var(--msf-radius);
  padding: clamp(1.1rem, 3vw, 1.8rem);
  min-height: 15.5rem;               /* fixed floor: content swap shifts nothing */
  flex-direction: column;
  gap: var(--space-3);
}
/* generated rule sets display:flex on the selected panel */

.msf-orb__status {
  display: inline-flex; align-items: center; gap: var(--space-2); align-self: flex-start;
  font-size: var(--text-xs); text-transform: uppercase; letter-spacing: .17em;
  font-weight: 700; padding: var(--space-1) var(--space-3); border-radius: 999px;
  border: 1px solid currentColor;
}
/* Status is carried by TEXT and by the marker SHAPE, never colour alone. */
.msf-orb__status::before {
  content: ""; width: .5rem; height: .5rem; border-radius: 50%;
  border: 2px solid currentColor; flex: none;
}
.msf-orb__status--now { color: #6FE39B; }
.msf-orb__status--now::before { background: currentColor; }       /* solid */
.msf-orb__status--approval { color: var(--msf-steel); }           /* hollow */
.msf-orb__status--confirm { color: var(--msf-steel); border-style: dashed; }
.msf-orb__status--confirm::before { border-style: dashed; }

.msf-orb__title {
  font-family: var(--msf-font-display); font-weight: 800; text-transform: uppercase;
  font-size: var(--text-lg); line-height: 1.1;
}
.msf-orb__desc { color: var(--msf-white); }
.msf-orb__benefit { color: var(--msf-muted); font-size: var(--text-sm); }
.msf-orb__act { margin-top: auto; display: flex; flex-wrap: wrap; gap: var(--space-2); }

/* ---------- controls ---------- */
.msf-orb__controls {
  display: flex; align-items: center; justify-content: center;
  gap: var(--space-2); margin-top: var(--space-4); flex-wrap: wrap;
}
.msf-orb__ctrl {
  min-width: 44px; min-height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; color: var(--msf-white);
  border: 1px solid var(--msf-border); border-radius: var(--msf-radius);
  cursor: pointer;
}
.msf-orb__ctrl:hover { border-color: var(--msf-red); color: var(--msf-red); }
.msf-orb__ctrl:focus-visible { outline: 3px solid var(--msf-white); outline-offset: 3px; }
.msf-orb__ctrl svg { width: 18px; height: 18px; }
.msf-orb__hint {
  font-size: var(--text-xs); color: var(--msf-steel);
  letter-spacing: .09em; text-transform: uppercase;
}

/* ---------- preview dialog ---------- */
.msf-orb__preview {
  border: 1px solid var(--msf-border); background: var(--msf-surface);
  color: var(--msf-white); border-radius: var(--msf-radius);
  padding: clamp(1.2rem, 3vw, 2rem); max-width: 34rem; width: calc(100% - 2rem);
}
.msf-orb__preview::backdrop { background: rgba(9, 9, 10, .82); }
.msf-orb__preview h3 { margin-bottom: var(--space-2); }
.msf-orb__notice {
  margin-top: var(--space-4); padding: var(--space-3) var(--space-4);
  border: 1px dashed var(--msf-red); color: var(--msf-white);
  font-weight: 700; font-size: var(--text-xs); border-radius: var(--msf-radius);
}
.msf-orb__illus { margin-top: var(--space-4); }

/* ---------- MOBILE: leave the ring, become a grid (amendment B6) ---------- */
@media (max-width: 480px) {
  .msf-orb__stage {
    width: 100%; height: auto;  /* square stage is for the ring only */
    display: flex; flex-direction: column; align-items: center; gap: var(--space-3);
  }
  .msf-orb__hub { position: static; translate: none; width: min(46vw, 11.5rem); }
  .msf-orb__nodes {
    display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-2); width: 100%;
  }
  .msf-orb__node {
    position: static;
    translate: none;
    scale: 1 !important;             /* beats the generated id-specificity rule */
    /* S7 — a rectangular card, not a squashed circle. */
    width: 100%; aspect-ratio: auto; border-radius: var(--msf-radius);
    flex-direction: row; justify-content: flex-start; gap: var(--space-3);
    padding: var(--space-3); min-height: 56px; text-align: left;
  }
  .msf-orb__node::before { display: none; }
  .msf-orb__nodelabel {
    position: static; translate: none; width: auto; max-width: none;
    text-align: left; text-shadow: none;
  }
  .msf-orb__nodeicon { width: 22px; height: 22px; }
}

/* On the ring, .msf-orb__nodes is a transparent positioning wrapper. */
.msf-orb__nodes { display: contents; }
@media (max-width: 480px) { .msf-orb__nodes { display: grid; } }

/* ---------- reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  .msf-orb__node, .msf-orb__hub, .msf-orb__panel { transition: none !important; }
  .msf-orb__node { scale: 1 !important; }
}

/* ==========================================================
   GENERATED BLOCK — appended by build_msf.py. Do not hand-edit.
   ========================================================== */

/* GENERATED — visitor-task spinner, 5 destinations. */
.msf-orb__stage { --n: 5; }
.msf-orb__node--0 { --i: 0; --x: 0; --y: -1; --spoke-rot: 90.0deg; }
.msf-orb__node--1 { --i: 1; --x: 0.95105652; --y: -0.30901699; --spoke-rot: 162.0deg; }
.msf-orb__node--2 { --i: 2; --x: 0.58778525; --y: 0.80901699; --spoke-rot: 234.0deg; }
.msf-orb__node--3 { --i: 3; --x: -0.58778525; --y: 0.80901699; --spoke-rot: 306.0deg; }
.msf-orb__node--4 { --i: 4; --x: -0.95105652; --y: -0.30901699; --spoke-rot: 378.0deg; }
#dst-0:checked ~ .msf-orb__stagewrap .msf-orb__node--0,
#dst-1:checked ~ .msf-orb__stagewrap .msf-orb__node--1,
#dst-2:checked ~ .msf-orb__stagewrap .msf-orb__node--2,
#dst-3:checked ~ .msf-orb__stagewrap .msf-orb__node--3,
#dst-4:checked ~ .msf-orb__stagewrap .msf-orb__node--4 {
  border-color: var(--msf-red);
  --node-scale: 1.06;
  z-index: 4;
  box-shadow: 0 0 0 2px var(--msf-red), 0 0 46px -8px rgba(200,16,46,.8);
}
#dst-0:checked ~ .msf-orb__stagewrap .msf-orb__node--0 .msf-orb__nodeglyph,
#dst-1:checked ~ .msf-orb__stagewrap .msf-orb__node--1 .msf-orb__nodeglyph,
#dst-2:checked ~ .msf-orb__stagewrap .msf-orb__node--2 .msf-orb__nodeglyph,
#dst-3:checked ~ .msf-orb__stagewrap .msf-orb__node--3 .msf-orb__nodeglyph,
#dst-4:checked ~ .msf-orb__stagewrap .msf-orb__node--4 .msf-orb__nodeglyph { color: var(--msf-red); }
#dst-0:focus-visible ~ .msf-orb__stagewrap .msf-orb__node--0,
#dst-1:focus-visible ~ .msf-orb__stagewrap .msf-orb__node--1,
#dst-2:focus-visible ~ .msf-orb__stagewrap .msf-orb__node--2,
#dst-3:focus-visible ~ .msf-orb__stagewrap .msf-orb__node--3,
#dst-4:focus-visible ~ .msf-orb__stagewrap .msf-orb__node--4 {
  outline: 3px solid var(--msf-white);
  outline-offset: 4px;
  box-shadow: 0 0 0 7px rgba(9,9,10,.95), 0 0 0 9px var(--msf-white);
}
.msf-cap-line, .msf-act, .msf-count { display: none; }
#dst-0:checked ~ .msf-cap-caption .msf-cap-line--0,
#dst-1:checked ~ .msf-cap-caption .msf-cap-line--1,
#dst-2:checked ~ .msf-cap-caption .msf-cap-line--2,
#dst-3:checked ~ .msf-cap-caption .msf-cap-line--3,
#dst-4:checked ~ .msf-cap-caption .msf-cap-line--4 { display: inline; }
#dst-0:checked ~ .msf-cap-actions .msf-act--0,
#dst-1:checked ~ .msf-cap-actions .msf-act--1,
#dst-2:checked ~ .msf-cap-actions .msf-act--2,
#dst-3:checked ~ .msf-cap-actions .msf-act--3,
#dst-4:checked ~ .msf-cap-actions .msf-act--4 { display: inline-flex; }
#dst-0:checked ~ .msf-home__controls .msf-count--0,
#dst-1:checked ~ .msf-home__controls .msf-count--1,
#dst-2:checked ~ .msf-home__controls .msf-count--2,
#dst-3:checked ~ .msf-home__controls .msf-count--3,
#dst-4:checked ~ .msf-home__controls .msf-count--4 { display: inline; }
