/* BrightKidz Hub — widget library styles
 *
 * Seven widgets, mounted by data-widget. See widgets.js for the DOM contract.
 *
 * COLOUR. Nothing here hardcodes a hex. Every rule reads a token through the
 * normalisation layer below, which prefers the page's own design tokens from
 * site.css and falls back to a CSS SYSTEM COLOUR when they are absent. The
 * fallback is written as var(--token, fallback) rather than as a competing
 * :root block on purpose: a second :root definition would fight site.css on
 * source order, and whichever file loaded last would silently win.
 *
 * MATERIAL. 3px rules, hard 2px/2px offset shadows with zero blur, 4px die-cut
 * corners — printed card, not soft UI elevation (DESIGN.md).
 *
 * NO REMOTE REQUESTS. No @import, no @font-face, no url() pointing off-origin.
 */

/* ------------------------------------------------------- token normalisation */

.try-it,
.quiz {
  /* currentColor is the ultimate ink fallback: it is always defined and always
     contrasts with the ground it sits on, so a widget stays legible even with
     no stylesheet but this one. */
  --w-ink:     var(--ink, currentColor);
  --w-stock:   var(--stock, Canvas);
  --w-board:   var(--board, CanvasText);
  --w-rule:    var(--rule, var(--board, CanvasText));
  --w-soft:    var(--stock-deep, var(--stock, Canvas));
  --w-veil:    var(--w-rule);

  --w-stroke:  var(--stroke, 3px);
  --w-cut:     var(--die-cut, 4px);
  --w-shadow:  var(--shadow, 2px 2px 0 var(--w-rule));

  --w-display: var(--font-display, "Arial Black", Impact, system-ui, sans-serif);
  --w-read:    var(--font-read, ui-sans-serif, system-ui, "Segoe UI", Roboto, Arial, sans-serif);
  --w-mono:    var(--font-mono, ui-monospace, Consolas, "Liberation Mono", monospace);

  /* Children, some on phones. DESIGN.md floor for widgets. */
  --w-tap: 48px;
}

/* A tinted surface needs colour arithmetic. Where color-mix is unsupported
   these declarations never apply and --w-soft stays the flat stock tint set
   above: legible and correct, just less tinted. */
@supports (color: color-mix(in srgb, red 50%, blue)) {
  .try-it,
  .quiz {
    --w-soft: var(--ink-soft, color-mix(in srgb, var(--w-ink) 12%, var(--w-stock)));
    --w-veil: color-mix(in srgb, var(--w-ink) 30%, var(--w-stock));
  }
}

/* --------------------------------------------------------------- the panel -- */

.try-it,
.quiz {
  font-family: var(--w-read);
  color: var(--w-board);
  background: var(--w-stock);
  border: var(--w-stroke) solid var(--w-rule);
  border-radius: var(--w-cut);
  box-shadow: var(--w-shadow);
  padding: 1.1rem;
  margin: 2rem 0;
}

/* The art is authored with fill="currentColor"; this hands it the subject ink,
   so one drawing restyles per subject with no per-subject CSS. */
.try-it > svg {
  color: var(--w-ink);
  display: block;
  width: 100%;
  max-width: 32rem;
  height: auto;
  margin: 0 auto 1rem;
}

.try-it-prompt > :first-child { margin-top: 0; }
.try-it-prompt > :last-child  { margin-bottom: 0; }

/* An enhanced widget is marked, so the two states can be styled apart. Absent
   this class the markup is the plain, readable, no-JS article. */
.try-it.bk-on,
.quiz.bk-on { padding-bottom: 1rem; }

/* hidden must beat any display the layout sets, or a "hidden" panel shows. */
.try-it [hidden],
.quiz [hidden] { display: none !important; }

/* ------------------------------------------------------------- turn marker -- */

.bk-turn {
  font-family: var(--w-display);
  font-size: .95rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--w-ink);
  margin: 0 0 .6rem;
}

/* ----------------------------------------------------------------- status -- */

/* Visible AND announced. Colour is never the only signal (DESIGN.md), so every
   widget state has its text equivalent here. */
