/*  plate.css — bespoke SVG plate.

    The plate is sized to fit its container; the SVG keeps its aspect ratio
    so the carefully placed coordinates stay consistent. All type sizing is
    declared here in user-space units (which match viewBox units).           */

/*  Wrap is a simple block matching the article#stage box. Flex centering
 *  + padding were dropped so the .plate-svg and the painted-mode frame
 *  layer share exactly the same bounding box (.plate-wrap's content box
 *  = SVG element box = frame box). With matching boxes, the painted
 *  cartouche aligns precisely with the colored area on every side.       */
.plate-wrap {
  position: relative;
  width: 100%;
  height: 100%;
}
.plate-svg {
  display: block;
  width: 100%;
  height: 100%;
  /*  Clip atmosphere washes etc. to the SVG element's box. The painted
   *  background uses huge userSpaceOnUse rects so the gradients cover
   *  the SVG even after pan/zoom; without explicit overflow:hidden some
   *  engines render those rects past the element box, bleeding past the
   *  painted-mode frame.                                                  */
  overflow: hidden;
  cursor: grab;
  touch-action: none;     /* prevent browser pan/zoom hijacking on trackpads */
  user-select: none;
}
.plate-svg.panning { cursor: grabbing; }
.plate-svg .zoom-pan { transition: none; }
.plate-svg .class-node,
.plate-svg .edge { cursor: pointer; }
.plate-svg.panning .class-node,
.plate-svg.panning .edge { cursor: grabbing; }

/*  Schema overlay (depth=1.0 band) fades over [0.95, 1.00]. By t=1
 *  only the painted fresco remains — labels, edges, sigils all gone.
 *  --schema-overlay-opacity is set on :root by applyPoeticismLight()
 *  in index.html (1 below t=0.95, ramps to 0 by t=1.00).              */
.plate-svg .parallax-band[data-depth="1.00"] {
  opacity: var(--schema-overlay-opacity, 1);
  transition: opacity 180ms ease-out;
}
@media (prefers-reduced-motion: reduce) {
  .plate-svg .parallax-band[data-depth="1.00"] { transition: none; }
}

/* Title block removed from the SVG — the HTML masthead shows the title.
 * The .plate-title and .plate-subtitle rules are gone with it.            */

/* — Section labels (italic Roman numeral + small-caps title). ——————— */
.plate-svg .section-label {
  font-family: var(--serif);
  font-size: 12px;
  fill: var(--ink-3);
  letter-spacing: 0.05em;
}
.plate-svg .section-roman {
  font-style: italic;
  fill: var(--ink-quiet);
}
.plate-svg .section-title {
  font-variant: small-caps;
  letter-spacing: 0.13em;
  font-size: 12px;
  fill: var(--ink-3);
}

/* — Commitment instances (italic marginalia under Commitment node) ———— */
.plate-svg .instance-caption {
  font-family: var(--serif);
  font-style: italic;
  font-variant: small-caps;
  letter-spacing: 0.1em;
  font-size: 10px;
  fill: var(--ink-quiet);
}
.plate-svg .instance-name {
  font-family: var(--serif);
  font-style: italic;
  font-size: 11.5px;
  fill: var(--ink-2);
  letter-spacing: 0.01em;
}

/* — Footnote mark on unencodable nodes ————————————————— */
.plate-svg .class-node .footnote-mark {
  fill: var(--accent);
  font-style: normal;
  letter-spacing: 0;
}
/* The mark sits to the right of the label; fill it with the accent so
   it reads as an editorial gloss, not as part of the name itself. */

.plate-svg .footnote-key-mark {
  font-family: var(--serif);
  font-size: 14px;
  fill: var(--accent);
}
.plate-svg .footnote-key-text {
  font-family: var(--serif);
  font-size: 11px;
  font-style: italic;
  fill: var(--ink-3);
  letter-spacing: 0.02em;
}

