/* instrument.css — the Living Instrument's own presentation.
 *
 * Depends on egk-tokens.css for every colour and duration. No literal colour
 * appears below: a hue in this file would be a hue that cannot be re-themed
 * and cannot be swept by the contrast test.
 *
 * The motion here is the "organic pulse". Two rules govern all of it:
 *
 *   1. AMBIENT MOTION MAY NEVER RESEMBLE A RELATIONSHIP OR A CHANGE. At rest
 *      the drift amplitude is below the threshold at which a body could be
 *      read as having moved somewhere. It says "alive", never "changed".
 *   2. DECIDING STOPS EVERYTHING. When a person is making a judgement the
 *      screen must not be arguing with them out of the corner of their eye.
 */

.egk-instrument {
  display: flex;
  flex-direction: column;
  gap: var(--egk-gap-wide);
  color: var(--egk-ink);
}

/* ---- Reclaiming the elements the estate styles by tag ------------------
 *
 * ⚠️ THE INSTRUMENT IS SEMANTIC HTML LIVING INSIDE A STYLESHEET THAT STYLES
 * BARE TAGS, AND THAT COMBINATION IS SILENT.
 *
 * style.css contains `header { background: var(--navy); color: #fff; }` — the
 * site chrome's banner, written long before anything else on the page was a
 * `<header>`. The instrument's own head IS a `<header>`, correctly, so it
 * inherited the banner treatment: in dark theme `--navy` resolves to a pale
 * mint and the rule paints white text on it. Measured in a clean, extension-
 * free Chrome as rgb(255,255,255) on rgb(195,234,219) — about 1.3:1, i.e. the
 * project's own name rendered as good as blank, with no rule of mine involved
 * and every structural test passing.
 *
 * So every element the instrument creates that style.css also names by tag —
 * header, footer, nav, section, aside, h1-h6, ul, li — takes its surface back
 * explicitly here. `background: none` rather than a colour: the instrument
 * sits on whatever panel it is given and must not paint over it.
 *
 * ⚠️ THIS RESET IS NOT THE GUARANTEE. It fixes the elements we use TODAY. The
 * guarantee is the composed-contrast sweep over every text node inside the
 * shell in tests/js/office-shell-browser.test.mjs, which is what noticing this
 * cost, and which does not care which tag arrives next.
 */
/* ⚠️ `:where()`, NEVER `:is()`. They select identically and differ only in
 * specificity, which is the entire point here: `:is(header)` would score as a
 * class-plus-element and BEAT `.egk-instrument__detail { color: quiet }` lower
 * down this same file — the reset would silently flatten every quiet line to
 * full ink while fixing the banner. `:where()` contributes nothing, so this is
 * a floor that every rule below overrides just by existing. */
.egk-instrument :where(header, footer, nav, section, aside,
                       h1, h2, h3, h4, h5, h6, ul, ol, li, dl, dt, dd, p) {
  background: none;
  color: inherit;
  padding: 0;
  margin: 0;
  border: 0;
  text-transform: none;
  letter-spacing: normal;
}

/* ---- Head ---- */

.egk-instrument__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--egk-gap);
}

.egk-instrument__title {
  margin: 0;
  font-size: 1.15rem;
  color: var(--egk-ink);
}

.egk-instrument__detail,
.egk-instrument__tempo {
  margin: 0;
  color: var(--egk-ink-quiet);
  font-size: 0.85rem;
}

.egk-instrument__tempo { flex-basis: 100%; }

/* The fixture badge is deliberately loud. Someone must never mistake a test
 * subject for their own business, and a quiet badge is how that happens. */
