/* Landing page (`/`, web/templates/home.html) — tile polish only.
 *
 * Page-scoped plain CSS, NOT in app.bundle.css, so editing it needs no Tailwind rebuild
 * (same pattern as 20-simulate.css / 22-chatbot.css / 29-search.css). Structure, colour
 * and hover behaviour come from `.practice-card` / `.lib-card` in 06-landing.css, which
 * the landing already loads; everything here is additive.
 *
 * NOTE: the landing puts `--accent-rgb` on the <a> itself rather than only on the icon
 * chip (the subject grids do the latter), so 06-landing.css's hover border + gradient and
 * the focus ring pick up each tile's own hue instead of the slate default.
 */

/* ─── Section tiles ──────────────────────────────────────────────────────────
   Every tile rests in a faint wash of its own accent, so the English and Tools rows sit
   in the same colour world as the Study block and the subject cards instead of being the
   only neutral cards left on the page. The hero (--big) tiles get a stronger wash; both
   stay well below the hover gradient's 0.08+ so hovering is still a clear change. */
.home-tile {
  background-image:
    linear-gradient(135deg, rgba(var(--accent-rgb), 0.11) 0%, rgba(255, 255, 255, 0.02) 60%);
  border-color: rgba(var(--accent-rgb), 0.22);
}
.home-tile--big {
  background-image:
    linear-gradient(135deg, rgba(var(--accent-rgb), 0.07) 0%, rgba(255, 255, 255, 0.02) 55%);
  border-color: rgba(var(--accent-rgb), 0.16);
}
.home-tile:hover {
  border-color: rgba(var(--accent-rgb), 0.4);
}
/* Glyphs are a LIGHT tint of their accent, not the accent itself. Once the chips behind
   them were pushed to 0.28-0.60 fill, a -400 mark on a -400 wash lost its contrast and the
   icons read as faded — the brighter the chip got, the more washed-out the symbol looked.
   Mixing toward white keeps the hue and puts the contrast back; the plain `color` line
   before each mix is the fallback for browsers without color-mix. */
.home-tile--square .practice-card-icon {
  color: rgb(var(--accent-rgb));
  color: color-mix(in srgb, rgb(var(--accent-rgb)) 52%, #fff);
  filter: drop-shadow(0 1px 3px rgba(2, 8, 22, 0.4));
}
.home-tile:not(.home-tile--square) .practice-card-icon {
  color: rgb(var(--accent-rgb));
  color: color-mix(in srgb, rgb(var(--accent-rgb)) 72%, #fff);
}

/* The hero chip earns a soft halo — it is the page's brightest accent mark. Must stay
   ABOVE `.home-tile--square .home-tile-icon`: the two selectors have identical
   specificity and every Study tile carries both classes, so whichever comes last wins. */
.home-tile--big .home-tile-icon {
  background-color: rgba(var(--accent-rgb), 0.24);
  box-shadow: 0 0 24px -5px rgba(var(--accent-rgb), 0.6);
}
/* ─── Square launcher tile (the Study block) ─────────────────────────────────
   A ceiling on how big the square gets: six tiles only divide evenly into 3 or 6 columns,
   and between ~560 and ~640px neither count is comfortable — three columns there are
   165-190px cells, and a square that size is a billboard on a phone. Capping the HEIGHT
   (rather than the width) keeps the tile flush with every other block on the page (a
   max-width would centre it in its cell and inset the whole row a few px from the section
   heading) and costs at most a 1.2 ratio inside that one band. Everywhere else the cap is
   not reached and the tile is exactly square.

   The accent lights the WHOLE face, as a glow rising from behind the icon. The flat 7%
   wash these carried as cards left six near-black boxes each wearing one small coloured
   chip — at card size that reads as restraint, at launcher size it reads as unfinished.

   The RESTING strength is deliberately held down. The rest of the page — subject chips,
   English and Tools cards, the level strip — is dark with small accents, so a fully-lit
   Study block looked like it came from a different site. The lit version is the HOVER
   state instead: the row reads as one calm set until you reach for something. */
.home-tile--square {
  max-height: 10rem;
  border-radius: 1.25rem;
  border-color: rgba(var(--accent-rgb), 0.34);
  background-color: rgba(var(--accent-rgb), 0.07);
  background-image:
    radial-gradient(115% 85% at 50% 6%, rgba(var(--accent-rgb), 0.4) 0%,
                                        rgba(var(--accent-rgb), 0.13) 42%,
                                        transparent 72%),
    linear-gradient(180deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.01));
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.09),
    0 12px 30px -14px rgba(var(--accent-rgb), 0.7);
  transition: transform 0.18s ease, border-color 0.18s ease,
              box-shadow 0.18s ease, background-image 0.18s ease;
}
.home-tile--square:hover,
.home-tile--square:focus-visible {
  transform: translateY(-3px);
  border-color: rgba(var(--accent-rgb), 0.48);
  background-image:
    radial-gradient(115% 85% at 50% 6%, rgba(var(--accent-rgb), 0.32) 0%,
                                        rgba(var(--accent-rgb), 0.11) 45%,
                                        transparent 75%),
    linear-gradient(180deg, rgba(255, 255, 255, 0.07), rgba(255, 255, 255, 0.02));
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.14),
    0 18px 40px -20px rgba(var(--accent-rgb), 0.95);
}

