/* ═══════════════════════════════════════════════════════════════════
   SAGE MANIPULATIVE KIT — shared core styles
   Tokens + reset + page chrome + common controls + widget rules.
   Included by every kit demo page AND every Canvas via a classic
   <link rel="stylesheet" href="…/Kit/kit-core.css">. Change once here,
   changes everywhere. Widget-specific rules are prefixed (.fp-* = pie).
   Someta · July 2026
   ═══════════════════════════════════════════════════════════════════ */

:root{
  --paper:#FAF7F2; --surface:#FFFFFF; --ink:#1C1917; --muted:#8A8177;
  --faint:#B8AFA3; --line:#E7E0D5; --rail:#D8CFC0;
  --sage:#0E7268; --sage-soft:#E3F1EF; --you:#D96C1E; --you-soft:#FBEBDD; --rose:#C0564A;
  --cell:#F1EBE1; --cell-line:#E3DACB;
  --serif:"New York","Iowan Old Style",Georgia,"Times New Roman",serif;
  --sans:-apple-system,BlinkMacSystemFont,"SF Pro Text","Segoe UI",Roboto,Helvetica,Arial,sans-serif;
}
*{margin:0;padding:0;box-sizing:border-box}
html,body{background:var(--paper);color:var(--ink);font-family:var(--sans)}
body{min-height:100vh;display:flex;flex-direction:column;align-items:center;padding:28px 20px 40px}

/* ── page chrome ─────────────────────────────────────── */
.frame{width:100%;display:flex;flex-direction:column;gap:16px}   /* pages set their own max-width */
header{display:flex;align-items:baseline;justify-content:space-between;gap:16px;padding:4px 6px 10px;border-bottom:1px solid var(--line)}
.title-block h1{font-family:var(--serif);font-weight:600;font-size:30px;letter-spacing:-.01em}
.title-block p{margin-top:4px;color:var(--muted);font-size:13.5px}
.wordmark{font-size:11px;letter-spacing:.18em;color:var(--faint);text-transform:uppercase;white-space:nowrap}
.card{background:var(--surface);border:1px solid var(--line);border-radius:14px}
svg{display:block;width:100%;height:auto;touch-action:none}   /* manipulables are dragged/painted, not scrolled-over */
footer{text-align:center;font-size:12px;color:var(--faint);padding-top:4px;line-height:1.6}