.bk-status {
  font-family: var(--w-read);
  font-size: 1rem;
  line-height: 1.5;
  margin: .9rem 0 0;
  padding: .6rem .75rem;
  border: var(--w-stroke) solid var(--w-rule);
  border-radius: var(--w-cut);
  background: var(--w-soft);
}
.bk-status:empty { display: none; }
.bk-status b { font-family: var(--w-display); }

/* ---------------------------------------------------------------- controls -- */

.bk-bar {
  display: flex;
  flex-wrap: wrap;
  gap: .6rem;
  align-items: center;
  margin-top: 1rem;
}

.bk-btn {
  font-family: var(--w-display);
  font-size: 1rem;
  line-height: 1.1;
  color: var(--w-board);
  background: var(--w-stock);
  border: var(--w-stroke) solid var(--w-rule);
  border-radius: var(--w-cut);
  box-shadow: var(--w-shadow);
  min-height: var(--w-tap);
  min-width: var(--w-tap);
  padding: .55rem 1rem;
  cursor: pointer;
  text-align: center;
  /* "Taking a turn": the press is discrete and consequential. */
  transition: transform .08s steps(2, end), box-shadow .08s steps(2, end);
}

.bk-btn:hover { background: var(--w-soft); }

/* The token lands: the card sits down onto the board and its shadow closes. */
.bk-btn:active {
  transform: translate(2px, 2px);
  box-shadow: 0 0 0 var(--w-rule);
}

.bk-btn[aria-pressed="true"],
.bk-btn.bk-sel {
  background: var(--w-ink);
  color: var(--w-stock);
}

.bk-btn[disabled] {
  opacity: .45;
  cursor: default;
  box-shadow: none;
  transform: translate(2px, 2px);
}

/* Focus matches site.css exactly. The second ring keeps the marker visible on
   an ink-filled (selected) button, where an ink outline would vanish. */
.try-it :focus-visible,
.quiz  :focus-visible {
  outline: 3px solid var(--w-ink);
  outline-offset: 3px;
  border-radius: 2px;
}
.bk-btn[aria-pressed="true"]:focus-visible,
.bk-btn.bk-sel:focus-visible {
  outline-color: var(--w-stock);
  box-shadow: var(--w-shadow), 0 0 0 6px var(--w-ink);
}

.bk-btn-go { background: var(--w-ink); color: var(--w-stock); }

/* ---------------------------------------------------------------- stepper -- */

.bk-step-count {
  font-family: var(--w-display);
  font-size: 1.1rem;
  color: var(--w-ink);
  display: flex;
  align-items: center;
}

/* Turn number in an ink circle — the world's own ornament (DESIGN.md). */
.bk-pip {
  display: inline-grid;
  place-items: center;
  width: 2.1em;
  height: 2.1em;
  margin-right: .5em;
  border-radius: 50%;
  background: var(--w-ink);
  color: var(--w-stock);
  font-family: var(--w-display);
  font-size: .95rem;
}

/* An <ol> renumbers when items are hidden, which would contradict the
   "Stage 2 of 4" readout. The pip carries the number instead. */
.bk-steplist { list-style: none; padding-left: 0; margin: 0; }

.bk-stage {
  border: var(--w-stroke) solid var(--w-rule);
  border-radius: var(--w-cut);
  background: var(--w-soft);
  padding: .85rem 1rem;
  margin: .9rem 0 0;
}
.bk-stage > :first-child { margin-top: 0; }
.bk-stage > :last-child  { margin-bottom: 0; }
.bk-stage svg { color: var(--w-ink); display: block; max-width: 100%; height: auto; }

/* Dashed move-path across the stages. */
.bk-track {
  display: flex;
  gap: .4rem;
  align-items: center;
  margin: .9rem 0 0;
  padding: 0;
  list-style: none;
}
.bk-track li {
  flex: 1 1 0;
  height: 0;
  margin: 0;
  border-top: var(--w-stroke) dashed var(--w-veil);
}
.bk-track li[data-done="yes"] { border-top-style: solid; border-top-color: var(--w-ink); }

/* ---------------------------------------------------------------- hotspot -- */

