/* ============================================================
   FoXpert Pro — product page
   Uses the tokens from mindfox-redesign.css. No new colours.
   ============================================================ */

/* ── Reveal (matches detail-page.css so the shared JS works) ── */
.mf-reveal {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity 0.65s ease, transform 0.65s ease;
}
.mf-reveal.is-visible { opacity: 1; transform: translateY(0); }
.mf-reveal-delay-1 { transition-delay: 0.12s; }
.mf-reveal-delay-2 { transition-delay: 0.24s; }

/* ── Shared bits ─────────────────────────────────────────── */
.fp-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--orange);
  margin-bottom: 14px;
}
.fp-eyebrow__dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--orange);
  box-shadow: 0 0 0 3px rgba(255, 123, 28, 0.14);
  animation: pulse-dot 2s ease-in-out infinite;
}
/* Section counter — gives the page a spine without extra chrome */
.fp-eyebrow__num {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
  font-size: 0.68rem;
  padding: 3px 8px;
  border-radius: 4px;
  background: var(--orange-glow);
  border: 1px solid rgba(255, 123, 28, 0.22);
  letter-spacing: 0.06em;
}

/* Hairline rule used at the top of cards on hover */
.fp-rule {
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--orange), transparent);
  opacity: 0;
  transition: opacity var(--transition);
  pointer-events: none;
}

/* ══ HERO ══════════════════════════════════════════════════ */
.fp-hero {
  position: relative;
  padding: calc(var(--section-padding) + 80px) 0 var(--section-padding);
  overflow: hidden;
  background: var(--bg-primary);
}
.fp-hero__bg {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 55% 45% at 20% 14%, rgba(255, 123, 28, 0.13) 0%, transparent 66%),
    radial-gradient(ellipse 45% 40% at 86% 82%, rgba(255, 123, 28, 0.06) 0%, transparent 60%);
  pointer-events: none;
}
/* Faint measurement grid — reads as instrumentation, not decoration */
.fp-hero__grid {
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.028) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.028) 1px, transparent 1px);
  background-size: 64px 64px;
  mask-image: radial-gradient(ellipse 70% 60% at 50% 30%, #000 0%, transparent 78%);
  -webkit-mask-image: radial-gradient(ellipse 70% 60% at 50% 30%, #000 0%, transparent 78%);
  pointer-events: none;
}
.fp-hero__inner {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.18fr);
  gap: 54px;
  align-items: center;
}
.fp-hero__content { max-width: 620px; }
.fp-hero__content h1 {
  font-size: clamp(2.2rem, 5vw, 3.5rem);
  font-weight: 900;
  line-height: 1.08;
  letter-spacing: -0.02em;
  margin-bottom: 8px;
}
/* Needs the parent in the selector: `.fp-hero__content p` below is (0,1,1) and
   comes later, so a bare `.fp-hero__sub` (0,1,0) loses and the tagline renders
   in muted body grey instead of brand orange. */
.fp-hero__content .fp-hero__sub {
  font-size: clamp(1rem, 2.2vw, 1.28rem);
  font-weight: 600;
  color: var(--orange);
  margin-bottom: 18px;
}
.fp-hero__content p {
  font-size: clamp(0.97rem, 2vw, 1.08rem);
  color: var(--text-secondary);
  line-height: 1.75;
  margin-bottom: 26px;
}
.fp-hero__actions { display: flex; gap: 14px; flex-wrap: wrap; }

/* ══ THE RIG — a FoXpert Pro session, rebuilt in the page ══
   The film plate is a capture of the app's own viewer canvas, so the
   dashed search region and the four traced indications on it are the
   application's output, not artwork. The interactive layer sits exactly
   on top of them (coordinates measured by colour-scanning the capture),
   and the list below is the app's indication panel.
   ────────────────────────────────────────────────────────── */
