/* The practice hub's own layout (general-studies-aptitude.html), moved out of an inline
   <style> verbatim so other study homes can share it: general-tamil.html uses the
   head, section heads, unit cells, list cards and paper rows. Loaded after
   hub.css and site.css, where the inline block used to sit, so the cascade
   is unchanged. Rules for parts a page does not have simply match nothing. */

/* Square corners on every box on this page. Every radius here and in
   hub.css and site.css reads these two tokens. */
:root { --radius: 0; --radius-lg: 0; }

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  transition: background-color 0.3s, border-color 0.3s, color 0.3s;
}

body {
  font-family: var(--font-ui);
  background-color: var(--surface-page);
  color: var(--text-main);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  overflow-x: hidden;
}

h1, h2, h3, h4 {
  font-family: var(--font-display);
}

/* Main Container */
main {
  flex: 1;
  width: 100%;
  max-width: 1200px;
  padding: 40px 24px 56px;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* flex-start, not center: main takes flex: 1 to push the footer down,
     so centring inside it opened ~200px of dead space above and below the
     two cards on a 900px-tall desktop. */
  justify-content: flex-start;
  /* 22, down from 32. That gap was set when main held two poster cards and
     a share band; it now holds six stacked sections, and at 32 the page
     read as a list of unrelated panels. */
  gap: 22px;
}

/* ── The head ─────────────────────────────────────────────────────────
   A title, what this reader can open, and what they have done — one band,
   read left to right. The page used to open on a centred question and two
   lines of prose, which is a poster; this is a study home, and the first
   thing on it should be the reader's own position.

   .hero-title is site.css's, shared with the landing, syllabus and exams
   pages, so arriving here from any of them is a change of page rather than
   a change of typeface. */
.hub-head {
  width: 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px 32px;
  /* .hero is site.css's and centres its contents, which is right for a
     landing page whose title is the whole first screen. Here the title
     sits at the left of a band with four figures opposite it. Only the
     alignment and the padding are overridden — the title's own size and
     face stay site.css's, so the two front doors cannot drift apart
     again (tests/shared-hero.test.mjs). */
  text-align: left;
  padding: 0;
}

.hub-head-left { min-width: 0; }

/* One line under the title: how much of the bank is open to this reader.
   Rewritten by the script at the foot of the page once /api/me answers;
   until then it says what is true for everybody, which is the right guess
   for an anonymous visit and is never wrong for a signed-in free reader
   either. */
.access-line {
  /* 0.85, down from 0.95. This line is a fact about the reader's account,
     not a subtitle for the page — it sits under a title at up to 3.1rem
     and beside four figures at 1.5rem, and at 0.95 it was competing with
     both for the second thing the eye landed on. */
  font-size: 0.85rem;
  color: var(--text-muted-on-page);
  line-height: 1.55;
}

.access-line .unlock-btn {
  margin-left: 10px;
  vertical-align: baseline;
}

/* ── The four figures ─────────────────────────────────────────────────
   This device's whole history in one row, at the top of the page rather
   than at the foot of it. Every number here was already computed for the
   tables further down — same roll-up, same definition of "correct" — so
   the strip is a second view of one answer, never a second count.

   Hidden until there is a history: a first-time visitor shown four zeroes
   has been told nothing, and told it in the most prominent place on the
   page. */
.hub-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 26px;
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 0.86rem;
  color: var(--text-muted-on-page);
  font-variant-numeric: tabular-nums;
}

.hub-stats[hidden] { display: none; }

.hub-stats li {
  display: flex;
  flex-direction: column;
  line-height: 1.25;
  white-space: nowrap;
}

.hub-stats b {
  font-family: var(--font-display);
  font-size: 1.5rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text-main);
}

.hub-stats .is-right b { color: var(--primary-text); }
.hub-stats .is-redo b { color: var(--accent-amber); }
/* The linked figure is one flex ITEM inside its <li>, so the column
   direction on the li did nothing to what is inside the anchor and "74 to
   redo" ran along one line beside three stacked figures. The anchor
   carries the same column itself. */
.hub-stats a {
  display: flex;
  flex-direction: column;
  line-height: inherit;
  color: inherit;
  text-decoration: none;
}
.hub-stats a:hover { text-decoration: underline; text-underline-offset: 3px; }
.hub-stats a:focus-visible { outline: 2px solid var(--primary); outline-offset: 3px; border-radius: 4px; }