/* MEASURED in a browser: the demo art's stem shape is 26px wide, under the
   44px a child's fingertip needs, and the shapes are the ONLY way to pick a
   part -- a text button is created only for a part the art does NOT label,
   so well-labelled art has none to fall back on.

   A transparent stroke widens the target without changing what is drawn.
   `non-scaling-stroke` keeps that halo in SCREEN pixels, so it does not
   shrink with the drawing on a phone, which is precisely where it is needed.
   20px total (10 a side) takes the stem to 46px: enough to clear the floor,
   small enough that a halo does not reach across and steal a neighbour's tap.

   Scoped to shapes with no stroke of their own, so art that draws a visible
   outline keeps it rather than having it inflated to 20px. */
.try-it svg [data-part] { pointer-events: all; }
.try-it svg [data-part]:not([stroke]) {
  stroke: transparent;
  stroke-width: 20;
  vector-effect: non-scaling-stroke;
  paint-order: stroke;
}

.bk-parts {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  margin-top: .9rem;
  padding: 0;
  list-style: none;
}
.bk-parts li { margin: 0; }

/* A thick tab down one side is the most recognisable tell of generated UI,
   and it is not in this world's vocabulary (DESIGN.md: turn numbers in ink
   circles, dashed move-paths, token discs, register marks). What marks a
   panel as the ANSWER to a turn here is the printed card itself -- an even
   rule and the hard offset shadow the rest of the system already uses. */
.bk-detail {
  margin-top: .9rem;
  padding: .8rem 1rem;
  border: var(--w-stroke) solid var(--w-rule);
  border-radius: var(--w-cut);
  box-shadow: var(--w-shadow);
  background: var(--w-soft);
}
.bk-detail h4 {
  font-family: var(--w-display);
  margin: 0 0 .3em;
  /* MEASURED 4.16 against the tinted panel with the bare ink -- under the 4.5
     this size needs. The ink is tuned for the --stock ground, and this panel
     is a shade darker, so the whole family lands short here rather than just
     one subject. Nudging the ink toward the board holds the subject's hue
     while clearing the bar for all six at once. */
  color: color-mix(in srgb, var(--w-ink) 84%, var(--w-board));
  font-size: 1.2rem;
}
.bk-detail p { margin: 0; }

/* Interactive shapes inside the artwork. */
.bk-part { cursor: pointer; }
.bk-part:focus-visible { outline: 3px solid var(--w-ink); outline-offset: 2px; }
.bk-part[data-on="yes"] { opacity: 1; }

/* A dashed ring around the chosen part — a mark, not a colour change alone. */
.bk-ring {
  fill: none;
  stroke: var(--w-ink);
  stroke-width: 3;
  stroke-dasharray: 6 4;
  pointer-events: none;
  vector-effect: non-scaling-stroke;
}

/* ----------------------------------------------------------------- sorter -- */

.bk-sort { list-style: none; margin: .9rem 0 0; padding: 0; }

.bk-row {
  display: flex;
  align-items: center;
  gap: .6rem;
  margin: .5rem 0;
  padding: .5rem .6rem;
  border: var(--w-stroke) solid var(--w-rule);
  border-radius: var(--w-cut);
  background: var(--w-stock);
  box-shadow: var(--w-shadow);
}
.bk-row-label { flex: 1 1 auto; min-width: 0; }
.bk-row-ctl { display: flex; gap: .35rem; flex: 0 0 auto; }

/* A token disc carrying the position number. */
.bk-disc {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 2.2rem;
  height: 2.2rem;
  border-radius: 50%;
  background: var(--w-ink);
  color: var(--w-stock);
  font-family: var(--w-display);
}

.bk-row[data-check="right"] { background: var(--w-soft); border-color: var(--w-ink); }
.bk-mark {
  font-family: var(--w-display);
  font-size: .85rem;
  color: var(--w-ink);
  flex: 0 0 auto;
}