.fp-rig {
  position: relative;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--bg-card);
  box-shadow: var(--shadow-md);
}
/* Title bar */
.fp-rig__bar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  background: var(--bg-secondary);
  border-bottom: 1px solid var(--border);
  font-size: 11px;
  color: var(--text-muted);
}
.fp-rig__app {
  font-weight: 800;
  font-size: 12px;
  color: var(--text-primary);
  letter-spacing: -0.01em;
  flex-shrink: 0;
}
.fp-rig__app b { color: var(--orange); font-weight: 800; }
.fp-rig__file {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
  color: var(--text-secondary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  /* min-width:0 is required, not optional. A flex item's floor is its
     min-content width, and `white-space: nowrap` makes that the entire
     filename — which propped the whole panel open to 373 px and clipped the
     title bar below a 360 px viewport. `scrollWidth` does not catch this
     under device emulation, so it has to be checked against real rects. */
  min-width: 0;
}
.fp-rig__badge {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 9px;
  border-radius: 20px;
  border: 1px solid var(--border);
  background: rgba(255, 255, 255, 0.03);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: var(--text-secondary);
  flex-shrink: 0;
}
.fp-rig__badge i {
  width: 6px; height: 6px; border-radius: 50%;
  background: #28c840;
  animation: live-pulse 1.4s ease-in-out infinite;
}

/* Film stage — % geometry, so the overlay tracks the image at any width.
   overflow:hidden so the sweep can run clean off the bottom edge instead of
   sliding over the status bar below. */
.fp-stage {
  position: relative;
  line-height: 0;
  background: #000;
  overflow: hidden;
}
.fp-stage img { width: 100%; height: auto; display: block; }

/* Inspection sweep. Cosmetic, so it is the first thing reduced-motion drops. */
.fp-stage__sweep {
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 13%;
  /* Soft trail with a 1 px leading edge. A thick band with a hard bottom stop
     read as an orange UI bar sitting on the film rather than a pass over it. */
  background: linear-gradient(180deg, transparent, rgba(255, 123, 28, 0.05) 55%, rgba(255, 123, 28, 0.20));
  border-bottom: 1px solid rgba(255, 176, 92, 0.55);
  mix-blend-mode: screen;
  pointer-events: none;
  animation: fp-sweep 7s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
/* translateY percentages resolve against the element's OWN height, not the
   stage. The band is 13% of the stage, so a full pass — top edge from above the
   plate to past its bottom — needs 100/13 = 769.23%, not the 455% it used to
   run, which stopped it around 59% down and looked like it quit halfway. */
@keyframes fp-sweep {
  0%          { transform: translateY(-100%); opacity: 0; }
  8%          { opacity: 1; }
  86%         { transform: translateY(769.23%); opacity: 1; }
  86.01%,100% { transform: translateY(769.23%); opacity: 0; }
}

/* Corner brackets — viewfinder framing on the plate */
.fp-stage__frame {
  position: absolute;
  inset: 10px;
  pointer-events: none;
  background:
    linear-gradient(var(--orange), var(--orange)) left  top    / 14px 1px no-repeat,
    linear-gradient(var(--orange), var(--orange)) left  top    / 1px 14px no-repeat,
    linear-gradient(var(--orange), var(--orange)) right top    / 14px 1px no-repeat,
    linear-gradient(var(--orange), var(--orange)) right top    / 1px 14px no-repeat,
    linear-gradient(var(--orange), var(--orange)) left  bottom / 14px 1px no-repeat,
    linear-gradient(var(--orange), var(--orange)) left  bottom / 1px 14px no-repeat,
    linear-gradient(var(--orange), var(--orange)) right bottom / 14px 1px no-repeat,
    linear-gradient(var(--orange), var(--orange)) right bottom / 1px 14px no-repeat;
  opacity: 0.5;
}

/* ── Traced indication outlines ────────────────────────────
   No boxes. The interactive shape IS the app's own traced outline, drawn
   from the same pixels as the plate underneath, so the highlight follows
   a defect's real contour — two lobes on the lack of fusion, not a rect. */
.fp-stage__marks {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;      /* only the shapes themselves take input */
  z-index: 2;
}
.fp-shape { pointer-events: auto; cursor: pointer; }
.fp-shape:focus { outline: none; }

/* A 30 px non-scaling stroke around the contour, plus the filled interior, is
   the hit area — it follows the outline instead of boxing it. 30 and not 26:
   the smallest porosity outline is 17 px tall, and 17 + 26 = 43 px came in one
   pixel under the 44 px touch minimum. */
.fp-shape__hit {
  fill: transparent;
  stroke: transparent;
  stroke-width: 30;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
  pointer-events: all;
}
/* The film is clean: the outline is drawn here, in the application's own yellow. */
.fp-shape__line {
  fill: rgba(255, 123, 28, 0);
  stroke: #F2C744;
  stroke-width: 1.6;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;   /* same weight at every plate size */
  pointer-events: none;
  transition: stroke var(--transition), stroke-width var(--transition),
              fill var(--transition), filter var(--transition);
}
.fp-shape:hover .fp-shape__line,
.fp-shape:focus-visible .fp-shape__line,
.fp-shape.is-on .fp-shape__line {
  stroke: #fff;
  stroke-width: 2.2;
  fill: rgba(255, 123, 28, 0.30);
  filter: drop-shadow(0 0 5px rgba(255, 123, 28, 0.95));
}

/* ── The detection run (js: "pro indication map") ─────────────
   While the scan crosses the search region, an outline exists only once the
   scan has passed it; each then draws itself and gets its number chip. Without
   JS there is no data-run, so the finished state shows as it always did. */
.fp-rig[data-run="scanning"] .fp-shape:not(.is-found) { pointer-events: none; }
.fp-rig[data-run="scanning"] .fp-shape:not(.is-found) .fp-shape__line { stroke: transparent; }
.fp-rig[data-run="scanning"] .fp-stage__sweep { animation: none; opacity: 1; }
.fp-shape.is-drawing .fp-shape__line {
  stroke-dasharray: 1;
  animation: fp-draw 0.7s ease-out forwards;
}
@keyframes fp-draw {
  from { stroke-dashoffset: 1; }
  to   { stroke-dashoffset: 0; }
}
/* Number chips: the app labels each candidate on the film; here it is a number,
   because the class is the inspector's call, not the detector's. */
.fp-chip {
  position: absolute;
  left: var(--cx);
  top: var(--ty);
  transform: translate(-50%, calc(-100% - 5px));
  min-width: 18px;
  padding: 2px 5px;
  border-radius: 4px;
  background: rgba(10, 10, 12, 0.92);
  color: #F2C744;
  font: 700 10.5px/1.2 var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
  text-align: center;
  pointer-events: none;
  z-index: 2;
  transition: opacity var(--transition), transform var(--transition);
}
.fp-chip[hidden] { display: none; }
.fp-chip.is-new { animation: fp-chip-in 0.35s ease-out; }
@keyframes fp-chip-in {
  from { opacity: 0; transform: translate(-50%, calc(-100% + 2px)); }
  to   { opacity: 1; transform: translate(-50%, calc(-100% - 5px)); }
}
/* The full callout replaces the chip while its indication is active. */
.fp-rig .fp-chip.is-muted { opacity: 0; }
.fp-inds__list li[hidden] { display: none; }
.fp-row.is-new { animation: fp-row-in 0.35s ease-out; }
@keyframes fp-row-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: none; }
}

