/* ===========================================================================
   Section 07's figure: capture the flag.

   Owned by web-ui, loaded after hero-figure.css - AFTER, because the four
   `fig-*` keyframes it animates with are defined there and a keyframe must
   exist for the rule naming it to resolve. Same `--d` / `--len` / `--march`
   conventions; read that file's head comment once rather than three times.

   WHY THIS EXISTS, AND WHOSE IDEA IT IS
     Jeremy's: "07 / Hunt mode - make a cool graphic that fits in. Maybe the
     chameleon with a flag on a pole? like he is playing capture the flag?"

     It is the right metaphor rather than a decoration, which is why it is built
     around the flag instead of beside it. Capture-the-flag is what `hunt` IS: a
     fault is sealed into the estate, you go and find it, and you are graded on
     the find. So the figure has two beats - the field you search, and what
     capturing the flag actually requires.

   THE SECOND BEAT IS THE PAYOFF, NOT THE FLAG
     `#hunt`'s prose spends four bullets on the grading rule and it is still the
     hardest thing on the page to hold in your head. Three axes at 50/30/20 with
     a double 70 threshold is a shape, and a shape is what a graphic is for.

   EVERY NUMBER IS FROM THE CODE, not from the page beside it:
     `_WEIGHTS = {"root_cause": 0.5, "affected": 0.3, "window": 0.2}` and
     `solved = root_cause >= 70 and overall >= 70`, both in `proteus/ai/hunt.py`;
     `DISCLOSE_AT = 70` in `proteus/ai/_hunt_grade.py`. The axis names and their
     one-line meanings are that grader's own prompt text.

   THE CHAMELEON IS RASTER AND CANNOT BE RE-POSED
     `logos/symbol/proteus-color-512` is the only chameleon asset and every one
     is a bitmap, so there is no version of him holding anything. What the mark
     already does is enough: he faces RIGHT with a pixel-dissolve trail behind
     him, so placing him at the left of a rightward hunt reads as motion without
     drawing a line of it. No second chameleon was drawn in another style - the
     mark is the brand, and an approximation of it would be worse than none.

     He is a CSS background on a positioned div rather than an SVG `<image>`,
     so `image-set()` can offer the webp with the png as fallback - the same
     mechanism `site.css` uses for the hero art. An SVG `<image>` would have to
     pick one file: the webp with no fallback, or the 128 KB png for everybody.

     His box holds 512/326 exactly. Distorting the brand mark to fit a layout is
     not a thing to do.
   =========================================================================== */

/* --- The two-column row -------------------------------------------------- */

/* Same shape as `.cf-row` in compose-figure.css: a grid holding the section
   head and the figure, leaving the rest of the section full width below. The
   62ch cap on `.section-head` is a readable measure and is not what changes. */
.ht-row { display: grid; gap: clamp(24px, 3vw, 44px); }

/* 1280px in PIXELS, for the reason compose-figure.css sets out at length: the
   figure's rendered text size is `figureWidthPx / 520 * 13` and has no
   relationship to the root font size, so `rem` would express the constraint in
   a unit it does not depend on. It also misfires - the harness browser resolves
   media-query `rem` against a 12px root, so `80rem` matched at 1100px there. */
@media (min-width: 1280px) {
  .ht-row {
    grid-template-columns: minmax(0, 62ch) minmax(0, 1fr);
    align-items: start;
  }
}

/* Below 700px the labels fall under 11px however the figure is placed, and the
   four cards below already carry the whole mechanic step by step. */
@media (max-width: 700px) {
  .ht-figure { display: none; }
}

/* `position: relative` so the chameleon can be placed in percentages of this
   box - which maps exactly onto viewBox units, because the viewBox is fixed.

   THE CAP LIVES HERE, NOT ON THE `<svg>`, and that is a bug fix rather than a
   preference. With `max-width` on the svg alone, the stage stayed full width
   while the svg capped at 620 - so every percentage on `.ht-cham` resolved
   against the wrong box. Measured at a 1279px viewport: the chameleon rendered
   **154px wide where it should be 81px** and sat 9px right of its mark.

   It was invisible at 1920, where stage and svg are both 523px and the cap does
   not bind, so the figure looked correct at the width I had been checking and was
   broken at every width where it drops to one column. Keeping the cap on the
   stage makes the two boxes identical by construction: the svg is `width: 100%`
   of the stage, so they cannot disagree again.

   The cap itself is for the same reason as `.cf`'s: below 1280px this becomes a
   single full-width column, and at a 1100px viewport that is a 997px box - nearly
   twice the intended scale. 620px is 1.19x, labels at 15.5px. */
.ht-stage {
  position: relative;
  max-width: 620px;
}

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

/* THE CHAMELEON. Percentages of the 520 x 404 viewBox: x 8-76, y 96-140 is
   left: 1.538%, top: 23.762%, width: 13.077%, and the height follows from the
   512/326 aspect via `aspect-ratio` rather than being stated twice and allowed
   to disagree with it. `contain` belts it: even if the box were wrong, the mark
   letterboxes rather than stretching. */
.ht-cham {
  position: absolute;
  left: 1.538%;
  top: 23.762%;
  width: 13.077%;
  aspect-ratio: 512 / 326;
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  background-image: url("brand/logos/symbol/proteus-color-512.png");
  background-image: image-set(
    url("brand/logos/symbol/proteus-color-512.webp") type("image/webp"),
    url("brand/logos/symbol/proteus-color-512.png") type("image/png"));
  animation: fig-rise 0.55s cubic-bezier(0.22, 0.61, 0.36, 1) 0.18s both;
}

