/* Termbook.
 *
 * Hand-written, no build step (ADR 0010). Tokens and idioms ported from
 * termbook-pricing.html, which is the design the product is sold on.
 *
 * The metaphor is an exercise book: paper ground, pen ink, a red margin rule, a
 * well-done stamp in green. Teacher input is set on feint rules in a handwriting face;
 * generated prose is set clean. That contrast is the product's argument made visible -
 * rough notes in, parent-ready prose out - so it is load-bearing, not decoration.
 */

:root {
  --ink: #16233F;          /* pen ink */
  --ink-soft: #4A5670;
  --ink-faint: #8B94A8;
  --paper: #FBFCFE;        /* exercise book paper */
  --paper-warm: #F4F1E9;
  --rule: #CBD9EE;         /* feint rule */
  --margin-red: #D8323F;   /* the red margin line */
  --stamp: #1F6B4D;        /* well done stamp green */
  --manila: #E9DEC4;
  --line: #DDE3EC;
  --danger: #B3261E;
  --danger-bg: #FDF3F2;
  --shadow: 0 1px 2px rgba(22, 35, 63, .06), 0 8px 24px -12px rgba(22, 35, 63, .18);
  --radius: 10px;
  --radius-sm: 6px;
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: "Literata", Georgia, serif;
  font-size: 17px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  /* Hold the footer to the bottom on a short page. Half the app's pages are one card
     and a sentence - a footer floating in the middle of the window looks like the page
     failed to load the rest of itself. */
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

h1, h2, h3, h4 {
  font-family: "Familjen Grotesk", system-ui, sans-serif;
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -.015em;
  margin: 0;
}

a { color: inherit; }
button { font: inherit; cursor: pointer; }

/* Red, thick, offset. Keyboard users get the same signature as the margin rule. */
:focus-visible {
  outline: 3px solid var(--margin-red);
  outline-offset: 3px;
  border-radius: 4px;
}

.mono {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
}

/* The signature: the red margin line down the left of the page. */
.page-rule {
  position: fixed;
  inset: 0 auto 0 0;
  width: 64px;
  border-right: 1px solid var(--margin-red);
  opacity: .3;
  pointer-events: none;
  z-index: 0;
}
@media (max-width: 1180px) { .page-rule { display: none; } }

.wrap { max-width: 1080px; margin: 0 auto; padding: 0 24px; }
.wrap-narrow { max-width: 620px; margin: 0 auto; padding: 0 24px; }

/* ---------- header ---------- */

header.site {
  position: relative;
  z-index: 2;
  border-bottom: 1px solid var(--line);
  background: var(--paper);
}
.hdr {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 24px;
  max-width: 1080px;
  margin: 0 auto;
}
.logo {
  display: flex;
  align-items: center;
  gap: 9px;
  font-family: "Familjen Grotesk", system-ui, sans-serif;
  font-weight: 700;
  font-size: 20px;
  letter-spacing: -.02em;
  text-decoration: none;
}
/* The spine of a small red exercise book. */
.logo i {
  display: block;
  width: 19px;
  height: 25px;
  border-radius: 2px 3px 3px 2px;
  background: var(--margin-red);
  box-shadow: inset 4px 0 0 rgba(0, 0, 0, .22);
}
.hdr nav { display: flex; align-items: center; gap: 22px; font-size: 15px; }
.hdr nav a { text-decoration: none; color: var(--ink-soft); }
.hdr nav a:hover, .hdr nav a[aria-current="page"] { color: var(--ink); }

.whoami {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 12.5px;
  color: var(--ink-faint);
  /* An email address is long and the least important thing in the header. Truncate
     rather than push the sign-out button off the edge, and drop it entirely on a
     phone - a teacher knows who they are signed in as. */
  max-width: 22ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Below 700px the email goes first - a teacher knows who they are signed in as. Below
   560px the nav wraps to its own line rather than pushing Sign out off the edge, which
   is what a phone or a narrow tablet split-screen actually does. */
@media (max-width: 700px) {
  .whoami { display: none; }
  .hdr { gap: 10px; padding: 14px 18px; }
  .hdr nav { gap: 14px; font-size: 14px; }
  .logo { font-size: 18px; }
}
@media (max-width: 560px) {
  .hdr { flex-wrap: wrap; }
  .hdr nav { width: 100%; justify-content: flex-start; gap: 16px; }
}

/* ---------- page furniture ---------- */

main { position: relative; z-index: 1; padding: 40px 0 72px; flex: 1 0 auto; }

.eyebrow {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 12.5px;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--margin-red);
  margin-bottom: 14px;
}
.page-head { margin-bottom: 32px; }
.page-head h1 { font-size: clamp(28px, 4.5vw, 40px); }
.page-head p { color: var(--ink-soft); margin: 12px 0 0; max-width: 58ch; }

.card {
  border: 1px solid var(--line);
  border-radius: 14px;
  background: #fff;
  padding: 26px;
  box-shadow: var(--shadow);
}
.card + .card { margin-top: 18px; }
.card-manila { background: var(--manila); border-color: #D3C4A2; }

.stack > * + * { margin-top: 18px; }
.row { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.row-between { display: flex; gap: 16px; align-items: center; justify-content: space-between; flex-wrap: wrap; }
/* Card headers keep the title and its status on one line and let the title shrink,
   rather than wrapping unpredictably depending on how long the metadata happens to be.
   Two cards side by side with the status in different places reads as a bug. */
.card-head { display: flex; gap: 16px; align-items: baseline; justify-content: space-between; }
.card-head > :first-child { min-width: 0; }
.card-status { flex: none; }
.grow { flex: 1 1 auto; }

/* ---------- forms ---------- */

label.field-label {
  display: block;
  font-family: "Familjen Grotesk", system-ui, sans-serif;
  font-weight: 600;
  font-size: 15px;
  margin-bottom: 6px;
}
.field-hint {
  font-size: 14px;
  color: var(--ink-faint);
  margin: -2px 0 8px;
}
input[type="text"], input[type="email"], input[type="number"], select, textarea {
  width: 100%;
  padding: 11px 13px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: #fff;
  font-family: inherit;
  font-size: 16px;
  color: var(--ink);
  line-height: 1.5;
}
input:focus, select:focus, textarea:focus { border-color: var(--ink-faint); }

/* Word targets are three digits. A full-width box invites someone to type an essay into
   it, and the spinners add nothing when the range is already bounded. */
input[type="number"] { max-width: 9ch; -moz-appearance: textfield; appearance: textfield; }
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
textarea { resize: vertical; min-height: 76px; }

/* Teacher input sits on feint rules in a handwriting face. Rough notes should look
 * like rough notes; that is what makes the generated prose feel like a step forward. */
textarea.scrawl {
  font-family: "Caveat", "Bradley Hand", cursive;
  font-size: 22px;
  line-height: 34px;
  color: #2A3E6B;
  background-image: repeating-linear-gradient(
    to bottom, transparent 0 33px, var(--rule) 33px 34px
  );
  background-attachment: local;
  padding-top: 6px;
  min-height: 108px;
}

fieldset { border: 0; margin: 0; padding: 0; }
legend { padding: 0; }

/* ---------- buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  font-family: "Familjen Grotesk", system-ui, sans-serif;
  font-weight: 600;
  font-size: 16px;
  padding: 12px 20px;
  border-radius: 8px;
  border: 1px solid transparent;
  text-decoration: none;
  transition: .15s;
  background: none;
  color: var(--ink);
}
.btn-primary { background: var(--ink); color: #fff; }
.btn-primary:hover:not(:disabled) { background: #0D1830; }
.btn-ghost { border-color: var(--line); }
.btn-ghost:hover:not(:disabled) { border-color: var(--ink-faint); }
.btn-danger { color: var(--danger); border-color: var(--line); }
.btn-danger:hover:not(:disabled) { border-color: var(--danger); background: var(--danger-bg); }
.btn-sm { font-size: 14px; padding: 8px 14px; }

/* Approve sits beside decline-with-a-reason, so two forms share one table cell. Without
   this they stack and the reason box takes the full column width. */
.row-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.row-actions form { display: flex; gap: 6px; align-items: center; margin: 0; }
.row-actions input[type="text"] { width: auto; max-width: 16ch; padding: 7px 10px; font-size: 14px; }
.btn:disabled { opacity: .45; cursor: not-allowed; }
.btn-block { width: 100%; }

/* Segmented control, for tone and length. */
.segmented {
  display: inline-flex;
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow: hidden;
  background: #fff;
}
.segmented button {
  border: 0;
  border-right: 1px solid var(--line);
  background: transparent;
  padding: 9px 16px;
  font-family: "Familjen Grotesk", system-ui, sans-serif;
  font-size: 15px;
  color: var(--ink-soft);
}
.segmented button:last-child { border-right: 0; }
.segmented button:hover { background: var(--paper-warm); }
.segmented button[aria-pressed="true"] {
  background: var(--ink);
  color: #fff;
  font-weight: 600;
}

/* ---------- the well-done stamp ---------- */

.stamp {
  display: inline-block;
  background: var(--stamp);
  color: #fff;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: .1em;
  text-transform: uppercase;
  padding: 5px 11px;
  border-radius: 4px;
  transform: rotate(-2deg);
}
/* Waiting on a decision from us. The same stamp without the congratulation, because a
   request that has not been approved yet must not look like a licence that has. */
.stamp-wait { background: var(--ink-faint); }

.pill {
  display: inline-block;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 11.5px;
  letter-spacing: .08em;
  text-transform: uppercase;
  padding: 4px 10px;
  border-radius: 100px;
  border: 1px solid var(--line);
  color: var(--ink-faint);
}
.pill-done { border-color: var(--stamp); color: var(--stamp); }
.pill-drafted { border-color: var(--margin-red); color: var(--margin-red); }

/* ---------- messages ---------- */

.notice {
  border-left: 3px solid var(--ink-faint);
  background: #fff;
  padding: 12px 16px;
  border-radius: var(--radius-sm);
  font-size: 15.5px;
}
.notice-error { border-left-color: var(--danger); background: var(--danger-bg); color: var(--danger); }
.notice-ok { border-left-color: var(--stamp); }

/* ---------- roster ---------- */

.roster { list-style: none; padding: 0; margin: 0; }
.roster li + li { border-top: 1px solid var(--line); }
.roster a {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 13px 4px;
  text-decoration: none;
}
.roster a:hover { background: var(--paper-warm); }
.roster .name { font-family: "Familjen Grotesk", system-ui, sans-serif; font-weight: 600; }
.roster .idx {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 12px;
  color: var(--ink-faint);
  min-width: 2.5ch;
}

/* Progress through a class. The point of the roster is the pile going down. */
.progress {
  height: 6px;
  border-radius: 100px;
  background: var(--line);
  overflow: hidden;
}
.progress > span { display: block; height: 100%; background: var(--stamp); }

/* ---------- generated draft ---------- */

.draft {
  font-size: 17px;
  line-height: 1.75;
  width: 100%;
  border: 0;
  background: transparent;
  padding: 0;
  min-height: 220px;
  color: var(--ink);
  font-family: inherit;
  resize: vertical;
}
.draft:focus { outline: none; }
.draft-meta {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 12px;
  color: var(--ink-faint);
}

.muted { color: var(--ink-soft); }
.faint { color: var(--ink-faint); }
.small { font-size: 14px; }
.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;
}

/* ---------- pupil workspace ---------- */

.workspace { display: grid; grid-template-columns: 1fr; gap: 22px; }
@media (min-width: 980px) {
  .workspace { grid-template-columns: minmax(0, 1fr) 440px; align-items: start; }
  /* The draft stays in view while the teacher scrolls their notes. Reading the two
     against each other is the actual task. */
  .workspace-draft { position: sticky; top: 24px; }
}

.qnum {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  margin-right: 9px;
  border-radius: 50%;
  background: var(--ink);
  color: #fff;
  font-size: 11px;
  vertical-align: 1px;
}

.draft-empty { min-height: 220px; display: flex; flex-direction: column; justify-content: center; }

/* htmx toggles this while a request is in flight. */
.htmx-indicator { opacity: 0; transition: opacity .15s; }
.htmx-request .htmx-indicator, .htmx-indicator.htmx-request { opacity: 1; }

/* Question labels arrive mixed-case from the prompt module - a capitalised heading
   followed by a lowercase gloss. Setting them at a smaller size lets both halves read
   naturally without transforming the string. */
.question-label {
  font-size: 14.5px;
  letter-spacing: .01em;
  line-height: 1.4;
}

/* ---------- settings ---------- */

.length-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
@media (max-width: 600px) { .length-grid { grid-template-columns: 1fr; } }

.preview-block + .preview-block { margin-top: 22px; }
.preview-table { width: 100%; border-collapse: collapse; }
.preview-table td { padding: 7px 12px 7px 0; border-top: 1px solid var(--line); }
.preview-table tr:first-child td { border-top: 0; }

/* The two states worth interrupting for: getting tight, and too tight to work. */
.flag-warn { color: var(--margin-red); }
.flag-block { color: #fff; background: var(--margin-red); padding: 2px 8px; border-radius: 4px; }

/* ---------- recent drafts ---------- */

.recent-list { list-style: none; padding: 0; margin: 0; }
.recent-list li + li { border-top: 1px solid var(--line); }
.recent-item {
  display: flex;
  align-items: baseline;
  gap: 10px;
  width: 100%;
  padding: 9px 4px;
  background: none;
  border: 0;
  text-align: left;
}
.recent-item:hover { background: var(--paper-warm); }
.recent-name {
  font-family: "Familjen Grotesk", system-ui, sans-serif;
  font-weight: 600;
  flex: 1 1 auto;
}
.recent-meta { font-size: 12px; color: var(--ink-faint); }
/* Marks an entry whose care notes are gone, so the flag is visible before restoring
   rather than only in the warning afterwards. */
.recent-flag {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 10.5px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--margin-red);
}

/* ---------- pricing ---------- */

.plans { display: grid; grid-template-columns: 1fr 1.15fr; gap: 22px; align-items: start; }
@media (max-width: 820px) { .plans { grid-template-columns: 1fr; } }

.price { display: flex; align-items: baseline; gap: 9px; margin: 14px 0 4px; }
.price b {
  font-family: "Familjen Grotesk", system-ui, sans-serif;
  font-size: 44px;
  font-weight: 700;
  letter-spacing: -.03em;
}
.price span { font-size: 13px; color: var(--ink-soft); }

.feat { list-style: none; padding: 0; margin: 18px 0 22px; font-size: 15.5px; }
.feat li { display: flex; gap: 11px; padding: 6px 0; color: var(--ink-soft); }
.feat li::before {
  content: "";
  flex: none;
  width: 7px;
  height: 7px;
  margin-top: 9px;
  border-radius: 2px;
  background: var(--margin-red);
  opacity: .55;
}
.card-manila .feat li::before { background: var(--ink); opacity: .4; }

/* ---------- skip link ---------- */

/* The workspace has a lot of header above the notes field. A keyboard user should not
   have to tab the whole nav to reach it. */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 8px;
  z-index: 5;
  background: var(--ink);
  color: #fff;
  padding: 10px 16px;
  border-radius: var(--radius-sm);
  text-decoration: none;
  font-size: 15px;
}
.skip-link:focus { left: 24px; }

/* Staff only, and the one item in the header that is not about your own account. Red
   pen, the same red as the margin rule: this is the marking view. */
/* On a phone the nav wraps to its own line and the buttons get narrow. "Sign out"
   breaking across two lines makes the header taller than the page head below it. */
.hdr nav .btn { white-space: nowrap; }

.hdr nav a.nav-admin { color: var(--margin-red); }
.hdr nav a.nav-admin:hover,
.hdr nav a.nav-admin[aria-current="page"] { color: #A8232E; }

/* ---------- landing ---------- */

.hero { padding: 26px 0 8px; max-width: 40rem; }
.hero h1 {
  font-size: clamp(34px, 5.6vw, 54px);
  letter-spacing: -.025em;
}
.hero p.lede {
  font-size: clamp(18px, 2.2vw, 21px);
  color: var(--ink-soft);
  margin: 18px 0 0;
}

/* The product's argument, made visible rather than described: rough notes on one side,
   parent-ready prose on the other. The CSS at the top of this file calls that contrast
   load-bearing, and until now the landing page only asserted it in words. */
.demo {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  gap: 20px;
  align-items: stretch;
  margin-top: 40px;
}
@media (max-width: 860px) {
  .demo { grid-template-columns: minmax(0, 1fr); }
  /* Rotated a quarter turn so it still reads as "this becomes that" when stacked. */
  .demo-arrow { transform: rotate(90deg); justify-self: center; }
}
.demo-panel { display: flex; flex-direction: column; }
.demo-label {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 11.5px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 10px;
}
.demo-panel .card { flex: 1 1 auto; margin: 0; }
.demo-arrow {
  align-self: center;
  color: var(--margin-red);
  font-size: 26px;
  line-height: 1;
}

/* The static twin of textarea.scrawl. Same hand, same feint rules; not a form control,
   because there is nothing here to type into. */
.scrawl-sample {
  font-family: "Caveat", "Bradley Hand", cursive;
  font-size: 22px;
  line-height: 34px;
  color: #2A3E6B;
  background-image: repeating-linear-gradient(
    to bottom, transparent 0 33px, var(--rule) 33px 34px
  );
  /* No top padding here, unlike the textarea. The rules repeat every 34px from the top
     of the box, so any offset walks the text off its own line. */
  padding: 0;
  margin: 0;
  min-height: 34px;
}
.scrawl-q {
  font-family: "Familjen Grotesk", system-ui, sans-serif;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-faint);
  line-height: 1.4;
  margin: 0 0 2px;
}
/* The feint rule sits on the very bottom edge of a sample, so the next question label
   needs air under it or the rule reads as an underline on the label instead. */
.scrawl-sample + .scrawl-q { margin-top: 20px; }
.demo-prose { font-size: 16.5px; line-height: 1.75; margin: 0; }

/* ---------- how it works ---------- */

.steps {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 26px;
  margin-top: 18px;
}
@media (max-width: 760px) { .steps { grid-template-columns: minmax(0, 1fr); gap: 20px; } }
.step h3 { font-size: 18px; margin-bottom: 6px; }
.step p { margin: 0; color: var(--ink-soft); font-size: 15.5px; }
/* Numbered in red pen, in the margin, where a teacher would number them. */
.step-n {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 12px;
  color: var(--margin-red);
  display: block;
  margin-bottom: 8px;
}

.section { margin-top: 64px; }
.section > h2 { font-size: clamp(22px, 3vw, 28px); }
.section > p.section-lede { color: var(--ink-soft); margin: 10px 0 0; max-width: 56ch; }

/* ---------- footer ---------- */

.site-footer {
  border-top: 1px solid var(--line);
  background: var(--paper);
  position: relative;
  z-index: 1;
  padding: 34px 0 44px;
  margin-top: 72px;
  flex: none;
}
.footer-inner {
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr) auto;
  gap: 30px;
  align-items: start;
}
@media (max-width: 760px) {
  .footer-inner { grid-template-columns: minmax(0, 1fr); gap: 22px; }
}
.logo-sm { font-size: 17px; }
.logo-sm i { width: 15px; height: 20px; }
.footer-promises {
  list-style: none;
  padding: 0;
  margin: 0;
  color: var(--ink-soft);
}
.footer-promises li { padding-left: 18px; position: relative; }
.footer-promises li + li { margin-top: 6px; }
/* A tick in the margin, in marking green. */
.footer-promises li::before {
  content: "\2713";
  position: absolute;
  left: 0;
  color: var(--stamp);
}
.footer-nav { display: flex; flex-direction: column; gap: 8px; }
.footer-nav a { color: var(--ink-soft); text-decoration: none; }
.footer-nav a:hover { color: var(--ink); text-decoration: underline; }
@media (max-width: 760px) {
  .footer-nav { flex-direction: row; gap: 20px; flex-wrap: wrap; }
}