.egk-instrument__badge {
  padding: 2px 10px;
  border-radius: 999px;
  border: 1px solid var(--egk-outcome);
  background: var(--egk-outcome-soft);
  color: var(--egk-ink);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* ---- Stage ---- */

.egk-instrument__stage {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(240px, 300px);
  gap: var(--egk-gap-wide);
  align-items: start;
}

.egk-instrument__field {
  display: block;
  width: 100%;
  height: auto;
  background: var(--egk-field);
  border: 1px solid var(--egk-field-edge);
  border-radius: var(--egk-radius);
}

.egk-body { cursor: pointer; }
.egk-body__label { font-size: 12px; font-weight: 600; }
.egk-body__value { font-size: 11px; }
.egk-school__label { font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; }
.egk-gate__mark { font-size: 12px; font-weight: 700; }

/* ---- Selection ---- */

.egk-body[data-selected] .egk-body__halo { opacity: 1; }

.egk-body:hover .egk-body__halo { opacity: 0.4; }

/* Lineage emphasis: the currents into and out of the selected body come
 * forward, and the rest stay exactly where they were. Nothing is hidden — a
 * relationship that disappears when you look at its neighbour is a lie about
 * the graph.
 *
 * ⚠️ EMPHASIS IS BY WEIGHT AND COLOUR, NEVER BY FADING THE REST. The recede
 * used to be `stroke-opacity: 0.3` on every non-lineage current, and that is
 * unfixable rather than merely wrong: a 0.3 alpha composites toward the field,
 * and on the LIGHT field no stroke colour whatsoever — including pure black —
 * reaches 3:1 at that alpha. The comment above promised nothing was hidden
 * while the rule below hid it. So the promoted currents get the strong ink and
 * real width, and the others are left legible.
 */
.egk-current { transition: stroke var(--egk-settle) var(--egk-settle-ease),
                           stroke-width var(--egk-settle) var(--egk-settle-ease); }

.egk-instrument__field .egk-current[data-lineage] {
  stroke: var(--egk-current-strong);
  stroke-width: 3;
}

/* ---- Ambient motion ----------------------------------------------------
 * Drift is applied to the body group only. Currents do not drift: a moving
 * line reads as flow, and flow would be an assertion about the data.
 *
 * ⚠️ IT IS APPLIED TO `.egk-body__drift`, THE INNER GROUP, AND NEVER TO
 * `.egk-body`, WHICH CARRIES THE POSITION. An SVG `transform` attribute and a
 * CSS `transform` are one property: animating the positioned element REPLACES
 * `translate(x y)` with the keyframe's translate, and the whole field collapses
 * into the top-left corner with nothing thrown and no warning. See the note in
 * instrument-render.js drawBody().
 */

@keyframes egk-drift {
  0%,  100% { transform: translateY(0); }
  50%       { transform: translateY(var(--egk-drift-amplitude)); }
}

.egk-instrument[data-tempo="resting"] .egk-body__drift,
.egk-instrument[data-tempo="exploring"] .egk-body__drift {
  animation: egk-drift var(--egk-drift, var(--egk-drift-a)) ease-in-out infinite;
}

/* Working: the transformation bodies pulse, and nothing else does, so the
 * motion names what is actually running. */
@keyframes egk-working {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.55; }
}

.egk-instrument[data-tempo="working"] .egk-body[data-family="transformation"] .egk-body__ring,
.egk-instrument[data-tempo="working"] .egk-body[data-family="transformation"] .egk-body__disc {
  animation: egk-working 1.4s ease-in-out infinite;
}

/* ⚠️ Deciding stops dead. Not slowed — stopped.
 *
 * `transform: none` appears on the DRIFT group only. On `.egk-body` it would
 * erase the positional transform attribute and stack the field at the origin —
 * the same defect as above, arriving through the stop rather than the start. */
.egk-instrument[data-tempo="deciding"] .egk-body__drift,
.egk-instrument[data-tempo="deciding"] .egk-body__disc,
.egk-instrument[data-tempo="deciding"] .egk-body__ring {
  animation: none;
  transform: none;
}

.egk-instrument[data-tempo="deciding"] .egk-body__value {
  font-weight: 600;
  fill: var(--egk-ink);
}

/* The complete reduced-motion mode. Every animation, not a subset: a person
 * who has asked for no motion has asked for no motion. */
@media (prefers-reduced-motion: reduce) {
  .egk-instrument * {
    animation: none !important;
    transition: none !important;
  }
}

/* ---- Lens ---- */

.egk-instrument__lens {
  padding: var(--egk-gap);
  border: 1px solid var(--egk-field-edge);
  border-radius: var(--egk-radius);
  background: var(--egk-field);
  font-size: 0.85rem;
}