/* Callout, anchored on the shape's centroid and lifted clear of its top edge */
.fp-callout {
  position: absolute;
  left: var(--cx);
  top: var(--ty);
  transform: translate(-50%, calc(-100% - 12px)) translateY(4px);
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px 11px;
  border-radius: var(--radius-sm);
  background: rgba(10, 10, 12, 0.94);
  border: 1px solid rgba(255, 123, 28, 0.55);
  box-shadow: var(--shadow-md);
  text-align: left;
  line-height: 1.3;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--transition), transform var(--transition), visibility var(--transition);
  pointer-events: none;
  z-index: 3;
}
.fp-callout.is-on {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, calc(-100% - 12px));
}
.fp-callout b {
  font-size: 12px;
  font-weight: 700;
  color: var(--text-primary);
  white-space: nowrap;
}
.fp-callout i {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
  font-size: 10px;
  font-style: normal;
  color: var(--orange);
  letter-spacing: 0.04em;
  white-space: nowrap;
}
/* Status bar — values read off the same session as the plate */
.fp-rig__status {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 18px;
  padding: 10px 14px;
  background: var(--bg-secondary);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
  font-size: 10.5px;
  color: var(--text-muted);
}
.fp-rig__status b { color: var(--orange); font-weight: 700; }

/* Indication panel */
.fp-inds { padding: 14px; }
.fp-inds__head {
  display: flex;
  align-items: center;
  gap: 9px;
  margin-bottom: 10px;
}
.fp-inds__title {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--orange);
}
.fp-inds__count {
  font-size: 10px;
  font-weight: 700;
  padding: 2px 7px;
  border-radius: 20px;
  background: var(--orange-glow);
  border: 1px solid rgba(255, 123, 28, 0.25);
  color: var(--orange);
}
.fp-inds__set {
  margin-left: auto;
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
  font-size: 10px;
  color: var(--text-muted);
  text-align: right;
}
.fp-inds__list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
}
.fp-row {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 9px 11px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: rgba(255, 255, 255, 0.02);
  font-family: var(--font);
  text-align: left;
  cursor: pointer;
  transition: border-color var(--transition), background var(--transition);
}
.fp-row:hover,
.fp-row:focus-visible,
.fp-row.is-on {
  border-color: rgba(255, 123, 28, 0.55);
  background: var(--orange-glow);
}
.fp-row__tag {
  flex-shrink: 0;
  min-width: 30px;
  padding: 2px 5px;
  border-radius: 3px;
  background: var(--orange);
  color: #0A0A0C;
  font-size: 9.5px;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-align: center;
}
.fp-row__name {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;   /* same flex min-content floor as .fp-rig__file above */
}
.fp-row__px {
  margin-left: auto;
  flex-shrink: 0;
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
  font-size: 10.5px;
  color: var(--text-secondary);
}
.fp-inds__note {
  margin: 11px 2px 0;
  font-size: 11px;
  line-height: 1.55;
  color: var(--text-secondary);
}
.fp-inds__note b { color: var(--text-primary); font-weight: 600; }