/* ---------- dashboard ---------- */

/* Two across on a desktop: a teacher with one class should not get a card stretched to
   the width of the window, and one with four should see them all without scrolling. */
.board {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
  gap: 18px;
}
.board-class { margin: 0; }
.board-class .progress { margin-top: 2px; }

/* Groups a list of classes by academic year. Only appears when there is more than one
   year to tell apart, so it has to read as a divider rather than a page heading. */
.year-head {
  font-size: 15px;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-weight: 500;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 34px 0 14px;
}
.year-head:first-of-type { margin-top: 0; }

/* ---------- editing a register ---------- */

.edit-roster { list-style: none; padding: 0; margin: 18px 0 0; }
.edit-roster li {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 9px 0;
}
.edit-roster li + li { border-top: 1px solid var(--line); }
.edit-roster input[type="text"] { width: auto; flex: 1 1 12ch; min-width: 12ch; }
/* Checkbox and label as one target: the labels are short and the boxes are small, and a
   teacher doing this on a laptop trackpad at 9pm should not have to aim. */
.edit-flag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--ink-soft);
  white-space: nowrap;
  cursor: pointer;
}
.edit-flag input { width: auto; margin: 0; }
.edit-note { flex-basis: 100%; padding-left: 3.4ch; }
/* The year group sits beside the name on a mixed-age register. Narrow enough that the
   row still fits on a laptop, and it wraps below the name on a phone. */
.edit-year { width: auto; max-width: 15ch; padding: 7px 10px; font-size: 14px; }