.egk-lens__prompt { margin: 0; color: var(--egk-ink-quiet); }
.egk-lens__title { margin: 0 0 2px; font-size: 0.95rem; color: var(--egk-ink); }
.egk-lens__family { margin: 0 0 var(--egk-gap); color: var(--egk-ink-quiet); font-size: 0.78rem; }
.egk-lens__subtitle { margin: var(--egk-gap) 0 4px; font-size: 0.8rem; color: var(--egk-ink); }

.egk-lens__facts { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 4px var(--egk-gap); margin: 0; }
.egk-lens__facts dt { color: var(--egk-ink-quiet); font-size: 0.75rem; }
.egk-lens__facts dd { margin: 0; color: var(--egk-ink); overflow-wrap: anywhere; }

.egk-lens__gates { margin: 0; padding-left: 1.1em; color: var(--egk-ink); }
.egk-lens__route { display: block; color: var(--egk-candidate); font-style: normal; }
/* The way through a gate, as a control.
 *
 * ⚠️ TOKENS AND `--egk-touch-min`, BOTH LEARNED THE HARD WAY IN ONE SITTING.
 * The first draft of this rule invented `--egk-surface`, which is not a token
 * this estate has: an undefined custom property resolves to nothing and the
 * button would simply have had no background, with every text test still
 * green. And it set a 22px-tall control, which is a bumpy target on the exact
 * screen this whole sweep exists to smooth. Both are copied from
 * `.egk-mirror__button` below rather than chosen, so the two controls in this
 * component cannot drift apart.
 *
 * Coloured as an ordinary action, not a primary one: it is a route to the
 * place the work is done, not the doing of it, and the preview still stands
 * between the two. */
.egk-lens__route-button {
  display: inline-flex; align-items: center;
  margin: 6px 0 2px; padding: 6px 12px;
  min-height: var(--egk-touch-min);
  font: inherit; font-size: 0.8rem; line-height: 1.3;
  color: var(--egk-ink); background: var(--egk-field);
  border: 1px solid var(--egk-candidate);
  border-radius: var(--egk-radius);
  cursor: pointer;
}

.egk-lens__route-button:hover { background: var(--egk-candidate-soft); }

.egk-lens__route-button:focus-visible {
  outline: var(--egk-focus-ring) solid var(--egk-focus);
  outline-offset: 2px;
}

/* ---- Mirror ------------------------------------------------------------
 * Always visible. It is the keyboard and assistive-technology surface, not a
 * hidden alternative — which is what stops it drifting away from the picture.
 */

.egk-instrument__mirror { display: flex; flex-direction: column; gap: 6px; }
.egk-mirror__title { margin: 0; font-size: 0.9rem; color: var(--egk-ink); }
.egk-mirror__note { margin: 0 0 4px; font-size: 0.78rem; color: var(--egk-ink-quiet); }

.egk-mirror__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 6px;
}

.egk-mirror__button {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  width: 100%;
  min-height: var(--egk-touch-min);
  padding: 8px 10px;
  text-align: left;
  border: 1px solid var(--egk-field-edge);
  border-radius: var(--egk-radius);
  background: var(--egk-field);
  color: var(--egk-ink);
  font: inherit;
  cursor: pointer;
}

.egk-mirror__button:hover { border-color: var(--egk-current); }

.egk-mirror__button:focus-visible {
  outline: var(--egk-focus-ring) solid var(--egk-focus);
  outline-offset: 2px;
}

.egk-mirror__button[data-selected] {
  border-color: var(--egk-focus);
  box-shadow: inset 0 0 0 1px var(--egk-focus);
}

/* The swatch repeats the family hue AND the epistemic treatment, so the list
 * carries the same two facts the diagram does. */
.egk-mirror__swatch {
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
  margin-top: 3px;
  border-radius: 50%;
  border: 2px solid currentColor;
  background: currentColor;
}

.egk-mirror__swatch[data-family="evidence"] {
  color: var(--egk-evidence); --egk-swatch-soft: var(--egk-evidence-soft);
}
.egk-mirror__swatch[data-family="transformation"] {
  color: var(--egk-transformation); --egk-swatch-soft: var(--egk-transformation-soft);
}
.egk-mirror__swatch[data-family="outcome"] {
  color: var(--egk-outcome); --egk-swatch-soft: var(--egk-outcome-soft);
}
.egk-mirror__swatch[data-family="candidate"] {
  color: var(--egk-candidate); --egk-swatch-soft: var(--egk-candidate-soft);
}
.egk-mirror__swatch[data-family="constraint"] {
  color: var(--egk-constraint); --egk-swatch-soft: var(--egk-constraint-soft);
}