/* ── Continue ─────────────────────────────────────────────────────────
   Deliberately not one of the cells below: it is the one control a
   returning student is looking for, and it must not be one of twelve
   equal-weight choices. Full width, above them, and gone entirely on a
   first visit.

   The whole card is the link now. It used to be an anchor sitting beside a
   list of counts, which made the tap target the words rather than the
   panel and printed the same three figures the head above now carries. */
.resume-card {
  width: 100%;
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 2px 20px;
  padding: 14px 20px;
  border: 1px solid var(--primary);
  border-left: 4px solid var(--primary);
  border-radius: var(--radius-lg);
  background: var(--primary-glow);
  text-decoration: none;
  color: inherit;
}

/* The `hidden` attribute on this element did nothing until now, and the
   card it was supposed to hide is the one thing on the page that must not
   appear before it has something to say.

   display:grid above and the browser's own `[hidden] { display: none }`
   are both specificity 0,1,0, and an author sheet beats the UA sheet at a
   tie — so the grid won, every time. A first-time visitor with no history
   got the card anyway: a teal panel reading "Continue" above an empty
   label and an arrow to nowhere.

   Stated explicitly rather than by raising the grid rule's specificity,
   because this is the pairing that has to survive somebody adding another
   display to .resume-card later. */
.resume-card[hidden] { display: none; }

.resume-kicker {
  grid-column: 1;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--primary-text);
}

.resume-label {
  grid-column: 1;
  font-family: var(--font-display);
  font-size: 1.2rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text-main);
  min-width: 0;
}

/* Under the unit name, not beside it: "General Science" and "Life Science
   › Genetics" are two different sizes of answer to "where was I", and
   setting them on one line makes the reader parse which is which. The
   label keeps the display face; this is plain UI type, one step down. */
.resume-scope {
  grid-column: 1;
  font-size: 0.8rem;
  color: var(--text-muted);
}

.resume-scope[hidden] { display: none; }

.resume-card:hover .resume-label { text-decoration: underline; text-underline-offset: 4px; }
.resume-card:focus-visible { outline: 2px solid var(--primary); outline-offset: 3px; }

/* ── Where you stand ─────────────────────────────────────────────────────
   The resume card says what this device was doing. This one says what it
   adds up to, which is the question a student actually revises from: not
   "how much have I done" but "what am I bad at, and what did I leave half
   finished".

   Quieter than the resume card on purpose. That card is a control and this
   is a readout, so it takes the plain card surface and a soft border
   rather than the teal wash — two teal blocks stacked would leave neither
   looking like the thing to press. */
/* Not a card itself — a stack of three. A bordered box holding three
   bordered boxes reads as one panel with dividers in it, whatever the
   inner borders do, because the outer frame is what the eye takes as the
   edge of the thing. So the frame comes off and each answer stands on its
   own surface, with the heading floating above all three. */
.stand-card {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* Two boxes arrive together in one container, so it carries the same gap
   rather than letting its own children stack flush. */
#stand-detail {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* No umbrella heading. "Where you stand" over three boxes that each say
   what they are was a label for a label — and once the box titles came out
   onto the page, it was a fourth heading in a stack of four. The section
   keeps the name for screen readers, where the landmark still needs one. */

/* Title, then the box it belongs to. Outside, because a heading inside a
   bordered box is inside the thing it names: it has to be read before the
   eye can decide whether to read the table under it, and a border between
   them is a step in the way. */
.stand-group {
  display: flex;
  flex-direction: column;
  gap: 7px;
}

.stand-title {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 8px;
  margin: 0;
  padding-left: 2px;
  font-family: var(--font-display);
  font-size: 1.02rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text-main);
}