.fp-rig__hint {
  margin-top: 12px;
  font-size: 12px;
  color: var(--text-secondary);
  text-align: center;
}

/* ══ SECTION SHELL ═════════════════════════════════════════ */
.fp-section { padding: var(--section-padding) 0; }
.fp-section--alt { background: var(--bg-secondary); }
.fp-head {
  max-width: 860px;
  margin: 0 auto 52px;
  text-align: center;
}
.fp-head h2 {
  font-size: clamp(1.75rem, 4vw, 2.7rem);
  font-weight: 800;
  line-height: 1.14;
  letter-spacing: -0.02em;
  margin-bottom: 14px;
  /* Centred two-line headings were dropping a single orphan word ("For", "Own")
     onto the second line. Balancing evens the lines out where supported and is
     inert everywhere else. */
  text-wrap: balance;
}
.fp-head p {
  font-size: clamp(0.95rem, 2vw, 1.05rem);
  color: var(--text-secondary);
  line-height: 1.7;
}

/* ══ SCREENSHOT TABS ═══════════════════════════════════════ */
.fp-tabs__list {
  display: flex;
  gap: 8px;
  margin-bottom: 26px;
  justify-content: center;
  flex-wrap: wrap;
}
.fp-tab {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 18px;
  border-radius: 40px;
  border: 1px solid var(--border);
  background: var(--bg-card);
  color: var(--text-secondary);
  font-size: 13px;
  font-weight: 600;
  font-family: var(--font);
  white-space: nowrap;
  cursor: pointer;
  transition: color var(--transition), background var(--transition), border-color var(--transition);
}
.fp-tab:hover { color: var(--text-primary); border-color: var(--border-hover); }
/* Near-black on orange (7.6:1); white was 2.6:1. */
.fp-tab[aria-selected="true"] {
  background: var(--orange);
  border-color: var(--orange);
  color: var(--bg-primary);
  box-shadow: 0 6px 22px -8px rgba(255, 123, 28, 0.7);
}
.fp-tab svg { width: 15px; height: 15px; flex-shrink: 0; }