/* — Class nodes ——————————————————————————————————— */
.plate-svg .class-node {
  cursor: pointer;
  transition: opacity 180ms ease;
}
.plate-svg .class-node .frame {
  fill: var(--paper);
  stroke: var(--ink-2);
  stroke-width: 1;
  rx: 1;
  ry: 1;
  /*  Fade in lockstep with the structural morph: invisible at t=0
   *  (where the embodiment tile + type-cluster carry the concept) and
   *  fully visible at t=0.5 (the bare plate). Use fill-opacity +
   *  stroke-opacity directly (rather than the shorthand `opacity`) so
   *  the faded-genus rule below can multiply its 0.55 against the
   *  same --morph-t without clobbering the morph.                       */
  fill-opacity: var(--morph-t, 1);
  stroke-opacity: var(--morph-t, 1);
  transition: fill-opacity 220ms ease-out, stroke-opacity 220ms ease-out;
}
@media (prefers-reduced-motion: reduce) {
  .plate-svg .class-node .frame { transition: none; }
}
.plate-svg .class-node .class-label {
  font-family: var(--serif);
  font-size: 13px;
  fill: var(--ink);
  pointer-events: none;
  /*  Smooth the painted-mode handoff: fill colour, italic toggle, and
   *  the translateY shift below the sigil all transition rather than
   *  jumping when the .painted class flips at the slider midpoint.     */
  transition: fill 220ms ease-out, transform 220ms ease-out;
}

@media (prefers-reduced-motion: reduce) {
  .plate-svg .class-node .class-label { transition: none; }
}

/*  Embodiment tile: at the data-driven end (t=0) each class is
 *  anchored to the Rust struct that backs it. The tile holds the
 *  curie, file path, struct stub, register tag, and a small status
 *  dot — every fact about the class's code embodiment. The whole
 *  group fades out as the slider approaches the bare plate at t=0.5
 *  so the curated figure reads uncluttered. The schema rests on its
 *  embodiment on the left half and lifts off into pure schema on the
 *  right.                                                              */
.plate-svg .class-node .embodiment-tile {
  pointer-events: none;
  opacity: calc(1 - var(--morph-t, 1));
  transition: opacity 220ms ease-out;
}
.plate-svg .class-node .class-curie {
  font-family: var(--mono);
  font-size: 8.5px;
  fill: var(--ink-3);
  letter-spacing: 0.02em;
}
.plate-svg .class-node .class-codepath {
  font-family: var(--mono);
  font-size: 7.5px;
  fill: var(--ink-quiet);
  letter-spacing: 0.01em;
}
.plate-svg .class-node .class-codestub {
  font-family: var(--mono);
  font-size: 7.5px;
  fill: var(--ink-3);
  letter-spacing: 0.01em;
}
.plate-svg .class-node .class-unembodied {
  font-family: var(--serif);
  font-size: 9px;
  font-style: italic;
  fill: var(--ink-quiet);
  letter-spacing: 0.04em;
}
.plate-svg .class-node .class-status-dot {
  fill: var(--ink-quiet);
}
/*  Constellation dot — one per code symbol in the embodiment tile.
 *  Fill colour is set inline from a per-crate hue so each crate gets
 *  a stable family. The primary anchor reads with a thin ink halo so
 *  it's pickable in a packed strip.                                  */
.plate-svg .class-node .embodiment-dot {
  pointer-events: auto;
  cursor: help;
}
.plate-svg .class-node .embodiment-dot.primary {
  stroke: var(--ink-2);
  stroke-width: 0.4;
}

/*  ─── Embodied type graph (low-poeticism) ─────────────────────────
 *  At low t, every ontology concept gets a satellite cluster of type-
 *  graph nodes (struct, enum, trait, extern). Cross-cluster edges
 *  span the plate. The whole layer fades with --morph-t — gone by
 *  t=0.5 (the bare plate). The painted register at t≥0.5 is on its
 *  own variable, --painted-opacity, so the painted layers can come
 *  in over a clean schema.                                            */
.plate-svg .type-graph {
  pointer-events: auto;
  opacity: calc(1 - var(--morph-t, 1));
  transition: opacity 220ms ease-out;
}
@media (prefers-reduced-motion: reduce) {
  .plate-svg .type-graph { transition: none; }
}
/*  Hide entirely once nearly faded so glyphs don't catch hover events
 *  through the bare plate at t > 0.4.                                 */
.plate-svg .type-graph[style*="opacity: 0"],
.plate-svg .type-graph[style*="opacity:0"] {
  pointer-events: none;
}

/*  Type-node glyphs. Sized by JS (rect/polygon/circle dims), styled
 *  here. Default fill is paper, stroke is ink-3 — quiet so hundreds
 *  of glyphs don't compete with the schema overlay.                  */