.stand-title small {
  font-family: var(--font-ui);
  font-size: 0.76rem;
  font-weight: 400;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* "1,204 of 1,410 open to you". The locked remainder is named here rather
   than folded into the bar: a free reader measured against the whole bank
   would be shown a ceiling they cannot move and told it was progress. */


/* ── The three boxes ─────────────────────────────────────────────────────
   Weakest and Pending answer two different questions — "what am I bad at"
   and "what did I walk away from" — and they used to run together as one
   list of six rows under two small kickers, which read as a single
   ranking. A box each, with a heading, and the columns named.

   Named columns are the point. "Human Diseases" over "Life Science" told a
   reader nothing about which was the theme and which the topic, and no
   amount of type weight fixes that — only a header does. */
/* An actual box. A border-top and some margin is a separator, and three
   things separated by lines still read as one list — which is what made
   the summary look like a caption for the two tables under it rather than
   an answer of its own. */
.stand-box {
  padding: 14px 16px 16px;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  /* A card's surface, not the page's. The box is defined by its border;
     giving it the page colour as well put it a shade away from the
     table's header band inside it, and the grid stopped reading. */
  background: var(--surface-card);
  box-shadow: var(--shadow-sm);
}

/* A grid, not a list with a rule or two. Every cell carries a border, so
   a reader can follow a row across four columns and a column down four
   rows — which is the whole reason these are tables and not paragraphs.
   --border-color, so it is visible in both themes; --line-soft was not. */
.stand-card {
  --grid-line: #8FA9A9;
}

html.dark-mode .stand-card,
body.dark-mode .stand-card {
  --grid-line: #4E8A93;
}

.stand-table {
  width: 100%;
  border-collapse: collapse;
  border: 1px solid var(--grid-line);
  border-radius: var(--radius);
  overflow: hidden;
  font-size: 0.86rem;
}

.stand-table th,
.stand-table td { border: 1px solid var(--grid-line); }

.stand-table thead th {
  padding: 4px 8px 5px;
  background: var(--surface-sunken);
  border-bottom: 2px solid var(--grid-line);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
  text-align: left;
}

.stand-table thead th:last-child { text-align: right; }

/* "ACCURACY" set in uppercase is wider than any value under it, and on a
   phone it was forcing its own column wide enough to push itself off the
   right edge of the card. The short label is the same column to a reader
   who can see the figures in it. */
.stand-table .th-short { display: none; }

/* The unit is in the markup twice and on screen once: its own merged
   column where there is room for four, folded into the theme cell on a
   phone where there is not. Four columns of NAMES inside a card is about
   280px of usable width, and at that size the topic column fell to 78px
   and split "Simplification" down the middle. display:none keeps the
   unused copy out of the accessibility tree as well as off the page.
   Same trade, same reasoning, as the progress table. */
.unit-inline,
/* Theme and unit as a caption inside the topic cell, for the phone where
   they have no column of their own. On the first row of each theme group
   only — the rows the merged cell used to cover — so it groups exactly as
   the column did rather than repeating. */
.topic-context { display: none; }

/* The two merged cells. A rowspan needs a top alignment or the name floats
   to the middle of the group it covers and stops reading as its heading. */
.stand-unit,
.stand-theme {
  padding: 9px 8px;
  font-weight: 600;
  color: var(--text-main);
  text-align: left;
  vertical-align: top;
}

.stand-unit {
  width: 26%;
  font-size: 0.78rem;
  background: var(--surface-sunken);
}

.stand-theme { width: 28%; font-size: 0.82rem; }

.stand-topic { padding: 9px 8px; }

/* Inline, not inline-flex. As a flex box the arrow is its own item and
   stays at the end of the FIRST line, so "Highest Common Factor (HCF)"
   wrapping put the arrow before "(HCF)" rather than after it. Inline lets
   it flow with the words and land after the last one. */
.stand-topic a {
  color: var(--text-main);
  text-decoration: none;
}

/* Bound to the last word by the &nbsp; in the markup, so a topic that
   wraps takes the arrow with it instead of leaving it alone on a line of
   its own — which is what a plain margin did to "Highest Common Factor
   (HCF)" at 360px. */
.stand-topic .stand-go { margin-left: 0; }

.stand-topic a:hover { color: var(--primary-text); text-decoration: underline; text-underline-offset: 3px; }
.stand-topic a:focus-visible { outline: 2px solid var(--primary); outline-offset: 3px; border-radius: 4px; }

.stand-figure {
  padding: 9px 8px;
  text-align: right;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--accent-amber);
  white-space: nowrap;
}

.stand-figure.is-pending { color: var(--primary-text); }
.stand-go { color: var(--text-faint); font-weight: 700; }
.stand-topic a:hover .stand-go { color: var(--primary-text); }

/* ── While it is working ─────────────────────────────────────────────────
   The card commits to its space as soon as there is a history to report,
   and shows the shape of what is coming rather than appearing fully formed
   a second later. Only the grey blocks and the note are on screen while
   .is-loading holds; everything real is hidden by the same switch, so
   there is never a half-drawn card. */
.stand-card.is-loading #stand-detail { display: none; }

.stand-card:not(.is-loading) .stand-skeleton,
.stand-card:not(.is-loading) .stand-loading-note { display: none; }

/* ── Before there is anything to stand on ────────────────────────────────
   The three switches are mutually exclusive by construction: .is-loading
   shows the skeleton, .is-empty shows the invitation, and neither class
   shows the report. Written as three rules over one element each rather
   than as a display:none race, so a card can never be caught showing two
   of the three. */
.stand-card.is-empty #stand-detail,
.stand-card.is-empty .stand-skeleton,
.stand-card.is-empty .stand-loading-note { display: none; }