/* The symbol chip, app-icon style. Placed AFTER the `--big` icon rule below-equal in
   specificity, so it is the cascade order that makes this win — keep it here. A gradient
   with a lit top edge rather than a flat 16% fill: at 48-64px a flat chip looks like a
   placeholder, and the icon is the whole content of the tile. */
.home-tile--square .home-tile-icon {
  border-radius: 1rem;
  background-color: transparent;
  background-image: linear-gradient(155deg, rgba(var(--accent-rgb), 0.6) 0%,
                                            rgba(var(--accent-rgb), 0.24) 100%);
  box-shadow:
    inset 0 0 0 1px rgba(var(--accent-rgb), 0.42),
    inset 0 1px 0 rgba(255, 255, 255, 0.22),
    0 8px 22px -6px rgba(var(--accent-rgb), 0.7);
  transition: transform 0.18s ease, box-shadow 0.18s ease, background-image 0.18s ease;
}
.home-tile--square:hover .home-tile-icon,
.home-tile--square:focus-visible .home-tile-icon {
  transform: scale(1.05);
  background-image: linear-gradient(155deg, rgba(var(--accent-rgb), 0.5) 0%,
                                            rgba(var(--accent-rgb), 0.2) 100%);
  box-shadow:
    inset 0 0 0 1px rgba(var(--accent-rgb), 0.5),
    inset 0 1px 0 rgba(255, 255, 255, 0.24),
    0 12px 26px -8px rgba(var(--accent-rgb), 0.8);
}

/* Two lines, then clip — every name is one short word today, but a longer one added later
   should wrap inside the square rather than push the label out of it. */
.home-tile-label {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}


/* ─── Subject picker (IGCSE / A-Level / AP) ──────────────────────────────────
   Three tabs side by side; the selected one's subjects fill the full-width panel below.
   CSS-only tabs: three same-named radios live before both the tab row and the panels, so
   `:checked ~` can reach forward to style the matching label and reveal its panel. That
   is why the radios are siblings of BOTH — a <details> per level can't do this, since its
   body is stuck inside its own narrow column. */
.home-level-radio {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* ── Segmented control ───────────────────────────────────────────────────────
   A single sliding thumb rather than three segments that light up in place, so changing
   level reads as one control moving instead of two things blinking. The thumb takes the
   colour of the selected level from --lvl-1..3, which the TEMPLATE sets from
   home_sections._LEVELS — the palette is not duplicated here. */
.home-level-thumb {
  position: absolute;
  /* Full-bleed: the strip has no padding and no gaps, so the pill is exactly one third of
     it and touches every edge — no 5px groove around it. It still reads as a PILL: the
     radius here is the strip's inner radius (its 1rem less the 1px border), so at the two
     ends the pill's corners nest exactly inside the strip's own rather than showing a
     sliver of track between them. */
  left: 0;
  top: 0;
  bottom: 0;
  width: calc(100% / 3);
  border-radius: 15px;
  background: linear-gradient(135deg, rgba(var(--accent-rgb), 0.3), rgba(var(--accent-rgb), 0.13));
  box-shadow:
    inset 0 0 0 1px rgba(var(--accent-rgb), 0.4),
    inset 0 1px 0 0 rgba(255, 255, 255, 0.1),
    0 6px 18px -10px rgba(var(--accent-rgb), 0.8);
  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1),
              background 0.25s ease, box-shadow 0.25s ease;
}
/* translateX percentages resolve against the THUMB's own width — exactly one segment —
   so each step is a clean 100%, with no gap to add now that the segments tile. */
