/* AP Calculus Practice for Central Magnet Students.
   One stylesheet, no CDN, no web fonts.
   Mobile first: every rule below is written for a 360px-wide phone and only
   widened at a breakpoint. The only thing on the page that has a natural width
   is the question PNG (853px), and it is clamped to the column. */

:root {
  --ink:        #17181a;
  --ink-soft:   #55585e;
  --line:       #d8dce2;
  --paper:      #ffffff;
  --sunken:     #f4f6f8;
  --brand:      #0b5cab;
  --brand-dark: #073f77;
  --right:      #1b6b33;
  --right-bg:   #e8f5ec;
  --wrong:      #a3282a;
  --wrong-bg:   #fdeceb;
  --radius:     10px;
  --tap:        48px;          /* nothing tappable is smaller than this */
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  padding: 0;
  background: var(--sunken);
  color: var(--ink);
  font: 17px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  overflow-wrap: break-word;   /* a long token must wrap, never widen the page */
}

/* ---- chrome -------------------------------------------------------- */

.bar {
  display: flex; align-items: baseline; gap: 4px 10px;
  /* The site name is long ("AP Calculus Practice for Central Magnet
     Students"), so the bar is allowed to take two lines on a narrow screen
     rather than truncating the school out of it. */
  flex-wrap: wrap;
  padding: 9px 14px;
  background: var(--brand); color: #fff;
  font-size: 15px;
  position: sticky; top: 0; z-index: 10;
  padding-top: max(9px, env(safe-area-inset-top));
}
.bar-title { font-weight: 600; min-width: 0; line-height: 1.25; }

/* The site mark. Inline SVG rather than a file: no second request, it inherits
   the bar's white through currentColor, and it is the same path as the favicon.
   align-self is required -- the bar is align-items: baseline, and an SVG has no
   useful baseline, so without this it hangs below the text. flex-shrink: 0
   keeps it whole when the long title wraps to two lines on a phone. */
.bar-icon { align-self: center; flex-shrink: 0; display: block; }

@media (max-width: 420px) {
  .bar-title { font-size: 13.5px; }
  .bar-icon { width: 18px; height: 18px; }
}
.bar-who { margin-left: auto; opacity: .85; font-size: 13px;
           max-width: 45%; overflow: hidden; text-overflow: ellipsis;
           white-space: nowrap; }

/* 880px, not 720, and the number is the question figure's. A question PNG is
   853px at 150dpi (see .qfig img). At a 720px column with 14px of padding each
   side the usable width was 692px, so EVERY figure in the bank was being scaled
   down to 81% -- readable, but softer than it needed to be, and the smallest
   text in an SVG figure sits near the bank's 8px floor before that reduction.
   853 + 14 + 14 = 881, so 880 renders a figure at very nearly 1:1 and gives the
   embedded graphing calculator ~140px more room. Widened 2026-09-30 at Randy's
   request, after a student screenshot showed the calculator cramped.
   This binds only above 880px; the phone layout is unchanged and
   tools/audit_site_phone.py still checks every item at 360px. */
#main {
  max-width: 880px;
  margin: 0 auto;
  padding: 16px 14px 40px;
}

.foot {
  max-width: 880px; margin: 0 auto;
  padding: 0 14px 24px;
  color: var(--ink-soft); font-size: 12px; text-align: center;
}

.screen { background: var(--paper); border: 1px solid var(--line);
          border-radius: var(--radius); padding: 16px 14px 20px; }

h1 { font-size: 21px; margin: 0 0 8px; line-height: 1.25; }

/* Visible to a screen reader and to nothing else. The question screen's only
   heading is one of these: the numbers beside it are already on the page, but
   not as anything that can be jumped to. Same technique the bank uses for
   .pwsep, spelled the same way. */
.vh { position: absolute; width: 1px; height: 1px; overflow: hidden;
      clip-path: inset(50%); white-space: nowrap; margin: 0; padding: 0; }
h2 { font-size: 15px; margin: 0 0 6px; letter-spacing: .02em;
     text-transform: uppercase; color: var(--ink-soft); }
.lede { margin: 0 0 16px; color: var(--ink-soft); font-size: 15px; }

/* ---- forms and buttons ---------------------------------------------- */

.field { display: block; margin-bottom: 14px; }
.field span { display: block; font-size: 14px; color: var(--ink-soft);
              margin-bottom: 5px; }