/* A GROUPING SORTER PUT ITS LABEL AND ITS BUTTONS ON ONE LINE, and the label
   lost. `.bk-row-label` is `flex: 1 1 auto; min-width: 0`, so with three group
   buttons beside it the item column collapsed to about two characters and
   "Sunlight" wrapped as Sun/ligh/t on a 390px phone. That is not a cosmetic
   defect: it made three-group sorters unusable, so an author writing a Nature
   article with a natural three-way split had to fall back to a quiz.

   Below 30rem the row stacks -- item on its own line, buttons under it, each
   at full width. The buttons then have room for real labels ("Comes out"
   rather than "Out"), which is the thing that was actually being sacrificed.
   Reported by the agent that hit it, which chose quizzes rather than shipping
   a broken sorter. */
@media (max-width: 30rem) {
  .bk-row { flex-wrap: wrap; row-gap: .45rem; }
  .bk-row-label { flex: 1 1 100%; }
  .bk-row-ctl { flex: 1 1 100%; flex-wrap: wrap; }
  .bk-row-ctl > * { flex: 1 1 auto; }
}

.bk-bins { display: grid; gap: .8rem; margin-top: .9rem; }
@media (min-width: 34rem) {
  .bk-bins { grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); }
}
.bk-bin {
  border: var(--w-stroke) dashed var(--w-rule);
  border-radius: var(--w-cut);
  padding: .6rem;
  background: var(--w-stock);
  min-height: 6rem;
}
.bk-bin h4 {
  font-family: var(--w-display);
  margin: 0 0 .5rem;
  color: var(--w-ink);
  font-size: 1rem;
  text-transform: uppercase;
  letter-spacing: .03em;
}
.bk-bin ul { list-style: none; margin: 0; padding: 0; }

/* Drag is a bonus on top of the buttons, never the mechanism. */
.bk-row[draggable="true"] { cursor: grab; }
.bk-row.bk-drag { opacity: .5; }
.bk-bin.bk-over { border-style: solid; border-color: var(--w-ink); background: var(--w-soft); }

/* ----------------------------------------------------------------- slider -- */

.bk-slider-row {
  display: flex;
  align-items: center;
  gap: .8rem;
  flex-wrap: wrap;
  margin-top: 1rem;
}
.bk-slider-label { font-family: var(--w-display); color: var(--w-ink); }

.bk-readout {
  font-family: var(--w-display);
  font-size: 1.25rem;
  min-width: 4ch;
  padding: .3rem .6rem;
  border: var(--w-stroke) solid var(--w-rule);
  border-radius: var(--w-cut);
  background: var(--w-soft);
  text-align: center;
}

/* The same face when it is the guess field rather than a readout. Children,
   some on phones: the entry box is a full tap target too. */
input.bk-readout {
  min-height: var(--w-tap);
  max-width: 7rem;
  color: var(--w-board);
}

.bk-range {
  flex: 1 1 14rem;
  /* JS-created, so it never has to pass the build's HTML allowlist — which
     does not admit <input>. */
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  min-height: var(--w-tap);
  cursor: pointer;
}
.bk-range:focus { outline: none; }
.bk-range:focus-visible { outline: 3px solid var(--w-ink); outline-offset: 3px; }

.bk-range::-webkit-slider-runnable-track {
  height: 10px;
  background: var(--w-soft);
  border: var(--w-stroke) solid var(--w-rule);
  border-radius: var(--w-cut);
}
.bk-range::-moz-range-track {
  height: 10px;
  background: var(--w-soft);
  border: var(--w-stroke) solid var(--w-rule);
  border-radius: var(--w-cut);
}
/* A token disc on a printed track: 32px visual inside a 48px touch row. */
.bk-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 32px;
  height: 32px;
  margin-top: -14px;
  border-radius: 50%;
  background: var(--w-ink);
  border: var(--w-stroke) solid var(--w-rule);
  box-shadow: var(--w-shadow);
}
.bk-range::-moz-range-thumb {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--w-ink);
  border: var(--w-stroke) solid var(--w-rule);
  box-shadow: var(--w-shadow);
}

/* ------------------------------------------------------------------ guess -- */

.bk-pad {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--w-tap), 1fr));
  gap: .5rem;
  margin-top: 1rem;
  max-width: 26rem;
}
.bk-pad .bk-btn { padding: .55rem .4rem; }
.bk-btn[data-tried="yes"] {
  background: var(--w-soft);
  opacity: .7;
  text-decoration: line-through;
}

