/* LearnLens base — reset, layout, page chrome, controls, charts, dialogs.
   Requires tokens.css. Everything here is lens-agnostic; anything that is
   specific to one subject belongs in that lens's own stylesheet. */

*, *::before, *::after { box-sizing: border-box; }

/* The browser's own `[hidden] { display: none }` is a bare element rule, so ANY
   class selector setting display beats it — and this kit sets `display: flex`
   on .ll-controls, .ll-row and .ll-segmented, every one of which is something
   a tool hides. Without this, `el.hidden = true` silently does nothing and
   ?controls= filtering half-works. */
[hidden] { display: none !important; }

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.55;
  color: var(--fg);
  background: var(--bg);
}

main { max-width: 860px; margin: 0 auto; padding: 1rem 1.5rem 4rem; }

h1, h2, h3 { color: var(--heading); margin: 0 0 0.6rem; line-height: 1.3; }
h1 { font-size: 1.5rem; display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
h2 { font-size: 1.2rem; margin-top: 1.8rem; }
h3 { font-size: 1.02rem; color: var(--ims-green); margin-top: 1.2rem; }
p { margin: 0 0 0.8rem; }
a { color: var(--accent); }

.ll-lede { color: #333; margin-bottom: 1.2rem; }
.ll-hint { color: var(--ims-gray-text); font-size: 0.88rem; }

/* ── Skip links + header actions ─────────────────────────────────────────── */

.skip-links a {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--bg); padding: 0.5rem 0.9rem; border: 2px solid var(--accent);
}
.skip-links a:focus { left: 0; }

.header-actions {
  margin-left: auto; display: inline-flex; gap: 0.4rem; align-items: center;
  font-size: 1rem; font-weight: normal;
}
.home-btn, .help-btn {
  display: inline-flex; align-items: center; gap: 0.3rem;
  min-height: 34px; padding: 0.25rem 0.6rem;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--bg); color: var(--accent);
  text-decoration: none; cursor: pointer; font: inherit; font-size: 0.9rem;
}
.home-btn:hover, .help-btn:hover { background: var(--surface); }
.home-btn svg { width: 16px; height: 16px; fill: currentColor; }

:focus-visible { outline: 2px solid var(--highlight-orange-dark); outline-offset: 2px; }

/* ── Panels and controls ─────────────────────────────────────────────────── */

.ll-panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.8rem 1rem;
  margin: 1rem 0;
}

.ll-controls { display: flex; flex-wrap: wrap; gap: 0.8rem 1.2rem; align-items: flex-end; }
.ll-controls label { display: inline-flex; flex-direction: column; gap: 0.2rem; font-size: 0.9rem; }
/* Flex at the top level, not only inside .ll-controls — a plain button row
   needs it too, and without it the mobile wrap rule below has nothing to act on. */
.ll-row { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }
label.ll-row { display: inline-flex; }