.field input {
  width: 100%; min-height: var(--tap);
  padding: 10px 12px;
  font: inherit; font-size: 17px;   /* 16px+ or iOS zooms the whole page in */
  color: var(--ink);
  border: 1.5px solid var(--line); border-radius: 8px; background: #fff;
}
.field input:focus { outline: 3px solid #bcd6f2; border-color: var(--brand); }

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: var(--tap); width: 100%;
  padding: 10px 16px;
  font: inherit; font-weight: 600;
  border-radius: 8px; border: 1.5px solid transparent;
  cursor: pointer;
}
.btn-primary { background: var(--brand); color: #fff; }
.btn-primary:active { background: var(--brand-dark); }
.btn-quiet { background: #fff; color: var(--brand); border-color: var(--line);
             font-weight: 500; margin-top: 12px; }
.btn:focus-visible { outline: 3px solid #bcd6f2; outline-offset: 2px; }

.row { display: flex; flex-direction: column; gap: 0; }

.err { color: var(--wrong); font-size: 14px; margin: -6px 0 12px; }

/* ---- topic list ------------------------------------------------------ */

.topics { list-style: none; margin: 0 0 4px; padding: 0; }
.topics li { margin-bottom: 8px; }
.topics button {
  display: flex; align-items: center; gap: 10px;
  width: 100%; min-height: var(--tap);
  padding: 11px 12px;
  font: inherit; text-align: left;
  background: #fff; color: var(--ink);
  border: 1.5px solid var(--line); border-radius: 8px; cursor: pointer;
}
.topics button:active { background: var(--sunken); }
.topics .tcode { font-variant-numeric: tabular-nums; font-weight: 700;
                 color: var(--brand); min-width: 3.1em; }
.topics .tname { flex: 1 1 auto; font-size: 15px; line-height: 1.3; }
.topics .tn    { color: var(--ink-soft); font-size: 13px; white-space: nowrap; }
.topics .done  { color: var(--right); font-weight: 600; }

/* ---- question -------------------------------------------------------- */

/* Four things now, so it wraps. At 360px the topic takes the first line and
   the count, the clock and the calculator badge sit on the second -- a second
   ROW, not a broken one. flex-wrap is what keeps the badge from being the
   element that pushes the page past the viewport; tools/audit_site_phone.py
   fails the whole run if it ever does. */
.qhead { display: flex; align-items: baseline; flex-wrap: wrap;
         gap: 4px 10px; margin-bottom: 6px; }
.qtopic { font-size: 14px; color: var(--ink-soft); flex: 1 1 auto; min-width: 0; }
.qcount { font-size: 14px; color: var(--ink-soft); font-variant-numeric: tabular-nums;
          white-space: nowrap; }

/* ---- may I use a calculator on this one? ----------------------------------

   TWO STATES, ONE CHIP. Identical border, background, padding and text colour;
   the ONLY difference is that one calculator has the prohibition bar drawn over
   it and says "No calculator". That is on purpose, and it is the whole answer to
   the risk Randy's red X carries: red on this screen already means a wrong
   answer (--wrong, --wrong-bg, a few hundred pixels below and moments later),
   so a badge with a red FIELD would read as a verdict on a question the student
   has not answered yet. A neutral chip with a red bar inside it reads as a rule.

   NOTHING HERE DEPENDS ON THE COLOUR. Printed in greyscale, or read by a student
   who cannot separate red from grey, the two badges are still a calculator and a
   crossed-out calculator, and they still say "Calculator" and "No calculator" in
   words. The red is agreement, not information. */
.qcalcb {
  display: inline-flex; align-items: center; gap: 5px;
  flex: 0 0 auto;
  padding: 2px 8px 2px 6px;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--paper);
  font-size: 12.5px; font-weight: 600; letter-spacing: .01em;
  color: var(--ink-soft); white-space: nowrap;
  /* The chip is taller than the 14px text beside it; line it up by its own text
     rather than letting the border sit on the row's baseline. */
  align-self: center;
}
.qcalcb-ico { flex: 0 0 auto; display: block; }
/* The bar, in two strokes: paper underneath so it reads as lying ON the
   calculator, --wrong on top. Same red the site already owns -- a second red
   would be one more thing to keep in step. */
.qcalcb-bar-under { stroke: var(--paper); }
.qcalcb-bar       { stroke: var(--wrong); }
/* The crossed badge's own ink stays exactly as dark as the allowed one's. */
.qcalcb-no { color: var(--ink-soft); }

/* A hidden badge must be hidden: display:inline-flex outranks [hidden]. Same
   shape as .choices[hidden] above and .veil[hidden] below. */
.qcalcb[hidden] { display: none; }

.qprogress { height: 4px; background: var(--line); border-radius: 2px;
             overflow: hidden; margin-bottom: 14px; }
.qprogress-fill { height: 100%; width: 0; background: var(--brand);
                  transition: width .18s ease; }

/* ---- the question ----------------------------------------------------
   An item is the bank's HTML (.qstem + .qfigure) or, if its markup did not
   survive the export, the PNG it always had (.qfig). One or the other. */

.qstem { margin: 0 0 4px; }
.qfigure { margin: 0 0 14px; }

.qfig { margin: 0 0 16px; }
/* The PNG is 853px at 150dpi. Cap it to the column and let it scale; never let
   it set the page width, which is what would create a horizontal scrollbar. */
.qfig img { display: block; width: 100%; max-width: 853px; height: auto;
            margin: 0 auto; background: #fff; }

/* ---- the choices -----------------------------------------------------
   TWO LAYOUTS, because there are two kinds of choice.

   A fallback item's choices are inside its picture, so its buttons carry only
   a letter and four of them fit across. An HTML item's button carries the
   choice itself -- a stacked fraction, a piecewise definition three clauses
   long -- so they go one per row, full width, the way the review page Randy
   approved puts them. The exception is the item whose choices ARE figures,
   which pairs them up because four mini-graphs in one column is a scroll. */

.choices { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
.choices.choices-html { grid-template-columns: 1fr; gap: 8px; }
.choices.choices-fig  { grid-template-columns: repeat(2, 1fr); }

.choice {
  min-height: 56px;
  font: inherit; font-size: 20px; font-weight: 700;
  background: #fff; color: var(--brand);
  border: 2px solid var(--line); border-radius: 8px; cursor: pointer;
}
/* An HTML choice is a row: the letter, then the choice. Left-aligned and set
   in the body weight, because it is mathematics to read rather than a label to
   press -- the button is still the whole row. */
.choices-html .choice {
  display: flex; align-items: flex-start; gap: 10px;
  text-align: left; font-size: 17px; font-weight: 400;
  color: var(--ink); padding: 10px 11px; min-height: 48px;
}
.choices-html .clet { flex: 0 0 auto; font-weight: 700; color: var(--brand);
                      font-size: 16px; min-width: 1.5em; }
.choices-html .cbody { flex: 1 1 auto; min-width: 0; }
/* A figure choice puts the picture under nothing: its (A)-(D) label is drawn
   inside it by the bank. */
.choices-fig .choice { display: block; padding: 8px 6px; }
/* Nothing inside a choice is pressable on its own. */
.choice * { pointer-events: none; }
.choice:active { background: var(--sunken); }
.choice:focus-visible { outline: 3px solid #bcd6f2; outline-offset: 2px; }
.choice[disabled] { cursor: default; opacity: .55; }
/* Answered state. Colour alone is not the signal: the chosen choice is also
   labelled, and the key is outlined, so this reads on a mono or colour-blind
   screen too. */
/* SELECTED BUT NOT YET SUBMITTED. Deliberately NOT the same look as .picked:
   picked means recorded and marked, this means "this is the one you are about
   to send". Border and tint only -- no green or red, because nothing has been
   judged yet. */
#screen-reply .card    { display: flex; flex-direction: column; gap: 14px; }
#reply2-ok             { width: 100%; }
.reply2-note           { margin: 0; font-size: 13px; color: var(--ink-soft);
                         text-align: center; }
.choice.selected      { opacity: 1; border-width: 3px; border-color: var(--brand);
                        background: var(--sunken); }
.mcsubmit             { margin-top: 14px; }
.mcsubmit .btn        { width: 100%; }
.mchint               { margin: 8px 0 0; font-size: 13px; color: var(--ink-soft);
                        text-align: center; }
.mcsubmit .btn:not([disabled]) + .mchint { visibility: hidden; }
.choice.picked        { opacity: 1; border-width: 3px; }
.choice.picked.right  { background: var(--right-bg); border-color: var(--right);
                        color: var(--right); }
.choice.picked.wrong  { background: var(--wrong-bg); border-color: var(--wrong);
                        color: var(--wrong); }
.choice.iskey         { opacity: 1; border-color: var(--right); color: var(--right); }
.choice .tag { display: block; font-size: 11px; font-weight: 600;
               letter-spacing: .04em; text-transform: uppercase; }
/* In a row layout the chip goes at the end of the row, not under the letter. */
.choices-html .choice .tag { flex: 0 0 auto; align-self: center;
                             margin-left: auto; padding-left: 8px; }

/* An emptied .choices grid is invisible anyway, but `hidden` should mean hidden:
   `display: grid` above outranks the browser's own [hidden] rule. Same shape as
   .veil[hidden] at the end of this file. */
.choices[hidden] { display: none; }

/* ---- free response: the student types the answer ----------------------
   Mobile first like everything else here, and this block is the one that had to
   be, because it is all form controls: an input at 360px with a unit beside it
   and an infinity chip beside that is exactly where a row goes 8px too wide and
   the whole page scrolls sideways. So every row is a flex line that WRAPS, every
   box is width:100% inside its own line, and nothing has a fixed width.

   16px is the floor on an input's font size, not a preference: iOS Safari zooms
   the page when it focuses anything smaller, and a zoomed page is a page that
   scrolls sideways.

   tools/audit_site_phone.py measures all 62 of these at 360px in headless
   Chrome, presses the button, and fails on a sideways scroll or a tap target
   under 44px. */

.frq { margin-top: 16px; }

/* The conventions box. Printed with EVERY free-response question -- the paper
   has an instruction page and a phone does not -- so it is set small and quiet:
   it has to be readable and it must not be the loudest thing under the stem. */
.frconv {
  font-size: 13.5px; line-height: 1.45; color: var(--ink-soft);
  background: var(--sunken); border: 1px solid var(--line);
  border-radius: 8px; padding: 9px 11px; margin-bottom: 14px;
}
.frconv b { display: block; color: var(--ink); font-size: 13.5px;
            margin-bottom: 4px; }
.frconv em { font-style: italic; }

.frfields { display: block; }
.frrow { margin-bottom: 12px; }
.frlab { display: block; font-size: 15px; font-weight: 600; color: var(--ink);
         margin-bottom: 5px; }
/* The box, its unit and its chips on one wrapping line. `min-width: 8em` on the
   input with `flex: 1 1 8em` is what lets the unit sit beside it on a wide
   screen and drop under it on a narrow one instead of pushing the page out. */
.frline { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.frinput {
  flex: 1 1 8em; min-width: 0; width: 100%;
  min-height: var(--tap);
  font: inherit; font-size: 18px;
  padding: 8px 10px;
  color: var(--ink); background: var(--paper);
  border: 2px solid var(--line); border-radius: 8px;
}
.frinput:focus-visible { outline: 3px solid #bcd6f2; outline-offset: 1px;
                         border-color: var(--brand); }
.frinput[disabled] { background: var(--sunken); opacity: .8; }
.frunit { flex: 0 0 auto; font-size: 15px; color: var(--ink-soft); }
.frchips { flex: 0 0 auto; display: flex; gap: 6px; }
.frchip {
  min-width: var(--tap); min-height: var(--tap);
  font: inherit; font-size: 17px;
  background: var(--paper); color: var(--brand);
  border: 2px solid var(--line); border-radius: 8px; cursor: pointer;
}
.frchip:active { background: var(--sunken); }
.frchip:focus-visible { outline: 3px solid #bcd6f2; outline-offset: 2px; }

/* Which side x approaches from. A real radio group, so a screen reader reads it
   as one question with three answers, and so the browser's own keyboard handling
   applies. Stacked at phone width; three across from 480px up. */
.frsides { border: 1px solid var(--line); border-radius: 8px;
           padding: 8px 10px 10px; margin: 0 0 12px; }
.frsides legend { font-size: 15px; font-weight: 600; padding: 0 4px; }
.frside { display: flex; align-items: center; gap: 8px;
          min-height: var(--tap); font-size: 16px; cursor: pointer; }
.frside input { width: 22px; height: 22px; accent-color: var(--brand); }

.frhint { font-size: 13.5px; color: var(--ink-soft); margin: 0 0 14px; }

/* ---- A WRITTEN LIMIT STATEMENT -------------------------------------------
   The student builds "lim x->2- h(x) = -inf" as it is printed: the condition
   under lim, the function beside it. Class names are w-prefixed and NOT
   lim-prefixed, because .lim, .limop and .limcond already belong to the bank's
   own rendered notation in bank-item.css -- reusing them would style the
   question's markup as well as the answer boxes.
   ------------------------------------------------------------------------- */
.wstmt { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 10px;
         margin-bottom: 14px; }
.wstack { display: flex; flex-direction: column; align-items: center; gap: 3px; }
/* The word sits in a box the height of a control, so the function beside it
   lands on the same line rather than against the top of a two-line stack. */
.wlim { min-height: var(--tap); display: flex; align-items: center;
        font-size: 20px; color: var(--ink); }
.wcond { display: flex; align-items: center; gap: 4px; }
.wtail { display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
         min-height: var(--tap); }
/* .frinput is `flex: 1 1 8em; width: 100%` because a single answer box fills
   its row. Every box in a statement is one word wide instead, so each one opts
   out of growing -- otherwise the function box eats the line and pushes the
   equals sign to the far edge. */
.wbox { flex: 0 0 auto; width: 74px; text-align: center; font-size: 17px;
        padding: 6px 6px; }
.wbox.wide { width: 96px; }
/* var(--tap), not 42px. The first version of this set 42 and the phone audit
   caught it: nothing tappable on this site is smaller than the floor. */
.wcond .wbox { width: 62px; min-height: var(--tap); font-size: 16px; }

.wsym { flex: 0 0 auto; font: 500 18px/1 inherit; min-width: 56px; min-height: var(--tap);
        text-align: center; text-align-last: center; padding: 4px 2px;
        border: 1px solid var(--line); border-radius: 8px;
        background: var(--paper); color: var(--ink);
        -webkit-appearance: none; appearance: none; }
.wcond .wsym { min-width: 48px; min-height: var(--tap); font-size: 16px; }
.wsym:focus-visible { outline: 3px solid #bcd6f2; outline-offset: 1px; }

/* The side is a slot like the other symbols, joined to the point it belongs to.
   It was two stacked buttons, 24px each, which broke the 48px tap floor. */
.wpoint { flex: 0 0 auto; display: inline-flex; align-items: stretch; }
.wpoint .wbox { border-top-right-radius: 0; border-bottom-right-radius: 0;
                border-right: none; }
.wpoint .wside { min-width: 44px; border-top-left-radius: 0;
                 border-bottom-left-radius: 0; }

/* What they have written, as it is printed. */
.wreads { font-size: 20px; color: var(--ink); margin: 2px 0 14px;
          min-height: 30px; }
.wreads .wr-lim { font-style: normal; }
.wreads sub { font-size: .62em; }
.wreads sup { font-size: .62em; vertical-align: .45em; }
.wreads .wgap { color: var(--ink-soft); }

.wvalue { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 8px; }
.wvalue .frchip { min-height: var(--tap); }

@media (max-width: 420px) {
  /* The tail wraps under the stack rather than squeezing the boxes. */
  .wstmt { gap: 8px; }
  .wbox.wide { width: 92px; }
  .wsym { min-width: 52px; }
}

/* The buttons wrap rather than shrink: "The answer is not defined" is a long
   label and a 360px screen cannot hold it beside Check. */
.frrow-btns { display: flex; flex-wrap: wrap; gap: 10px; }
.frrow-btns .btn { flex: 1 1 100%; }

.frstate { font-size: 15px; color: var(--ink-soft); margin: 12px 0 0; }
.frstate-slow { color: var(--wrong); }

/* The answer, printed inside the verdict line when they got it wrong. Inherits
   the verdict's weight and colour on purpose: it is part of that sentence. */
.frexp { font-weight: 700; }

@media (min-width: 480px) {
  .frsides { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 18px; }
  .frrow-btns .btn { flex: 0 1 auto; }
}

/* ---- feedback -------------------------------------------------------- */

.feedback { margin-top: 18px; border-top: 1px solid var(--line); padding-top: 14px; }
.verdict { font-size: 19px; font-weight: 700; margin: 0 0 12px; }
.verdict.right { color: var(--right); }
.verdict.wrong { color: var(--wrong); }

.note { background: var(--wrong-bg); border-left: 4px solid var(--wrong);
        border-radius: 0 8px 8px 0; padding: 10px 12px; margin-bottom: 14px; }
.note p { margin: 0; font-size: 15.5px; }

.solution { margin-bottom: 16px; }

/* ---- summary --------------------------------------------------------- */

.score { font-size: 19px; font-weight: 700; margin: 0 0 14px; }
.reviewlist { list-style: none; margin: 0 0 18px; padding: 0; }
.reviewlist li { border-top: 1px solid var(--line); padding: 9px 0; font-size: 15px; }
.reviewlist .mark { font-weight: 700; margin-right: 8px; }
.reviewlist .mark.right { color: var(--right); }
.reviewlist .mark.wrong { color: var(--wrong); }
/* A typed answer the server has not marked yet -- neither right nor wrong, and
   drawn as neither. */
.reviewlist .mark.unmarked { color: var(--ink-soft); }
.reviewlist .why { display: block; color: var(--ink-soft); font-size: 14px;
                   margin-top: 2px; }

/* ---- what Mr. McClellan wrote back ----------------------------------- */

/* On the START screen, above everything else on it, because the clock has not
   started there -- see the comment in index.html. Written at 360px like the
   rest of this file: one column, nothing beside anything, and the choices in
   the re-read reuse .choices-html so they cannot drift from the real ones. */
.reply {
  margin: 0 0 18px; padding: 14px 12px;
  background: var(--right-bg);
  border: 1px solid var(--right); border-left-width: 4px;
  border-radius: 0 8px 8px 0;
}
.reply-head {
  margin: 0 0 10px; font-size: 17px; font-weight: 700;
  letter-spacing: 0; text-transform: none; color: var(--right);
}
.replyc { margin: 0 0 12px; }
.replyc + .replyc { padding-top: 12px; border-top: 1px solid var(--line); }

/* Which question, in one line that wraps to two on a narrow phone rather than
   pushing the card sideways. */
.reply-which {
  margin: 0 0 2px; display: flex; flex-wrap: wrap; gap: 2px 8px;
  font-size: 13px; color: var(--ink-soft);
}
.reply-code { font-weight: 700; color: var(--ink); }
.reply-stem {
  margin: 0 0 10px; font-size: 14px; line-height: 1.45;
  color: var(--ink-soft);
}
/* His words. A quote, so it reads as something a person wrote. */
.reply-msg {
  margin: 0; padding: 10px 12px;
  background: var(--paper); border: 1px solid var(--line); border-radius: 8px;
  font-size: 16px; line-height: 1.5; color: var(--ink);
}
.reply-open, .reply-sol { display: block; margin-top: 6px; }

/* The question again, read-only. Boxed so it is plainly a quotation of a past
   screen and not the question they are about to be given. */
.replyq {
  margin: 8px 0 0; padding: 12px 10px;
  background: var(--paper); border: 1px solid var(--line); border-radius: 8px;
}
.replyq[hidden] { display: none; }
.replyq .qstem { font-size: 16px; }
.replyq .choices { margin-top: 12px; }
.replyimg { display: block; width: 100%; height: auto; }
.replynote {
  margin: 12px 0 0; font-size: 14px; line-height: 1.45; color: var(--ink);
}
.replysol { margin-top: 8px; font-size: 15px; }
.replysol[hidden] { display: none; }

/* ---- flagging a question --------------------------------------------- */

/* Deliberately quiet. It must be findable, not inviting: a link that looks
   like the main action gets pressed by students who have nothing to report. */
.flagrow { margin: 14px 0 0; display: flex; flex-wrap: wrap; gap: 4px 18px; }
.linkbtn {
  appearance: none; background: none; border: 0; padding: 6px 0;
  font: inherit; font-size: 14px; color: var(--ink-soft);
  text-decoration: underline; text-underline-offset: 3px;
  cursor: pointer; text-align: left; min-height: 32px;
}
.linkbtn:hover, .linkbtn:focus-visible { color: var(--brand); }
.linkbtn:focus-visible { outline: 3px solid #bcd6f2; outline-offset: 2px; }
.linkbtn[disabled] { text-decoration: none; cursor: default; opacity: .6; }

.flagbox {
  margin: 12px 0 0; padding: 14px 12px;
  background: var(--sunken); border: 1px solid var(--line); border-radius: 8px;
}
.flagbox h2 { margin-bottom: 8px; color: var(--ink); text-transform: none;
              font-size: 16px; letter-spacing: 0; }
.flagbox .field { margin-bottom: 8px; }
.flagbox textarea {
  width: 100%; min-height: 96px; padding: 10px 12px;
  font: inherit; font-size: 17px;  /* 16px+ or iOS zooms the page in */
  color: var(--ink); background: #fff;
  border: 1.5px solid var(--line); border-radius: 8px; resize: vertical;
}
.flagbox textarea:focus { outline: 3px solid #bcd6f2; border-color: var(--brand); }
.flagmeta { margin: 0 0 10px; font-size: 13px; color: var(--ink-soft); }

.flagsent {
  margin: 12px 0 0; padding: 9px 12px;
  background: var(--right-bg); border-left: 4px solid var(--right);
  border-radius: 0 8px 8px 0;
  font-size: 15px; color: var(--right); font-weight: 600;
}

/* ---- the little "still saving" note ---------------------------------- */

.saving {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: max(10px, env(safe-area-inset-bottom));
  margin: 0; padding: 7px 14px;
  background: #23262b; color: #fff; font-size: 13px;
  border-radius: 999px; z-index: 20; max-width: calc(100vw - 28px);
}

/* ======================================================================
   REMOVED 2026-09-19: the site's hand-ported copy of the bank's math rules.

   .math, .frac, .lim, .bp, .sq1 and the figure/table rules used to be written
   out here by hand, with values chosen by eye, because the student page had no
   bank markup in it except the worked solutions. It has all of it now, so the
   page loads assets/bank-item.css -- the bank's OWN rules, generated from its
   web render target, whose values audit_phone.py measures at 360px in headless
   Chrome -- and there is one copy rather than two.

   Everything below is what is left: the site's deliberate DEPARTURES from
   those rules. That is what this section has always been for; it is simply no
   longer sitting underneath a second set of the same rules.
   ====================================================================== */

/* ======================================================================
   BANK HTML  (.bankhtml) -- the site's DEPARTURES from the bank's own rules.

   The item rules themselves are no longer written here. They are generated
   into assets/bank-item.css from the bank's web render target, whose values
   audit_phone.py measures at 360px in headless Chrome. That file is loaded
   FIRST; everything below overrides it, and each override says why it exists.
   If a rule here has no reason beside it, it should not be here.
   ====================================================================== */

/* The bank sets these on `body`, which is page-level and deliberately not
   imported -- the site lays out its own page. They have to land somewhere, and
   the scope root is the honest place. */
.bankhtml { font-size: 17px; line-height: 1.55; }

/* The bank draws figure and radical text in Noto Serif. The site loads no web
   fonts at all (no CDNs), so a serif stack stands in. This is the one place the
   site cannot match the bank and knows it. */
.bankhtml svg text.tick,
.bankhtml svg text.orig,
.bankhtml svg text.axl,
.bankhtml svg text.ptlab,
.bankhtml .sq1 text { font-family: Georgia, "Times New Roman", serif; }

/* The bank rules a fraction in #000, which is right on paper. On screen a
   fraction can sit inside coloured text -- a rejected item's state line, a
   misconception note -- and a black rule through red text reads as a mistake. */
.bankhtml .frac .num { border-bottom-color: currentColor; }

/* Subscripts and superscripts: the bank styles neither. Its `.sub` is a CLASS
   (its page subtitle), not the <sub> ELEMENT, so there is nothing upstream to
   inherit and these numbers are the site's own -- tuned on a phone by Randy,
   for the 55 items that use subscripted log bases and sequence terms.
   0.66em is about 11px here, which is where a two-character base like log_(-2)
   stops being legible; `vertical-align` in em resolves against the element's
   OWN size, so -0.42em is 0.28 of the parent em, about half again the browser
   default, which is what makes the drop visible. */
.bankhtml sub { font-size: 0.66em; vertical-align: -0.42em; line-height: 0; }
.bankhtml sup { font-size: 0.74em; }

/* A stacked fraction inside a superscript -- (3)^(t/4), the only one in the
   bank. What read as "too big" was the stack HEIGHT, not the digits, so both
   levers are pulled. ~0.59 of the page em is about 10px, and that is the floor:
   below it the fraction rule and the digit strokes stop separating. */
.bankhtml sup .frac { font-size: 0.80em; vertical-align: -0.30em;
                      line-height: 1; margin: 0 0.05em; }
.bankhtml sup .frac .num,
.bankhtml sup .frac .den { padding: 0 0.14em; }
.bankhtml sup .frac .num { border-bottom-width: 1px; }

/* The bank sets .minicell to 46% because its own page puts the four
   graph-valued choices in one flow. The review page and the student page both
   put each choice in its own grid cell and do the columns themselves, so the
   cell should fill what it is given. */
.bankhtml .minicell { display: block; width: 100%; margin: 0; }

/* AND THE DRAWING INSIDE IT HAS TO SCALE WITH THE CELL.
   The bank's own rule for this is `.figwrap svg { max-width: 100% }`, and a
   graph-valued choice is not in a .figwrap -- it is in a .minicell -- so that
   rule does not reach it. Without this, the four mini-graphs of unit1:1.1.B.1
   render at their intrinsic 150px inside a ~145px cell and push the whole page
   sideways: measured at 369px against a 360px phone before this rule, 360px
   after. It is the one item in the bank with figure-valued choices, and it is
   the reason the review page never showed this -- its panes are wider. */
.bankhtml .minicell svg { max-width: 100%; height: auto; }

/* ---- wider screens --------------------------------------------------- */

@media (min-width: 560px) {
  #main { padding: 22px 18px 48px; }
  .screen { padding: 22px 22px 26px; }
  h1 { font-size: 24px; }
  /* Four across, for the four letter buttons under a fallback item's picture.
     An HTML choice stays one per row at every width: the constraint is the
     length of a piecewise definition, not the width of the screen. */
  .choices { grid-template-columns: repeat(4, 1fr); }
  .choices.choices-html { grid-template-columns: 1fr; }
  .btn { width: auto; min-width: 190px; }
  .row { flex-direction: row; gap: 12px; }
  .btn-quiet { margin-top: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .qprogress-fill { transition: none; }
}

/* The session countdown. Tabular figures so the width does not jitter every
   second, and a colour change in the last minute rather than an alarm. */
.qclock { font-size: 14px; font-variant-numeric: tabular-nums; white-space: nowrap;
          color: var(--ink-soft); font-weight: 600; }
.qclock.low { color: var(--wrong); }

/* ---- ten in a row ---------------------------------------------------- */

/* The celebration decorates; it never interrupts. pointer-events:none is the
   whole contract -- the session is timed, so the next question stays reachable
   while this is on screen and there is nothing to dismiss. */
.streak-canvas {
  position: fixed; inset: 0; z-index: 9998;
  pointer-events: none;
}

.streak-banner {
  position: fixed; left: 50%; top: 18%;
  transform: translateX(-50%);
  z-index: 9999; pointer-events: none;
  padding: 10px 20px; border-radius: 999px;
  background: var(--ink); color: #fff;
  font-size: 17px; font-weight: 700; letter-spacing: 0.01em;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.22);
  animation: streak-in 260ms ease-out both;
}

.streak-banner-out { animation: streak-out 380ms ease-in both; }

@keyframes streak-in {
  from { opacity: 0; transform: translateX(-50%) translateY(-8px) scale(0.94); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0)    scale(1); }
}

@keyframes streak-out {
  from { opacity: 1; transform: translateX(-50%) translateY(0); }
  to   { opacity: 0; transform: translateX(-50%) translateY(-10px); }
}

/* A student who has asked the OS for less motion still gets told -- the banner
   just arrives instead of springing in, and streak.js draws no particles. */
@media (prefers-reduced-motion: reduce) {
  .streak-banner { animation: none; }
  .streak-banner-out { animation: none; opacity: 0; }
}

/* ---- Your progress (students; only when the teacher turns it on) --------- */
/* Its own rules rather than the admin sheet's: the student page does not load
   admin.css, and `.sub` here already means a subscript in a question. */
#screen-progress .tiles { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
#screen-progress .tile { background: var(--sunken); border-radius: 8px; padding: 10px 12px; }
#screen-progress .tile .v { display: block; font-size: 24px; font-weight: 700;
  font-variant-numeric: tabular-nums; color: var(--ink); }
#screen-progress .tile .k { display: block; font-size: 12px; color: var(--ink-soft);
  text-transform: uppercase; letter-spacing: .04em; margin-top: 2px; }
#screen-progress .prog-head { font-size: 16px; margin: 18px 0 6px; }
#screen-progress .rowitem { border-top: 1px solid var(--line); padding: 10px 0; }
#screen-progress .rowitem:first-child { border-top: none; }
#screen-progress .rt { display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: baseline; }
#screen-progress .rcode { font-weight: 700; font-size: 14.5px; overflow-wrap: anywhere; }
#screen-progress .rnums { margin-left: auto; font-size: 13.5px; color: var(--ink-soft);
  font-variant-numeric: tabular-nums; white-space: nowrap; }
#screen-progress .rdesc { font-size: 14px; color: var(--ink-soft); margin-top: 3px; }
#screen-progress .empty { color: var(--ink-soft); font-size: 14.5px; padding: 8px 0; }

/* ---- "pencils down": the pause veil --------------------------------------
   A pause is an instruction to look up, so it covers the question rather than
   sitting beside it. Opaque, not translucent: a readable question behind a
   notice is a question still being worked on. */
.veil { position: fixed; inset: 0; z-index: 60; background: var(--ink);
  display: flex; align-items: center; justify-content: center; padding: 24px; }
.veil[hidden] { display: none; }
.veil-card { max-width: 30rem; text-align: center; color: #fff; }
.veil-head { font-size: 26px; font-weight: 800; margin: 0 0 12px; }
.veil-note { font-size: 17px; line-height: 1.5; margin: 0; color: #e7eaee; }

/* The copyright line sits with whatever else the footer says, and wraps under
   it on a phone rather than squeezing beside it. */
.foot .copyright { display: inline-block; margin-left: 6px; white-space: nowrap; }
@media (max-width: 480px) {
  .foot .copyright { display: block; margin-left: 0; margin-top: 4px; }
}

/* ---- graphing calculator (calculator-active questions only) ----------- */
/* Hidden entirely on a question that does not need one -- see syncCalculator()
   in app.js. The panel needs a real height before Desmos will draw into it. */
.qcalc { margin-top: 18px; }
.qcalc-toggle { width: 100%; }
.qcalc-panel {
  width: 100%;
  height: 380px;
  margin-top: 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
/* Room to work on a desktop, without crowding a phone where the choices
   matter more than the graph. */
@media (min-width: 760px) { .qcalc-panel { height: 460px; } }
.qcalc-err {
  margin-top: 10px; padding: 10px 12px;
  background: var(--wrong-bg); border-radius: var(--radius);
  color: var(--wrong); font-size: 14px;
}
/* NOT red. A calculator question with no key behind it is not a failure the
   student caused or can do anything about except reach for their own handheld,
   and --wrong-bg here would put a second red mark on a screen this badge work
   was trying to keep unambiguous. */
.qcalc-own {
  margin: 0; padding: 10px 12px;
  background: var(--sunken); border: 1px solid var(--line);
  border-radius: var(--radius); color: var(--ink-soft); font-size: 14px;
}

/* ---- the calculator BESIDE the question, on a desktop (2026-09-30) -------

   Randy: "when clicking the desmos button ... have it appear to the right of
   the question and full screen width for both, maybe giving desmos 2/3 of the
   screen and the question the other 1/3 (on desktop of course)."

   THE RATIO IS ONE LINE. --qsplit is the whole knob; nothing else in this block
   encodes the proportion. It is `2fr 3fr` -- 40/60 -- CHOSEN BY RANDY 2026-09-30
   from rendered screenshots, over the 1/3 he first asked for. The measurement
   that decided it: across every calculator-active item the site serves, with the
   calculator open at 1366x768, the last answer choice falls below the fold on
   11 of 146 items at 1fr 2fr and on 2 of 146 at 2fr 3fr. A student who scrolls
   to read choice D scrolls the graph out from under themselves, which is the
   thing this layout exists to prevent. At 1920x1080 both are clean, 0 of 146 --
   so this is entirely a 1366 decision. The eleven failures are all PROSE
   choices, not algebraic ones: four stacked fractions render identically at
   419px and 503px. And the cost is paid by the graph, not the sidebar --
   Desmos's expression list is a fixed ~320px at any width, so the 84px this
   gives the question column comes off the graph (511 -> 435).

   IT IS A STATE, NOT A LAYOUT. body.calcwide is on only while the panel is
   actually open. Closed -- which is how every question starts, and the only way
   a non-calculator question is ever seen -- the column is the 880px centred one
   and this block may as well not exist. That is why widening #main to 880 this
   morning and narrowing the question here do not fight: the narrow column
   exists only during the moments a student has deliberately opened a graphing
   calculator and is looking at the graph.

   AND IT IS GRID, NOT A DOM MOVE. The calculator is still between the answer
   area and "Report a problem" in the markup, where it reads and tabs correctly;
   grid-template-areas is what puts it in the second column, spanning every row.
   Reading order and tab order are identical in both layouts. */
:root {
  --qsplit: 2fr 3fr;

  /* EVERYTHING ON THE PAGE THAT IS NOT THE QUESTION BOX, top and bottom, so
     that "as tall as the screen" can be written as 100vh minus one number.
     Measured off the rendered page, not assumed:

        48  the header bar
        22  #main padding-top
         1  .screen border-top
        22  .screen padding-top      -- 93px above the box
        26  .screen padding-bottom
         1  .screen border-bottom
        16  #main padding-bottom     -- 43px below it, in .calcwide
        42  the footer               -- the copyright line under everything
       ---
       178

     The footer is in there on purpose. Leaving it out is what makes a layout
     that "fits" still show a scrollbar, and a scrollbar is the one thing a
     student reads as "there is more of this below". */
  --qchrome: 178px;
}

/* THE BREAKPOINT IS MEASURED. At 1180px a third of the viewport is a ~370px
   question column, which is a hair wider than the 360px phone every one of the
   1408 items is already proven to render in. Below that the split would be
   asking the choices to do something no test covers; above it, the narrow
   column is a case the phone audit has effectively already answered. Nothing in
   this block can reach a phone. */
@media (min-width: 1180px) {
  /* "full screen width for both". The 880px cap is a reading measure for a
     single column of prose and has nothing to say about a two-column
     workspace. */
  body.calcwide #main { max-width: none; padding-left: 20px; padding-right: 20px;
    /* 48px of air under the card is right when the card is a column of prose
       that has just ended. Here the card IS the screen, and that air is 48px
       taken off the graph. */
    padding-bottom: 16px; }

  body.calcwide #screen-q {
    display: grid;
    grid-template-columns: var(--qsplit);
    /* The LAST row takes the slack from min-height below. If an earlier row
       took it, a short question would stretch the metadata row or push the
       report link away from the choices it belongs to. */
    grid-template-rows: auto auto auto auto 1fr;
    grid-template-areas:
      "qhd gcalc"
      "qpr gcalc"
      "qbd gcalc"
      "qfl gcalc"
      "qfb gcalc";
    column-gap: 22px;
    /* SO THERE IS A COLUMN WORTH FILLING under a two-line question -- and so
       that filling it costs no scrollbar. --qchrome is the page's own furniture
       above and below this box, MEASURED IN THE RENDERED PAGE rather than
       guessed, and it is the only number here that is not a proportion. The
       previous 150 was three separate things short, and the result was a
       calculator whose bottom 40px were under the fold on a 768px laptop --
       on a layout whose entire claim is that the graph and the choices are on
       one screen. */
    min-height: calc(100vh - var(--qchrome));
  }
  /* A HIDDEN SCREEN MUST STAY HIDDEN: `display: grid` above outranks the
     browser's own [hidden] rule, exactly as .choices[hidden], .qcalcb[hidden]
     and .veil[hidden] in this file already have to say. show() drops
     .calcwide on the way out of the question screen, so the two should never
     be true at once -- but "should never" is what the other three rules were
     written after. */
  body.calcwide #screen-q[hidden] { display: none; }

  body.calcwide #screen-q > .qhead     { grid-area: qhd; min-width: 0; }
  body.calcwide #screen-q > .qprogress { grid-area: qpr; }
  body.calcwide #screen-q > .qbody     { grid-area: qbd; min-width: 0; }
  body.calcwide #screen-q > .flagrow   { grid-area: qfl; }
  body.calcwide #screen-q > .feedback  { grid-area: qfb; }

  /* THE CHOICES STAY IN THE LEFT COLUMN, under the stem, rather than running
     full width beneath both. Full-width choices would sit below a calculator
     that is most of the screen tall, so on a 768px laptop the student would
     scroll between the graph and the buttons on every calculator question.
     Stem, choices and graph on one screen is the entire point of the split. */

  /* THE CALCULATOR COLUMN IS THE HEIGHT OF THE SCREEN, and the panel inside it
     is whatever that column has left over. That is the answer to "how tall
     should the calculator be": not 380px, not 460px, not a third number chosen
     here -- the column says, and the button is the only other thing in it.
     The two old numbers were sized for a panel UNDERNEATH the question, where
     the only constraint was not pushing the choices too far down; beside the
     question the constraint is the opposite one and the screen is the answer.

     STICKY, NOT STRETCHED. The column is as tall as the screen allows and then
     STAYS there while the question column scrolls, so a long stem, or the
     worked solution after an answer, never scrolls the graph away.
     align-self:start is what gives sticky somewhere to move: a stretched grid
     item is already the full height of its area and has nothing to slide
     within. It also keeps the calculator its own height on a question whose
     left column is very long, instead of drawing a 2000px graph. */
  body.calcwide #screen-q > .qcalc {
    grid-area: gcalc;
    margin-top: 0;
    align-self: start;
    position: sticky;
    top: 56px;
    display: flex;
    flex-direction: column;
    /* The same page chrome as #screen-q above: this column starts at the top of
       that box, so it ends where the viewport does. */
    height: calc(100vh - var(--qchrome));
    /* The cap keeps a very tall monitor from drawing a calculator nobody can
       take in at once. It does not bind at 1080p (1080 - 168 = 912). */
    max-height: 920px;
  }
  /* The leftover. flex-basis 0 so the button keeps its own height and the panel
     takes everything else; min-height is a floor for a short window, and beats
     the min-height:auto a flex item would otherwise have. The base rule's
     460px, and the 380px below 760px, are both overridden -- they described a
     panel in the linear flow. */
  body.calcwide .qcalc-panel {
    flex: 1 1 0;
    height: auto;
    min-height: 240px;
    max-height: none;
  }
  /* Full width made sense for a button above a stacked panel; beside the
     question it would be a 900px target for a two-word action. align-self is
     what actually shrinks it now that .qcalc is a flex column -- width:auto on
     a stretched flex item is still the whole column. */
  body.calcwide .qcalc-toggle {
    width: auto; min-width: 280px; align-self: flex-start; flex: 0 0 auto;
  }
}

/* "Your progress" in the header bar, reachable from every screen including a
   question in progress. Pushed to the right so it never crowds the title, and
   sized down so it reads as a way out rather than an action competing with the
   answer buttons. */
.bar-progress {
  margin-left: auto;
  font-size: 13px;
  padding: 4px 10px;
  min-height: 0;
  white-space: nowrap;
}
/* No width rule needed: .bar is already flex-wrap: wrap, so on a narrow
   screen the button drops to its own line rather than squeezing the title or
   the student's name. Hiding the name here would have been solving a problem
   the layout does not have. */

/* A second route to the answer, offered and never forced (2026-10-07).
   Closed by default: a student who did not follow the first explanation should
   be able to ask for another way, not be handed two at once. */
details.altsol { margin: .6rem 0 0; border-top: 1px solid #e4e8ee; padding-top: .5rem; }
details.altsol > summary { cursor: pointer; font-weight: 600; color: #0b5cab; list-style: none; }
details.altsol > summary::-webkit-details-marker { display: none; }
details.altsol > summary::before { content: "\25B8\00a0"; display: inline-block; transition: transform .15s; }
details.altsol[open] > summary::before { transform: rotate(90deg); }
details.altsol > .bankhtml { margin-top: .45rem; }