.stand-card:not(.is-empty) #stand-empty { display: none; }

/* The same box the two tables stand in, so the card does not change
   shape when the first answer turns one into the other. */
#stand-empty p {
  margin: 0;
  font-size: 0.88rem;
  line-height: 1.55;
  color: var(--text-muted);
}

#stand-empty p + p { margin-top: 8px; }

#stand-empty .stand-empty-lead {
  color: var(--text-main);
  font-weight: 600;
}

/* Dressed as a box too, so the wait occupies the same shape the answer
   will — a bare stack of grey bars on the page background would have the
   card arrive AND grow a frame. */
.stand-skeleton {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px 16px 16px;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  background: var(--surface-card);
  box-shadow: var(--shadow-sm);
}

.sk-line, .sk-block {
  display: block;
  border-radius: var(--radius);
  background: var(--surface-sunken);
  /* A pulse, not a sweep. The sweeping-highlight kind needs a gradient the
     width of the element and a transform to move it, and at three blocks
     on one card it is more animation than the wait deserves. */
  animation: sk-pulse 1.4s ease-in-out infinite;
}

.sk-bar { height: 10px; }
.sk-figs { height: 14px; width: 72%; animation-delay: 0.15s; }
.sk-block { height: 96px; margin-top: 6px; animation-delay: 0.3s; }

@keyframes sk-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}

/* Said in words as well as in grey, and role="status" so it is announced
   rather than only drawn. */
.stand-loading-note {
  margin: 0;
  font-size: 0.82rem;
  color: var(--text-muted);
}

@media (prefers-reduced-motion: reduce) {
  .sk-line, .sk-block { animation: none; }
}

.stand-hint {
  margin: 0;
  font-size: 0.82rem;
  color: var(--text-muted);
}

@media (max-width: 520px) {
  .stand-card { gap: 10px; }
  #stand-detail { gap: 10px; }

  /* Every pixel of box padding comes out of the topic column inside it, so
     the boxes are tighter here than the room on a desktop would suggest. */
  .stand-box,
  .stand-skeleton { padding: 10px 8px 10px; }

  /* Columns, at this width too. The rows were stacked here — theme as a
     heading with its topics indented under it — which is the safer shape
     for long names, and it was rejected on the grounds that a table is
     easier to read across: which topic, under which theme, at what figure,
     all on one line.

     So it stays a table and the space is bought back instead. The type
     steps down, the padding closes up, and the theme column is capped at
     a third so a long theme wraps inside its own cell rather than pushing
     the topic and the figure out of the row. The merged cell is doing real
     work here: a theme that covers three topics is set once, so most rows
     spend nothing on it at all. */
  /* Fixed, so the widths below are obeyed rather than treated as hints —
     with auto layout the ACCURACY header's own min-content decided the
     column and the figures under it fell off the card. */
  .stand-table { font-size: 0.8rem; table-layout: fixed; }
  .stand-table .th-long { display: none; }
  .stand-table .th-short { display: inline; }

  .stand-table thead th {
    padding: 0 6px 4px;
    font-size: 0.62rem;
    letter-spacing: 0.04em;
  }

  /* Theme and unit both give up their columns here and become a caption
     over the group they used to head. The topic column then holds the
     longest name in the bank — "தெரிந்துதெளிதல்" wants 153px — instead of
     breaking it across two lines with no hyphen to say it had been split.

     The body cells AND their headers: hiding only the <td>s once left a
     four-column header over three-column rows, and every value sat one
     column left of its own heading. */
  .stand-unit,
  .stand-theme,
  .stand-table thead th:nth-child(1),
  .stand-table thead th:nth-child(2) { display: none; }

  .topic-context {
    display: block;
    margin-bottom: 3px;
    font-size: 0.68rem;
    font-weight: 600;
    line-height: 1.3;
    color: var(--text-muted);
  }

  .topic-context span { font-weight: 400; color: var(--text-faint); }
  .topic-context span::before { content: ' · '; }

  .stand-theme small { font-size: 0.66rem; }

  .stand-topic {
    padding: 8px 6px;
    overflow-wrap: break-word;
  }

  /* Pinned narrow. Under table-layout: fixed the topic and the figure were
     the two auto columns and split what the theme left EQUALLY — so a cell
     holding "0%" was as wide as one holding "Highest Common Factor (HCF)".
     42px holds "100%"; the topic takes everything else. */
  /* On the HEADER cell, because table-layout: fixed reads the first row
     and .stand-figure is only on the <td>s — the width was declared where
     nothing was looking at it. */
  .stand-table thead th:last-child { width: 42px; }
  .stand-figure { padding: 8px 4px 8px 6px; }

  /* The arrow is decoration, and at this width it is decoration that costs
     a wrap: "Highest Common Factor (HCF)" fits its cell without it and
     needs a second line with it. The whole cell is the tap target and the
     link is styled as one. */
  .stand-topic .stand-go { display: none; }

  .stand-title { font-size: 0.94rem; }
  .stand-group { gap: 6px; }
}