#home-level-igcse:checked   ~ .home-level-tabs .home-level-thumb { --accent-rgb: var(--lvl-1); transform: translateX(0); }
#home-level-a_level:checked ~ .home-level-tabs .home-level-thumb { --accent-rgb: var(--lvl-2); transform: translateX(100%); }
#home-level-ap:checked      ~ .home-level-tabs .home-level-thumb { --accent-rgb: var(--lvl-3); transform: translateX(200%); }

/* Segments sit ABOVE the thumb and are otherwise unstyled — the thumb is the only fill,
   so nothing double-paints as it slides through.

   min-w-0 in the markup is load-bearing: `flex-1` is `flex: 1 1 0%`, but a flex item's
   default `min-width: auto` stops it shrinking below its content. "A-Level" is wider than
   "AP", so without it the three segments come out UNEQUAL on a narrow viewport — and the
   thumb, which is a fixed one third, then stops lining up with them (measured broken at
   381-390px, where the icon reappears and space is tightest). */
.home-level-tab {
  color: rgb(148, 163, 184);
  transition: color 0.2s ease;
}
.home-level-icon {
  background: rgba(var(--accent-rgb), 0.12);
  color: rgb(var(--accent-rgb));
  color: color-mix(in srgb, rgb(var(--accent-rgb)) 74%, #fff);
  transition: background 0.2s ease, box-shadow 0.2s ease;
}
.home-level-label { color: rgb(203, 213, 225); transition: color 0.2s ease; }
.home-level-tab:hover .home-level-label { color: #fff; }
.home-level-tab:hover .home-level-icon { background: rgba(var(--accent-rgb), 0.2); }

#home-level-igcse:checked   ~ .home-level-tabs [data-level="igcse"]   .home-level-label,
#home-level-a_level:checked ~ .home-level-tabs [data-level="a_level"] .home-level-label,
#home-level-ap:checked      ~ .home-level-tabs [data-level="ap"]      .home-level-label { color: #fff; }
#home-level-igcse:checked   ~ .home-level-tabs [data-level="igcse"]   .home-level-icon,
#home-level-a_level:checked ~ .home-level-tabs [data-level="a_level"] .home-level-icon,
#home-level-ap:checked      ~ .home-level-tabs [data-level="ap"]      .home-level-icon {
  background: rgba(var(--accent-rgb), 0.24);
  box-shadow: 0 0 16px -4px rgba(var(--accent-rgb), 0.6);
}

/* Where the icon has to go. A segment is (viewport - 32px page padding - 2px border) / 3;
   the widest label, "A-Level" at 16px, needs ~60px, plus a 36px icon, a 10px gap and 24px
   of padding — so the icon only fits from ~424px up. Below that it is dropped rather than
   left to truncate the label (measured: at 381-390px it clipped to "A-Lev…"). */
@media (max-width: 440px) {
  .home-level-icon { display: none; }
  .home-level-tab { padding-left: 0.5rem; padding-right: 0.5rem; }
}

@media (prefers-reduced-motion: reduce) {
  .home-level-thumb { transition: none; }
}

/* The selected level's subject panel. Matched on data-level rather than :nth-of-type,
   because nth-of-type counts among ALL sibling <div>s — the tab strip is one, so the
   panels would be off by one and the whole thing would silently show the wrong level. */
.home-level-panel { display: none; }

#home-level-igcse:checked   ~ .home-level-panel[data-level="igcse"],
#home-level-a_level:checked ~ .home-level-panel[data-level="a_level"],
#home-level-ap:checked      ~ .home-level-panel[data-level="ap"] {
  display: block;
  margin-top: 0.5rem;
}