.bk-temp {
  font-family: var(--w-display);
  font-size: 1.15rem;
  color: var(--w-ink);
  display: block;
}

.bk-tally { font-size: .95rem; margin-top: .5rem; }

/* ------------------------------------------------------------------- quiz -- */

.quiz-q { font-family: var(--w-display); font-size: 1.2rem; margin: 0 0 .3em; }

.quiz ul { list-style: none; margin: .9rem 0 0; padding: 0; }
.quiz li { margin: .5rem 0; }

.bk-opt {
  display: block;
  width: 100%;
  text-align: left;
  font-family: var(--w-read);
  font-size: 1rem;
}

/* Two signals per state, never colour alone: a mark and a word in the status. */
.bk-opt[data-state="right"] { background: var(--w-ink); color: var(--w-stock); }
.bk-opt[data-state="right"]::before { content: "\2713\00a0"; font-family: var(--w-display); }
.bk-opt[data-state="wrong"] { background: var(--w-soft); }
.bk-opt[data-state="wrong"]::before { content: "\2717\00a0"; font-family: var(--w-display); }
.bk-opt[data-state="miss"]  { border-style: dashed; opacity: .75; }

/* ------------------------------------------------------------ code-runner -- */

.try-it pre {
  font-family: var(--w-mono);
  font-size: .95rem;
  line-height: 1.55;
  background: var(--w-soft);
  /* MEASURED at 1.21 in a browser before this line existed -- the code was
     effectively invisible. site.css styles `pre` as a DARK panel with light
     text (background: var(--board); color: var(--stock)); overriding only the
     background to a light one left the light text behind it. Whoever
     overrides one half of a colour pair owns the other half. */
  color: var(--w-board);
  border: var(--w-stroke) solid var(--w-rule);
  border-radius: var(--w-cut);
  padding: .8rem 1rem;
  overflow-x: auto;
}
.try-it pre code { background: none; padding: 0; font-size: inherit; color: inherit; }

.bk-out {
  font-family: var(--w-mono);
  font-size: .95rem;
  line-height: 1.55;
  white-space: pre-wrap;
  word-break: break-word;
  margin: .9rem 0 0;
  padding: .8rem 1rem;
  border: var(--w-stroke) solid var(--w-rule);
  border-radius: var(--w-cut);
  box-shadow: var(--w-shadow);
  background: var(--w-soft);
  max-height: 22rem;
  overflow: auto;
}
/* Dashed all round, not down one edge: still a signal that does not depend
   on colour, and a die-cut card drawn with a broken rule reads as "not the
   final answer" inside a printed rulebook. The label above it says so in
   words as well, so the state never rests on the border alone. */
.bk-out[data-kind="error"] { border-style: dashed; }

.bk-out-label,
.bk-note-label {
  font-family: var(--w-display);
  font-size: .9rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--w-ink);
  margin: .9rem 0 .3rem;
}

.bk-note {
  margin: .9rem 0 0;
  padding: .8rem 1rem;
  border: var(--w-stroke) dashed var(--w-rule);
  border-radius: var(--w-cut);
  background: var(--w-soft);
}
.bk-note p { margin: 0; }
.bk-note p + p { margin-top: .6em; }

/* --------------------------------------------------------------- utility -- */

.bk-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* --------------------------------------------------------- reduced motion -- */

/* State still changes; it simply arrives without travel (DESIGN.md). */
@media (prefers-reduced-motion: reduce) {
  .try-it *,
  .quiz * {
    transition-duration: .001ms !important;
    animation-duration: .001ms !important;
  }
  .bk-btn:active { transform: none; box-shadow: var(--w-shadow); }
}

/* -------------------------------------------------------- forced colours -- */

/* Windows High Contrast discards background and box-shadow. Keep the borders
   and the focus marker, which are what carry meaning there. */
@media (forced-colors: active) {
  .bk-btn,
  .bk-row,
  .bk-stage,
  .bk-detail,
  .bk-status,
  .bk-out,
  .bk-bin {
    border: var(--w-stroke) solid;
    box-shadow: none;
  }
  .try-it :focus-visible,
  .quiz :focus-visible { outline: 3px solid Highlight; }
}