/* --- Section heads ---------------------------------------------------
   A name and the size of what is under it, on one line. Both sections
   below carry the same pair, so "By unit" and "By exam" read as two halves
   of one choice rather than as a heading and a card. */
.hub-sec {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.sec-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 2px 16px;
  padding: 0 2px;
}

.sec-head h2 {
  font-size: 1.15rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text-main);
}

/* The counts, as a run of spans with a middot between them. Written by
   scripts/build_static_counts.mjs, which emits one <span> per fact. */
.sec-meta,
.band-meta,
.rail-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0 6px;
  margin: 0;
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--text-muted-on-page);
  font-variant-numeric: tabular-nums;
}

/* The separator hangs off the END of the span before it, never off the
   START of the one after. As a ::before it belonged to the following span
   and wrapped with it, so "8 units · 102 themes · 630 topics" in the
   syllabus tile broke to a second line reading "· 630 topics" — a middot
   opening a line, which reads as a bullet nobody asked for. */
.sec-meta span:not(:last-child)::after,
.band-meta span:not(:last-child)::after,
.rail-meta span:not(:last-child)::after {
  content: '\00B7';
  margin-left: 6px;
  color: var(--text-faint);
}

/* --- The unit map ----------------------------------------------------
   Twelve cells, one per unit, and each one is both the door into that unit
   and the readout of how far through it this reader is. That pairing is
   the point of the page: the old hub asked "what do you want to practise?"
   and answered with seven identical panels, none of which knew anything
   about the reader. A student picking a unit is choosing where to spend an
   evening, and the fact that decides it -- how much of this is left, and
   how it has been going -- was two scrolls away at the foot of the page.

   The names and the question counts are written into the HTML by
   scripts/build_static_counts.mjs; only the bar is drawn here. So the grid
   is complete and useful on the first paint, and gains the reader's own
   history a moment later. */
/* Two labelled runs of cells: General Studies, then Aptitude & Mental
   Ability. Each run is its own grid so the two cells of the second keep
   the width of the cells above them. */
.unit-grid {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.unit-subhead {
  margin: 6px 2px 0;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted-on-page);
}

.unit-subgrid {
  width: 100%;
  display: grid;
  /* 240, and the number is arithmetic rather than taste. main is capped at
     1200 and auto-fill fits floor((1200 + gap) / (min + gap)) columns, so
     200px gave FIVE — and twelve cells in five columns is 5, 5, 2, a row
     that stops two thirds of the way across and makes the grid look
     unfinished. 240 lands on four (1212 / 252 = 4.8), which divides twelve
     exactly, and it still falls to three around 1000px and two around 750
     without a media query saying so. Anything above 291 would break the
     four, because that is what a quarter of this column measures. */
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 12px;
}

/* General Studies, one line per split unit (3A/3B, 5A/5B, 6A/6B/6C)
   under its own label and rule; whole units share a line. Each line is
   a plain .unit-subgrid, so the cells keep their usual size. */