.plate-svg .type-node .type-glyph {
  fill: var(--paper);
  stroke: var(--ink-3);
  stroke-width: 0.5;
  vector-effect: non-scaling-stroke;
}
.plate-svg .type-node.tn-struct .type-glyph {
  fill: var(--paper-warm);
}
.plate-svg .type-node.tn-enum .type-glyph,
.plate-svg .type-node.tn-variant .type-glyph {
  fill: var(--paper-warm);
  stroke: var(--ink-3);
}
.plate-svg .type-node.tn-trait .type-glyph {
  fill: var(--paper);
  stroke: var(--ink-2);
}
.plate-svg .type-node.tn-extern .type-glyph,
.plate-svg .type-node .type-glyph.extern {
  fill: none;
  stroke: var(--ink-quiet);
  stroke-dasharray: 1.2 1.2;
}
.plate-svg .type-node.tn-impl .type-glyph {
  fill: var(--paper);
  stroke: var(--ink-3);
}
.plate-svg .type-node.tn-alias .type-glyph {
  fill: var(--paper);
  stroke: var(--ink-3);
  stroke-dasharray: 0.8 0.8;
}

/*  Primary anchor of a concept's cluster — the struct/enum the
 *  ontology concept "is" — gets a slightly darker stroke so it can
 *  be picked out within its cluster, but not so much that it
 *  overrides the schema's prominence.                                  */
.plate-svg .type-node.primary .type-glyph {
  stroke: var(--ink-2);
  stroke-width: 0.6;
}

/*  Hover: glyph lifts. Cursor signals that titles/data exist.        */
.plate-svg .type-node {
  cursor: help;
  transition: transform 100ms ease-out;
}
.plate-svg .type-node:hover .type-glyph {
  stroke: var(--accent);
  stroke-width: 1.0;
}

/*  Tiny always-visible labels. Faint at rest so 200 of them coexist;
 *  on hover the parent .type-node:hover lifts label opacity so you
 *  can read it without the tooltip.                                   */
.plate-svg .type-label {
  font-family: var(--mono);
  font-size: 4.5px;
  fill: var(--ink-3);
  fill-opacity: 0.75;
  pointer-events: none;
  letter-spacing: 0.01em;
}
.plate-svg .type-label.extern {
  fill: var(--ink-quiet);
  fill-opacity: 0.55;
  font-style: italic;
}
.plate-svg .type-label.primary {
  font-size: 5px;
  fill: var(--ink);
  fill-opacity: 0.6;
  font-weight: 500;
}
.plate-svg .type-node:hover .type-label {
  fill: var(--accent);
  fill-opacity: 1;
  font-size: 6px;
}

/*  Type-graph edges. Stroke is faint by default so the edge thicket
 *  doesn't dominate; the schema overlay reads on top.                 */
.plate-svg .type-edge .type-edge-path {
  fill: none;
  stroke: var(--ink-quiet);
  stroke-width: 0.35;
  stroke-linecap: round;
}
.plate-svg .type-edge.te-field .type-edge-path {
  stroke: var(--ink-3);
  stroke-width: 0.4;
}
.plate-svg .type-edge.te-variant .type-edge-path {
  stroke: var(--ink-3);
  stroke-dasharray: 0 0;
  stroke-width: 0.45;
}
.plate-svg .type-edge.te-vtype .type-edge-path {
  stroke: var(--ink-3);
  stroke-width: 0.35;
}
.plate-svg .type-edge.te-impl .type-edge-path {
  stroke: var(--ink-quiet);
  stroke-dasharray: 1.5 1.5;
  stroke-width: 0.4;
}
.plate-svg .type-edge.te-implfor .type-edge-path {
  stroke: var(--ink-3);
  stroke-width: 0.5;
}
.plate-svg .type-edge.te-alias .type-edge-path {
  stroke: var(--ink-quiet);
  stroke-dasharray: 0.8 1.2;
  stroke-width: 0.35;
}

/*  Cluster wrapper — translation set per frame by JS. The wrapper
 *  itself doesn't need a transition (the position morph is user-
 *  driven via the slider, not animated).                             */