/* ⚠️ THE MIRROR USED TO CARRY THREE APPEARANCES FOR SEVEN STATES, and the
 * comment above claimed it repeated the diagram's treatment. It did not.
 * `absent`, `refused` and `failed` shared one rule and were pixel-identical —
 * "we never had it", "a rule forbade it" and "it broke" told apart nowhere in
 * this list. `supplied` and `derived` were both a solid disc. The only real
 * distinctions were the half-fill for `partial` and an `opacity: 0.65` for
 * `inferred`. A semantic mirror that cannot say which of seven states a body
 * is in is decoration wearing the name of an instrument.
 *
 * ⚠️ AND EVERY TREATMENT BELOW IS OPAQUE, WHICH IS A RULE AND NOT A STYLE.
 * The `opacity: 0.65` this replaces composited the whole swatch, border
 * included, to 2.77–2.85:1 against the light field for three of the five
 * families — under the 3:1 floor for non-text, so the one class that most
 * needs to look uncertain was the one hardest to see. `epistemicTreatment` in
 * instrument-render.js had already rejected exactly this for the diagram and
 * written down why: a distinctness that lives in compositing cannot be checked
 * without a browser, and a treatment whose meaning cannot be checked drifts.
 * The sibling surface simply did not follow it. Hard-stop gradients below are
 * opaque by construction — every pixel is fully the family colour or fully the
 * field, never a blend.
 *
 * The grammar is the diagram's, so the two surfaces agree:
 *   supplied  solid disc — the base rule, no hedge
 *   derived   soft body, firm edge, inner ring: the trace of arrival
 *   inferred  soft body, DASHED edge: substance we are unsure of
 *   absent    a hole you see the field through, dashed
 *   refused   a hole with a bar: deliberate, not chance
 *   failed    a hole with a cross: attempted, did not complete
 *   partial   half and half: some of it exists
 */
.egk-mirror__swatch[data-epistemic="derived"] {
  background: var(--egk-swatch-soft);
  box-shadow: inset 0 0 0 1.5px currentColor;
}
.egk-mirror__swatch[data-epistemic="inferred"] {
  background: var(--egk-swatch-soft);
  border-style: dashed;
}
.egk-mirror__swatch[data-epistemic="absent"] {
  background: var(--egk-field);
  border-style: dashed;
}
.egk-mirror__swatch[data-epistemic="refused"] {
  background: linear-gradient(to bottom,
    var(--egk-field) 42%, currentColor 42%, currentColor 58%, var(--egk-field) 58%);
}
.egk-mirror__swatch[data-epistemic="failed"] {
  /* Two hard-stop layers over an opaque field. `transparent` appears only at a
   * hard stop, so no pixel is ever a partial blend of the two. */
  background:
    linear-gradient(45deg, transparent 44%, currentColor 44%,
                    currentColor 56%, transparent 56%),
    linear-gradient(-45deg, transparent 44%, currentColor 44%,
                    currentColor 56%, transparent 56%),
    var(--egk-field);
}
.egk-mirror__swatch[data-epistemic="partial"] {
  background: linear-gradient(to bottom, var(--egk-field) 50%, currentColor 50%);
}

.egk-mirror__text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.egk-mirror__label { font-weight: 600; font-size: 0.85rem; }
.egk-mirror__meta { font-size: 0.75rem; color: var(--egk-ink-quiet); }
.egk-mirror__reason { font-size: 0.75rem; color: var(--egk-ink-quiet); }
.egk-mirror__blocked { font-size: 0.75rem; color: var(--egk-constraint); font-weight: 600; }

/* ---- Provenance ---- */

.egk-instrument__provenance {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  font-size: 0.75rem;
  color: var(--egk-ink-quiet);
  border-top: 1px solid var(--egk-field-edge);
  padding-top: 8px;
}

/* ---- Narrow and iPad-sized viewports ---------------------------------- */

@media (max-width: 900px) {
  .egk-instrument__stage { grid-template-columns: minmax(0, 1fr); }
  .egk-instrument__lens { order: 2; }
}