.unit-lines { display: flex; flex-direction: column; gap: 12px; }
.unit-lines > * + * { padding-top: 12px; border-top: 1px solid var(--border-strong); }
.unit-family { display: flex; flex-direction: column; gap: 6px; }
.unit-family-label {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.unit-cell {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 13px 15px 13px 13px;
  background: var(--surface-card);
  border: 1px solid var(--line-soft);
  /* The unit's own hue, from the twelve in tokens.css -- the same twelve
     the reader's navigator dots and the syllabus map use, so a unit is the
     same colour wherever a student meets it. Set per cell as --u in the
     generated markup; nothing here hard-codes a colour.

     A rule down the edge, and NEVER ink behind text. The twelve were
     picked as dots and four of them fail AA under white type at this size
     -- --unit-4 is #f59e0b and --unit-8 is #84cc16 -- so a coloured chip
     with the unit number in it would have been unreadable in a third of
     the grid. As an edge the hue carries no information the name does not
     also carry, which is the only condition under which colour alone is
     allowed to do anything here. */
  border-left: 4px solid var(--u, var(--primary));
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  text-decoration: none;
  color: inherit;
  transition: box-shadow 0.2s, transform 0.2s;
}

.unit-cell:hover {
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}

.unit-cell:focus-visible { outline: 2px solid var(--primary); outline-offset: 3px; }

.unit-no {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.unit-name {
  font-family: var(--font-display);
  font-size: 0.98rem;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -0.01em;
  color: var(--text-main);
  overflow-wrap: break-word;
  /* Pushes the bar to the foot of the cell, so twelve cells of differing
     name lengths still line their bars up with each other -- which is the
     whole reason they are bars and not numbers. */
  margin-bottom: auto;
}

.unit-cell:hover .unit-name { color: var(--primary-text); }

/* The slot the script writes the bar into. Until it does -- and for a
   reader with nothing answered in this unit, and for one with no
   JavaScript -- it holds the unit's question count, which is still an
   answer to "how big is this". */
.unit-read {
  display: flex;
  flex-direction: column;
  gap: 5px;
  font-size: 0.78rem;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

.unit-read .prog-bar { height: 6px; min-width: 0; }

.unit-figs b {
  font-weight: 600;
  color: var(--text-main);
}

.unit-figs .is-right { color: var(--primary-text); }

/* --- By exam ---------------------------------------------------------
   A band rather than a cell, because it is the other whole way into the
   bank and must not read as a thirteenth unit. It carries a sample of what
   is inside for the same reason the unit cells carry counts: a name that
   lists its contents is a name a reader can decide on. */
.band-card {
  width: 100%;
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 6px 20px;
  padding: 16px 20px;
  background: var(--surface-card);
  border: 1px solid var(--line-soft);
  border-left: 4px solid var(--accent-amber-line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  text-decoration: none;
  color: inherit;
  transition: border-color 0.2s, box-shadow 0.2s;
}

.band-card:hover {
  border-color: var(--accent-amber-line);
  box-shadow: var(--shadow-md);
}

.band-card:focus-visible { outline: 2px solid var(--primary); outline-offset: 3px; }

.band-head {
  grid-column: 1;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 14px;
}

.band-title {
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text-main);
}

/* Four exam names, run together on one line with middots. Not a column
   like the unit names: these are a sample of eighty-two, not a list to
   pick from, and set vertically they would claim the same weight as the
   grid above. */
.band-list {
  grid-column: 1;
  display: flex;
  flex-wrap: wrap;
  gap: 0 6px;
  font-size: 0.86rem;
  color: var(--text-muted);
}

.band-list span:not(:last-child)::after {
  content: '\00B7';
  margin-left: 6px;
  color: var(--text-faint);
}

.band-card .go-btn { --btn-bg: var(--accent-amber); }

/* --- By group and By exam ------------------------------------------
   Each card is a heading link and a list of papers, one row per paper.
   Groups stack in one column; the two newest years sit side by side and
   stack on a phone. */
.group-grid { display: grid; gap: 12px; }

/* One year under another, newest first: a column of years reads as a
   timeline, where side-by-side cards of different heights did not. */
.year-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
  align-items: start;
}

/* A year with more than four papers shows four until its button is pressed
   (assets/js/more-toggle.js). The rows are all in the markup either way. */
.list-card[data-collapse]:not(.is-open) .paper-list > li:nth-child(n+5) { display: none; }
.more-toggle {
  align-self: flex-start;
  padding: 5px 12px;
  border: 1px solid var(--line-soft);
  background: var(--surface-card);
  color: var(--primary-text);
  font: 600 0.8rem var(--font-ui);
  cursor: pointer;
}
.more-toggle:hover { background: var(--surface-sunken); }
.more-toggle:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }

.list-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 14px 16px;
  background: var(--surface-card);
  border: 1px solid var(--line-soft);
  border-left: 4px solid var(--accent-amber-line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}

.list-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 14px;
}

.list-title {
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text-main);
  text-decoration: none;
}

.list-title::after { content: '\00A0\2192'; color: var(--accent-amber); }
.list-title:hover { color: var(--primary-text); }
.list-title:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

/* Group IV / VAO vs All, on the Group IV card. Pushed to the right of
   the heading; the hidden rows and count follow the card's data-show. */
.show-toggle { margin-left: auto; display: inline-flex; border: 1px solid var(--line-soft); }
.show-toggle button {
  padding: 5px 10px;
  border: 0;
  background: var(--surface-card);
  color: var(--text-muted);
  font: 600 0.78rem var(--font-ui);
  cursor: pointer;
}
.show-toggle button + button { border-left: 1px solid var(--line-soft); }
.show-toggle button[aria-pressed="true"] { background: var(--primary-text); color: var(--surface-card); }
.show-toggle button:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
.list-card[data-show="core"] .is-extra,
.list-card[data-show="core"] [data-when="all"],
.list-card[data-show="all"] [data-when="core"] { display: none; }

