/* grade-selection.css — the grade chooser.
   Page-specific only: the shell (nav, skip link, ad slots, page-layout, footer)
   comes from critical.css + common.css, and .float-in lives in critical.css.

   Position in the funnel: typing-test (1/3/5 min) → THIS PAGE → the test
   itself. The eight grade tiles are sub-category links, so each one is a whole
   <a> rather than a link on the icon plus another on the label — two links to
   the same URL split the anchor text and read twice to a screen reader.

   Built one piece at a time; sections get added below as they are made. */

/* ══════════════════════════
   HERO — page heading, built the same way as the heroes on typing-test.html
   and the lessons page so all three read as one family. The tokens
   (--yellow, --text-dim, --text-strong) come from critical.css; nothing is
   redefined here.
══════════════════════════ */
.grade-hero {
  padding: 48px 0 32px;
  text-align: center;
}

/* The duration is a Twig variable — "1 Minute", "3 Minute", "5 Minute" —
   so it gets its own span. Not for a different look: the heading is one
   sentence and reads as one. The span is there so the template has a single
   place to write into, and so the pair can never be split across two lines
   ("1" left dangling at the end of line one reads as a list number). */
.grade__h1-length {
  white-space: nowrap;
}

.grade__h1 {
  font-size: clamp(28px, 3.8vw, 52px);
  font-weight: 700;
  line-height: 1.12;
  letter-spacing: -0.5px;
  color: #eef6ff;
  text-wrap: balance;            /* keeps "by Grade" off a line of its own */
}

.grade__h1 .c-yellow {
  color: var(--yellow);
}

/* CMS region — the paragraphs arrive from TinyMCE with no classes, so every
   text property sits on the WRAPPER and inherits down. Anything that has to
   target an element uses a descendant selector.

   The BLOCK is centred under the heading; the TEXT inside runs left. Centred
   text gives two ragged edges, so uneven line lengths show on both sides at
   once; left-aligning gives one straight edge to sit against. */
.grade__intro {
  max-width: 68ch;
  margin: 14px auto 0;
  text-align: left;
  font-size: clamp(15px, 1.5vw, 18px);
  font-weight: 600;
  line-height: 1.9;
  color: var(--text-dim);
  opacity: 0;
  animation: gradeHeadIn 0.9s cubic-bezier(0.22, 1, 0.36, 1) 0.75s forwards;
}
/* Direct child, so the measure cap can never reach a <p> the editor drops
   inside a table cell or a list. */