.fp-tabs__panels {
  position: relative;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--bg-card);
  box-shadow: var(--shadow-md);
}
.fp-panel { display: none; }
.fp-panel.is-active { display: block; }
.fp-panel img { width: 100%; height: auto; display: block; }
.fp-panel__cap {
  padding: 14px 18px;
  border-top: 1px solid var(--border);
  font-size: 13px;
  color: var(--text-secondary);
  line-height: 1.6;
}
.fp-panel__cap b { color: var(--text-primary); font-weight: 600; }

/* ══ DUPLEX WIRE LADDER ════════════════════════════════════
   Ordered magnitude per wire pair, with a threshold. Two states, so a
   legend is mandatory and the unresolved bars carry a dashed outline as
   well as a duller fill — never colour alone.
   Value text uses --text-secondary, not --text-muted: muted measures
   3.22:1 on --bg-card, under the 4.5:1 needed for text, while secondary
   measures 6.95:1.
   ────────────────────────────────────────────────────────── */
.fp-ladder {
  margin: 0;
  padding: 18px;
  border-top: 1px solid var(--border);
  background: var(--bg-card);
}
.fp-ladder__head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 14px 28px;
  margin-bottom: 18px;
}
.fp-ladder__hero { display: flex; flex-direction: column; gap: 2px; }
.fp-ladder__val {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
  font-size: clamp(1.5rem, 4vw, 2rem);
  font-weight: 800;
  line-height: 1;
  color: var(--orange);
  letter-spacing: -0.01em;
}
.fp-ladder__val i { font-size: 0.5em; font-style: normal; font-weight: 700; }
.fp-ladder__key {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-secondary);
}
/* The SRb figure is real output from a detector that is still being worked on,
   so the caveat rides with the number rather than living only in the caption. */
.fp-ladder__dev {
  margin-top: 5px;
  font-size: 11px;
  font-weight: 600;
  color: var(--orange);
}
.fp-ladder__facts {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 26px;
  margin: 0 0 0 auto;
}
.fp-ladder__facts dt {
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-secondary);
  margin-bottom: 3px;
}
.fp-ladder__facts dd {
  margin: 0;
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
  font-size: 13px;
  font-weight: 700;
  color: var(--text-primary);
}

.fp-ladder__plot { position: relative; }
/* No padding or border on this box. Bar fills size with `height: %`, which
   resolves against the content box, while the threshold's `bottom: %` resolves
   against the padding box — 1px of padding was enough to put the 20% rule and
   the 20% bar at different heights. The baseline rule moved to the axis row. */