/* --- Ink ----------------------------------------------------------------- */

.ht text { font-family: var(--p-mono); }

.ht-eyebrow {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.14em;
  fill: var(--p-accent);
}
.ht-note { font-size: 12px; fill: var(--p-muted); }
.ht-axis { font-size: 13px; fill: var(--p-text); }
.ht-gloss { font-size: 12px; fill: var(--p-muted); }
.ht-weight { font-size: 13px; font-weight: 600; fill: var(--p-accent); text-anchor: end; }

/* The anonymous nodes. A `?` in each, because the point of the exercise is that
   you are not told which one - a labelled estate here would answer the question
   the section is asking.

   `--p-muted` AND NOT `--p-border`, which is what these were first. A node's
   outline IS the node: remove it and four question marks float in empty space
   with nothing saying they are services. That makes it a meaning-bearing graphic
   needing 3:1, and `--p-border` measured 1.27:1 in light and 1.59:1 in dark -
   the same reasoning that kept `--p-border` off the hero figure's node boxes.

   `--p-muted` rather than the hero figure's `--p-accent` because it passes
   (5.09:1 / 6.76:1) AND says the right thing: these are the unknowns, and the
   only accent-coloured object in the field should be the flag. It also matches
   the `?` glyphs, so node and glyph read as one object. */
.ht-node { fill: none; stroke: var(--p-muted); stroke-width: 1.4; }
.ht-unknown { font-size: 15px; fill: var(--p-muted); text-anchor: middle; }

/* THE FLAG. `--p-accent` filled, so it reads as the one solid thing in a field
   of hollow boxes - which is the point: everything else here is unknown.

   THE POLE IS `--p-accent`, THE GROUND IS `--p-border`, and the split is on
   purpose rather than an oversight. The pole holds the flag and is part of the
   metaphor, so it is a meaning-bearing graphic and carries 5.03:1. The ground is
   a baseline that stops the flag floating; remove it and nothing is lost but the
   composition, so it takes the hairline token at 1.35:1 like every `.card`
   border on this page. Stated because an earlier draft of this comment claimed
   both were accent while the rule below set one of them to border - the comment
   was wrong, not the rule. */
.ht-flag { fill: var(--p-accent); stroke: none; }
.ht-pole,
.ht-tip {
  fill: none;
  stroke: var(--p-accent);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.ht-ground {
  fill: none;
  stroke: var(--p-border);
  stroke-width: 1.6;
  stroke-linecap: round;
}

/* The trail. Marching dashes, `5 9` summing to 14 so travelling exactly -14
   lands the pattern where it started and the loop has no seam - the same
   mechanism as the hero figure's four signal lanes, and the same keyframe. */
.ht-trail {
  fill: none;
  stroke: var(--p-accent);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-dasharray: 5 9;
  stroke-dashoffset: 0;
  animation: fig-march 1.25s linear 1s infinite both;
}

/* The weight bars. All one colour: the axes differ in weight, not in kind, and
   colour-coding three bars would invent a distinction the grader does not make.
   The number is printed beside each, so the length is never the only cue. */
.ht-bar { fill: var(--p-accent); }
.ht-bar-track { fill: var(--p-surface); }
/* THE 70 LINE, on its own and in `--p-warning`, because it is the threshold
   rather than a quantity - the same token the degraded node uses in the other
   two figures, for the same "this is the state that matters" job. */
.ht-gate { stroke: var(--p-warning); stroke-width: 1.6; stroke-dasharray: 3 3; }
.ht-gate-label { font-size: 11px; font-weight: 600; letter-spacing: 0.06em; fill: var(--p-warning); }

/* --- Timeline ------------------------------------------------------------ */

.ht-eyebrow,
.ht-note,
.ht-axis,
.ht-gloss,
.ht-weight,
.ht-unknown,
.ht-flag,
.ht-tip,
.ht-gate,
.ht-gate-label {
  animation: fig-fade 0.5s ease-out var(--d, 0s) both;
}

.ht-node,
.ht-bar,
.ht-bar-track {
  animation: fig-rise 0.55s cubic-bezier(0.22, 0.61, 0.36, 1) var(--d, 0s) both;
}

.ht-pole,
.ht-ground {
  stroke-dasharray: var(--len);
  stroke-dashoffset: 0;
  animation: fig-draw 0.5s ease-in-out var(--d, 0s) both;
}

/* --- Reduced motion ------------------------------------------------------ */

/* The base styles ARE the finished figure - flag planted, pole and ground
   drawn, trail dashed, bars at full length - so switching every animation off
   renders a complete static end state with both beats present. Said directly
   rather than relying on site.css's global `0.001ms !important`, which is a
   hammer in a file this one does not own. */
@media (prefers-reduced-motion: reduce) {
  .ht-eyebrow, .ht-note, .ht-axis, .ht-gloss, .ht-weight, .ht-unknown,
  .ht-flag, .ht-tip, .ht-gate, .ht-gate-label, .ht-node, .ht-bar,
  .ht-bar-track, .ht-pole, .ht-ground, .ht-trail, .ht-cham {
    animation: none;
  }
}