.plate-svg .type-cluster { pointer-events: auto; }
.plate-svg .class-node .class-reg-tag {
  font-family: var(--mono);
  font-size: 7.5px;
  letter-spacing: 0.06em;
  fill: var(--ink-quiet);
  font-style: italic;
}
.plate-svg .class-node .class-reg-tag-structural { fill: var(--reg-structural); }
.plate-svg .class-node .class-reg-tag-decision   { fill: var(--reg-decision); }
.plate-svg .class-node .class-reg-tag-lifecycle  { fill: var(--reg-lifecycle); }
.plate-svg .class-node .class-reg-tag-actor      { fill: var(--reg-actor); }

/*  Plate-specific decorations (section labels, marginalia, lifecycle
 *  hairlines, footnote-key) sit at fixed plate positions; when the
 *  slider is at the data-driven end the auto-laid nodes don't share
 *  those positions and the decorations float disconnected. Fade them
 *  IN with --morph-t so they only appear once the bare plate emerges,
 *  AND OUT with --schema-graph-opacity so they retire alongside edges
 *  + class labels when the poeticized phase begins.                     */
.plate-svg .sections,
.plate-svg .commitment-instances,
.plate-svg .lifecycle-rule,
.plate-svg .footnote-key {
  opacity: calc(var(--morph-t, 1) * var(--schema-graph-opacity, 1));
  transition: opacity 220ms ease-out;
}

/*  Schema-graph fade — edges, self-loops, class labels, footnote marks,
 *  and frame rects on label-only nodes all retire over [0.40, 0.50] as
 *  the poeticized phase begins, leaving only sigils visible. Sigils
 *  ride their own --sigil-opacity envelope and stay through poeticized.  */
.plate-svg .edges,
.plate-svg .self-loops,
.plate-svg .class-node > .class-label,
.plate-svg .class-node > .footnote-mark,
.plate-svg .class-node:not(.has-painted-sigil):not(.faded) > .frame {
  opacity: var(--schema-graph-opacity, 1);
  transition: opacity 220ms ease-out;
}

@media (prefers-reduced-motion: reduce) {
  .plate-svg .class-node .embodiment-tile,
  .plate-svg .sections,
  .plate-svg .commitment-instances,
  .plate-svg .lifecycle-rule,
  .plate-svg .footnote-key,
  .plate-svg .edges,
  .plate-svg .self-loops,
  .plate-svg .class-node > .class-label,
  .plate-svg .class-node > .footnote-mark,
  .plate-svg .class-node > .frame { transition: none; }
}
/*  Self-relation arcs sit above each looping class as proper curved
 *  edges with a label — more prominent than the old inline italic gloss.
 *  Same thin/light treatment as subclass arrows so the eye reads them
 *  as structural-loop relations rather than primary narrative arrows. */
.plate-svg .self-loop-path {
  fill: none;
  stroke: var(--ink-quiet);
  stroke-width: 0.7;
  stroke-linecap: round;
  pointer-events: none;
}
.plate-svg .self-loop-label {
  font-family: var(--serif);
  font-style: italic;
  font-size: 11px;
  fill: var(--ink-3);
  pointer-events: none;
}

/*  Register-tinted borders.                                                */
.plate-svg .class-node[data-uri$="#Affordance"] .frame,
.plate-svg .class-node[data-uri$="#Techne"] .frame,
.plate-svg .class-node[data-uri$="#Signifier"]  .frame,
.plate-svg .class-node[data-uri$="#Bundle"]     .frame { stroke: var(--reg-structural); }

.plate-svg .class-node[data-uri$="#Decision"]     .frame,
.plate-svg .class-node[data-uri$="#Commitment"]   .frame,
.plate-svg .class-node[data-uri$="#Design"]       .frame,
.plate-svg .class-node[data-uri$="#Architecture"] .frame { stroke: var(--reg-decision); }

.plate-svg .class-node[data-uri$="#Seed"]           .frame,
.plate-svg .class-node[data-uri$="#Intent"]         .frame,
.plate-svg .class-node[data-uri$="#Session"]        .frame,
.plate-svg .class-node[data-uri$="#Artifact"]       .frame,
.plate-svg .class-node[data-uri$="#SoftwareModule"] .frame,
.plate-svg .class-node[data-uri$="#LiterateSpec"]   .frame { stroke: var(--reg-lifecycle); }

.plate-svg .class-node[data-uri$="#Actor"]   .frame,
.plate-svg .class-node[data-uri$="#Human"]   .frame,
.plate-svg .class-node[data-uri$="#AIAgent"] .frame,
.plate-svg .class-node[data-uri$="#Surface"] .frame { stroke: var(--reg-actor); }