.grade__intro > p { text-wrap: pretty; }   /* no lone word on the last line */
.grade__intro > p + p { margin-top: 12px; }
.grade__intro strong { color: var(--text-strong); }
.grade__intro a {
  color: var(--yellow);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.grade__intro a:hover { color: #ffffff; }

/* Defensive: if an editor picks "Heading 1" in TinyMCE the page ends up with
   two h1s. The real fix is restricting block_formats in the editor — this just
   stops the second one looking broken until then. */
.grade__intro h1,
.grade__intro h2 {
  font-size: clamp(20px, 2.2vw, 28px);
  font-weight: 700;
  line-height: 1.25;
  color: #eef6ff;
}

/* H1 fades up on load; the accent word floats in just after it settles.
   .float-in itself lives in critical.css — only its delay is overridden. */
.grade__h1--anim {
  opacity: 0;
  transform: translateY(20px);
  animation: gradeHeadIn 0.9s cubic-bezier(0.22, 1, 0.36, 1) 0.1s forwards;
}
.grade__h1--anim .float-in {
  animation-delay: 0.55s;
}

@keyframes gradeHeadIn {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* The heading is decoration on a page you came to in order to pick a grade —
   nobody should have to sit through it, and it is motion on load. */
@media (prefers-reduced-motion: reduce) {
  .grade__h1--anim,
  .grade__intro {
    opacity: 1;
    transform: none;
    animation: none;
  }
}

@media (max-width: 640px) {
  .grade-hero {
    padding: 28px 0 20px;
  }
  /* At phone width the block already fills the column, so there is no centred
     box left to preserve — and short lines make a ragged left edge more
     obvious than the raggedness it was fixing. */
  .grade__intro {
    text-align: center;
    line-height: 1.75;
  }
}

/* ══════════════════════════
   GRADE TILES — the eight sub-category links.

   Deliberately the same card as .sec-card on typing-selection.html: plain
   surface, big icon, a rule, then the label. Icon-rule-label is now the site's
   picker pattern, so a child who has used the lesson picker already knows how
   to read this one.

   No skill line and no progress rail under the label. The grade number is the
   only thing anyone is choosing on, and every extra line pushed the eight
   cards further apart and made the choice slower.

   What it replaces: the live page repeats one rosette graphic eight times with
   a different number stamped in it, so seven eighths of the artwork tells you
   nothing. Here each grade gets its own object, and the eight climb a ladder a
   child reads without being told — sprout, paper plane, kite, star, rocket,
   medal, trophy, crown.

   Colour is taken straight from .sec-card and not re-picked: neutral
   rgba(255,255,255,0.6) icons, --card-bdr border, and the same cool-white
   rgba(219,238,255,0.25) on hover. On the lesson picker, colour on an icon
   MEANS something — green for done, --yellow for current. Nothing on this
   page has a state, so tinting all eight would spend that signal on nothing
   and break the one flow both pickers share. A colour per grade is out for
   the same reason it was on the belts: colour-only ranking fails for
   colour-blind users, and eight hues far enough apart to read would glare on
   navy.
══════════════════════════ */
.grades {
  padding: 8px 0 48px;
}

/* Heading + sub, animated as one block — the same grouping as
   .section-picker__head on the lesson picker, so the two lines arrive
   together instead of the sub trailing its own heading. It lands between the
   intro and the first card in the page cascade. */
.grades__head {
  margin-bottom: 26px;
  text-align: center;
  opacity: 0;
  animation: gradeHeadIn 0.7s cubic-bezier(0.22, 1, 0.36, 1) 0.85s forwards;
}

.grades__h2 {
  margin: 0;
  font-size: clamp(20px, 2.4vw, 28px);
  font-weight: 700;
  letter-spacing: -0.2px;
  color: #eef6ff;
}

.grades__sub {
  margin: 10px 0 0;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.6;
  color: var(--text-dim);
}

/* 8 tiles, so only 4 and 2 columns give a full rectangle — 3 would leave a
   ragged 3+3+2. */
.grade-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 18px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.grade-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  height: 100%;
  min-height: 180px;
  padding: 24px 20px 18px;
  background: var(--card-bg);
  border: 1px solid var(--card-bdr);
  border-radius: 16px;
  text-align: center;
  text-decoration: none;
  transition: transform 0.18s ease, border-color 0.18s ease, background 0.18s ease;
}

/* No tile behind the icon — it sits straight on the card, like .sec-card.
   One colour drives every glyph: the SVGs are stroke="currentColor" with
   fill="currentColor" at a fill-opacity, so the fills follow too. */
.grade-card__icon {
  display: block;
  color: rgba(255, 255, 255, 0.6);
  transition: transform 0.18s ease;
}
.grade-card__icon svg {
  display: block;
  width: 54px;
  height: 54px;
}

/* Label under a divider — icon, rule, name. */
.grade-card__name {
  width: 100%;
  padding-top: 14px;
  border-top: 1px solid var(--card-bdr);
  font-size: 16px;
  font-weight: 700;
  letter-spacing: 0.2px;
  color: rgba(241, 250, 255, 0.85);
}

.grade-card:hover {
  transform: translateY(-3px);
  border-color: rgba(219, 238, 255, 0.25);
  background: rgba(255, 255, 255, 0.07);
}
.grade-card:hover .grade-card__icon { transform: scale(1.06); }

/* The card IS the link, so :focus-visible on it covers keyboard arrival —
   no :focus-within needed. */
.grade-card:focus-visible {
  outline: 2px solid var(--yellow);
  outline-offset: 3px;
}

/* Cards fade up in sequence. One rule with nth-child for the delay would be
   eight rules; --i on the item makes it one multiplication. Kept short so the
   last card is not still arriving while someone is already choosing. */
.grade-grid > li {
  opacity: 0;
  animation: gradeCardIn 0.5s cubic-bezier(0.22, 1, 0.36, 1) forwards;
  animation-delay: calc(0.9s + var(--i, 0) * 0.05s);
}
.grade-grid > li:nth-child(1) { --i: 1; }
.grade-grid > li:nth-child(2) { --i: 2; }
.grade-grid > li:nth-child(3) { --i: 3; }
.grade-grid > li:nth-child(4) { --i: 4; }
.grade-grid > li:nth-child(5) { --i: 5; }
.grade-grid > li:nth-child(6) { --i: 6; }
.grade-grid > li:nth-child(7) { --i: 7; }
.grade-grid > li:nth-child(8) { --i: 8; }

@keyframes gradeCardIn {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .grades__head,
  .grade-grid > li {
    opacity: 1;
    transform: none;
    animation: none;
  }
  .grade-card,
  .grade-card__icon { transition: none; }
  .grade-card:hover { transform: none; }
  .grade-card:hover .grade-card__icon { transform: none; }
}

/* 4 -> 2. Three columns would break the rectangle, and four at phone width
   leaves ~140px per card, too little for a 54px icon plus the label. */
@media (max-width: 720px) {
  .grade-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 12px;
  }
  .grade-card {
    min-height: 180px;
    padding: 20px 14px 16px;
    gap: 13px;
  }
  .grade-card__icon svg { width: 46px; height: 46px; }
  .grade-card__name { padding-top: 12px; font-size: 15px; }
}

/* ══════════════════════════
   SEO BLOCK — the prose panel under the grid.

   Card and placement come from .lesson-guide on typing-lesson.html: a
   --card-bg panel with --card-bdr and --r-md, sitting inside col-content
   directly below the picker. It does NOT break out to full width the way
   .test-info does on typing-test.html — that block only spans the ad column
   because a four-column benchmark table is unreadable at the content measure.
   There is no table here, so the column measure is right.

   Two things are taken from .test-info rather than .lesson-guide, both
   because this block has headings and .lesson-guide has one paragraph:
   left-aligned text (centred prose gives two ragged edges once it runs past a
   few lines), and the short accent rule under the h2.

   CMS region — the content is authored in TinyMCE and arrives as bare
   h2/h3/p/ul/strong/a with no classes, so every rule below targets ELEMENTS
   through the wrapper. A rule hung on a class would never match.
══════════════════════════ */
.grade-info {
  margin-top: 40px;
}

.grade-info__inner {
  padding: 36px;
  background: var(--card-bg);
  border: 1px solid var(--card-bdr);
  border-radius: var(--r-md);
  font-size: clamp(15px, 1.5vw, 16px);
  font-weight: 600;
  line-height: 1.8;
  /* NOT --text-dim. That token is white-at-58%, and --card-bg lifts the
     background 5%, so it lands at 5.68:1 here. 0.62 restores it to ~6.2:1 —
     the same correction, for the same reason, as .test-info__inner. */
  color: rgba(255, 255, 255, 0.62);
}

/* h1 is styled defensively in case an editor reaches for it, but the page h1
   is the hero — a second one here is wrong for search and for screen-reader
   navigation. The real fix is limiting TinyMCE's block_formats to Heading 2,
   Heading 3 and Paragraph. */
.grade-info__inner h1,
.grade-info__inner h2 {
  position: relative;
  padding-bottom: 15px;                /* room for the accent rule */
  font-size: clamp(20px, 2.2vw, 25px);
  font-weight: 800;
  line-height: 1.3;
  color: var(--text-strong);
}

/* A pseudo-element because the heading comes from the CMS and cannot carry
   extra markup. h3 deliberately has no rule — its absence is what separates
   the two heading levels. */
.grade-info__inner h1::after,
.grade-info__inner h2::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 46px;
  height: 3px;
  border-radius: 2px;
  background: var(--yellow);
}

