/*
 * Its own identity, taken from the logo.
 *
 * The first attempt copied helpcoai.com token for token, which was wrong twice
 * over: it read as a clone rather than a sibling, and this is a business that
 * may be sold on its own, so wearing another company's look is a liability.
 * The second was a warm ivory and moss field guide, chosen before there was a
 * logo to answer to.
 *
 * There is a logo now, so the palette comes from it rather than from taste.
 * Navy carries authority, blue is the action colour, gold is the accent the
 * loop's right half already gives the brand. Full derivation, and the measured
 * contrast table, in grants/design/clients/scholarshiploop/design-system.md.
 *
 * THE ONE RULE THAT IS NOT NEGOTIABLE: gold never carries text on a light
 * ground. No gold that still reads as gold clears 4.5:1 on white; the nearest
 * passing value is a brown. Measured: gold-500 is 1.85 on white and gold-600 is
 * 2.28. So on paper the gold appears as bars, rules and underlines, and the
 * text beside it is navy. On the navy bands gold carries text freely, which is
 * where the brand's gold belongs anyway: gold-300 on navy-900 is 11.17.
 *
 * What survived the repaint, because it was never about the colours: a real
 * typographic scale, a serif for headings so the page reads as edited rather
 * than generated, structure carried by rules and borders, and exactly one
 * saturated colour spent on action.
 */

/* ---------------------------------------------------------------- typefaces */
/*
 * SELF-HOSTED, AND THAT IS A PRIVACY DECISION BEFORE IT IS A PERFORMANCE ONE.
 *
 * These loaded from fonts.googleapis.com until 2026-08-12. That meant every
 * visitor's browser handed Google an IP address and a referring URL before a
 * word of the page rendered, on a site whose core user is 13 to 17. Nothing was
 * gained for it: two files, 84KB together, now served from our own domain.
 *
 * Writing the privacy policy is what surfaced it. The policy had to either
 * disclose the third party or stop being true, and disclosing something
 * avoidable is the worse of the two answers.
 *
 * VARIABLE, WEIGHT AXIS ONLY. Both faces have other axes (Fraunces has optical
 * size and softness) and nothing here sets them, so the wght-only subsets carry
 * every weight the design uses in one file each and no bytes for axes nobody
 * asked for.
 *
 * unicode-range is kept from Fontsource. Only the latin file is shipped, so a
 * character outside that range falls through to the next family in the stack
 * rather than rendering from a font that has no glyph for it.
 *
 * Files come from @fontsource-variable/*, which is a devDependency. They are
 * committed rather than copied at build time: a build that reaches the network
 * for a font is a build that fails when the network says no.
 */
@font-face {
  font-family: "Fraunces";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(/fonts/fraunces-latin-wght-normal.woff2) format("woff2-variations");
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(/fonts/inter-latin-wght-normal.woff2) format("woff2-variations");
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}