/*  Faded genus nodes (Decision, Actor) get lighter borders + lighter type. */
.plate-svg .class-node.faded .frame {
  stroke-dasharray: 3 2;
  /*  Faded genus nodes (Decision, Actor genus) keep their lighter
   *  stroke at t≥0.5 (0.55) but still fade out at t=0 (multiplied by
   *  --morph-t). Without the multiplication the .faded rule would
   *  override the morph and the outline would stay visible at t=0.    */
  stroke-opacity: calc(0.55 * var(--morph-t, 1));
}

/*  Unencodable concepts (Memory, Decision genus, Actor genus, etc. —
 *  marked with ° on the bare plate) are scaffolding for the schema's
 *  taxonomy but aren't directly realised in code. At low t the page
 *  is focused on what HAS been encoded; fade these whole nodes with
 *  --morph-t so they only appear once the bare plate emerges.        */
.plate-svg .class-node.unencodable {
  opacity: var(--morph-t, 1);
  transition: opacity 220ms ease-out;
}
@media (prefers-reduced-motion: reduce) {
  .plate-svg .class-node.unencodable { transition: none; }
}
.plate-svg .class-node.faded .class-label {
  fill: var(--ink-3);
  font-style: italic;
}

/*  Keystone (Affordance) — heavier rule, larger type, double-rule effect.  */
.plate-svg .class-node.keystone .frame {
  stroke-width: 1.6;
}
.plate-svg .class-node.keystone .keystone-outer {
  stroke: var(--ink-2);
  stroke-width: 0.5;
  rx: 1; ry: 1;
}
.plate-svg .class-node.keystone .class-label {
  font-size: 19px;
  font-style: italic;
  letter-spacing: 0.01em;
}

/*  Hover/select interactions — single accent.                              */
.plate-svg .class-node.hl .frame,
.plate-svg .class-node.selected .frame {
  stroke: var(--accent);
  stroke-width: 1.6;
}
.plate-svg .class-node.selected .frame {
  fill: var(--paper-warm);
}
.plate-svg .class-node.dim {
  opacity: 0.35;
}

/* — Edges ———————————————————————————————————————— */
.plate-svg .edge .edge-path {
  fill: none;
  stroke: var(--ink-2);
  stroke-width: 1;
  transition: stroke 160ms;
}
.plate-svg .edge .edge-label {
  font-family: var(--serif);
  font-style: italic;
  font-size: 11px;
  fill: var(--ink-2);
  pointer-events: none;
}
/*  All "is-a" arrows use the same thin/light treatment, matching the
 *  long faint subclass curves (Observation→Artifact, Module→Artifact).
 *  Decision-stratum subclass arrows used to be heavier and read as a
 *  different convention; they are now consistent with artifact ones.   */
.plate-svg .edge.subclass .edge-path {
  stroke: var(--ink-quiet);
  stroke-width: 0.7;
  stroke-dasharray: 1.4 3;
}
.plate-svg .edge.primary .edge-path {
  stroke: var(--ink-2);
  stroke-width: 1;
  stroke-dasharray: 4 2.5;
}
/*  Faint cross-cut edges are always visible with their labels — the old
 *  reveal-on-hover policy was removed; the plate now shows every relation
 *  at rest. Solid stroke (subclass-style dashes are reserved for "is-a"
 *  relations); medium-light weight so faint edges read as quiet cross-
 *  cuts rather than primary narrative threads.                           */
.plate-svg .edge.faint .edge-path {
  stroke: var(--ink-3);
  stroke-width: 0.75;
}
.plate-svg .edge.faint .edge-label {
  fill: var(--ink-3);
  font-size: 10.5px;
}

/*  Hover state — light up the edge in accent. */
.plate-svg .edge.hl .edge-path {
  stroke: var(--accent);
  stroke-width: 1.4;
  stroke-dasharray: none;
}
.plate-svg .edge.hl .edge-label {
  fill: var(--accent);
  display: block;
}
/*  When a node is hovered, override marker arrows on its incident edges
    by swapping marker reference. We use CSS to drop opacity on dim edges
    so the arrows fade with them.                                            */
.plate-svg .edge.dim {
  opacity: 0.18;
}

.plate-svg .edge {
  cursor: pointer;
}