.paper-list { list-style: none; margin: 0; padding: 0; }
.paper-list li + li { border-top: 1px solid var(--line-soft); }

.paper-row {
  display: flex;
  align-items: baseline;
  gap: 4px 12px;
  padding: 9px 4px;
  color: var(--text-main);
  text-decoration: none;
  font-size: 0.82rem;
  line-height: 1.35;
}

.paper-row:hover { background: var(--surface-sunken); }
.paper-row:hover .paper-name { color: var(--primary-text); }
.paper-row:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
.paper-year { flex: none; font-weight: 700; color: var(--primary-text); font-variant-numeric: tabular-nums; }
.paper-name { flex: 1; min-width: 0; }
.paper-facts {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.paper-row.is-locked .paper-name,
.paper-row.is-locked .paper-year { color: var(--text-muted); }
.paper-facts .lock { width: 12px; height: 12px; }


/* --- The button on a band --------------------------------------------
   Every band on this page is a link, and the whole panel has always been
   the tap target. What it did not have was anything that LOOKED like a
   control: a title, some counts and a bare arrow at the far edge reads as
   a row in a list, and a student who has not been taught that whole cards
   are clickable has no reason to try.

   So each band ends in a filled pill. It is the one shape nobody has to be
   taught, and it is what the landing page's own CTA already looks like
   (.cta-btn in app.css, whose colours these follow).

   The unit cells deliberately do NOT get one. Twelve filled buttons in a
   grid is a wall of colour, and it would put the loudest element on the
   page twelve times over on the section that is meant to be read as a map.

   NOT a <button>, and not a nested <a>: both are invalid inside the anchor
   that wraps the band, and the browser would break the markup apart.
   A styled <span> inside the existing link is the correct construction —
   the card is still one link with one tap target.

   aria-hidden, all of them. The link is already named by the title and the
   counts beside it; a screen reader that also read "Open" would be hearing
   the affordance rather than the destination. This pill is for the eye. */
.go-btn {
  /* Filled with --primary-text rather than --primary: white on the logo
     teal is 3.96:1 and this label is not large text. One step deeper
     clears AA at 6.03:1 — the same trade .cta-btn makes, for the same
     reason. */
  --btn-bg: var(--primary-text);
  --btn-ink: #FFFFFF;

  grid-column: 2;
  grid-row: 1 / -1;
  align-self: center;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 0 16px;
  min-height: 40px;
  border-radius: var(--radius);
  background: var(--btn-bg);
  color: var(--btn-ink);
  font-family: var(--font-ui);
  font-size: 0.85rem;
  font-weight: 600;
  white-space: nowrap;
  transition: box-shadow 0.2s;
}

/* All three fills lighten in dark mode — that is what the dark palette
   does to every accent — so white ink would stop being readable on any of
   them and the dark surface takes the dark ink instead. Exactly the flip
   app.css makes for .cta-btn. */
html.dark-mode .go-btn,
body.dark-mode .go-btn { --btn-ink: #0B1A1C; }

.go-arrow { font-weight: 700; transition: transform 0.2s; }
a:hover > .go-btn { box-shadow: var(--shadow-sm); }
a:hover > .go-btn .go-arrow { transform: translateX(3px); }

/* And Saved keeps the bookmark blue, for the reason in .rail-tile-saved. */
.rail-tile-saved .go-btn { --btn-bg: var(--accent-blue); }

/* --- The rail --------------------------------------------------------
   Five things a student looks up or comes back to, rather than practises
   from. They were six cards the size of the two that open the bank, which
   made the page a menu of equals and buried the choice it exists to offer.

   Then they were a row of five small tiles, and that traded one problem
   for another: five boxes of equal size in one row, separated by a 10px
   gap, read as one strip of text rather than as five destinations. A
   reader could not tell where one ended and the next began.

   So they take the shape of the exam band above them — a full-width card
   with a rule down its edge, its counts beside its name and an arrow at
   the end — stacked with real space between them. Six bands in a column,
   and the eye counts six things.

   The hierarchy survives the change because the bands are not identical.
   By exam is the other whole way INTO the bank: it keeps the warm rule,
   the larger title, and a second line sampling what is inside. These five
   are one line each and take the cool one. */
.hub-rail {
  width: 100%;
  display: flex;
  flex-direction: column;
  /* 14, against main's 22 between sections. Wide enough that each band is
     its own object, tight enough that the five still read as one group
     rather than as five more sections of the page. */
  gap: 14px;
}

.rail-tile {
  /* The band's own two colours, overridable per tile. Teal by default:
     the one accent this system spends on anything that is not an answer
     being right or wrong. */
  --rail-rule: var(--primary);
  --rail-ink: var(--primary-text);

  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 0 20px;
  padding: 14px 20px;
  min-height: var(--tap-target);
  background: var(--surface-card);
  border: 1px solid var(--line-soft);
  border-left: 4px solid var(--rail-rule);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  text-decoration: none;
  color: inherit;
  transition: box-shadow 0.2s, transform 0.2s;
}

/* Saved is the exception, and it is not decoration. --accent-blue IS the
   bookmark, everywhere else in the product (see the note on it in
   tokens.css); the tile that opens the bookmarks wears the colour of the
   thing it holds. The other four stay teal rather than taking a hue each:
   the twelve unit colours are reserved for units, and inventing four more
   for a reference rail is exactly the drift that palette exists to stop. */
.rail-tile-saved {
  --rail-rule: var(--accent-blue-line);
  --rail-ink: var(--accent-blue);
}

.rail-tile:hover {
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}

.rail-tile:focus-visible { outline: 2px solid var(--primary); outline-offset: 3px; }

/* Name and counts on one line, the way the exam band sets its own. They
   wrap onto two lines together on a phone rather than the counts being
   pushed under a name that still has room beside it. */
.rail-head {
  grid-column: 1;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 14px;
  min-width: 0;
}

.rail-name {
  font-family: var(--font-display);
  /* A step under .list-title's 1.15. These five are where a reader goes
     after they have chosen, not how they choose. */
  font-size: 1.02rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text-main);
}

.rail-tile:hover .rail-name { color: var(--rail-ink); }


/* Footer */
footer {
  width: 100%;
  padding: 24px;
  text-align: center;
  border-top: 1px solid var(--border-color);
  font-size: 0.8rem;
  color: var(--text-muted);
  font-weight: 500;
  background-color: var(--surface-card);
}

/* Wide desktop: the head is the largest thing on the page, so the extra
   width becomes size rather than margin. */
@media (min-width: 1100px) {
  /* The access line does NOT grow with the column. Everything else on
     this band scales because more width means more room for the figures;
     that line is the one element the extra width should not promote. */
  .hub-stats b { font-size: 1.7rem; }
  .unit-cell { padding: 15px 17px 14px 15px; }
}

/* "General Studies & Aptitude" is two lines of display type on a phone; the
   short name says the same in one. [data-narrow] is view-mode.js's mark for
   a phone, set in both layouts — the desktop one is 1024px wide, so the
   width query below alone would miss it. */
.title-short { display: none; }
[data-narrow] .title-long { display: none; }
[data-narrow] .title-short { display: inline; }

@media (max-width: 768px) {
  .title-long { display: none; }
  .title-short { display: inline; }

  /* The head stacks: the title and the four figures each need the full
     width on a phone, and side by side they were two truncated columns. */
  .hub-head { align-items: flex-start; gap: 14px; }
  .hub-stats { gap: 4px 20px; }
  .hub-stats b { font-size: 1.3rem; }
  .resume-card { padding: 13px 16px; }
  /* Two across, not one. A unit cell is a badge, a name and a bar -- it
     reads perfectly at half a phone's width, and twelve of them in a
     single file is six screens of scrolling to reach Reasoning. */
  .unit-subgrid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; }
  .unit-cell { padding: 12px 12px 11px 10px; }
  .unit-name { font-size: 0.92rem; }
  .hub-rail { gap: 12px; }
  .rail-tile { padding: 12px 12px 12px 16px; }
  .list-card { padding: 12px 12px 12px 14px; }
  .band-card { padding: 14px 12px 14px 16px; }
  .resume-card { padding: 13px 12px 13px 16px; }
  /* The pill is the last thing that should shrink on a phone: it is the
     element a student is looking for. The text column beside it wraps
     instead, which it can — every band's name and counts are already a
     wrapping flex row. */
  .go-btn { padding: 0 13px; font-size: 0.82rem; }
}

@media (max-width: 330px) {
  /* Only for the genuinely tiny. 380 was wrong: it caught the 360 and 390
     phones that are most of the traffic and gave them twelve cells in a
     single file, which is the scroll the two-column grid exists to avoid.
     Below 330 the two columns are under 150px and the unit names break
     badly enough to be worth the length. */
  .unit-subgrid { grid-template-columns: 1fr; }
}