/* Keyboard focus lands on the visually-hidden radio, so the ring must be drawn on its
   label — otherwise arrow-keying through the group shows nothing at all. */
#home-level-igcse:focus-visible   ~ .home-level-tabs [data-level="igcse"],
#home-level-a_level:focus-visible ~ .home-level-tabs [data-level="a_level"],
#home-level-ap:focus-visible      ~ .home-level-tabs [data-level="ap"] {
  outline: 2px solid rgba(var(--accent-rgb), 0.7);
  outline-offset: -1px;
}

/* ─── Subject disclosure (chip → what you can do with it) ────────────────────
   One <details> per subject, exclusive via name="home-subject". Not overflow-hidden, so
   the hover lift isn't clipped — hence the hover fill goes on the <details> via
   :has(> summary:hover) rather than on the summary, so it follows the rounded corners. */
.home-subject {
  position: relative;   /* anchor for the absolutely-positioned action overlay below */
  /* A resting wash of the subject's own hue rather than flat white/3% — with 8 to 19 of
     these in a panel, the colour is what makes the grid scannable, and it matches how the
     Study tiles above carry their accent. */
  background:
    linear-gradient(135deg, rgba(var(--accent-rgb), 0.2) 0%, rgba(255, 255, 255, 0.025) 62%);
  border-color: rgba(var(--accent-rgb), 0.32);
  transition: border-color 0.16s ease, background 0.16s ease, transform 0.16s ease,
              box-shadow 0.16s ease;
}
.home-subject:has(> summary:hover) {
  border-color: rgba(var(--accent-rgb), 0.45);
  background:
    linear-gradient(135deg, rgba(var(--accent-rgb), 0.18) 0%, rgba(255, 255, 255, 0.03) 62%);
  transform: translateY(-2px);
  box-shadow: 0 12px 26px -16px rgba(0, 0, 0, 0.9),
              0 0 22px -12px rgba(var(--accent-rgb), 0.7);
}
.home-subject[open] {
  border-color: rgba(var(--accent-rgb), 0.45);
  background:
    linear-gradient(135deg, rgba(var(--accent-rgb), 0.16) 0%, rgba(255, 255, 255, 0.03) 62%);
}

/* The longest name on the site — "Physics C: Electricity and Magnetism" — does not fit a
   card's text column on one line at any width. Wrapping to two lines shows it in full
   instead of ellipsising it; the grid stretches its row-mates to match. */