.fp-ladder__bars {
  position: relative;
  display: flex;
  align-items: flex-end;
  gap: 6px;                      /* well past the 2px surface gap minimum */
  height: 116px;
}
/* Whole column is the hover target, so the hit area is larger than the mark */
.fp-ladder__bar {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  height: 100%;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
.fp-ladder__fill {
  display: block;
  width: 100%;
  max-width: 26px;
  height: calc(var(--v) * 1%);
  min-height: 2px;               /* a 0% pair still needs a visible baseline mark */
  background: var(--orange);
  border-radius: 4px 4px 0 0;    /* rounded data-end, square on the baseline */
  transition: filter var(--transition);
}
.fp-ladder__bar.is-unresolved .fp-ladder__fill {
  background: rgba(255, 255, 255, 0.14);
  border: 1px dashed rgba(255, 255, 255, 0.45);
  border-bottom: none;
}
.fp-ladder__bar:hover .fp-ladder__fill { filter: brightness(1.25); }

/* Threshold rule at 20%, sitting above the bars it disqualifies */
.fp-ladder__thresh {
  position: absolute;
  left: 0; right: 0;
  bottom: 20%;
  height: 0;
  border-top: 1px dashed rgba(255, 255, 255, 0.3);
  pointer-events: none;
  z-index: 2;
}
.fp-ladder__thresh i {
  position: absolute;
  right: 0;
  bottom: 3px;
  font-style: normal;
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.05em;
  color: var(--text-secondary);
  background: var(--bg-card);
  padding: 0 4px;
}

.fp-ladder__tip {
  position: absolute;
  bottom: calc(100% - 4px);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  white-space: nowrap;
  padding: 5px 9px;
  border-radius: var(--radius-sm);
  background: rgba(10, 10, 12, 0.95);
  border: 1px solid rgba(255, 123, 28, 0.5);
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
  font-size: 10px;
  color: var(--text-primary);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--transition), transform var(--transition), visibility var(--transition);
  pointer-events: none;
  z-index: 4;
}
.fp-ladder__bar:hover .fp-ladder__tip {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(0);
}

.fp-ladder__axis {
  display: flex;
  gap: 6px;
  padding-top: 7px;
  border-top: 1px solid var(--border);   /* the plot's baseline */
}
.fp-ladder__axis span {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
  text-align: center;
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
  font-size: 9.5px;
  color: var(--text-secondary);
}
.fp-ladder__axis b { font-size: 10.5px; font-weight: 700; color: var(--text-primary); }
.fp-ladder__axis .is-unresolved b,
.fp-ladder__axis span.is-unresolved { color: var(--text-secondary); }

.fp-ladder__legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 18px;
  margin-top: 16px;
  padding-top: 13px;
  border-top: 1px solid var(--border);
  font-size: 11px;
  color: var(--text-secondary);
}
.fp-ladder__legend span { display: inline-flex; align-items: center; gap: 7px; }
.fp-ladder__sw {
  width: 11px; height: 11px;
  border-radius: 3px;
  background: var(--orange);
  flex-shrink: 0;
}
.fp-ladder__sw--un {
  background: rgba(255, 255, 255, 0.14);
  border: 1px dashed rgba(255, 255, 255, 0.45);
}
.fp-ladder__sw--rule {
  height: 0;
  border-radius: 0;
  background: none;
  border-top: 1px dashed rgba(255, 255, 255, 0.55);
  width: 15px;
}
.fp-ladder__src {
  margin-left: auto;
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
  font-size: 10px;
  color: var(--text-secondary);
}

/* ══ TOOL GROUPS ═══════════════════════════════════════════ */
.fp-tools {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 20px;
}
.fp-tool {
  position: relative;
  padding: 26px 24px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition: border-color var(--transition), transform var(--transition);
}
.fp-tool:hover { border-color: var(--border-hover); transform: translateY(-3px); }
.fp-tool:hover .fp-rule { opacity: 1; }
.fp-tool__icon {
  width: 42px; height: 42px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-sm);
  background: var(--orange-glow);
  border: 1px solid rgba(255, 123, 28, 0.2);
  color: var(--orange);
  margin-bottom: 16px;
  transition: box-shadow var(--transition);
}
.fp-tool:hover .fp-tool__icon { box-shadow: 0 0 24px -4px rgba(255, 123, 28, 0.45); }
.fp-tool__icon svg { width: 20px; height: 20px; }
.fp-tool h3 { font-size: 16px; font-weight: 700; margin-bottom: 8px; }
.fp-tool > p { font-size: 13.5px; color: var(--text-secondary); line-height: 1.65; margin-bottom: 14px; }
/* `.fp-tool > p` is (0,1,1), so this override has to carry the parent too or it
   loses every declaration it shares with the base rule. */