/* Compact project overview. The ordinary instrument above keeps its layout. */
.egk-instrument[data-compact] {
  gap: 12px;
  min-width: 0;
}

.egk-instrument[data-compact] .egk-instrument__stage {
  grid-template-columns: minmax(0, 1fr) minmax(240px, 300px);
  grid-template-areas: "diagram lens" "items lens";
  gap: 12px;
  min-width: 0;
}

.egk-instrument[data-compact] .egk-instrument__diagram {
  grid-area: diagram;
  min-width: 0;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  border-radius: var(--egk-radius);
}

.egk-instrument[data-compact] .egk-instrument__field {
  min-width: 660px;
  box-sizing: border-box;
}

.egk-instrument[data-compact] .egk-instrument__lens {
  grid-area: lens;
  min-width: 0;
}

.egk-instrument[data-compact] .egk-instrument__mirror {
  grid-area: items;
  display: block;
  min-width: 0;
}

.egk-instrument[data-compact] .egk-mirror__summary {
  min-height: var(--egk-touch-min);
  box-sizing: border-box;
  padding: 12px;
  color: var(--egk-ink);
  background: var(--egk-field);
  border: 1px solid var(--egk-field-edge);
  border-radius: var(--egk-radius);
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
}

.egk-instrument[data-compact] .egk-mirror__list {
  grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
}

.egk-instrument[data-compact] .egk-mirror__note {
  margin: 8px 0;
}

.egk-instrument[data-compact] .egk-mirror__text {
  overflow-wrap: anywhere;
}

.egk-instrument__view-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.egk-instrument__view-button {
  min-height: var(--egk-touch-min);
  padding: 8px 16px;
  color: var(--egk-ink);
  background: var(--egk-field);
  border: 1px solid var(--egk-field-edge);
  border-radius: var(--egk-radius);
  font: inherit;
  font-size: 0.85rem;
  cursor: pointer;
}

.egk-instrument__view-button[aria-pressed="true"] {
  border-color: var(--egk-focus);
  box-shadow: inset 0 0 0 1px var(--egk-focus);
  font-weight: 600;
}

.egk-instrument__view-button:focus-visible,
.egk-instrument[data-compact] .egk-mirror__summary:focus-visible,
.egk-instrument[data-compact] .egk-instrument__diagram:focus-visible {
  outline: var(--egk-focus-ring) solid var(--egk-focus);
  outline-offset: 2px;
}

.egk-instrument[data-compact][data-view-mode="list"] .egk-instrument__stage {
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas: "items" "lens";
}

.egk-instrument[data-compact][data-view-mode="list"] .egk-instrument__diagram,
.egk-instrument[data-compact][data-view-mode="list"] .egk-mirror__summary,
.egk-instrument[data-compact][data-view-mode="list"] .egk-instrument__lens[data-empty] {
  display: none;
}

@media (max-width: 1000px) {
  .egk-instrument[data-compact] .egk-instrument__stage {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "diagram" "items" "lens";
  }
}


/* The outer project heading already explains selection. Give its initial map
 * the whole width instead of reserving a column for the same empty hint. */
.egk-instrument[data-compact]:not([data-selection]) .egk-instrument__stage {
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas: "diagram" "items";
}
.egk-instrument[data-compact] .egk-instrument__lens[data-empty] { display: none; }
.egk-instrument[data-compact][data-view-mode="list"]:not([data-selection]) .egk-instrument__stage {
  grid-template-areas: "items";
}

/* Beat the phone shell's section#pane details > summary display rule. */
#pane .egk-instrument[data-compact][data-view-mode="list"] .egk-mirror__summary {
  display: none;
}


.egk-instrument[data-compact][data-view-mode="list"] .egk-instrument__stage {
  grid-template-areas: "items";
}
.egk-instrument[data-compact] .egk-mirror__row > .egk-instrument__lens {
  margin-top: 8px;
}


/* Reading the desktop list should answer beside the chosen row, without
 * reserving an unused inspector column or resizing the diagram above it. */
.egk-instrument[data-compact][data-inline-selection] .egk-instrument__stage {
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas: "diagram" "items";
}
.egk-instrument[data-compact][data-inline-selection][data-view-mode="list"] .egk-instrument__stage {
  grid-template-areas: "items";
}