.home-subject-name {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

/* The subject symbol. Bigger than the old 28px chip and with a halo of its own hue — it
   is the thing you actually scan for when picking a subject out of nineteen. */
.home-subject-icon {
  background: rgba(var(--accent-rgb), 0.28);
  color: rgb(var(--accent-rgb));
  color: color-mix(in srgb, rgb(var(--accent-rgb)) 68%, #fff);
  box-shadow: inset 0 0 0 1px rgba(var(--accent-rgb), 0.34),
              0 0 20px -6px rgba(var(--accent-rgb), 0.9);
  transition: background 0.16s ease, box-shadow 0.16s ease;
}
.home-subject:has(> summary:hover) .home-subject-icon,
.home-subject[open] .home-subject-icon {
  background: rgba(var(--accent-rgb), 0.26);
  box-shadow: inset 0 0 0 1px rgba(var(--accent-rgb), 0.35),
              0 0 22px -6px rgba(var(--accent-rgb), 0.85);
}

.home-subject-chevron {
  color: rgba(var(--accent-rgb), 0.6);
  transition: transform 0.2s ease, color 0.18s ease;
}
.home-subject[open] .home-subject-chevron { transform: rotate(90deg); }
.home-subject[open] .home-subject-chevron,
.home-subject:has(> summary:hover) .home-subject-chevron { color: rgb(var(--accent-rgb)); }

/* The action overlay. Anchored to the subject card and absolutely positioned, so
   unfolding overlays whatever is below instead of growing the grid row — the card itself
   never changes size. Accent-tinted glass, the same vocabulary as the /code landing's
   resources popover (16-code.css .code-resources-pop). */
.home-subject-pop {
  position: absolute;
  /* Right-aligned and only as wide as its labels: a full-card-width box around five
     short buttons looked empty. The card's right half is blank anyway (subject names are
     short), so the panel hangs under exactly that space. max-width keeps it inside the
     card on the narrowest phone, where the name can run the full width. */
  left: auto;
  right: 0;
  width: max-content;
  min-width: 8.5rem;
  max-width: 100%;
  /* Flush with the card's OWN top edge — it sits ON the card's blank right half rather
     than hanging beneath it. Exactly 0, not a negative offset: a few pixels of overhang
     read as a misalignment rather than as intent. */
  top: 0;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  padding: 0.3rem;
  border-radius: 0.75rem;
  border: 1px solid rgba(var(--accent-rgb), 0.34);
  background: linear-gradient(162deg, rgba(var(--accent-rgb), 0.16) 0%, rgba(13, 21, 40, 0.94) 58%);
  backdrop-filter: blur(22px) saturate(155%);
  -webkit-backdrop-filter: blur(22px) saturate(155%);
  box-shadow:
    inset 0 1px 0 0 rgba(255, 255, 255, 0.08),
    0 22px 48px -18px rgba(0, 0, 0, 0.85),
    0 0 30px -14px rgba(var(--accent-rgb), 0.45);
}

/* An OPEN card must paint above the cards after it in the grid, or the overlay is
   covered by them. z-index on a positioned element is what raises the whole card; the
   hover lift is dropped while open because a transform would create a stacking context
   that traps the overlay inside the card again. */
.home-subject[open] { z-index: 30; }
.home-subject[open]:has(> summary:hover) { transform: none; }

/* Menu rows. The icon carries the hue of the surface it goes to — the same cyan for Know
   as the Know tile further down the page — while the label stays neutral so five colours
   don't fight; hovering tints the whole row with that surface's accent. */
.home-action {
  color: #cbd5e1;
  transition: background 0.14s ease, color 0.14s ease;
}
.home-action > svg:first-child { color: rgb(var(--accent-rgb)); }
.home-action-go { color: transparent; transition: color 0.14s ease, transform 0.14s ease; }
.home-action:hover {
  background: rgba(var(--accent-rgb), 0.18);
  color: #fff;
}
.home-action:hover .home-action-go { color: rgba(var(--accent-rgb), 0.9); transform: translateX(2px); }
.home-action:focus-visible {
  outline: 2px solid rgba(var(--accent-rgb), 0.6);
  outline-offset: -1px;
}

/* Subject chips stagger in when a level tab is selected, and the action row when a
   subject unfolds — same motion vocabulary as the code landing's lesson cards. */
.home-level-panel .home-subject {
  animation: home-subject-in 0.3s cubic-bezier(0.22, 0.9, 0.32, 1) backwards;
  animation-delay: calc(20ms + min(var(--i, 0), 18) * 16ms);
}
.home-subject[open] .home-action {
  animation: home-subject-in 0.24s cubic-bezier(0.22, 0.9, 0.32, 1) backwards;
}
@keyframes home-subject-in {
  from { opacity: 0; transform: translateY(6px) scale(0.985); }
}

/* ─── /about explainer cards ─────────────────────────────────────────────────
   The four "how this site works" blocks. Same sheet as the landing because /about is
   built from the landing's vocabulary — the section tiles above them are literally the
   same `.practice-card` markup, driven by the same registry. */
.about-card {
  transition: border-color 0.18s ease, background 0.18s ease;
}
.about-card:hover {
  border-color: rgba(var(--accent-rgb), 0.28);
  background: rgba(var(--accent-rgb), 0.04);
}
.about-card-icon {
  background: rgba(var(--accent-rgb), 0.14);
  color: rgb(var(--accent-rgb));
}
/* Card body: short bullets, not prose. The marker is a small accent-tinted dot, absolutely
   positioned out of the text flow with the text indented past it — so a wrapped second line
   aligns under the TEXT, not under the bullet.
   ⛔ The <li> must NOT be `display: grid` (or flex). Every bullet contains <strong> from the
   `bold_marks` filter, and a grid container makes each element child its OWN grid item — the
   bold words break onto separate rows and a one-line bullet renders ~150px tall. Measured,
   not guessed. Keep the li a normal block box. */
.about-bullets {
  margin: 0.75rem 0 0;
  padding: 0;
  list-style: none;
  display: grid;              /* safe here: every child is an <li>, whitespace-only nodes
                                 between them are not rendered */
  gap: 0.45rem;
}
.about-bullets li {
  position: relative;
  padding-left: 0.9rem;
  font-size: 0.875rem;
  line-height: 1.6;
  color: rgb(203, 213, 225);
}
.about-bullets li::before {
  content: "";
  position: absolute;
  left: 0.0625rem;
  top: 0.62em;                /* onto the first line's x-height, at any font size */
  width: 0.3125rem;
  height: 0.3125rem;
  border-radius: 50%;
  background: rgb(var(--accent-rgb));
  opacity: 0.85;
}
.about-bullets strong { font-weight: 600; color: #fff; }

.about-link {
  color: rgb(var(--accent-rgb));
  text-underline-offset: 2px;
}
.about-link:hover { text-decoration: underline; }

/* ─── Hero: icon tile + title flow ───────────────────────────────────────────
   This sheet loads on the two FRONT-DOOR pages, / and /about, and both get the treatment
   — deliberately: they are the pages a stranger lands on, and the deep pages keep the
   quieter shared hero. Three changes, all to the same complaint — it read as small and
   flat:

   * The tile goes 56 -> 76px with a 38px glyph, so the mark holds its own against a 48px
     title instead of sitting beside it as punctuation.
   * Its fill spans the page ramp (cyan -> violet) at roughly double the shared opacity,
     with a real cast beneath it, rather than two near-identical blues at 22%.
   * The TITLE gradient spans cyan -> violet -> fuchsia. The shared one runs cyan-300 ->
     blue-300 -> indigo-300 — three pale neighbours, which is why a gradient that has
     always been there reads as a solid fill. Same shimmer animation, real distance
     between the stops. */
.site-hero-icon {
  width: 4.75rem;
  height: 4.75rem;
  border-radius: 1.5rem;
  /* The icon is 76px and a one-line title is 48px, so the icon — not the title — was
     setting the flex row's height, and the bullets below it sat 44px under the title with
     the icon running on past them. This takes the icon's contribution back down to the
     SMALLEST title's height (text-2xl, 24px + the h1's mt-1). It still DRAWS at full size,
     spilling into the band below, which is safe because the bullets are indented clear of
     it. Sized against the smallest and not the largest title on purpose: the h1 steps
     2xl -> 4xl -> 5xl across breakpoints while the icon stays 76px, so anything less left
     the icon driving the row at some widths and the gap came out 12 / 20 / 28px depending
     on where you measured. Now the title drives it everywhere and the gap is always 12.
     The declaration itself is the :has() rule below this block. */
  /* Two layers: a lit corner over the ramp fill, so the tile has a light SOURCE instead of
     an even wash. That is the difference between a coloured square and an object. */
  background:
    radial-gradient(125% 115% at 22% 10%, rgba(255, 255, 255, 0.22) 0%, transparent 58%),
    linear-gradient(140deg, rgba(34, 211, 238, 0.62) 0%, rgba(99, 102, 241, 0.5) 52%,
                            rgba(192, 132, 252, 0.46) 100%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.38),
    inset 0 0 0 1px rgba(255, 255, 255, 0.1),
    inset 0 -10px 20px -12px rgba(0, 0, 0, 0.5),
    0 0 0 1px rgba(34, 211, 238, 0.45),
    0 16px 40px -14px rgba(34, 211, 238, 0.6),
    0 0 54px -14px rgba(167, 139, 250, 0.75);
}

/* White glyph, not a tinted one: on a fill this saturated a cyan-200 mark disappeared into
   its own background. The shadow lifts it off the gradient.
   ⚠️ THIS rule sets the glyph size, not the `h-*` utility on the <svg> in
   partials/hero.html — `.site-hero-icon svg` (0,1,1) outspecifies a single class
   (0,1,0), so editing the template class alone changes nothing here. Size lives in
   one place: change it below.
   3.1rem, not the old 2.375rem: against this 4.75rem tile that was exactly 50%, and
   the mark read as a small glyph marooned in a large tile. 3.1rem ≈ 65%, which is
   what a hero tile wants. */
.site-hero-icon svg {
  width: 3.1rem;
  height: 3.1rem;
  color: #fff;
  filter: drop-shadow(0 2px 5px rgba(2, 8, 22, 0.45));
}

.site-hero-title {
  background-image: linear-gradient(100deg,
    rgb(103, 232, 249) 0%, rgb(147, 197, 253) 26%, rgb(196, 181, 253) 58%,
    rgb(240, 171, 252) 82%, rgb(103, 232, 249) 100%);
  background-size: 220% 100%;
  filter: drop-shadow(0 0 26px rgba(34, 211, 238, 0.28));
}

/* ─── Hero bullets ──────────────────────────────────────────────────────────
   The hero's chip row is a 0.7rem wrapping flex line, sized to support a paragraph. Here
   the bullets ARE the page's description — there is no paragraph — so they carry the
   weight: bigger, and on a 2x2 grid rather than a wrapping row. The hero is capped at
   max-w-3xl, so four of these always wrapped, and a wrap lands 3-then-1, which reads as
   a mistake. A grid makes the same break deliberate and keeps the dots on one axis.
   Page-scoped, since this sheet only loads on `/`. */
/* ─── Hero bullets ──────────────────────────────────────────────────────────
   FOUR columns at every width, so the block is always exactly TWO lines: row one is what
   the site is, row two the four things you get. Below lg those four columns are wider
   than a phone, so the block scrolls sideways — a swipe, with the right edge faded to
   show there is more. Stacking is what this replaced: two columns meant four lines and
   ~100px of a 390px screen, one column meant eight lines and ~220px.

   Content-sized columns (`max-content`) rather than fractions: eight bullets of eight
   different lengths have no reason to occupy equal widths, and on a scroll track equal
   widths would only make the overflow longer.

   No auto-scrolling marquee, though it was asked about: text that moves on its own has to
   carry a pause control to meet WCAG 2.2.2, and a hero is the wrong place for one. A
   swipe costs the reader nothing and stops where they let go. */
.site-hero-chips {
  display: grid;
  grid-template-columns: repeat(4, max-content);
  gap: 0.55rem 1.1rem;
  font-size: 0.8rem;
  color: rgb(203, 213, 225);          /* slate-300: these are the page's description now */
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;              /* the fade is the affordance; a scrollbar under a
                                         two-line block is more chrome than signal */
  padding-block: 2px;                 /* a scroll container clips the other axis too, and
                                         the dots carry a glow that would be cut */
  padding-right: 1.75rem;             /* the last bullet clears the fade instead of dying in it */
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 2.25rem), transparent);
          mask-image: linear-gradient(to right, #000 calc(100% - 2.25rem), transparent);
}
.site-hero-chips::-webkit-scrollbar { display: none; }
/* The macro sets gap-2 between a dot and its text; 6px reads tighter at this size. */
.site-hero-chips > span { gap: 0.375rem; }

/* Three columns from lg — and the hero box widens to the content column to make room.
   The shared hero is capped at max-w-3xl (768px), which is right for a paragraph but
   gives three bullet columns only 240px each, and the materials bullet ("Handouts,
   Exercise sheets, PPT slides, Vocab lists") then wrapped to two lines and made the
   first row taller than the second. The TITLE keeps the old cap so its balanced two-line
   wrap is unchanged — only the bullet row gets the extra width. */
@media (min-width: 1024px) {
  .site-hero { max-width: none; }
  .site-hero h1 { max-width: 48rem; }
  /* First width where four columns fit outright, so the scroll track, the fade and the
     padding that served it all come off, the block gains the indent that aligns it with
     the title, and the type steps up. */
  .site-hero-chips {
    margin-left: calc(4.75rem + 1rem + 0.6rem);
    margin-top: 0.75rem;
    font-size: 0.85rem;
    row-gap: 0.9rem;
    overflow-x: visible;
    padding-right: 0;
    -webkit-mask-image: none;
            mask-image: none;
  }
  .site-hero-chips > span { gap: 0.5rem; }
  /* The icon draws past the row's bottom so the gap is measured from the TITLE rather than
     from the taller of icon and title. Gated with the indent, not global: it is only safe
     while the bullets are indented clear of the icon, and below lg they start at the page
     edge. (/about shares this sheet and has a full-width subtitle — the :has() keeps it
     out entirely.) */
  .site-hero:has(.site-hero-chips) .site-hero-icon { margin-bottom: -3.25rem; }
  /* `auto` columns, not `1fr`: the bullets are five different lengths, and equal thirds
     gave each 309px while the materials bullet needs ~340 — so that one wrapped and its
     row grew taller than the other. Content-sized columns let the long one have the room
     it needs and the short ones give it up; the whole block still ends well inside the
     page, and row 2 stays aligned under row 1. */
  /* Four columns: eight bullets fall as two even rows of four. `auto` columns rather than
     equal fractions — eight bullets of eight different lengths have no reason to occupy
     identical widths, and content-sizing keeps every one on a single line. */
  .site-hero-chips {
    grid-template-columns: repeat(4, auto);
    justify-content: start;
    column-gap: 2.25rem;
    row-gap: 0.9rem;
  }
  /* The two rows say different things — what the site IS, then what you GET — so they get
     a rule between them. Scoped to this breakpoint on purpose: items 5-8 are exactly the
     second row only at four columns. At two columns they are rows 3 AND 4, so the same
     selector would draw two lines mid-list; below lg the wider row-gap does the job. */
  .site-hero-chips > span:nth-child(n + 5) {
    padding-top: 0.9rem;
    border-top: 1px solid rgba(255, 255, 255, 0.08);
  }
}

/* ─── Blurbs ─────────────────────────────────────────────────────────────────
   Titles are `truncate`, but a blurb must stay readable, so it wraps — capped at two
   lines so one long translation can't make a tile taller than its row-mates. */
.home-tile-blurb {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}
/* Tiny phones: a single column ~185px of text can't hold the longest blurbs in two
   lines, so let them take three there rather than ellipsise — the column is single, so
   uneven card heights cost nothing. */
@media (max-width: 379px) {
  .home-tile-blurb { -webkit-line-clamp: 3; line-clamp: 3; }
}

/* ─── Entrance stagger ───────────────────────────────────────────────────────
   Tiles rise in sequence on load. `--i` is set inline per tile (its index within its
   group), and the delay is capped with min() so a group can grow without the last tile
   crawling in. `backwards` (not `both`) keeps the tile hidden through its delay while
   still releasing transform afterwards, so hover lift works — same reasoning as
   16-code.css's card stagger. Gated on the page-entrance class base.html toggles, so
   this runs once per load and never on a re-render. */
body:not(.page-entered) .home-tile {
  animation: home-tile-in 0.34s cubic-bezier(0.22, 0.9, 0.32, 1) backwards;
  animation-delay: calc(60ms + min(var(--i, 0), 8) * 34ms);
}
@keyframes home-tile-in {
  from { opacity: 0; transform: translateY(8px) scale(0.99); }
}

@media (prefers-reduced-motion: reduce) {
  body:not(.page-entered) .home-tile { animation: none; }
  /* Only the movement goes — the glow is decoration, not motion, and dropping it here
     would leave these users the one flat-looking version of the page. */
  .home-tile--square:hover,
  .home-tile--square:focus-visible,
  .home-tile--square:hover .home-tile-icon,
  .home-tile--square:focus-visible .home-tile-icon { transform: none; }
  .home-level-panel .home-subject,
  .home-subject[open] .home-action { animation: none; }
  .home-subject:has(> summary:hover),
  .home-level-tab:hover { transform: none; }
  .home-subject-chevron { transition: none; }
}