input[type="text"], input[type="number"], select {
  font: inherit; padding: 0.4rem 0.5rem;
  border: 1px solid #999; border-radius: 5px; background: var(--bg); color: var(--fg);
  min-height: 38px;
}
input[type="text"] { font-family: var(--font-mono); }
input[aria-invalid="true"] { border-color: var(--bad); background: #FFF6F5; }

input[type="range"] { width: 100%; min-width: 160px; accent-color: var(--ims-blue-text); height: 24px; }

button {
  font: inherit; cursor: pointer;
  min-height: 38px; padding: 0.4rem 0.9rem;
  border: 1px solid var(--accent); border-radius: 6px;
  background: var(--bg); color: var(--accent);
}
button:hover { background: #EAF3F8; }
button[aria-pressed="true"] { background: var(--ims-blue-text); color: #fff; }
button.ll-primary { background: var(--ims-blue-text); color: #fff; }
button.ll-primary:hover { background: #2F6A8F; }

.ll-error { color: var(--bad); font-size: 0.88rem; margin: 0.3rem 0 0; font-weight: bold; }

.ll-segmented { display: inline-flex; flex-wrap: wrap; gap: 0.3rem; }
.ll-segmented button { border-radius: 5px; padding: 0.35rem 0.7rem; font-size: 0.9rem; }

/* ── Charts ──────────────────────────────────────────────────────────────── */

.ll-chart { border: 1px solid var(--border); border-radius: 8px; padding: 0.3rem; background: var(--bg); }
.ll-chart-svg { width: 100%; height: auto; display: block; }
.ll-chart + .ll-chart { margin-top: 0.6rem; }

.ll-grid line { stroke: #ECECEC; stroke-width: 1; }
.ll-axis path, .ll-axis line { stroke: #555; }
.ll-axis text { font-family: var(--font-body); font-size: var(--chart-text, 12px); fill: #444; }
.ll-axis-label { font-family: var(--font-body); font-size: calc(var(--chart-text, 12px) * 1.08);
                 font-style: italic; fill: #333; }

/* Each curve has BOTH a hue and a dash signature — colour is never the only cue. */
.ll-curve      { stroke: var(--curve-f);     stroke-width: 2.5; fill: none; }
.ll-curve-df   { stroke: var(--curve-df);    stroke-width: 2.5; fill: none; stroke-dasharray: 7 4; }
.ll-curve-acc  { stroke: var(--curve-accum); stroke-width: 2.5; fill: none; }
.ll-curve-ghost{ stroke: #B0B0B0;            stroke-width: 1.5; fill: none; stroke-dasharray: 2 3; }
/* Two bounding curves that must both be visible. drawCurve joins on the class
   name, so they cannot share one. */
.ll-bound-lo, .ll-bound-hi { stroke: var(--curve-df); stroke-width: 2; fill: none; stroke-dasharray: 7 4; }
.ll-tangent    { stroke: var(--tangent);     stroke-width: 2;   fill: none; }
.ll-secant     { stroke: var(--tangent);     stroke-width: 2;   fill: none; stroke-dasharray: 6 3; }

.ll-area-pos { fill: var(--area-pos); }
.ll-area-neg { fill: var(--area-neg); }
.ll-rect { stroke: var(--ims-green); stroke-width: 1; }

.ll-point { fill: var(--tangent); stroke: #fff; stroke-width: 1.5; }
.ll-point-acc { fill: var(--curve-accum); stroke: #fff; stroke-width: 1.5; }
.ll-marker-line { stroke: #444; stroke-width: 1; stroke-dasharray: 3 3; }

/* Drag handles are 44px of touch target around a smaller visible dot. */
.ll-handle { cursor: grab; touch-action: none; }
.ll-handle:active { cursor: grabbing; }
.ll-handle-hit { fill: transparent; }
.ll-handle:focus-visible .ll-handle-dot { stroke: var(--highlight-orange-dark); stroke-width: 3; }

/* ── Legend and readouts ─────────────────────────────────────────────────── */

.ll-legend { display: flex; flex-wrap: wrap; gap: 0.4rem 1.1rem; font-size: 0.88rem; margin: 0.5rem 0 0; }
.ll-legend span { display: inline-flex; align-items: center; gap: 0.4rem; }
.ll-legend svg { width: 26px; height: 10px; flex: none; }

.ll-readout {
  display: flex; flex-wrap: wrap; gap: 0.5rem 1.5rem;
  font-family: var(--font-mono); font-size: 0.95rem;
  margin: 0.6rem 0 0;
}
.ll-readout b { font-family: var(--font-body); font-weight: normal; color: var(--ims-gray-text); }

.ll-result { font-size: clamp(1.05rem, 1.15vw, 1.4rem); margin: 0.6rem 0; }
.ll-verdict { font-weight: bold; }
.ll-verdict.ok { color: var(--ok); }
.ll-verdict.bad { color: var(--bad); }

.ll-tex-fallback { font-family: var(--font-mono); font-size: 0.92em; background: var(--surface); padding: 0 0.2em; }
.katex { font-size: 1.05em; }
.katex-display { margin: 0.6rem 0; }

/* ── Tool index cards ────────────────────────────────────────────────────── */

.ll-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 0.9rem; margin: 1rem 0 2rem; }
.ll-card {
  display: block; padding: 0.9rem 1rem; text-decoration: none; color: inherit;
  border: 1px solid var(--border); border-left: 4px solid var(--ims-blue); border-radius: 8px;
  background: var(--bg);
}
.ll-card:hover { background: var(--surface); }
.ll-card strong { display: block; color: var(--ims-blue-text); margin-bottom: 0.2rem; }
.ll-card span { font-size: 0.88rem; color: #444; }
.ll-card.ll-soon { border-left-color: var(--ims-lgray); opacity: 0.72; cursor: default; }
.ll-card.ll-soon strong { color: var(--ims-gray-text); }

.ll-tag {
  display: inline-block; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em;
  background: var(--surface); border: 1px solid var(--border); border-radius: 999px;
  padding: 0.05rem 0.5rem; margin-left: 0.4rem; color: var(--ims-gray-text); vertical-align: middle;
}

/* ── Expression input: preview and palette ───────────────────────────────── */

.ll-preview {
  min-height: 1.9em; margin: 0.35rem 0 0; padding: 0.15rem 0.4rem;
  border-left: 3px solid var(--ims-blue); background: var(--surface); border-radius: 0 4px 4px 0;
  font-size: 1.02rem;
}
.ll-preview:empty { display: none; }
.ll-preview::before {
  content: "reading: "; font-size: 0.8rem; color: var(--ims-gray-text);
  font-family: var(--font-body);
}
/* A failed parse leaves the last good reading visible but visibly out of date,
   rather than blanking — the reader is usually mid-edit, and a field that
   empties itself as you type is worse than one that lags. */
.ll-preview-stale { opacity: 0.4; border-left-color: var(--ims-lgray); }

.ll-palette { display: flex; flex-wrap: wrap; gap: 0.3rem; margin: 0.4rem 0 0; }
.ll-palette-btn {
  min-height: 34px; min-width: 38px; padding: 0.15rem 0.5rem;
  border: 1px solid var(--border); border-radius: 5px; background: var(--bg);
  color: var(--fg); line-height: 1.1;
}
.ll-palette-btn:hover { background: #EAF3F8; border-color: var(--ims-blue-text); }
.ll-palette-btn .katex { font-size: 0.95em; }

/* ── Draggable points ────────────────────────────────────────────────────── */

/* A point the reader can move, and one they cannot, must be told apart before
   they try — so the movable one carries a halo and a grab cursor, and the
   anchored one is a plain filled dot with a ring. Shape and cursor, not colour
   alone. */
.ll-grab { cursor: grab; touch-action: none; }
.ll-grab:active { cursor: grabbing; }
.ll-grab-hit { fill: transparent; }
.ll-point-halo { fill: var(--tangent); opacity: 0.18; pointer-events: none; }
.ll-grab:focus-visible .ll-point-halo { opacity: 0.4; }
.ll-grab:focus-visible .ll-point-move { stroke: var(--highlight-orange-dark); stroke-width: 3.5; }
.ll-point-move { fill: var(--tangent); stroke: #fff; stroke-width: 2; }
.ll-point-anchor { fill: #222; stroke: #fff; stroke-width: 2; }
.ll-point-anchor-ring { fill: none; stroke: #222; stroke-width: 1.5; opacity: 0.55; }

/* ── Tables ──────────────────────────────────────────────────────────────── */

/* Scales with the viewport like the caption and the quotient above it, but
   stays clearly subordinate to them — on a projector this is what the room
   reads numbers off, so it must not be the smallest thing in its column. The
   floor is today's size, so nothing anywhere gets smaller. */
.ll-table { border-collapse: collapse; width: 100%; max-width: 38rem;
            font-variant-numeric: tabular-nums; font-family: var(--font-mono);
            font-size: clamp(1rem, 1.05vw, 1.3rem); }
/* Headers and caption in em, so they follow the table rather than drifting. */
.ll-table caption { text-align: left; font-family: var(--font-body);
                    font-size: 0.92em; color: var(--ims-gray-text); padding-bottom: 0.35rem; }
.ll-table th, .ll-table td { text-align: right; padding: 0.25rem 0.7rem 0.25rem 0; }
.ll-table th { font-family: var(--font-body); font-weight: 700; font-size: 0.9em;
               color: var(--ims-gray-text); border-bottom: 1px solid var(--border); }
/* The current row is marked by weight and a rule as well as tint — a highlight
   carried by background colour alone disappears for a good many readers. */
.ll-table tr.ll-row-current td { font-weight: 700; background: #EAF3F8;
                                 box-shadow: inset 3px 0 0 var(--ims-blue-text); }

.ll-code { font-family: var(--font-mono); font-size: 0.85rem; background: var(--surface);
           border: 1px solid var(--border); border-radius: 6px; padding: 0.5rem 0.7rem;
           overflow-x: auto; white-space: pre-wrap; word-break: break-all; }

/* ── Steps (guided tools) ────────────────────────────────────────────────── */

.ll-step { border-left: 3px solid var(--ims-blue); padding: 0.2rem 0 0.2rem 0.9rem; margin: 1rem 0; }
.ll-step h3 { margin-top: 0; }

/* ── Dialog ──────────────────────────────────────────────────────────────── */

dialog.ll-help-dialog {
  max-width: 640px; width: calc(100% - 2rem);
  border: 1px solid var(--border); border-radius: 10px; padding: 1.2rem 1.4rem;
  color: var(--fg);
}
dialog.ll-help-dialog::backdrop { background: rgba(0, 0, 0, 0.45); }
dialog.ll-help-dialog h2 { margin-top: 0; }
dialog.ll-help-dialog dl { display: grid; grid-template-columns: auto 1fr; gap: 0.3rem 0.8rem; }
dialog.ll-help-dialog dt { font-family: var(--font-mono); }
dialog.ll-help-dialog dd { margin: 0; }
kbd {
  font-family: var(--font-mono); font-size: 0.85em;
  border: 1px solid var(--border); border-bottom-width: 2px; border-radius: 4px; padding: 0 0.3em;
}

/* ── Screen-reader live region ───────────────────────────────────────────── */

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

/* ── Embed mode (?embed=true) ────────────────────────────────────────────── */

body.ll-embed .header-actions,
body.ll-embed .ll-breadcrumb,
body.ll-embed .ll-lede,
body.ll-embed .ll-embed-hide { display: none; }
body.ll-embed main { padding: 0.5rem 0.7rem 1rem; max-width: none; }
body.ll-embed h1 { font-size: 1.1rem; }

/* ── Prose level (?prose=lean | none) ────────────────────────────────────── */

/* The reveal prompt survives every level: it is the question being asked, not
   commentary on it. */
[data-prose] .ll-lede,
[data-prose] .ll-hint:not(.ll-reveal-hint) { display: none; }

/* Two things that look like prose and are not, so they survive every level:
   the IDENTITY of what is on screen, and the WORKING behind the answer.
   `prose` strips explanation — the intro, the hints, "slide right to left to
   close the gap". A figure that does not say which function it draws is not
   less chatty, it is anonymous, and the QR case has no instructor in it to say
   so aloud. */
/* Both scale with the viewport: on a projector these are read from the back of
   a room, and at reading distance they should sit just above body text. The
   floor is what a phone gets, the ceiling stops the quotient outgrowing its
   column. */
.ll-figure-caption {
  margin: 0 0 0.4rem; text-align: center; color: var(--fg);
  font-size: clamp(1.15rem, 1.5vw, 1.75rem);
}
.ll-working {
  margin: 0.5rem 0 0; text-align: center; overflow-x: auto;
  font-size: clamp(1.1rem, 1.3vw, 1.5rem);
}
.ll-working:empty { display: none; }

[data-prose="none"] .ll-legend,
[data-prose="none"] .ll-readout { display: none; }

/* At `none` an instructor is narrating to a room, so the figure takes the whole
   projector rather than the reading-width column the prose needed. */
[data-prose="none"] main { max-width: none; padding-left: 1rem; padding-right: 1rem; }

/* On a projector, "make the figure as large as possible" and "keep the table
   visible" pull against each other, and the table wins the tie: it is the
   evidence the class is being asked to read. A full-width chart is 623px tall
   on a 1280x760 screen, which pushes the table off the bottom — so the figure
   is sized by HEIGHT here, taking all the room that leaves the table on screen.
   Landscape and roomy only; a phone keeps the full-width figure. */
@media (min-width: 1000px) and (min-height: 500px) {
  /* Stacked, a figure and a seven-row table need about 1050px of height and a
     projector gives 760 — so at this level they go SIDE BY SIDE instead, figure
     left and evidence right, and the class can see both without being scrolled.
     Placement is by column rather than by wrapper so it needs no per-tool
     markup: the heading spans, everything else falls down column one, and the
     table (or the evaluator's results) is pinned to column two. */
  [data-prose="none"] main {
    display: grid;
    grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
    column-gap: 1.4rem;
    align-content: start;
  }
  [data-prose="none"] main > * { grid-column: 1; }
  [data-prose="none"] main > h1 { grid-column: 1 / -1; }
  [data-prose="none"] main > #table,
  [data-prose="none"] main > #results { grid-column: 2; grid-row: 2 / 200; }
  /* Sized so the whole page lands inside a 1366x768 projector without a
     scroll. Measured, not guessed — the caption above the figure and the
     working over in the evidence column both take height off this budget. */
  [data-prose="none"] .ll-chart-svg {
    height: 54vh; width: auto; max-width: 100%; margin-inline: auto;
  }
  /* Its viewBox was built to match this box, so let it size itself. */
  [data-prose="none"] .ll-chart-svg.ll-chart-fitted {
    height: auto; width: 100%; max-width: 100%;
  }
}

/* Wide enough to size the figure by height, but too narrow for two columns.
   Bounded above, or this would override the two-column rule at every width —
   equal specificity, and it comes later in the file. */
@media (min-width: 900px) and (max-width: 999px) and (min-height: 500px) {
  [data-prose="none"] .ll-chart-svg { height: 54vh; width: auto; max-width: 100%; margin-inline: auto; }
}
[data-prose] .ll-panel { padding-top: 0.6rem; padding-bottom: 0.6rem; }

/* ── Mobile: 44px touch targets ──────────────────────────────────────────── */

@media (max-width: 600px) {
  main { padding: 0.8rem 0.9rem 3rem; }
  h1 { font-size: 1.25rem; }
  button, .home-btn, .help-btn { min-height: 44px; padding: 0.5rem 0.8rem; }
  input[type="text"], input[type="number"], select { min-height: 44px; font-size: 16px; }
  input[type="range"] { height: 44px; }
  .ll-controls { gap: 0.7rem; }
  .ll-controls label { width: 100%; }
  /* A checkbox label next to buttons wraps mid-phrase at this width. */
  .ll-row > label { flex: 1 1 100%; }
  .ll-readout { font-size: 0.88rem; gap: 0.3rem 1rem; }
}

/* ── Motion ──────────────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important;
      transition-duration: 0.01ms !important; }
}

/* ── Print (instructor guides) ───────────────────────────────────────────── */

@media print {
  .header-actions, .skip-links, .ll-no-print { display: none !important; }
  body { font-size: 11pt; }
  main { max-width: none; }
  .ll-step { break-inside: avoid; }
}

/* ── Share dialog ────────────────────────────────────────────────────────── */

.share-btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 34px; min-width: 34px; padding: 0.25rem 0.5rem;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--bg); color: var(--accent); cursor: pointer;
}
.share-btn:hover { background: var(--surface); }
@media (max-width: 600px) { .share-btn { min-height: 44px; min-width: 44px; } }

.ll-share-dialog input[type="text"] {
  width: 100%; font-family: var(--font-mono); font-size: 0.85rem;
}
.ll-qr { display: flex; justify-content: center; margin: 0.6rem 0; }
.ll-qr svg { width: 100%; max-width: 260px; height: auto; }

/* ── Reveal ──────────────────────────────────────────────────────────────── */

.ll-reveal-slot { display: flex; align-items: center; gap: 0.7rem; flex-wrap: wrap; margin: 0.7rem 0; }
/* Nested in a control row it supplies no margin of its own — the row spaces it. */
.ll-row > .ll-reveal-slot { margin: 0; }
.ll-reveal-btn {
  font-size: 1rem; font-weight: bold; min-height: 42px; padding: 0.45rem 1.1rem;
  border: 2px solid var(--accent); border-radius: 8px; background: var(--bg); color: var(--accent);
}
.ll-reveal-btn:hover { background: #EAF3F8; }
/* While the answer is withheld the button is the loudest thing on the page —
   it is what the instructor is reaching for in front of the class. */
.ll-reveal-btn.ll-reveal-armed {
  background: var(--highlight-orange-dark); border-color: var(--highlight-orange-dark); color: #fff;
}
.ll-reveal-btn.ll-reveal-armed:hover { background: #A34F0C; }
.ll-reveal-hint:empty { display: none; }
@media (max-width: 600px) { .ll-reveal-btn { min-height: 48px; width: 100%; } }