/* ── common controls ─────────────────────────────────── */
.ctl-label{font-size:10px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--faint);margin-bottom:8px;display:block}
.stepper{display:flex;align-items:center;gap:12px}
.stepper button{width:34px;height:34px;border-radius:50%;border:1px solid var(--line);background:var(--paper);color:var(--ink);font-size:19px;font-weight:600;cursor:pointer;transition:all .15s;line-height:1}
.stepper button:hover:not(:disabled){border-color:var(--sage);color:var(--sage);background:var(--sage-soft)}
.stepper button:disabled{opacity:.25;cursor:default}
.stepper .val{font-family:var(--serif);font-size:24px;font-weight:600;min-width:38px;text-align:center;font-variant-numeric:tabular-nums}
input[type=range]{width:100%;accent-color:var(--sage)}
.rval{font-family:var(--serif);font-size:15px;color:var(--muted);font-variant-numeric:tabular-nums}
.swatches{display:flex;gap:9px}
.sw{width:30px;height:30px;border-radius:50%;cursor:pointer;border:2px solid transparent;transition:transform .15s,border-color .15s}
.sw:hover{transform:scale(1.1)}
.sw.on{border-color:var(--ink)}
.seg{display:inline-flex;border:1px solid var(--line);border-radius:9px;overflow:hidden}
.seg button{border:none;background:var(--surface);color:var(--muted);font-family:var(--sans);font-size:12.5px;font-weight:600;padding:7px 12px;cursor:pointer;transition:all .15s}
.seg button:not(:last-child){border-right:1px solid var(--line)}
.seg button.on{background:var(--sage-soft);color:var(--sage)}
.switch{position:relative;width:38px;height:22px;border-radius:99px;background:var(--line);cursor:pointer;transition:background .2s;border:none}
.switch::after{content:"";position:absolute;top:2.5px;left:2.5px;width:17px;height:17px;border-radius:50%;background:#fff;transition:left .2s;box-shadow:0 1px 3px rgba(0,0,0,.18)}
.switch.on{background:var(--sage)}
.switch.on::after{left:18.5px}
.row{display:flex;align-items:center;justify-content:space-between}

/* ═══ FractionPie (piece № 01) ═══ */
.fp-bg,.fp-arc{transition:fill .3s ease}
.fp-rim{transition:stroke .3s ease,stroke-width .3s ease}
.fp-lab text{font-family:var(--serif);font-weight:600;text-anchor:middle;transition:fill .3s ease}
.fp-lab line{stroke-linecap:round;transition:stroke .3s ease}
.fp-hi-wrap{opacity:0;transition:opacity .25s ease}       /* steady ring; fades IN on click, OUT on un-click */
.fp-spokes,.fp-rim,.fp-lab,.fp-hi-wrap{pointer-events:none}   /* clicks pass through to the fill */
.fp-interactive .fp-arc,.fp-interactive .fp-bg{cursor:pointer}

/* shade mode (v1.7) — per-slice fills. .on = shaded; the fade IS the shading animation. */
.fp-shade-g{pointer-events:none}
.fp-shading .fp-shade-g{pointer-events:all}
.fp-wedge{fill-opacity:0;transition:fill-opacity .22s ease,fill .3s ease}
.fp-wedge.on{fill-opacity:1}
.fp-shading .fp-wedge{cursor:pointer}
/* a locked given: still drawn per-wedge, just untouchable */
.fp-locked .fp-shade-g,.fp-locked .fp-cut{pointer-events:none}
.fp-locked .fp-cut{opacity:.3}
.fp-shading .fp-wedge:not(.on):hover{fill-opacity:.22}   /* hover previews the shading */
.fp-shading .fp-wedge.on:hover{fill-opacity:.85}         /* …and hints un-shading */
/* the knife (v1.8) — the only control that lives inside a piece, so it
   travels to surfaces that have no control panel (the student's stage) */
.fp-cut-btn{cursor:pointer}
.fp-cut-btn circle{fill:var(--paper);stroke:var(--line);stroke-width:1;transition:fill .15s,stroke .15s}
.fp-cut-btn:hover circle{stroke:var(--sage);fill:var(--sage-soft)}
.fp-cut-btn.off{opacity:.22;pointer-events:none}
.fp-cut-sign{font-family:var(--sans);font-size:17px;font-weight:600;fill:var(--ink);
             text-anchor:middle;dominant-baseline:central;pointer-events:none;user-select:none}
.fp-cut-lab{font-family:var(--sans);font-size:12.5px;font-weight:600;fill:var(--muted);
            text-anchor:middle;letter-spacing:.04em;pointer-events:none;user-select:none}

/* the guide's pointer — a ring on any slice, shaded or empty */
.fp-whi-g{pointer-events:none}
.fp-whi{opacity:.95;animation:fpWhiIn .25s ease}
@keyframes fpWhiIn{from{opacity:0}}

/* ═══ PercentGrid (piece № 02) ═══ */
/* ONLY fill transitions — that's the count/pattern sweep. Geometry (x/y/w/h)
   is set imperatively in the same frame the parent group re-centers, so it
   must NOT transition, or cells lerp their size while the parent jumps to the
   new center → the grid drifts on resize/reshape. Layout atomic; fill animates. */
.pg-cell{stroke:var(--cell-line);stroke-width:1;transition:fill .22s ease;transform-box:fill-box;transform-origin:center}
.pg-cell.pg-interactive{cursor:pointer}
.pg-cell.pg-interactive:hover{stroke:var(--you)}
.pg-cell.pg-flash{animation:pgPulse .8s ease}
@keyframes pgPulse{45%{fill:var(--sage-soft);stroke:var(--sage);transform:scale(1.07)}}
.pg-partial{pointer-events:none}                  /* the frontier sweep — clicks pass through */

/* ═══ NumberLine (piece № 03) ═══ */
.nl{cursor:grab}
.nl.grabbing{cursor:grabbing}
.nl-rail{transition:stroke .3s ease}              /* soft recolor on setColor */
.nl-tick line{transition:stroke .3s ease}
.nl-num{font-family:var(--serif);fill:var(--ink);font-variant-numeric:tabular-nums}
.nl-dot{transition:r .22s cubic-bezier(.34,1.5,.6,1)}   /* the amber selection dot pops in */

.nl-tick.nl-hi line{stroke:var(--you);stroke-opacity:1}  /* the selected mark lights amber */
.nl-tick.nl-hi .nl-num{fill:var(--you)}

/* ═══ FractionPieFamily (piece № 04) ═══ */
/* Glide + fade are armed only AFTER a member's first placement, so a new
   circle fades in where it belongs instead of flying in from the origin. */
.fpf-mem{transition:opacity .32s ease,transform .38s cubic-bezier(.34,1.2,.64,1)}
.fpf-eq{font-family:var(--serif);fill:var(--faint);text-anchor:middle;transition:opacity .32s ease}
.fpf-hit{cursor:pointer}

/* ═══ Expression (№ 05) + Hanger (№ 06) ═══ */
/* Blocks carry ONLY a transform transition — survivors glide to their new
   packing while the departed fall. Geometry (w/h/rx) is fixed at birth, so
   there's nothing to lerp against the parent pan swinging on its string. */
.hg-b{transition:transform .38s cubic-bezier(.34,1.2,.64,1),opacity .3s ease;cursor:pointer}
.hg-b rect{stroke-width:1.5;transition:fill .2s ease,stroke .2s ease}
.hg-bt{font-family:var(--serif);font-size:14px;text-anchor:middle;dominant-baseline:central;pointer-events:none;user-select:none}

.hg-b-one rect{fill:var(--sage-soft);stroke:var(--sage)}
.hg-b-one .hg-bt{fill:var(--sage)}
.hg-b-x   rect{fill:var(--sage);stroke:var(--sage)}      /* solid = the one you can't see into */
.hg-b-x   .hg-bt{fill:#fff;font-style:italic;font-size:16px}

/* hover = amber, the learner's own mark: "this is what you'd take" */
.hg-b:hover rect{stroke:var(--you);stroke-width:2.5}
.hg-exp.hg-grouped .hg-b{cursor:pointer}
.hg-exp.hg-grouped .hg-b:hover rect{stroke:var(--you)}
.hg-out{fill:none;stroke:var(--rail);stroke-width:1.5;stroke-dasharray:5 4;pointer-events:none;
        animation:hgOut .34s ease}
@keyframes hgOut{from{opacity:0}to{opacity:1}}

.hg-post,.hg-hook{stroke:var(--rail);stroke-width:2.5;stroke-linecap:round}
.hg-beam{stroke:var(--ink);stroke-width:5;stroke-linecap:round;transition:stroke .35s ease}
.hg-str{stroke:var(--rail);stroke-width:1.5}
.hg-knot{fill:var(--rail)}
.hg-piv{fill:var(--ink)}
/* tipped isn't "wrong" — it's just untrue. The beam warms, and says nothing. */
.hg.hg-tipped .hg-beam{stroke:var(--rose)}
.hg-ring{stroke:var(--sage);stroke-width:3;opacity:0;pointer-events:none}
.hg-ring.hg-bloom{animation:hgBloom 1.1s ease-out}
@keyframes hgBloom{
  0%{opacity:.9;r:8;stroke-width:3}
  100%{opacity:0;r:78;stroke-width:.5}
}

/* the selected point's other names, stacked beneath it (amber = the kid's mark) */
.nl-alias{font-family:var(--serif);fill:var(--you);font-variant-numeric:tabular-nums}
.nl-alias-bar{stroke:var(--you);stroke-width:1.1;stroke-linecap:round}

/* "which ones simplify?" — a soft pill behind marks that have other names.
   Uses an existing token (no new hue), and composes with the amber selection. */
.nl-pill{fill:var(--sage-soft);opacity:0;transition:opacity .25s ease}
.nl-tick.nl-simp .nl-pill{opacity:1}

/* ═══ DoubleNumberLine (piece № 07) ═══ */
/* The grab cursor belongs to the molecule — the rails inside are passengers. */
.dnl{cursor:grab}
.dnl.grabbing{cursor:grabbing}
.dnl .nl,.dnl .nl.grabbing{cursor:inherit}
.dnl-conn{stroke:var(--rail);stroke-width:1.2;stroke-dasharray:3 5;fill:none;pointer-events:none}
.dnl-name{font-size:10px;font-weight:700;letter-spacing:.16em;text-transform:uppercase;font-family:var(--sans);pointer-events:none}

/* ═══ Numeral (№ 08) + Fraction (№ 09) + Arrow ═══ */
/* The roll: each glyph animates itself, so the group can hold two at once
   mid-change without either fighting the other. */
.nm-t{font-family:var(--serif);text-anchor:middle;dominant-baseline:alphabetic;
      font-variant-numeric:tabular-nums;user-select:none;transition:fill .3s ease}
.nm-roll{}
/* the guide's finger — a soft pill, NOT the amber fill (that is the
   student's own mark, and the two must never be confusable) */
.nm-pill{transition:opacity .25s ease}

.fr-bar{stroke-linecap:round;transition:stroke .3s ease}
/* Named for Fraction's `suffix`, which was removed as speculative — the rule
   outlived it because numeral.html borrows it to style a hand-drawn "=" at
   hardcoded coordinates. That page is the clearest thing Relation should
   absorb: it is already drawing 1/2 = 2/4 by hand. */
.fr-sfx{font-family:var(--serif);font-weight:600;text-anchor:middle;
        transition:opacity .3s ease,fill .3s ease}

/* ═══ NumberLine (№ 11) ═══ */
/* Three marks, three meanings — the same distinction the pie keeps between
   a student's fill and a guide's ring, held here between tick, point and
   ring. Ticks fade rather than jump, so stepping the denominator reads as
   marks arriving, not as the line being redrawn. */
.nln-axis{stroke-width:2.4;stroke-linecap:round}
.nln-tick{transition:opacity .3s ease,stroke .3s ease}
.nln-hit{pointer-events:all}
.nln-stem{stroke-width:2;stroke-linecap:round;opacity:.55}
.nln-dot{transition:fill .3s ease}
.nln-pt{transition:transform .28s cubic-bezier(.34,1.2,.64,1)}
.nln-ring{transition:opacity .25s ease}

/* ═══ Area (№ 12) — the area model ═══ */
/* The grid and the dimension bars are the region's own structure — thin,
   its own colour, like the line's ticks. stroke-opacity carries the
   softness so each element's own opacity stays free for the fade-in/out
   reconcile. The fill stays quiet so the Numeral can sit ON it and read. */
.area-fill{fill-opacity:.13;stroke-width:2;transition:fill .3s ease,stroke .3s ease}
.area-grid line{stroke-width:1.1;stroke-opacity:.5;transition:opacity .3s ease}
.area-ring{stroke-width:3.5;transition:opacity .25s ease}
.area-rule{transition:opacity .3s ease}
.area-rule line{stroke-width:1.6;stroke-opacity:.7;stroke-linecap:round}

/* ═══ Relation (№ 10) ═══ */
/* The marks and terms are Numerals and Fractions, already styled above —
   a slot only carries POSITION. It glides so a term that changes width
   pushes its neighbours along instead of the row jumping, the same
   discipline the pie's spokes and the stage's members keep. */
.rel-slot{transition:transform .34s cubic-bezier(.34,1.2,.64,1),opacity .3s ease}

.ar{transition:opacity .3s ease}
.ar-line{stroke-width:1.8;stroke-linecap:round;transition:stroke .3s ease}
.ar-head{transition:fill .3s ease}
.ar-lab{font-family:var(--sans);font-size:12.5px;font-weight:700;text-anchor:middle;
        letter-spacing:.02em;user-select:none;transition:fill .3s ease}

/* ═══ AreaModel ═══ */
/* Cells born and killed by opacity; geometry is imperative (like PercentGrid),
   so nothing lerps its size against the box rescaling — only opacity/fill ease. */
.am-cell{stroke:var(--cell-line);stroke-width:1;fill-opacity:.2;
         transition:opacity .3s ease,fill .25s ease}
/* the dimension signs — a curly brace spanning each side, quiet so the number
   at its tip carries the reading. Annotation, not the math, so it stays neutral
   whatever colour the cells are. */
.am-brace{fill:none;stroke:var(--muted);stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;
          transition:opacity .25s ease}
/* the split opens a clean gutter between the two blocks — the cut you can see */
.am-div{stroke:var(--surface);stroke-width:5;stroke-linecap:round;transition:opacity .25s ease}
/* whole-rectangle ring — the guide's accent, never the student's amber fill */
.am-hi{stroke-width:3;transition:opacity .25s ease}
/* a soft plate so the product reads over the grid under it */
.am-prod-bg{fill:var(--surface);opacity:.82;transition:opacity .2s ease}
/* the + that makes a split a SUM: k·h + (w−k)·h, and k + (w−k) under it */
.am-plus{font-family:var(--serif);font-size:22px;text-anchor:middle;
         dominant-baseline:central;user-select:none;pointer-events:none}
.am-plus-sm{font-size:16px}