:root {
  /* ---- the logo palette, sampled from the mark ---- */
  --navy-900: #0E2244;   /* deepest tone in the cap and the dollar sign */
  --navy-800: #14305E;   /* "Scholarship" in the wordmark */
  --navy-600: #1D4585;   /* mid tone in the blue loop's shadow */
  --blue-600: #1E6FD0;   /* the blue loop's body. 4.95 on white, so it is the CTA */
  --blue-500: #2B7FE0;   /* "Loop" in the wordmark. 4.03 on white: never carries text */
  --blue-300: #6FB2F2;   /* highlight along the top of the blue loop */
  --gold-600: #E0A016;   /* deeper amber on the gold loop's underside */
  --gold-500: #F5B31A;   /* the gold loop's body */
  --gold-300: #FFD469;   /* highlight on the gold loop */

  /* Cool neutrals. Screen, not paper: the mark is a screen mark. */
  --paper:   #f4f7fc;
  --surface: #ffffff;
  --ink:     #1b2438;
  --muted:   #5a6478;
  --line:    #e3e8f1;
  --line-2:  #c9d3e4;

  /* Semantic names, so a rule says what it means rather than which hex it wants.
     --fig is the colour of a counted figure. It is NAVY and not gold, which is
     the rule above doing its job: the numbers are the product, so they are the
     one thing that must never be hard to read. */
  --blue:    var(--blue-600);
  --blue-d:  var(--navy-600);
  --blue-l:  #eaf2fc;
  --fig:     var(--navy-800);
  --gold:    var(--gold-500);
  --gold-l:  #fff7e6;

  /* Aliases kept alive on purpose. Four files outside this one reference
     --moss and --clay with a hex fallback, and a rename that leaves a stale
     fallback behind is a colour that silently survives a repaint. */
  --moss:    var(--blue-600);
  --moss-d:  var(--navy-600);
  --moss-l:  var(--blue-l);
  --clay:    var(--fig);
  --clay-l:  var(--gold-l);

  --accent:     var(--blue-600);
  --accent-ink: #ffffff;

  --r-sm: 6px;
  --r:    10px;
  --r-lg: 14px;
  --wrap: 70rem;

  --serif: "Fraunces", "Iowan Old Style", Georgia, "Times New Roman", serif;
  --sans:  "Inter", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper: #0b1729; --surface: #12203a; --ink: #e8edf6; --muted: #9fadc4;
    --line: #23334f; --line-2: #33455f;
    /* On a dark ground the constraint inverts: the blues go light enough to
       carry text, and the gold is finally allowed to. */
    --blue: #6FB2F2; --blue-d: #8CC4F7; --blue-l: #14253f;
    --fig: #FFD469; --gold: var(--gold-500); --gold-l: #241c0c;
    --moss: var(--blue); --moss-d: var(--blue-d); --moss-l: var(--blue-l);
    --clay: var(--fig); --clay-l: var(--gold-l);
    --accent: var(--blue); --accent-ink: #08203a;
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 400 17px/1.7 var(--sans);
  -webkit-text-size-adjust: 100%;
  -webkit-font-smoothing: antialiased;
  /* The footer goes to the bottom on a short page. It did not matter while the
     footer was the same colour as the page; now that it is navy, /sponsors/ and
     /signin/ ended in a navy block with a pale strip below it. */
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
main { flex: 1 0 auto; }

/*
 * `width: 100%` and `min-width: 0` are load-bearing now that body is a flex
 * column. An auto margin on the cross axis switches OFF stretch, so main sized
 * itself to its own max-content instead of to the viewport, and /search/ became
 * 882px wide in a 390px window because the awards table is 882 at min-content.
 * check-overflow caught it on the first build after the change.
 */
main { width: 100%; min-width: 0; max-width: var(--wrap); margin: 0 auto;
  padding: 1.7rem 1.5rem 5rem; }
/* The homepage opens on a full-bleed navy band that has to touch the header, so
   it takes the padding back. Every other page opens on a heading or an eyebrow
   and wants the gap. This replaces a `main > h1:first-child` rule that only
   worked when the heading was a direct child, which stopped being true the
   moment /alerts/ put its h1 inside a form. */
main > .hero:first-child { margin-top: -1.7rem; }
/* No top padding on main, because the homepage's first child is a full-bleed
   band that has to touch the header. Every other page opens on a heading, and
   with the header now white against a tinted page the join was a hard line
   under the h1's ascenders. Only the pages that need the gap get it. */

a { color: var(--accent); text-underline-offset: 3px; }

/* Serif display. The single strongest signal that a person edited this. */
h1, h2, h3, .stat .n, .s-name {
  font-family: var(--serif);
  font-weight: 600;
  letter-spacing: -.015em;
  line-height: 1.15;
}
h1 { font-size: clamp(2.1rem, 4.8vw, 3.4rem); margin: 0 0 1.1rem; }
h2 { font-size: clamp(1.45rem, 2.6vw, 1.95rem); margin: 3.25rem 0 1rem; }
h3 { font-size: 1.12rem; margin: 0 0 .4rem; }
.lede { font-size: 1.16rem; color: var(--muted); max-width: 44rem; }
.sub { color: var(--muted); margin: -.4rem 0 1.2rem; }
.sr, .skip { position: absolute; left: -9999px; }
.skip:focus { left: 1rem; top: 1rem; background: var(--surface); padding: .6rem;
  border: 1px solid var(--line-2); border-radius: var(--r-sm); z-index: 20; }

/* Small caps label. Set in the sans so it reads as apparatus, not headline.
   navy-600 rather than navy-800: it sits directly above a navy-800 heading and
   two identical navies stacked read as one block of text rather than as a
   label and its heading. 9.37 on white, so the step down costs nothing. */
.kicker, .eyebrow {
  font-family: var(--sans); color: var(--navy-600); font-weight: 700;
  letter-spacing: .1em; font-size: .74rem; text-transform: uppercase;
  margin: 0 0 .7rem;
}
/* The page header, matching the homepage's section headers: label, heading,
   lede. Templates that carry an .eyebrow before their h1 get it for free. */
.crumbs + h1, .eyebrow + h1 { margin-top: 0; }

/* ---------------------------------------------------------------- chrome */

header.site { background: var(--surface); border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 10; }
/*
 * NOT STICKY ON A PHONE, until there is a real menu.
 *
 * Seven links plus two buttons wrap to four rows at 390px, which is a 215px
 * header. Stuck to the top that is a quarter of the viewport gone on every
 * page, permanently, on the device most of these readers are on.
 *
 * The honest fix is a disclosure menu and it is not built yet, so this is the
 * interim: the header scrolls away like any other block and the nav is one
 * scroll-to-top away. Recorded in OUTSTANDING.md rather than left as a
 * one-line workaround nobody remembers making.
 */
@media (max-width: 64rem) { header.site { position: static; } }
header.site nav { max-width: 82rem; margin: 0 auto; padding: .8rem 1.5rem;
  display: flex; gap: 1.25rem; align-items: center; flex-wrap: wrap; }
/* The lockup. Set in the sans at a heavy weight, which is how the wordmark is
   drawn; the serif that carries every heading on this site is not what the
   logo does, and a brand that disagrees with itself in the corner of every
   page is worse than one that disagrees with the headings. */
.brand { display: flex; align-items: center; gap: .55rem; text-decoration: none;
  color: var(--navy-800); font-family: var(--sans); font-weight: 800;
  font-size: 1.22rem; letter-spacing: -.02em; }
.brand img { display: block; width: 52px; height: 26px; }
.brand em { font-style: normal; color: var(--blue-600); }
@media (prefers-color-scheme: dark) {
  .brand { color: var(--ink); } .brand em { color: var(--blue-300); }
}
.navcta { margin-left: auto; display: flex; gap: .6rem; align-items: center; }
.btn.sm { padding: .55rem 1rem; font-size: .92rem; }
header.site ul { list-style: none; display: flex; gap: 1.25rem; margin: 0;
  padding: 0; flex-wrap: wrap; align-items: center; }
header.site ul a { text-decoration: none; color: var(--muted); font-weight: 500;
  font-size: .95rem; padding-bottom: 2px; border-bottom: 2px solid transparent; }
/* SCOPED TO THE LINK LIST, not to every anchor in the header.
   As `header.site a` this rule was (0,1,1) and beat `.btn` at (0,1,0), so the
   primary call to action rendered grey-on-blue and was unreadable. It only
   ever meant the nav items. */
header.site ul a:hover, header.site ul a:focus { color: var(--ink); border-color: var(--gold); }

/*
 * NAVY, like the hero, and for the same reason the hero is navy: a page has to
 * end somewhere, and a white footer under a white last section does not end it.
 *
 * Every colour below is measured on navy-900: #C9D6EA is 10.74, gold-300 is
 * 11.17, white is 15.78. The gold that cannot carry text on white carries it
 * freely here, which is the palette rule working in the direction it allows.
 */
footer.site { background: var(--navy-900); color: #c9d6ea; margin-top: 0; }
footer.site p { max-width: var(--wrap); margin: 0 auto; padding: 1.3rem 1.5rem;
  color: #93a8c4; font-size: .92rem; }
footer.site .small { padding-top: 0; }
footer.site a { color: #dbe6f5; }
footer.site a:hover { color: #fff; }

/* ------------------------------------------------------------------ hero */
/*
 * The homepage's hero and its applicant-pool card live in index.astro, scoped
 * to that page. They used to live here, and the rules were a full-bleed grid
 * plus a .showcase card, both used by exactly one page.
 *
 * That mattered, not as tidiness. The new hero reuses the class name .hero for
 * its band, and the old `display:grid; grid-template-columns:1.15fr .85fr` was
 * still in force underneath it, so the band became a two-column grid whose
 * single child was placed in the first column. The hero rendered 612px wide
 * inside a 1120px page and the h1 broke one word per line. Nothing errored and
 * every check passed.
 *
 * A dead rule under a reused name is not dead.
 */

/* ------------------------------------------------------------- buttons */

.btn {
  display: inline-flex; align-items: center; gap: .5rem;
  padding: .8rem 1.4rem; border-radius: var(--r-sm); border: 1px solid var(--moss-d);
  cursor: pointer; font-family: var(--sans); font-weight: 600; font-size: 1rem;
  text-decoration: none; background: var(--moss); color: #fff;
  transition: background .15s, border-color .15s;
}
.btn:hover { background: var(--moss-d); }
.btn.ghost { background: transparent; color: var(--ink); border-color: var(--line-2); }
.btn.ghost:hover { border-color: var(--ink); }
.btn.light { background: var(--surface); color: var(--moss); border-color: var(--line-2); }

/* ---------------------------------------------------------- form controls */
/*
 * A BASELINE, because the styled ones were styled one page at a time.
 * /scholarship-check/'s county picker was a native select: grey, system font,
 * sitting between two fields the site had painted. Four other pages had each
 * remembered to style their own. Elements rather than classes, so every
 * per-page rule that already exists still wins.
 */
input[type=text], input[type=email], input[type=search], input[type=number],
input[type=tel], select, textarea {
  font-family: var(--sans); font-size: 1rem; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line-2);
  border-radius: var(--r-sm);
}
select { padding: .6rem 2rem .6rem .7rem; appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%231D4585' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right .7rem center; }
:is(input, select, textarea):focus-visible { outline: 2px solid var(--blue-600);
  outline-offset: 1px; }

/* -------------------------------------------------------------- surfaces */

/*
 * THE AWARD CARD, and the one change here that is not paint.
 *
 * The facts row was `display:flex; flex-wrap:wrap`, so a card with four facts
 * broke three-and-one and a card with three kept them on one line. Twelve cards
 * in a grid therefore had twelve different internal layouts, and "Open to"
 * dropped to its own row on about half of them. A fact row is a table, so it is
 * a grid: three columns, always, and the row is pinned to the foot of the card
 * by a top rule so the rules line up across a row of cards of unequal height.
 */
.card { background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r); padding: 1.35rem 1.3rem; height: 100%;
  display: flex; flex-direction: column;
  transition: border-color .15s, box-shadow .15s; }
.card:hover { border-color: var(--blue-300);
  box-shadow: 0 6px 22px rgba(20, 48, 94, .08); }
.card h3 { font-size: 1.06rem; line-height: 1.32; }
.card h3 a { text-decoration: none; color: var(--navy-800); }
.card h3 a:hover { text-decoration: underline; text-decoration-color: var(--gold);
  text-decoration-thickness: 2px; }
.sponsor { color: var(--muted); font-size: .88rem; margin: .35rem 0 0; }
/*
 * Columns sized to their content, not to an equal third.
 *
 * "Jan. 31, 2027" needs 101px and an equal third of a card gives 91, so every
 * dated card wrapped its date onto two lines. Widening the middle column fixed
 * 95 of 98 and left 3, because which column the deadline lands in depends on
 * whether that award publishes an amount: a card with no amount puts the date
 * in column one. A positional width cannot work when the fields are optional.
 *
 * max-content columns pushed apart by space-between give every value the width
 * it needs and spend the slack on the gaps. The cost is that columns no longer
 * line up across a row of cards, which is worth less than a date that reads.
 */
.facts { display: grid; grid-template-columns: repeat(3, minmax(0, max-content));
  justify-content: space-between; gap: .75rem 1rem;
  margin: auto 0 0; padding-top: 1.05rem; border-top: 1px solid var(--line); }
.facts dt { font-size: .64rem; text-transform: uppercase; letter-spacing: .09em;
  color: var(--muted); font-weight: 700; }
.facts dd { margin: .15rem 0 0; font-weight: 700; font-size: .94rem;
  color: var(--navy-800); font-variant-numeric: tabular-nums; }
.unknown, .note, .verified, .thin { color: var(--muted); font-size: .92rem; }
.thin { background: var(--gold-l); border: 1px solid var(--line);
  border-left: 3px solid var(--gold); padding: .85rem 1.1rem;
  border-radius: 0 var(--r-sm) var(--r-sm) 0; }

.grid, .counties { list-style: none; margin: 1.4rem 0; padding: 0;
  display: grid; gap: 1rem; }
.grid { grid-template-columns: repeat(auto-fill, minmax(19.5rem, 1fr)); }
/* THREE COLUMNS, NOT auto-fill.
   auto-fill packed six tiles as 4 + 2 at 1280px and let the two longest names
   wrap to a second line while the others stayed on one, so the row heights
   went ragged. A curated set has a known size, so the grid is stated rather
   than inferred: 3 across on a desktop, 2 on a tablet, 1 on a phone. */
.counties { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 60rem) { .counties { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 34rem) { .counties { grid-template-columns: 1fr; } }
.counties a { display: flex; justify-content: space-between; gap: 1rem;
  align-items: baseline; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r); padding: 1rem 1.15rem; text-decoration: none;
  color: var(--ink); transition: border-color .15s; }
.counties a:hover { border-color: var(--blue-300);
  box-shadow: 0 6px 22px rgba(20, 48, 94, .08); }
.counties strong { font-weight: 600; }
.counties span { color: var(--fig); font-size: .9rem; white-space: nowrap;
  font-weight: 700; font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------- home page blocks */

/* Numbers set in the serif, in navy. Gold would fail contrast on white and
   the numbers are the product, so they are the one thing that must be legible. */
/* .headline used to be defined twice: a centred display figure for the
   homepage and, 200 lines later, the fact row on an award page. The homepage
   one is gone with the block that used it. Leaving it would have left a second
   rule under a live name, which is exactly what made the hero render 612px
   wide in the commit before this one. */

/*
 * THE LEDGER, the same band the homepage runs, in a rounded block rather than
 * full bleed because on an inner page it sits inside the measure rather than
 * across it. Gold on navy-800 is 7.03 and #C9D6EA is 8.85, both comfortable,
 * and this is where the brand's gold gets to carry text.
 */
.stats { display: grid; gap: 0; margin: 1.6rem 0 1rem;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  border-radius: var(--r); background: var(--navy-800);
  padding: 1.8rem 0; overflow: hidden; }
.stat { padding: .2rem 1.7rem; border-left: 1px solid rgba(255, 255, 255, .16); }
.stat:first-child { border-left: 0; }
.stat .n { display: block; font-size: 2.35rem; line-height: 1;
  color: var(--gold-300); letter-spacing: -.02em;
  font-variant-numeric: tabular-nums; }
.stat .l { display: block; color: #c9d6ea; font-size: .88rem; margin-top: .55rem;
  line-height: 1.45; }
@media (max-width: 46rem) {
  .stats { padding: 1.4rem 1.5rem; gap: 1.4rem; }
  .stat { padding: 0; border-left: 0; }
}

/*
 * SEPARATE CARDS, not one ruled slab. The homepage runs three bordered cards
 * and this ran a single bordered box divided by hairlines, so the same three
 * steps looked like two different components on two pages of one site.
 *
 * The counter sits BESIDE its heading rather than above it, which needs the li
 * to be a two-column grid: the ::before takes column one, the strong takes
 * column two, and everything after the strong is an anonymous item that has to
 * be told to span both. That is what the `> *` rule does.
 */
/* Both selectors, because the class lands on the <ol> on one page and on a
   wrapping <section> on another. `.steps ol` alone matched the wrapper case
   only, so the stacked variant rendered as three cards butted together with no
   gap: the grid was never established, so `gap` had nothing to apply to. */
.steps ol, ol.steps { counter-reset: s; list-style: none; padding: 0; margin: 0;
  display: grid; gap: 1.4rem;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); }
.steps li { counter-increment: s; padding: 1.5rem 1.4rem; color: var(--muted);
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--surface);
  display: grid; grid-template-columns: auto 1fr;
  gap: .35rem .8rem; align-items: center; }
.steps li strong, .steps li h2, .steps li h3 {
  color: var(--navy-800); font-family: var(--serif); font-weight: 600;
  font-size: 1.08rem; line-height: 1.3; margin: 0; }
.steps li > *:not(strong):not(h2):not(h3) { grid-column: 1 / -1; }
.steps li::before { content: counter(s); display: inline-grid; place-items: center;
  width: 2.15rem; height: 2.15rem; border-radius: 50%; background: var(--blue-600);
  color: #fff; font-weight: 700; font-size: .95rem; }
/* One column, for steps that are three paragraphs each rather than one
   sentence. /how-it-works/ is the case: the same component, stacked, because
   three columns of four paragraphs is a wall. */
.steps.stack, .steps.stack ol { grid-template-columns: 1fr; gap: 1.6rem; }
.steps.stack li { padding: 1.7rem 1.8rem; }
.steps.stack li h2 { font-size: 1.45rem; }
.steps.stack li p { margin: .8rem 0 0; }
.steps li p:first-of-type { margin-top: .2rem; }

/* Three abreast on a desktop, like the homepage's rules block, with the gold
   doing the marking it is allowed to do on a light ground. */
/*
 * Two selectors, because the class sits on the <dl> itself on five pages and on
 * a wrapping element elsewhere, and `.how dl` alone matched neither.
 *
 * STACKED BY DEFAULT, three abreast only where asked. /privacy/ and /terms/
 * carry four of these lists between them, and a policy read under stress by
 * somebody checking one specific thing is the last place to put a three-column
 * grid. /about/ opts in, because its three are the design's rules block.
 */
.how dl, dl.how { display: grid; gap: 1.5rem; margin: 1.6rem 0 0; }
.how dt { font-family: var(--serif); font-weight: 600; font-size: 1.08rem;
  color: var(--navy-800); line-height: 1.3; margin: 0 0 .5rem;
  padding-top: .85rem; border-top: 3px solid var(--gold); }
.how dd { margin: 0; color: var(--muted); font-size: .98rem; }
/* The columns variant expects each dt and its dd wrapped in a div, so a pair
   is ONE grid item. Six loose items across three columns puts a heading and
   its own paragraph in different columns, which is what it did. */
.how.cols, .how .cols { grid-template-columns: repeat(3, 1fr); gap: 1.9rem 2.2rem; }
.how.cols > div > dt { margin-top: 0; }
@media (max-width: 58rem) { .how.cols, .how .cols { grid-template-columns: 1fr 1fr; } }
@media (max-width: 40rem) { .how.cols, .how .cols { grid-template-columns: 1fr; } }

.cta { margin: 3.5rem 0 0; padding: 2.25rem; border-radius: var(--r-lg);
  background: var(--paper); border: 1px solid var(--line-2); }
.cta h2 { margin-top: 0; }
.cta p { color: var(--muted); max-width: 40rem; }

.faq { margin-top: 3rem; }
.faq dl { margin: 0; border-top: 1px solid var(--line-2); }
.faq dt { font-family: var(--serif); font-weight: 600; font-size: 1.1rem;
  color: var(--navy-800); margin: 0; padding: 1.35rem 0 .35rem; }
.faq dd { margin: 0; padding-bottom: 1.35rem; color: var(--muted);
  border-bottom: 1px solid var(--line-2); }

.alt { margin: 1rem 0 0; font-size: .96rem; color: var(--muted); }

/* -------------------------------------------------------------- zip search */

.zipsearch { margin: 1.75rem 0 .5rem; max-width: 31rem; }
.zipsearch label { display: block; font-weight: 600; margin-bottom: .5rem; }
.zipsearch .row { display: flex; gap: .5rem; }
.zipsearch input { flex: 1 1 auto; min-width: 0; padding: .85rem 1rem;
  font-size: 1.05rem; font-family: var(--sans); border: 1px solid var(--line-2);
  border-radius: var(--r-sm); background: var(--surface); color: var(--ink);
  font-variant-numeric: tabular-nums; }
.zipsearch input:focus { outline: 2px solid var(--moss); outline-offset: 1px; }
.zipsearch button { padding: .85rem 1.5rem; font-size: 1rem; font-weight: 600;
  cursor: pointer; font-family: var(--sans); border: 1px solid var(--moss-d);
  border-radius: var(--r-sm); background: var(--moss); color: #fff; }
.zipsearch button:hover { background: var(--moss-d); }
.zipsearch .hint { font-size: .87rem; margin: .55rem 0 0; color: var(--muted); }
.zipresult { margin: .9rem 0 0; padding: .9rem 1.1rem; background: var(--moss-l);
  border: 1px solid var(--moss); border-radius: var(--r-sm); }

/* ------------------------------------------------------------ award detail */

.detail { max-width: 45rem; }
.crumbs { font-size: .85rem; color: var(--muted); margin: 1.6rem 0 1.1rem; }
.crumbs a { color: var(--navy-600); text-decoration: none; }
.crumbs a:hover { text-decoration: underline; }
.answer { font-size: 1.1rem; margin: 0 0 1.5rem; }
.headline { display: flex; flex-wrap: wrap; gap: 2.4rem; padding: 1.4rem 1.6rem;
  background: var(--navy-800); border-radius: var(--r); margin: 1.6rem 0; }
.headline .k { display: block; font-size: .68rem; text-transform: uppercase;
  letter-spacing: .09em; color: #c9d6ea; font-weight: 700; }
.headline .v { font-family: var(--serif); font-size: 1.5rem; font-weight: 600;
  color: var(--gold-300); font-variant-numeric: tabular-nums; }
.effort { color: var(--muted); font-size: .96rem; }
.flags { list-style: none; padding: 0; margin: 1.1rem 0; display: flex;
  flex-wrap: wrap; gap: .45rem; }
.flags li { background: var(--moss-l); border: 1px solid var(--moss);
  color: var(--moss-d); border-radius: 999px; padding: .25rem .8rem;
  font-size: .84rem; font-weight: 600; }
@media (prefers-color-scheme: dark) { .flags li { color: var(--moss); } }
.criteria { margin: 0; border-top: 1px solid var(--line); }
.criteria div { display: grid; grid-template-columns: 13rem 1fr; gap: 1rem;
  padding: .7rem 0; border-bottom: 1px solid var(--line); }
.criteria dt { color: var(--muted); font-size: .9rem; }
.criteria dd { margin: 0; }
@media (max-width: 34rem) { .criteria div { grid-template-columns: 1fr; gap: .1rem; } }

/* The sponsor's own words, set apart like a pulled quotation. */
.raw { margin: 1rem 0; padding: 1.2rem 1.5rem; background: var(--surface);
  border: 1px solid var(--line); border-left: 3px solid var(--gold);
  border-radius: 0 var(--r-sm) var(--r-sm) 0; font-size: .97rem;
  overflow-wrap: anywhere; }
.apply { margin: 2rem 0 1rem; }
/* Shown only when we could not identify a real application page, so the button
   above goes to the listing instead. Saying which one it is beats letting a
   student find out by clicking. */
.apply-note { display: block; margin-top: .55rem; color: var(--muted);
  font-size: .9rem; max-width: 46rem; }
.related { list-style: none; padding: 0; margin: .8rem 0;
  border-top: 1px solid var(--line); }
.related li { border-bottom: 1px solid var(--line); padding: .6rem 0; }
.related span { color: var(--muted); font-size: .9rem; }

/* ------------------------------------------------------------------ forms */

.alerts { max-width: 33rem; }
.alerts fieldset { border: 1px solid var(--line); border-radius: var(--r);
  padding: 1.2rem 1.35rem; margin: 0 0 1.2rem; background: var(--surface); }
.alerts legend { font-family: var(--serif); font-weight: 600; font-size: 1.05rem;
  padding: 0 .45rem; }
.alerts label { display: block; font-size: .9rem; margin: .95rem 0 .35rem;
  color: var(--muted); }
.alerts label:first-of-type { margin-top: 0; }
/* Not the checkboxes. `width: 100%` on a tick box renders a full-width control
   with the glyph centred in it, which is how the age gate came to have its
   checkbox floating in the middle of its own line. */
.alerts input:not([type=checkbox]), .alerts select { width: 100%; padding: .65rem .8rem; font-size: 1rem;
  font-family: var(--sans); border: 1px solid var(--line-2);
  border-radius: var(--r-sm); background: var(--paper); color: var(--ink); }
.alerts .hint, .filters .hint { color: var(--muted); font-size: .85rem; margin: .5rem 0 0; }
.formstatus { margin-top: 1rem; padding: .8rem 1rem; border-radius: var(--r-sm);
  font-size: .95rem; }
.formstatus.ok { background: var(--moss-l); border: 1px solid var(--moss); }
.formstatus.err { background: var(--gold-l); border: 1px solid var(--gold-600); }

/* ----------------------------------------------------------------- search */

.linkish { background: none; border: 0; color: var(--accent); cursor: pointer;
  font-size: .9rem; padding: 0; text-decoration: underline; font-family: var(--sans); }
.searchwrap { display: grid; gap: 2rem; grid-template-columns: 17.5rem 1fr;
  align-items: start; margin-top: 1.5rem; }
@media (max-width: 58rem) { .searchwrap { grid-template-columns: 1fr; } }
.filters { background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r); padding: 1.2rem; position: sticky; top: 4.75rem; }
.fhead { display: flex; justify-content: space-between; align-items: baseline; }
.fhead h2 { margin: 0 0 .5rem; font-size: 1.1rem; }
.filters details { border-top: 1px solid var(--line); padding: .85rem 0 .3rem; }
.filters summary { cursor: pointer; font-weight: 600; font-size: .95rem; }
.filters label { display: block; font-size: .83rem; color: var(--muted);
  margin: .85rem 0 .3rem; }
.filters select, .filters input[type=text], .filters input[type=search] {
  width: 100%; padding: .5rem .6rem; font-size: .92rem; font-family: var(--sans);
  background: var(--paper); color: var(--ink); border: 1px solid var(--line-2);
  border-radius: var(--r-sm); }
.checks { display: grid; gap: .4rem; margin-top: .35rem; }
.checks .c { display: flex; gap: .5rem; align-items: flex-start; margin: 0;
  font-size: .88rem; color: var(--ink); }
.checks input { margin-top: .3rem; flex: 0 0 auto; accent-color: var(--moss); }
.zipnote { font-size: .83rem; color: var(--muted); margin: .4rem 0 0; }
.rhead { display: grid; gap: .8rem; margin-bottom: 1.3rem; }
.rhead input[type=search] { width: 100%; padding: .8rem 1rem; font-size: 1rem;
  font-family: var(--sans); background: var(--surface); color: var(--ink);
  border: 1px solid var(--line-2); border-radius: var(--r-sm); }
.rmeta { display: flex; justify-content: space-between; align-items: center;
  gap: 1rem; padding-bottom: .6rem; border-bottom: 1px solid var(--line); }
.rmeta span { color: var(--ink); font-size: .95rem; font-weight: 600;
  font-variant-numeric: tabular-nums; }
.rmeta select { padding: .4rem .55rem; background: var(--surface); color: var(--ink);
  border: 1px solid var(--line-2); border-radius: var(--r-sm); font-size: .9rem;
  font-family: var(--sans); }

/* ---------------------------------------------------------------- sponsors */
/* The claim block sits at the foot of every sponsor page. It is the only
   route a sponsor has to correct a page we wrote about them without asking,
   so it is given the weight of a call to action rather than a footnote. */
.claim { margin: 2.5rem 0 0; padding: 1.6rem 1.7rem; background: var(--moss-l);
  border: 1px solid var(--line-2); border-radius: var(--r-lg); }
.claim h2 { margin: 0 0 .5rem; font-size: 1.15rem; }
.claim p { margin: 0 0 .8rem; color: var(--muted); max-width: 46rem; }
.claim-actions { display: flex; flex-wrap: wrap; gap: 1rem 1.4rem;
  align-items: center; }
.claim-actions a:not(.btn) { color: var(--moss); font-size: .92rem;
  font-weight: 500; }
.claim .small { margin: .9rem 0 0; font-size: .84rem; }

.promises { list-style: none; padding: 0; margin: 1.4rem 0 2rem;
  display: grid; gap: .55rem; max-width: 46rem; }
.promises li { padding-left: 1.5rem; position: relative; color: var(--muted);
  font-size: .95rem; }
.promises li::before { content: "✓"; position: absolute; left: 0;
  color: var(--moss); font-weight: 700; }
.promises strong { color: var(--ink); }

form.alerts textarea { width: 100%; padding: .7rem .8rem; font-size: .95rem;
  font-family: var(--sans); background: var(--paper); color: var(--ink);
  border: 1px solid var(--line-2); border-radius: var(--r-sm); resize: vertical; }
.ranknote { font-size: .86rem; color: var(--muted); margin: 0 0 1.2rem;
  padding-left: .9rem; border-left: 2px solid var(--line-2); max-width: 46rem; }
/* Precision note on a search card: says what the ranking could see, so a card
   ordered on geography alone does not look like one ordered on all inputs. */
.card .prec { margin: .7rem 0 0; padding-top: .55rem; font-size: .78rem;
  color: var(--muted); border-top: 1px dashed var(--line); }

/* Numbered method steps in prose. The .steps grid on the homepage is a
   four-across card row and reads badly at four sentences per item. */
.steps-plain { counter-reset: p; list-style: none; padding: 0; display: grid;
  gap: .9rem; }
.steps-plain li { counter-increment: p; position: relative; padding-left: 2.6rem;
  color: var(--muted); }
.steps-plain li strong { color: var(--ink); }
.steps-plain li::before { content: counter(p); position: absolute; left: 0;
  top: .05rem; display: inline-grid; place-items: center;
  width: 1.8rem; height: 1.8rem; border-radius: 50%; background: var(--moss-l);
  color: var(--moss); font-weight: 700; font-size: .9rem;
  border: 1px solid var(--moss); }

/* The honeypot. Off-screen rather than display:none, because some bots skip
   fields that are explicitly hidden and this one needs to look fillable. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px;
  overflow: hidden; }

/* ------------------------------------------------- policy and terms pages */
/* Two lists and one checkbox, used only by /privacy/ and /terms/. Kept small
   deliberately: these pages are read under stress, by somebody checking one
   specific thing, and every bit of decoration is a thing between them and it. */
.plain { margin: .9rem 0 1.4rem; padding-left: 1.2rem; max-width: 46rem;
  display: grid; gap: .45rem; color: var(--muted); font-size: .95rem; }
.plain li { padding-left: .2rem; }
.plain strong { color: var(--ink); }

/* The age gate. Given the weight of a field rather than fine print, because it
   asks the reader to assert something and a tick box that looks like a
   footnote gets ticked without being read. */
.check { display: flex; gap: .6rem; align-items: flex-start; margin: 1rem 0 0;
  padding: .8rem .9rem; background: var(--moss-l); border-radius: var(--r-sm);
  font-size: .9rem; color: var(--ink); font-weight: 400; }
.check input { margin-top: .2rem; flex: 0 0 auto; accent-color: var(--moss); }

/* Single-column utility pages: /unsubscribe/ and the confirmation page that
   functions/api/unsubscribe.ts returns. Narrower than the wrap, because these
   are read once, under mild irritation, and the only thing that matters is
   finding the field and leaving. */
.narrow { max-width: 40rem; margin: 2.5rem auto 0; }
.narrow h2 { margin-top: 2.2rem; }