.fp-tool > p.fp-tool__note {
  margin: 16px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--border);
  font-size: 12px;
  line-height: 1.55;
}
.fp-tool > p.fp-tool__note b { color: var(--orange); font-weight: 700; }
.fp-pills { display: flex; flex-wrap: wrap; gap: 6px; }
.fp-pills li {
  font-size: 11px;
  font-weight: 600;
  padding: 4px 10px;
  border-radius: 20px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border);
  color: var(--text-secondary);
}

/* ══ PLATFORM / HARDWARE ═══════════════════════════════════ */
.fp-plat {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 18px;
}
.fp-plat__card {
  position: relative;
  padding: 22px 20px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
  transition: border-color var(--transition), transform var(--transition);
}
.fp-plat__card:hover { border-color: var(--border-hover); transform: translateY(-3px); }
.fp-plat__card:hover .fp-rule { opacity: 1; }
.fp-plat__num {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
  font-size: 26px;
  font-weight: 800;
  color: var(--orange);
  line-height: 1;
  margin-bottom: 8px;
  font-variant-numeric: tabular-nums;
}
.fp-plat__card h4 { font-size: 13px; font-weight: 700; margin-bottom: 8px; }
.fp-plat__card p { font-size: 12.5px; color: var(--text-secondary); line-height: 1.6; }

/* ══ WORKFLOW STEPS ════════════════════════════════════════ */
.fp-flow {
  position: relative;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(215px, 1fr));
  gap: 18px;
  counter-reset: fp-step;
}
.fp-flow__step {
  position: relative;
  padding: 26px 22px 24px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  transition: border-color var(--transition);
}
.fp-flow__step:hover { border-color: rgba(255, 123, 28, 0.42); }
/* Step number sits on the card's top edge, and the edge itself carries the
   connector — so the row reads as one run without an extra pseudo-element
   that would break when the grid wraps. */
.fp-flow__step::after {
  content: '';
  position: absolute;
  left: 22px; right: 22px; top: 0;
  height: 2px;
  background: linear-gradient(90deg, var(--orange), rgba(255, 123, 28, 0.12));
  border-radius: 2px;
}
.fp-flow__step::before {
  counter-increment: fp-step;
  content: counter(fp-step, decimal-leading-zero);
  display: block;
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
  font-size: 12px;
  font-weight: 800;
  color: var(--orange);
  letter-spacing: 0.08em;
  margin-bottom: 10px;
}
.fp-flow__step h4 { font-size: 15px; font-weight: 700; margin-bottom: 7px; }
.fp-flow__step p { font-size: 13px; color: var(--text-secondary); line-height: 1.65; }

/* ══ CTA ═══════════════════════════════════════════════════ */
.fp-cta { position: relative; padding: var(--section-padding) 0; overflow: hidden; }
.fp-cta__bg {
  position: absolute; inset: 0;
  background: radial-gradient(ellipse 60% 70% at 50% 50%, rgba(255, 123, 28, 0.1) 0%, transparent 70%);
  pointer-events: none;
}
.fp-cta__inner { position: relative; text-align: center; max-width: 720px; margin: 0 auto; }
.fp-cta__inner h2 {
  font-size: clamp(1.7rem, 4vw, 2.6rem);
  font-weight: 800;
  line-height: 1.15;
  margin-bottom: 16px;
  text-wrap: balance;
}
.fp-cta__inner p { color: var(--text-secondary); font-size: 1.02rem; line-height: 1.7; margin-bottom: 30px; }
.fp-cta__actions { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; }

/* ══ RESPONSIVE ════════════════════════════════════════════ */
/* Both grids hold exactly 6 cards. Left on auto-fit they resolve to 4+2 and 5+1
   at desktop widths, which leaves an orphaned row; pin them to 3 columns so they
   land as a clean 3 × 2. */
@media (min-width: 1101px) {
  .fp-tools { grid-template-columns: repeat(3, 1fr); }
  .fp-plat  { grid-template-columns: repeat(3, 1fr); }
}

/* minmax(0, …) rather than a bare `1fr` throughout: a `1fr` track's automatic
   minimum is min-content, so the widest thing inside the rig was propping the
   single-column hero open to 373 px and clipping it below a 390 px viewport.
   The desktop rule above already used minmax for this reason. */