.grade-info__inner h3 {
  font-size: clamp(17px, 1.8vw, 19px);
  font-weight: 800;
  line-height: 1.35;
  color: var(--text-strong);
}

/* Spacing as sibling relationships, not fixed margins on each element: an
   editor can reorder blocks freely, and `> * + *` covers pairs nobody thought
   to enumerate. */
.grade-info__inner > * + *  { margin-top: 14px; }
.grade-info__inner > * + h2 { margin-top: 36px; }
.grade-info__inner > * + h3 { margin-top: 30px; }
.grade-info__inner > h1 + *,
.grade-info__inner > h2 + *,
.grade-info__inner > h3 + * { margin-top: 12px; }

/* The global reset in critical.css sets `ul { list-style: none }`, so a list
   from the CMS arrives with no marker at all — and `li::marker` cannot bring
   one back once list-style is gone. A dash drawn with ::before restores it and
   echoes the heading rule. `ol` is untouched by that reset and keeps its
   numbers: an editor who picks a numbered list wants numbers. */
.grade-info__inner ul { padding-left: 0; }
.grade-info__inner ul > li {
  position: relative;
  padding-left: 22px;
}
.grade-info__inner ul > li::before {
  content: '–';
  position: absolute;
  left: 2px;
  top: 0;
  font-weight: 800;
  color: var(--yellow);
}
.grade-info__inner ol { padding-left: 22px; }
.grade-info__inner ol > li::marker { color: var(--yellow); font-weight: 800; }
.grade-info__inner li + li { margin-top: 8px; }

.grade-info__inner strong { font-weight: 800; color: var(--text-strong); }
.grade-info__inner em { font-style: italic; }

.grade-info__inner a {
  color: var(--yellow);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.grade-info__inner a:hover { color: #ffffff; }

@media (max-width: 720px) {
  /* 36px of card padding leaves under 290px of text on a 360px phone. */
  .grade-info { margin-top: 28px; }
  .grade-info__inner { padding: 24px 18px; }
}