@media (max-width: 1100px) {
  .fp-hero__inner { grid-template-columns: minmax(0, 1fr); gap: 42px; }
  .fp-hero__content { max-width: 100%; }
  /* 6 cards → 2 × 3 in the mid range, still no orphan */
  .fp-tools { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .fp-plat  { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 700px) {
  .fp-tools, .fp-plat { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 768px) {
  .fp-hero { padding: calc(var(--section-padding) + 58px) 0 var(--section-padding); }

  /* Keep the tab labels. Icon-only pills fail WCAG name-role-value unless every
     button also carries an aria-label, and a scrolling rail is the friendlier
     fallback anyway. */
  .fp-tabs__list {
    flex-wrap: nowrap;
    justify-content: flex-start;
    overflow-x: auto;
    padding-bottom: 6px;
    scrollbar-width: thin;
    -webkit-overflow-scrolling: touch;
  }
  .fp-tab { padding: 9px 14px; font-size: 12px; }

  .fp-tool { padding: 22px 18px; }

  /* Ladder: 10 columns in ~330 px leaves ~27 px each. Tighten the gaps and drop
     the inline threshold caption, which would collide with the D12 label. */
  .fp-ladder { padding: 14px 12px; }
  .fp-ladder__bars { height: 92px; gap: 3px; }
  .fp-ladder__axis { gap: 3px; }
  .fp-ladder__axis span { font-size: 8.5px; }
  .fp-ladder__axis b { font-size: 9.5px; }
  .fp-ladder__facts { margin-left: 0; gap: 10px 18px; }
  .fp-ladder__thresh i { display: none; }
  .fp-ladder__src { margin-left: 0; width: 100%; }
  /* Touch has no hover, so these can never open here — and a 150 px nowrap
     callout centred on a 27 px column hangs off the viewport. Every value is
     already printed on the axis, so drop them rather than reposition them. */
  .fp-ladder__tip { display: none; }

  .fp-rig__status { font-size: 9.5px; gap: 3px 12px; }
  .fp-inds__list { grid-template-columns: 1fr; }
  /* Reflow rather than hide — the detection settings are part of the readout,
     so they drop to their own row instead of disappearing on small screens. */
  .fp-inds__head { flex-wrap: wrap; }
  .fp-inds__set { order: 3; width: 100%; margin: 2px 0 0; text-align: left; }
  .fp-hero__actions .mf-btn { width: 100%; justify-content: center; }
  /* The shallowest anchor sits 31.5% down the plate — about 57 px at a 320 px
     viewport. Shrink the callout so it still clears the plate's top edge from
     there instead of flipping it under the outline it points at. */
  .fp-callout { padding: 6px 9px; transform: translate(-50%, calc(-100% - 8px)) translateY(4px); }
  .fp-callout.is-on { transform: translate(-50%, calc(-100% - 8px)); }
  .fp-callout b { font-size: 11px; }
  .fp-callout i { font-size: 9.5px; }
}

@media (max-width: 480px) {
  .fp-plat__num { font-size: 22px; }
  .fp-rig__bar { padding: 8px 11px; font-size: 10px; }
  .fp-rig__app { font-size: 11px; }
  .fp-stage__frame { inset: 6px; }
}

@media (prefers-reduced-motion: reduce) {
  .fp-eyebrow__dot,
  .fp-rig__badge i { animation: none; }
  /* Full selector repeated on purpose: the sweep animation is declared at
     `.fp-stage__sweep` (0,1,0) but a bare class here would tie on specificity
     and rely on order alone — this keeps the override unambiguous. */
  .fp-stage .fp-stage__sweep { animation: none; display: none; }
  .fp-shape.is-drawing .fp-shape__line,
  .fp-chip.is-new,
  .fp-row.is-new { animation: none; }
  .fp-tool:hover,
  .fp-plat__card:hover { transform: none; }
  .mf-reveal { transition: none; }
}
