/* deltaPlus marketing site — single stylesheet, no external dependencies.
   Colors are the brand values from data/referenced/dP_colors.py: dP_BLUE and
   dP_GREEN (the logo's d + P), dP_BLACK as ink. Keep these in sync with that
   file if it changes. */

:root {
  --blue:        #0927b1;  /* dP_BLUE  rgb(9, 39, 177) */
  --blue-hover:  #071d85;  /* dP_BLUE deepened a quarter step — the filled buttons' hover */
  --green:       #07a516;  /* dP_GREEN rgb(7, 165, 22) */
  --green-soft:  #3fbf4a;

  /* THE dP SAT BLUE — the blue of the app icon, of the trailing word of the wordmark, and of
     every link on this site that hands a reader to College Board. Same value on both grounds:
     3.02:1 on white and 6.05:1 on the dark paper, so it clears the dP 2:1 floor either way,
     and the app and the icon are the same blue everywhere. Mirrored BY HAND from
     mobile/dP_SAT/lib/theme/dP_colors.dart (satBlue). */
  --sat-blue:   #349cde;
  /* dP_PINK, from data/referenced/dP_colors.py, and Math Strategies' color. One value on
     both grounds: 4.13:1 on white and 4.42:1 on the dark paper, clear of the dP 2:1 floor
     either way, so it needs no brightened twin the way the blue and green do. */
  --pink:       #f40071;
  /* The sticky bar's own ground, per theme so the white and the near-black are declared in
     one place. Only `.site-header` paints it now; it also carried the handwritten note's band
     until 26-09-10, when the note became a floating card and the band went with it. */
  --header-ground: rgba(255, 255, 255, 0.9);

  /* THE SHINE'S GOLD, as three channels so the band and the letter's own color can both be
     built from one value. It is per theme because the band is painted INTO the letters: on
     white paper the ink is dark and a pale gold reads as light catching it, but on the dark
     paper "Learning" is very nearly white and the app's own gold-white (#fff3c4) is nearly
     white too, so the sweep simply vanished (Kevin, 26-09-12). The dark value is a real
     saturated gold for that reason, and the light one was warmed to match it. */
  --shine-gold: 255 224 138;

  --ink:        #1a1a1b;   /* dP_BLACK */
  --ink-soft:   #55555a;
  --paper:      #ffffff;
  --paper-card: #ffffff;
  --line:       #e6e8ee;
  /* The SAT taxonomy colors: the six SKILL AREAS the breakdown filters by, and so the
     workbooks, which take their area's color, their chips and circles too (Kevin, 26-09-22).
     Math Strategies became an area with the eighteen workbooks (26-09-22), and the three
     whole-section workbooks take the brand --green.
     Mirrored BY HAND from the SAT_DOMAIN_* block of data/referenced/dP_colors.py, where each is
     measured. One value on both grounds except --d-geometry, which is dP blue by day and the
     night blue at night, written out in both dark blocks below rather than borrowed from
     --blue. WHITE words on every fill in both modes (Kevin, 26-09-22), so the dark ink the
     yellow used to take (--d-data-ink) is retired. The contrast floor is 2:1, a HARD LIMIT
     (Kevin, 26-09-22: "Change the condition to 2:1 ratio or clearer, and this is a hard limit
     of clarity. I don't expect to almost ever get this close, but the yellow needs to be
     yellow."). Nothing fails it: the lowest are the yellow on white and white words on the
     yellow, 2.00:1, at the limit by design, then the red on the dark card, 2.51:1. */
  --d-comp:     #f26e13;   /* SAT_DOMAIN_COMPREHENSION (dP_ORANGE) */
  --d-english:  #19b1c3;   /* SAT_DOMAIN_STANDARD_ENGLISH */
  --d-algebra:  #b3281d;   /* SAT_DOMAIN_ALGEBRA */
  --d-geometry: #0927b1;   /* SAT_DOMAIN_GEOMETRY (dP_BLUE), #6f8dff on dark */
  --d-data:     #efab00;   /* SAT_DOMAIN_DATA_LITERACY (dP_YELLOW, one value on both grounds) */
  --d-strategies: #f40071; /* SAT_DOMAIN_MATH_STRATEGIES (dP_PINK, one value on both grounds) */
  /* RETIRED 26-09-22: the five workbook colors --wb-1 to --wb-5 (SAT_WORKBOOK_RAMP), which no
     rule here read any more. A workbook's color is its skill area's now. They last lived at
     commit 9d6c5bf. */
  /* The workbook map's window: the panel's ground, the lead (the roads between workbooks),
     and the pale outline and gray name of a workbook a visitor cannot open. The dark values
     are the window seen from the lawn with the house lit: the card ground and a darker lead.
     An open or tapped tile takes its own workbook color (--c, set per tile in sat.html) rather
     than a token here. Mirrored BY HAND from the app: the ground from its palette
     (mobile/dP_SAT/lib/theme/dP_colors.dart, paper and paperCard), the rest from
     mobile/dP_SAT/lib/data/workbook_map.dart (leadLight, leadFadedLight, grayNameLight, and
     their dark twins). The pale outline is about 1.41:1 on white, the one deliberate exception
     to the dP 2:1 floor: a locked workbook should recede, and its gray name (2.74:1) carries it. */
  --map-panel:   #ffffff;
  --map-lead:    #b9bdc8;
  --map-locked:  #d6d9e0;
  --map-locked-ink: #9a9ca4;

  /* THE TWO VEILS over the Boston field (26-09-10). `--sheet-veil` is the hero's paper: solid
     enough that the wordmark is never set on a photograph, sheer enough that the photographs
     are plainly there behind it. `--field-veil` is a whisper of the page's own light over the
     whole field, so the margins sit in the page rather than shouting out of it. Both are
     defined per theme, because a veil is the PAPER color and the paper changes. */
  --sheet-veil: rgba(255, 255, 255, 0.74);
  --field-veil: rgba(255, 255, 255, 0.16);

  /* BODY COPY, AND WHY IT IS A LIGHT-MODE SETTING (Kevin, 26-09-10: "for light mode on the
     website, make the body text a little bigger and heavier; it's hard to read. titles are
     fine"). Dark ink on white genuinely reads lighter than white ink on dark at the same
     size and weight — the pale ground bleeds into the letterforms — so the same type that
     is comfortable in dark mode is thin in light. These two are therefore LIGHT values
     here and are set back to the old ones inside both dark blocks.
     THE WEIGHT IS THE FONT'S OWN since 10/7/2026 (Kevin: "the text on the website seems
     fuzzy"). Until then part of the extra weight came from `-webkit-font-smoothing: auto` in
     light mode, which on a Mac thickens every letter by smearing its edges, and that smear
     was the fuzz. Every page now draws its letters crisp (on `body`, below), and the light
     weight went up a step, 450 to 500, so the copy stays as heavy as Kevin asked for. */
  --copy:        1.06;   /* every body size a step up; the titles are untouched */
  --copy-weight: 500;

  /* THE SHEET'S WIDTH. 1040 until 10/7/2026, when Kevin asked for the margins on either side
     "made 2/3 the size they are now": the sheet takes back a third of whatever the window has
     beyond 1040, so the Boston margins are two thirds of their old width at every window size,
     and a window narrower than 1040 is filled exactly as before. */
  --max:        calc(1040px + (100vw - 1040px) / 3);
  --radius:     14px;
  --shadow:     0 10px 30px rgba(9, 39, 177, 0.08);
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  color: var(--ink);
  background: var(--paper);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  line-height: 1.65;
  font-size: calc(16px * var(--copy));
  font-weight: var(--copy-weight);
  -webkit-font-smoothing: antialiased;   /* Safari and Chrome on a Mac: crisp, never smeared */
  -moz-osx-font-smoothing: grayscale;    /* the same, in Firefox on a Mac */
}

/* Every body size that is set outright rather than inherited carries `* var(--copy)` where it
   is declared, so each stays where a reader would look for it instead of being overridden from
   the bottom of the file. Headings, the wordmark, the price figures, the store badges, and the
   countdown number are deliberately NOT multiplied: Kevin said the titles are fine. */

a { color: var(--blue); }
/* A ® INSIDE A LINK, as in the home page's "SAT®" (Kevin, 10/7/2026: "don't underline the
   reserved mark"). A link's underline never reaches into an inline block, so the mark keeps
   the link's color and loses only the line. */
.reg { display: inline-block; }

/* WHERE A LINK GOES DECIDES HOW IT LOOKS (Kevin, 26-09-09). deltaPlus links take the company
   blue above; a link that hands the reader to College Board takes the test's own blue, italic
   and underlined, so it is obvious before the tap that it leaves for someone else's site. The
   app does exactly this in `linkColor`, lib/data/links.dart.

   Matched on the href rather than a class that has to be remembered — the same reason the app
   derives the color from the URL. Every College Board host is under collegeboard.org, and
   there are already four of them across this site. */
a[href*="//collegeboard.org"],
a[href*=".collegeboard.org"] {
  color: var(--sat-blue);
  text-decoration: underline;
  text-decoration-color: var(--sat-blue);
}
/* EVERY LINK INSIDE A SENTENCE IS ITALIC, wherever it goes (Kevin, 10/13/2026: "links should be all
   italicized or none italicized, all else equal"). Until then only College Board's links were, by
   the rule above. All, rather than none, because the app's inline links (`dPLink`, links.dart) and
   the website's own grading page are already italic. Menus, buttons, footer link rows, and the home
   page's word tiles are not sentences and stay upright. */
main :is(p, li, td, dd) a:not(.btn),
footer p a {
  font-style: italic;
}

h1, h2, h3 {
  font-family: Georgia, "Times New Roman", serif;
  line-height: 1.2;
  letter-spacing: -0.01em;
}

/* ---------- brand wordmark: delta = blue, Plus = green, mirroring the logo ---------- */
/* The wordmark, in all three places the page furniture prints it. NORMAL, with `Plus`
   alone bold in the rule below — the header and the footer already read this way; the
   hero did NOT, because an <h1> is bold by default and nothing here said otherwise, so
   `deltaPlus Learning` came out bold end to end. Setting the weight outright fixes that
   and makes the three agree with each other and with both openings (Kevin, 26-09-09). */
.brand-name, .hero-title, .footer-name { font-family: Georgia, serif; font-weight: 400; color: var(--blue); }
.brand-name strong, .hero-title strong, .footer-name strong { color: var(--green); font-weight: 700; }
.hero-title .hero-learning { color: var(--ink); }  /* the opening's landing word, at rest */

/* ---------- header / nav ---------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 8px clamp(16px, 4vw, 40px);
  background: var(--header-ground);
  backdrop-filter: saturate(1.4) blur(8px);
  border-bottom: 1px solid var(--line);
}
.brand { display: flex; align-items: center; gap: 10px; text-decoration: none; color: var(--ink); }
/* THE MARK AND THE NAME, both a size up (Kevin, 26-09-12). At 20px the company's own name in
   the corner was set smaller than the menu items beside it, which read as an afterthought;
   26 puts it a little above them, where the thing everything else on the page belongs to
   ought to sit. */
.brand-mark { width: 42px; height: 42px; background: #fff; border-radius: 9px; padding: 3px; border: 1px solid var(--line); }
.brand-name { font-size: 26px; }
/* Wraps now, rather than running off the edge: the SAT page's nav gained a seventh item
   with the breakdown chart, and a single unbreakable row overflowed a narrow phone. */
.site-nav { display: flex; flex-wrap: wrap; justify-content: flex-end;
            /* The wider gap belongs with the bigger type, so its FLOOR stays where it was:
               on a phone the menu is already wrapping and every point of gap is another row. */
            align-items: center; gap: clamp(10px, 3vw, 34px) clamp(10px, 3vw, 34px); }
/* HALF AGAIN AS BIG, on every page (Kevin, 26-09-10): 15px to 22.5, with a little tracking so
   the words do not crowd each other at the new size and a wider gap between them. The menu is
   the first thing on the page and it was reading as fine print.

   BUT ONLY WHERE THERE IS ROOM FOR IT, which is what the clamp is doing. This menu is seven or
   eight items wide and it WRAPS rather than scrolling, so its size decides the header's height.
   Held flat at 22.5 it measured 606 points tall on a 390-point phone — two thirds of the screen,
   sticky, on every page. The clamp gives the full 22.5 from about 1400 points up, eases down
   through the tablet widths, and hands a phone back the 15 it always had. The phone's header
   was already tall at 318 points before any of this; that is a separate problem and not one to
   fix by quietly ignoring the size Kevin asked for on the screens he asked for it on. */
.site-nav a { text-decoration: none; color: var(--ink-soft); font-size: clamp(15px, 1.6vw, 22.5px); font-weight: 500; letter-spacing: 0.015em; }
.site-nav a:hover { color: var(--blue); }
.nav-cta {
  padding: 7px 16px;
  border-radius: 999px;
  background: var(--blue);
  color: #fff !important;
}
.nav-cta:hover { background: var(--blue-hover); }
/* ---------- About deltaPlus: the home page's list (Kevin, 10/9/2026) ----------
   "turn Philosophy and Mission into About deltaPlus, where hovering brings down a list of options,
   Philosophy, the Mission, the Contact Us". The words go to Philosophy; the list drops when a pointer
   rests on them, when the keyboard reaches them (`:focus-visible`, so a mouse click leaves nothing
   hanging open), or when the small arrow is tapped (`is-open`, the script in index.html), for a phone.
   The list's links take the menu's own type and colors (`.site-nav a`). */
.nav-about { position: relative; display: inline-flex; align-items: center; gap: 2px; }
/* THE ARROW BIGGER (Kevin, 10/9/2026: "Make the arrow point down next to About deltaPlus on the home page of
   the website bigger"): its size alone, from a fixed 13 points to 20 growing with the window to 29, about
   1.3 times the menu's words beside it (`.site-nav a`, 15 to 22.5), since the small triangle fills only
   about half its own height.
   IT ALWAYS POINTS DOWN (Kevin, 10/10/2026: "make the top menu arrow not switch directions when it's
   clicked, since this doesn't do anything"). A tap still opens and closes the list for a phone; until
   then the arrow also turned over to point up while the list was open. */
.nav-about-toggle {
  font: inherit; font-size: clamp(20px, 2.1vw, 29px); line-height: 1; padding: 6px 4px; border: 0; background: none;
  color: var(--ink-soft); cursor: pointer; transition: color 160ms ease;
}
.nav-about:hover .nav-about-toggle, .nav-about.is-open .nav-about-toggle { color: var(--blue); }
.nav-about-menu {
  position: absolute; top: calc(100% + 8px); left: -14px; z-index: 20;
  min-width: 190px; margin: 0; padding: 8px 0; list-style: none;
  background: var(--paper-card); border: 1px solid var(--line); border-radius: 14px;
  box-shadow: 0 14px 30px rgba(0, 0, 0, 0.16);
  opacity: 0; visibility: hidden; transform: translateY(-6px);
  transition: opacity 160ms ease, transform 160ms ease, visibility 0s linear 160ms;
}
/* A bridge over the 8 points between the words and the list, so a pointer moving down never leaves
   the item and lets the list close under it. */
.nav-about-menu::before { content: ""; position: absolute; left: 0; right: 0; top: -9px; height: 9px; }
.nav-about:hover .nav-about-menu,
.nav-about:has(:focus-visible) .nav-about-menu,
.nav-about.is-open .nav-about-menu {
  opacity: 1; visibility: visible; transform: none;
  transition: opacity 160ms ease, transform 160ms ease;
}
.site-nav .nav-about-menu a { display: block; padding: 9px 18px; white-space: nowrap; }
.site-nav .nav-about-menu a:hover { background: color-mix(in srgb, var(--blue) 9%, transparent); }
@media (prefers-reduced-motion: reduce) {
  .nav-about-menu, .nav-about-toggle { transition: none; }
}
/* APPS IS BOXED IN dP_GREEN (Kevin, 26-09-10) — the one nav item pointing at a product rather
   than at a page of prose, and the handwritten line under the menu points down at it. The box
   is drawn with an inset shadow rather than a border so adding it does not change the item's
   width and shove the rest of the menu sideways. */
.site-nav a.nav-apps {
  color: var(--green);
  border-radius: 8px;
  padding: 5px 13px;
  margin: -5px -4px;
  box-shadow: inset 0 0 0 2px var(--green);
}
.site-nav a.nav-apps:hover { background: var(--green); color: #fff; }
/* AND IT SHINES EVERY FIVE SECONDS (Kevin, 26-09-10), which is the app's own gesture: the
   dumbbell's alert ring takes a band of gold-white across it on exactly this period, and the
   launch animation sweeps the same band over "SAT". Same color, same direction, right to
   left.

   ONE ANIMATION WITH A LONG DEAD STRETCH rather than a short one on a timer: the band crosses
   in the first 18 % of a five-second loop and sits off the edge for the rest. The browser
   composites a transform it is not changing for free, so the four-and-a-bit idle seconds cost
   nothing — the same reasoning the app uses for holding its own ring still four seconds in
   five rather than looping a controller.

   `left: 0` IS THE WHOLE FIX, and its absence is what Kevin saw on 26-09-10 ("too slow and
   pauses mid-shine, so that gold just hovers statically"). An absolutely-positioned box with
   no `left` sits at its STATIC position — for an ::after, immediately after the text — so
   every percentage below was measured from the far side of the word rather than from the
   box's left edge. The band therefore came to rest a few dozen points INSIDE the button and
   parked there for the remaining four seconds, which is exactly the stationary gold. Anchored
   at the left edge, the two ends of the sweep mean what they say: at 230 % of its own 45 %
   width the band is clear of the right edge, at -110 % it is clear of the left, and nothing is
   visible at rest.

   MATCHED TO THE HERO TITLE'S SHINE (Kevin, 26-09-11), and matched on the RIGHT QUANTITY at
   the second attempt. Copying the hero's 920 ms did nothing, because duration was never what
   made this look slow. Measured: the hero's band crosses 1318 px in 920 ms, which is 1432 px
   a second; this band crossed 90 px in the same 920 ms, which is 98 — fourteen times slower.
   The eye follows the band as a moving object and reads its actual speed, not its proportions.
   The sweep was taken to 200 ms on that reasoning and BROUGHT BACK to 920 ms the same day
   (Kevin, 26-09-11) — the arithmetic was right and the result was not, which is the usual way
   round. A band this size crossing in a fifth of a second reads as a twitch rather than as
   light. The speed difference from the hero title is therefore deliberate now rather than
   accidental: a button is not a headline, and it does not want a headline's pace.
   The band is 36 % of the box, which is the app's own `_Shine._half` of 0.18 doubled.

   `--dP-shine-offset` is set per element by shine.js, negative, so two shines on one page
   never fire on the same frame. It inherits into this pseudo-element from the anchor. */
.site-nav a.nav-apps { position: relative; overflow: hidden; }
.site-nav a.nav-apps::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 36%;
  pointer-events: none;
  background: linear-gradient(90deg, rgba(255, 243, 196, 0) 0%, rgba(255, 243, 196, .85) 50%, rgba(255, 243, 196, 0) 100%);
  animation: nav-apps-shine 5s cubic-bezier(.42, 0, .58, 1) var(--dP-shine-offset, 0s) infinite;
}
/* 285 % and -110 % are the band's OWN widths, not the box's: at 36 % of the box it takes
   278 % of itself to clear the right edge and 100 % to clear the left, and the extra few
   points at each end are margin. 18.4 % of five seconds is 920 ms. */
@keyframes nav-apps-shine {
  0%     { transform: translateX(285%); }
  18.4%  { transform: translateX(-110%); }
  100%   { transform: translateX(-110%); }
}
@media (prefers-reduced-motion: reduce) {
  .site-nav a.nav-apps::after { animation: none; opacity: 0; }
}

/* ======================= THE DOWNLOAD NOTE =======================
   A HANDWRITTEN CARD THAT FOLLOWS THE READER TO THE APPS (Kevin, 26-09-10). It used to be two
   handwritten rows sitting in a full-width band under the menu, where it was read once and
   then scrolled away for good. It is now one row — "Download the app!" — with the deltaPlus
   SAT icon beside it, on its own small card, and the card does not scroll away: it travels to
   the foot of the window with a green arrow under it, rides down the page, and comes to rest
   level with "The apps" when the reader reaches it.

   The band is gone with it, which is the other half of what Kevin asked for ("make the top
   white banner again thinner"): the note carried the band, the band carried eighty-odd points
   of the header's height, and taking the note out of the document takes the band with it.

   WHY IT IS `position: fixed` AND WHY JAVASCRIPT SETS `top`. What the card has to do is hold a
   viewport position at the two ends of the page and a DOCUMENT position in the middle, and no
   single CSS value does that — `sticky` sticks within one scrolling ancestor, and this card
   has to leave the header, cross the whole page, and stop on a heading two sections down. So
   the card is fixed, and one line of arithmetic in index.html decides its `top` each frame:
   the viewport position of the "The apps" heading, clamped between just under the menu and
   just above the bottom edge. The clamp IS the behavior — when the heading is below the
   window the clamp pins the card to the bottom, when it has scrolled off the top the clamp
   pins it to the top, and in between the card simply sits where the heading sits. The arrow
   reads straight off the same clamp: pointing down while the heading is still below, hidden
   while the card is level with it, pointing up once it is above.

   NOT A CURSIVE FACE. The first version used Snell Roundhand and Kevin's word for it was
   "too curly": a copperplate script is a wedding invitation, and this is a note somebody
   jotted. Six of the system's own hands were set side by side in dP_PINK and Kevin chose
   NOTEWORTHY — rounder and friendlier than the alternatives, and drawn rather than written,
   with no loops at all. It ships on every Mac and iPhone; Bradley Hand and Segoe Print cover
   the other platforms, and the last resort is the page's own sans, which the italic keeps at
   least a little hand-written in feel. Still no downloaded font, so this stylesheet keeps its
   promise of no external dependencies and there is no font license to record. The GREEN is
   dP_GREEN rather than the old dP_PINK, because it now sits on its own white card next to a
   green arrow rather than on a photograph. */
/* BACK TO THE RIGHT EDGE (Kevin, 10/9/2026: "Move the coming this fall card back out to the right,
   enough so that the arrow up or down can sit on the card's left side"). On 10/7 it moved in to
   sit wholly on the sheet, `right: max(clamp(10px, 1.8vw, 22px), calc((100vw - var(--max)) / 2 +
   14px))`; now it hugs the window's edge, closer than before 10/7, and carries its arrow inside. */
.app-note {
  position: fixed;
  right: clamp(8px, 1vw, 14px);
  top: 0;                       /* index.html owns this from here on */
  z-index: 9;                   /* under the sticky header (10), over the sheet (1) */
  pointer-events: none;         /* only the card itself is clickable */
}
/* THE CARD ARRIVES WITH ITS OWN FIRST STROKE (Kevin, 26-09-11). It used to sit under the menu
   as an empty white rectangle from the moment the page loaded, waiting for the writing — so a
   first visitor watched a blank card through the whole title sequence before anything was
   written in it.
   Scoped to `html.dP-js` on purpose: with scripting off, `is-writing` is never added, and
   without that scope the card would stay hidden for the whole visit rather than showing
   complete, which is what it has always done in that case.
   `visibility` rides along with the opacity so the empty card is not reachable by the keyboard
   while it cannot be seen; opacity alone would leave its link in the tab order. */
html.dP-js .app-note {
  opacity: 0;
  /* OPACITY ONLY, and `visibility` deliberately not used. Hiding it that way was tried first
     and broke both halves of the reveal: neither the fade nor the pen's own animation starts
     on an element that is `visibility: hidden` at the instant the class lands, so the card
     stayed invisible with its line still clipped to nothing.
     The transition sits on the BASE rule rather than on the class that arrives, which is the
     form that reliably has a transition to run at the moment of the change. */
  transition: opacity 220ms ease;
}
html.dP-js .app-note.is-writing { opacity: 1; }
/* Invisible and therefore not clickable either. The keyboard is handled in index.html, which
   parks the link out of the tab order until the writing starts — in script, so that a reader
   without it is never left with a link nobody can reach. */
html.dP-js .app-note:not(.is-writing) .app-note-card { pointer-events: none; }

/* The travel down to the foot of the window, run ONCE when the writing finishes. It is added
   and then removed again a beat later, because for the rest of the page's life this element
   is tracking a heading frame by frame and any transition on `top` would read as lag. */
.app-note.is-traveling,
html.dP-js .app-note.is-traveling {
  /* BOTH properties, and the second selector, because of a collision worth remembering: the
     rule above that fades the card in sets `transition` — the shorthand — and outranks a bare
     `.app-note.is-traveling`. So the card stopped gliding to the foot of the window and
     simply appeared there (Kevin, 26-09-11). A shorthand replaces everything it does not
     mention, which is why the fade has to be restated here rather than assumed. */
  transition: opacity 220ms ease, top 1200ms cubic-bezier(.4, 0, .2, 1);
}

/* THE CARD: white paper in light, the page's card black in dark, sized to the words and no
   wider. The corners are generous on purpose — Kevin asked for a rectangle whose corners are
   "not too sharp", and a note torn off a pad has no sharp corners. */
.app-note-card {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 18px;
  background: var(--paper-card);
  border: 1px solid var(--line);
  border-radius: 16px;
  box-shadow: 0 8px 26px rgba(0, 0, 0, 0.16);
  text-decoration: none;
  pointer-events: auto;
}
.app-note-card:hover { border-color: var(--green); }
.app-note-line {
  font-family: "Noteworthy", "Bradley Hand", "Segoe Print", -apple-system, BlinkMacSystemFont, sans-serif;
  font-style: italic;
  font-weight: 700;
  font-size: clamp(19px, 2vw, 26px);
  /* Noteworthy is a wide face on a tall body; this is the leading that keeps one clean row. */
  line-height: 1.35;
  color: var(--green);
  /* One row, always. The card is sized to the words, so a wrap would not save space — it
     would only turn a written line into a written paragraph. */
  white-space: nowrap;
  /* Unwritten at rest: the ink is clipped away to nothing and the animation opens the clip
     left to right. A clip, not a fade, because ink is either laid down or it is not. */
  clip-path: inset(0 100% -25% 0);
}
/* THE ICON AND ITS NAME, STACKED THE WAY A HOME SCREEN STACKS THEM (Kevin, 26-09-11): the app
   as it will actually look on the reader's phone, rather than a picture of it. The card and
   its handwriting grew to carry the name without crowding the line beside it.
   The pair fades in TOGETHER, as one thing, which is why the opacity and the animation moved
   here from the icon — an icon arriving before its own name would read as two events. */
.app-note-app {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  opacity: 0;                   /* fades in once the line has been written */
}
/* The icon already carries its own dP_BLUE border in the artwork — see
   assets/SAT/dP_SAT_app_icon_blue.png, of which this is the served copy — so nothing here
   draws one. It only rounds the corners, the way a phone's home screen would. */
.app-note-icon {
  display: block;
  width: clamp(44px, 4.4vw, 56px);
  height: clamp(44px, 4.4vw, 56px);
  border-radius: 11px;
}
/* Small against its icon, exactly as a home-screen label is, and in the page's own text color
   rather than the handwriting's green — it is the app's NAME, not part of the note. */
.app-note-name {
  font-size: clamp(10.5px, 1.05vw, 13px);
  line-height: 1.1;
  font-weight: 600;
  color: var(--ink);
  white-space: nowrap;
}
/* SUPERSEDED 10/9/2026: the arrow now sits inside the card, on its left (see the rule itself). The
   two notes below record how it hung outside the card until then, halo included. */
/* ABSOLUTELY POSITIONED, and that is not a detail: laid out in flow, the arrow moving from
   under the card to over it would shove the card down by its own height at the exact moment
   the reader passes the heading. Out of flow, the card holds still and only the arrow changes
   sides. The room it needs at each end is reserved by the clamp in index.html. */
/* BIGGER, AND HALOED, so it carries in light mode (Kevin, 26-09-11). The green clears the dP
   2:1 floor against white on its own — it measures about 3.2:1 — but the arrow is a thin
   stroke over the Boston collage rather than over flat paper, and a thin stroke at 3.2:1 on a
   photograph is not the same as a word at 3.2:1 on a page. So the BRAND GREEN IS UNTOUCHED and
   two things around it changed: the arrow is a quarter larger, which thickens its stroke in
   proportion since the viewBox is fixed, and it carries a soft halo in the page's own ground
   color — WHITE, WRITTEN OUT, in both themes (Kevin, 26-09-12).
   It used to be `var(--paper)`, which is white on light and near-black on dark, and a halo in
   the ground's own color is invisible against that ground. A green glow was tried on dark and
   rejected. So the halo is the same white everywhere: on light it is the paper the arrow is
   nominally sitting on, and on dark it is a light edge that lifts the green off the ground.
   One value, one behavior, and nothing to keep in step with the palette. */
.app-note-arrow {
  /* In the card's own row since 10/9/2026, on its left: its width is always held, so the card never
     changes size as the arrow comes and goes, and it fades rather than moving. */
  flex: none;
  width: 22px;
  height: 34px;
  color: var(--green);
  opacity: 0;
  transition: opacity 350ms ease, transform 350ms ease;
  /* UNHIGHLIGHTED SINCE 10/7/2026 (Kevin: "Change the arrow ... to unhighlighted"): the white
     halo described above came off when the note moved onto the page's own ground, where the
     green reads without it. The halo was:
     filter: drop-shadow(0 0 2px #fff) drop-shadow(0 0 5px #fff); */
}
.app-note.is-down .app-note-arrow { opacity: 1; }
.app-note.is-up .app-note-arrow { opacity: 1; }
/* WHICH WAY IT POINTS is kept apart from WHETHER IT SHOWS (Kevin, 10/9/2026), so the arrow holds its
   last direction while hidden and turns visibly as it returns, scrolling up as well as down;
   `is-pointing-up` is set and cleared in index.html. It was one rule with `is-up` until then. */
.app-note.is-pointing-up .app-note-arrow { transform: rotate(180deg); }
/* Level with the heading, an arrow would be pointing at nothing, so there is no arrow. */

/* The writing runs off a class rather than a delay, so it starts when the reader can actually
   see it: on a first visit index.html holds it until the title has shone and turned its
   first letter, so the two do not run over each other. */
.app-note.is-writing .app-note-line,
.cal-app-note .app-note-line {
  animation: app-note-write 1400ms cubic-bezier(.36, .5, .3, 1) 250ms forwards;
}
.app-note.is-writing .app-note-app,
.cal-app-note .app-note-app {
  animation: app-note-fade 520ms ease 1650ms forwards;
}
/* THE CALENDAR PAGE'S COPY (Kevin, 10/9/2026; calendar.html): the same card standing still under the
   sentence, centered, written in once as the page opens with no script, and with no arrow. Its
   message is longer than "Coming this November!", so on a phone it may take two rows rather than run off
   the screen; the pen opens both at once. */
.cal-app-note { display: flex; justify-content: center; margin: 0 auto 24px; max-width: 100%; }
.cal-app-note .app-note-line { white-space: normal; text-align: left; }
@keyframes app-note-write { to { clip-path: inset(0 -5% -25% 0); } }
@keyframes app-note-fade { to { opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
  .app-note.is-traveling { transition: none; }
  .app-note-line { clip-path: none; }
  .app-note-app { opacity: 1; }
  .app-note.is-writing .app-note-line,
  .app-note.is-writing .app-note-app { animation: none; }
  .cal-app-note .app-note-line,
  .cal-app-note .app-note-app { animation: none; }
  .app-note-arrow { transition: none; }
}

/* ---------- hero ---------- */
.hero {
  text-align: center;
  padding: clamp(48px, 9vw, 104px) 20px clamp(40px, 7vw, 80px);
  /* Was capped at 820 and centered. It now fills the sheet, because a hero 220 points
     narrower than every section below it would step the sheet's edge in at the top. The
     text keeps its old measure through the max-widths on the lines themselves. */
  margin: 0 auto;
}
.hero-logo {
  width: clamp(104px, 24vw, 164px);
  height: auto;
  margin-bottom: 20px;
  background: #fff;                 /* logo art has a white ground — tile keeps it clean in dark mode */
  border-radius: 22px;
  padding: 10px;
  box-shadow: var(--shadow);
  border: 1px solid var(--line);
}
.hero-title { font-size: clamp(42px, 9vw, 72px); margin: 0 0 6px; }
.hero-eyebrow {
  font-size: 15px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-soft);
  font-weight: 600;
  margin: 0 0 20px;
}
/* "A BOSTON EDUCATION COMPANY", BIGGER (Kevin, 10/7/2026), on the home page alone: the SAT and
   calendar pages set their own small line in this style and keep it. 15 until then. */
.hero .hero-eyebrow { font-size: clamp(16px, 2.2vw, 21px); }
.hero-tagline {
  font-size: clamp(20px, 3.4vw, 28px);
  color: var(--ink);
  font-family: Georgia, serif;
  font-style: italic;
  margin: 0 auto 14px;
  max-width: 30ch;  /* fits the longer goals line as ~2 lines, not 4 */
}
/* The wordmark inside a tracked-caps eyebrow. The eyebrow shouts on purpose, but deltaPlus is
   never shouted — its lowercase d IS the mark — so the word passes through in its own casing
   and its own two colors, exactly as the header prints it, and only what follows takes the
   caps (Kevin, 26-09-10). */
.hero-eyebrow .eyebrow-wordmark {
  text-transform: none;
  letter-spacing: 0;
  font-family: Georgia, serif;
  font-weight: 400;
  font-size: 1.15em;
  color: var(--blue);
}
.hero-eyebrow .eyebrow-wordmark strong { color: var(--green); font-weight: 700; }

/* ---------- the Boston collage ----------
   THE PAGE'S GROUND, not the hero's decoration (Kevin, 26-09-10: larger, clearer, and
   "static while the text scrolls down as it currently does").

   How the three layers stack, bottom to top:
     1. `body`'s paper, which is what shows if the photographs never load.
     2. `.boston-collage` — POSITION: FIXED, so it is pinned to the viewport and does not
        move when the page scrolls. It fills the whole window, so each photograph is now
        several times the size it was inside an 820-pixel hero.
     3. the content, on a paper SHEET down the middle of the page.

   The sheet is what makes this legible, and it is the whole design. Kevin asked for the
   photographs at full size and much clearer, with the fallback of "cover the margins only"
   if legibility could not survive it. This does both at once rather than choosing: the
   photographs cover the entire window, and the reading column rides over them on paper, so
   the photographs are seen full-height down BOTH MARGINS all the way to the footer while no
   sentence is ever set on top of a photograph. On a narrow screen the margins close up, the
   sheet fills the width, and the field simply stops showing — which is the right behavior
   rather than a compromise.

   The hero is the one place text does sit over the field, and there the sheet is a VEIL
   instead of solid paper, so the photographs come through behind the wordmark. */
.boston-collage {
  position: fixed;
  inset: 0;
  z-index: 0;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  grid-template-rows: repeat(4, 1fr);
  gap: 8px;
  padding: 8px;
  pointer-events: none;
  /* 0.22 until 26-09-10. The photographs now live in the margins rather than under the
     words, so they can be most of the way to their real selves; the sheet, not the fade,
     is what protects the reading. */
  opacity: 0.72;
}
.boston-collage div { background-size: cover; background-position: center; border-radius: 8px; }
/* Six slots, six subjects (Kevin, 26-09-10): the skyline with the CITGO sign small and
   incidental in it, Boston Common, a public-domain statue, two campuses that read as "a
   campus" rather than a postcard, and the Zakim bridge. No slot is the sign itself. */
.boston-collage .tile-1 { grid-column: 1 / 4; grid-row: 1 / 3; background-image: url(images/boston/boston_skyline.jpg); }
.boston-collage .tile-2 { grid-column: 4 / 6; grid-row: 1 / 2; background-image: url(images/boston/boston_common.jpg); }
.boston-collage .tile-3 { grid-column: 6 / 7; grid-row: 1 / 3; background-image: url(images/boston/boston_statue.jpg); }
.boston-collage .tile-4 { grid-column: 1 / 3; grid-row: 3 / 5; background-image: url(images/boston/boston_campus_1.jpg); }
.boston-collage .tile-5 { grid-column: 3 / 6; grid-row: 2 / 5; background-image: url(images/boston/boston_campus_2.jpg); }
.boston-collage .tile-6 { grid-column: 6 / 7; grid-row: 3 / 5; background-image: url(images/boston/boston_zakim_bridge.jpg); background-position: 32% center; }  /* a tall slot on a wide photo: hold the left tower */
/* A whisper of paper over the whole field, so the photographs sit in the page's own light
   rather than shouting out of it. Far weaker than the old wash, which had to carry the
   headline's contrast on its own and no longer does. */
.boston-collage::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--field-veil);
}

/* THE SHEET. `main` gives it its SHAPE and its edge; the sections paint its PAPER. The two
   are separate on purpose — if `main` itself were opaque, the hero could not be a veil. The
   header and the footer already paint their own grounds and only need lifting above the
   field. */
main {
  position: relative;
  z-index: 1;
  max-width: var(--max);
  margin: 0 auto;
  box-shadow: 0 0 60px rgba(0, 0, 0, 0.13);
}
.site-footer { position: relative; z-index: 1; }
/* THE FOOTER IS THE SHEET'S WIDTH, so the photographs run down both margins to the very bottom
   (Kevin, 10/10/2026: "let the images still show in the margins"); it spanned the window until then
   and covered them. Only on a page that has the photographs: the policy pages have none, and their
   footer still spans the window. It wears the sheet's own shadow, clipped at its top edge so the
   shadow does not fall back across the sheet above it. */
.boston-collage ~ .site-footer {
  max-width: var(--max);
  margin: 0 auto;
  box-shadow: 0 0 60px rgba(0, 0, 0, 0.13);
  clip-path: inset(0 -60px -60px -60px);
}
.section { background: var(--paper); }
/* The hero's paper is a VEIL, not a wall: the photographs read through it behind the
   wordmark, which is the one place on the page where that is the point. The blur lifts the
   letters off whatever tile happens to be behind them without dimming the field further. */
.hero, .sat-hero, .cal-hero {
  position: relative;
  background: var(--sheet-veil);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}
/* The field is part of the page, so it fades in with everything else rather than sitting
   there fully lit behind a curtain that is still playing. */

.hero-sub { font-size: calc(18px * var(--copy)); color: var(--ink-soft); margin: 0 auto 28px; max-width: 46ch; }
.hero-actions { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; }

/* ---------- what deltaPlus does: the reel's words, each with a destination ---------- */
.section-offerings { text-align: center; padding-top: 0; }
.word-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 12px;
  justify-content: center;
  max-width: 760px;
  margin: 0 auto;
}
.word-grid a {
  padding: 8px 18px;
  border: 1.5px solid var(--line);
  border-radius: 999px;
  background: var(--paper-card);
  color: var(--ink);
  text-decoration: none;
  font-size: 15.5px;
  font-weight: 600;
}
.word-grid a:hover { border-color: var(--green); color: var(--blue); }

/* ---------- buttons ---------- */
.btn {
  display: inline-block;
  padding: 12px 24px;
  border-radius: 999px;
  font-weight: 600;
  text-decoration: none;
  font-size: 16px;
  transition: transform 0.06s ease, background 0.15s ease, box-shadow 0.15s ease;
}
.btn:active { transform: translateY(1px); }
.btn-primary { background: var(--blue); color: #fff; box-shadow: var(--shadow); }
.btn-primary:hover { background: var(--blue-hover); }
.btn-ghost { background: transparent; color: var(--blue); border: 1.5px solid var(--line); }
.btn-ghost:hover { border-color: var(--green); color: var(--ink); }
/* GOLD WORDS IN A GREEN OUTLINE, AND THE SHINE (Kevin, 10/9/2026: "the gold pill should have green outline and
   gold text [as] in the app, with the same periodic shine across not synced with any of the other webpage
   shines"; until then, the same evening, it was filled with the gold). The outline is the menu's "deltaPlus
   SAT App" button's, 2 points of the green drawn inside so the button keeps its size; the words are the app
   menu's metallic gold, dark at the top and foot and light through the middle (`dPDrawer.headerGold` in
   mobile/dP_SAT/lib/widgets/dP_drawer.dart), about 3.1:1 on white at their lightest by day and 7:1 and up
   at night, over deltaPlus's 2:1 floor; and the band is the menu button's (`nav-apps-shine` below the
   menu's rules), which shine.js spaces half a loop from that button's, so the two never cross together.
   Pointed at, it fills with the green and its words turn white, as the menu button does. */
.btn-gold { position: relative; overflow: hidden; background: transparent; box-shadow: inset 0 0 0 2px var(--green); }
.btn-gold .gold-words {
  background: linear-gradient(180deg, #8a6914, #b48a1f 50%, #8a6914);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.btn-gold:hover { background: var(--green); }
.btn-gold:hover .gold-words { background: none; color: #fff; }
.btn-gold::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 36%;
  pointer-events: none;
  background: linear-gradient(90deg, rgba(255, 243, 196, 0) 0%, rgba(255, 243, 196, .85) 50%, rgba(255, 243, 196, 0) 100%);
  animation: nav-apps-shine 5s cubic-bezier(.42, 0, .58, 1) var(--dP-shine-offset, 0s) infinite;
}
@media (prefers-reduced-motion: reduce) {
  .btn-gold::after { animation: none; opacity: 0; }
}
.btn-lg { padding: 15px 34px; font-size: 18px; }

/* ---------- generic section ---------- */
.section {
  max-width: var(--max);
  margin: 0 auto;
  padding: clamp(48px, 8vw, 88px) clamp(20px, 5vw, 40px);
}
.section-title {
  font-size: clamp(28px, 4.5vw, 40px);
  margin: 0 0 12px;
  text-align: center;
}
.section-title::after {
  content: "";
  display: block;
  width: 54px;
  height: 3px;
  margin: 14px auto 0;
  background: linear-gradient(90deg, var(--blue), var(--green));
  border-radius: 2px;
}
.section-lead {
  text-align: center;
  font-size: 18px;
  color: var(--ink-soft);
  max-width: 60ch;
  margin: 0 auto 32px;
}

/* ---------- where the photographs come through ----------
   THE SHEET IS CUT INTO BANDS (Kevin, 26-09-10: "include more gaps in sections so that the
   picture in the background becomes visible sometimes"). Until now `main` was one unbroken
   run of paper from the menu to the footer, so the Boston field could only ever be seen down
   the two side margins — and on a phone, where the margins close up, not at all.

   A section carrying this class simply pushes off the one above it. The paper stops, the
   fixed field behind it shows for the width of the whole column, and the paper starts again.
   It is a MARGIN rather than a transparent section because margins collapse into nothing when
   a rule is later added between two sections, whereas a see-through band would leave a hole.

   `main`'s soft edge-shadow stays on `main`, so it continues down both sides of a gap without
   drawing a line across it. Deliberately used on SOME boundaries, not all: a gap everywhere
   would be a rhythm rather than a glimpse, and the sections that belong together (the hero
   and the words under it; the app panel and the dates it points at) stay joined. */
.field-gap { margin-top: clamp(30px, 5vw, 60px); }

/* ---------- apps ---------- */
/* THE APPS SECTION IS THE ONE THAT SHOULDS (Kevin, 26-09-10: "draw more attention to this
   section. The apps are hugely important."). Everything else on the home page is set on plain
   paper; this one alone gets a tinted ground, a green rule across its top, and a row of claims
   between its lead and its badges. That is the whole of the emphasis, and it is deliberately
   only one section's worth — if every section were loud, none would be. */
/* SYMMETRICAL, TOP AND BOTTOM (Kevin, 26-09-10: "good on top, but should match on the
   bottom"). The section is now held between two green rules of the same weight, with the
   tint falling away from each of them towards the middle — the top wash fades DOWN and the
   bottom wash fades UP, so the block reads as one lit panel rather than a heading with a
   long tail. Two gradients rather than one, because a single gradient cannot be pale at both
   ends and clear in the center; the upper one is transparent below its 46 % mark, which is
   what lets the lower one show through underneath it. */
.section-apps {
  text-align: center;
  background:
    linear-gradient(180deg, rgba(7, 165, 22, 0.055) 0%, rgba(7, 165, 22, 0) 46%),
    linear-gradient(0deg,   rgba(7, 165, 22, 0.055) 0%, rgba(7, 165, 22, 0) 46%),
    var(--paper);
  border-top: 3px solid var(--green);
  border-bottom: 3px solid var(--green);
}
.section-apps .section-title { color: var(--green); }
.section-apps .section-title::after { background: var(--green); }
/* The four claims, in a row on a wide screen and stacked on a phone. Each is a short bold
   line and one sentence under it — a claim a reader can scan, and a sentence that says why it
   is true. `auto-fit` rather than four fixed columns so two of them pair up on a tablet
   instead of squeezing to four. */
.app-claims {
  list-style: none;
  margin: 26px auto 30px;
  padding: 0;
  max-width: 940px;
  display: grid;
  gap: 18px 22px;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  text-align: left;
}
.app-claims li {
  border-left: 3px solid var(--green);
  padding: 2px 0 2px 14px;
  color: var(--ink-soft);
  font-size: calc(17.5px * var(--copy));
  line-height: 1.5;
}
/* A SIZE UP, AND ALONE UNDER THE TITLE (Kevin, 10/7/2026: the lead paragraph above them "just
   repeats" them, so it went, and "make this line-separated text bigger"): 15 and 16.5 until
   then. */
.app-claims strong {
  display: block;
  color: var(--ink);
  font-size: calc(19.5px * var(--copy));
  margin-bottom: 2px;
}
/* The availability line is housekeeping, not a claim, so it sits under the claims and reads
   quieter than the lead above them. */
.app-status { font-size: calc(15px * var(--copy)); margin-bottom: 22px; }
.store-badges { display: flex; gap: 16px; justify-content: center; flex-wrap: wrap; }
.store-badge {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 12px 22px;
  border-radius: 12px;
  background: var(--ink);
  color: #fff;
  text-decoration: none;
  min-width: 220px;
}
.store-badge[data-inactive] {
  opacity: 0.55;
  cursor: not-allowed;
  filter: grayscale(0.3);
}
.store-badge-icon { font-size: 30px; line-height: 1; }
.store-badge-text { display: flex; flex-direction: column; text-align: left; line-height: 1.25; }
.store-badge-text small { font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; opacity: 0.8; }
.store-badge-text strong { font-size: 19px; font-family: Georgia, serif; }

/* ---------- philosophy ---------- */
.delta-lead { font-size: calc(19px * var(--copy)); color: var(--ink); max-width: 64ch; }
/* ONE COLUMN, IN ORDER, AND THE MARK IS THE ARGUMENT (Kevin, 26-09-10). These four were a
   two-by-two grid of cards, each opened by a numeral in a green tile. Two things were wrong
   with that. A grid has no order — the reader's eye can take the four in any sequence — and
   these four ARE an order. And a numeral is a label for the idea rather than a picture of it.

   So: a single vertical column, and the numeral replaced by that many small green boxes, all
   starting at the same left edge and growing a box per step. One box, then two, then three,
   then four. The row of boxes IS the section's sentence — small changes made over and over
   are what a big change is made of — said in the shape of the thing instead of in words, and
   said before the reader has read a line of the card.

   The markup is an <ol>, so the order a sighted reader sees in the boxes is the order a
   screen reader announces; the boxes themselves are decoration and are hidden from it. */
.values {
  list-style: none;
  padding: 0;
  margin: 8px auto 0;
  display: grid;
  gap: 18px;
  grid-template-columns: 1fr;
  max-width: 760px;
}
.value {
  background: var(--paper-card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 22px 24px;
  box-shadow: var(--shadow);
}
/* A ROW, ON ITS OWN LINE, ABOVE THE WORDS — `flex` rather than `inline-flex` is what puts the
   text on the next line without a <br> to remember. The boxes never wrap: four of them at 15
   points is 72 points wide, narrower than any phone. */
.value-mark {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-bottom: 12px;
}
.value-mark i {
  display: block;
  width: 15px;
  height: 15px;
  border-radius: 3px;
  background: linear-gradient(180deg, var(--green-soft), var(--green));
}
.value h3 { font-size: 20px; margin: 0 0 8px; }
.value p { margin: 0; color: var(--ink-soft); font-size: calc(15.5px * var(--copy)); }
.delta-summary {
  text-align: center;
  font-family: Georgia, serif;
  font-style: italic;
  /* Larger since 10/9/2026 (Kevin: "Make this italicized text bigger to be more legible because of
     the italicizing"); it was 20px. */
  font-size: clamp(21px, 2.2vw, 25px);
  line-height: 1.5;
  color: var(--ink);
  max-width: 52ch;
  margin: 34px auto 0;
}

/* ---------- mission ---------- */
.section-mission { text-align: center; }
.mission-statement {
  font-family: Georgia, serif;
  font-size: clamp(22px, 3.6vw, 30px);
  line-height: 1.45;
  color: var(--ink);
  max-width: 24ch;
  margin: 8px auto 0;
  border-left: 4px solid var(--green);
  padding-left: 22px;
  text-align: left;
}

/* ---------- the founder (10/13/2026; code/design/website/website_blueprint.md, section 3) ----------
   Words alone until the photo exists. Once index.html's figure is uncommented, `:has` turns the block
   into two columns on a wide screen, photo first, and keeps it one column on a phone, the photo above.
   The site's own tokens only, so light and dark both hold with no new colors. */
.founder { max-width: 64ch; margin: 0 auto; }
.founder:has(.founder-photo) { display: grid; gap: clamp(20px, 4vw, 40px); max-width: 900px; align-items: start; }
@media (min-width: 760px) {
  .founder:has(.founder-photo) { grid-template-columns: 260px minmax(0, 1fr); }
}
.founder-photo { margin: 0; }
.founder-photo img {
  display: block; width: 100%; max-width: 260px; height: auto; margin: 0 auto;
  border-radius: var(--radius); box-shadow: var(--shadow);
}
.founder-name { font-size: clamp(24px, 3.2vw, 30px); margin: 0; color: var(--ink); }
.founder-role { margin: 4px 0 20px; color: var(--ink-soft); font-size: calc(15px * var(--copy)); }
.founder-words p { line-height: 1.65; margin: 0 0 14px; }
.founder-words .founder-book { margin: 22px 0 0; }

/* ---------- schedule ---------- */
.section-schedule { text-align: center; }
.schedule-note { font-size: calc(14px * var(--copy)); color: var(--ink-soft); margin-top: 18px; }

/* ---------- footer ----------
   TWO THIRDS AS TALL (Kevin, 10/10/2026: "Strive to make this area 2/3 as tall"): 434 points to 290
   in a 1440-point window. The dP mark now sits BESIDE the name, as it does in the header, rather than
   on a row of its own above it, and the padding and the gaps between rows are about half what they
   were (48 and 56 points of padding, 6 to 10 between rows, the mark 46 points square). */
.site-footer {
  text-align: center;
  padding: 28px 20px 30px;
  border-top: 1px solid var(--line);
  background: #f5f6f8;
}
.footer-mark { width: 34px; height: 34px; margin: 0 8px 0 0; vertical-align: middle; background: #fff; border-radius: 8px; padding: 4px; border: 1px solid var(--line); }
.footer-name { display: inline-block; vertical-align: middle; font-size: 22px; line-height: 1.3; margin: 0; }
.footer-contact { margin: 4px 0 2px; line-height: 1.5; font-weight: 600; }
.footer-fine { margin: 0; line-height: 1.5; color: var(--ink-soft); font-size: calc(14px * var(--copy)); }
/* WORDS, NOT LOGOS, and that is a license decision rather than a taste one: YouTube forbids
   altering the colors of its marks and TikTok forbids using its marks at all without written
   permission, so the tidy monochrome icon row is the one thing neither of them allows. Kept in
   step BY HAND with mobile/dP_SAT/lib/data/social.dart, which is where the accounts and their
   status live; only the claimed ones appear in either place. */
.footer-social {
  margin: 0 0 4px;
  line-height: 1.5;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 4px 18px;
}
.footer-social a { font-weight: 600; }
/* THE POLICIES ROW AND THE NOTICES (26-10-05). The four policy links sit under the social ones,
   in the same centered row; the notices (the trademark legend, College Board's notice, and the
   copyright line) are obligations rather than messages, so they are set small, like the photo
   credit. Every page carries both, copied by hand like the rest of the footer. */
.footer-legal {
  margin: 0 0 6px;
  line-height: 1.5;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 4px 18px;
  font-size: calc(14px * var(--copy));
}
/* Wide and short since 10/9/2026 (Kevin: "The legal stuff at the bottom of pages, spread out much wider
   and shorter"); it was held to 70 characters a line. */
.footer-notice { font-size: 11px; line-height: 1.5; max-width: 1100px; margin: 4px auto 0; color: var(--ink-soft); }

/* THE ™ IN A WORDMARK (Kevin's answer 18, 26-10-04): small and high after "deltaPlus" in the
   header, the footer, and the SAT page's eyebrow, never on the home page's large title. Line
   height 0 keeps it from pushing its line apart. In running text the plain ™ character is used
   instead, since it is already set small.
   BIGGER, AND IN THE INK since 10/7/2026 (Kevin: "make the TMs on the website bigger, white in
   dark mode, black in light mode"): 0.45em of the word until then, and the wordmark's blue. */
.tm { font-size: 0.62em; vertical-align: super; line-height: 0; font-weight: 400; margin-left: 0.04em; color: var(--ink); }

/* ---------- the policy pages (privacy, terms, support, bootcamp terms; 26-10-05) ----------
   One readable column, left-aligned, in the site's own type. */
.legal { max-width: 70ch; margin: 0 auto; text-align: left; }
.legal h1 { font-size: clamp(32px, 5.5vw, 46px); color: var(--blue); margin: 0 0 6px; }
.legal h2 { font-size: 22px; margin: 34px 0 8px; }
.legal p, .legal li { font-size: calc(16px * var(--copy)); line-height: 1.6; }
.legal ul { padding-left: 1.2em; }
.legal li { margin: 0 0 8px; }
.legal-dates { color: var(--ink-soft); font-size: calc(14px * var(--copy)); margin: 0 0 24px; }
.legal .table-wrap { margin: 12px 0; max-width: none; }
/* In the reading column a table wraps its cells rather than scrolling sideways, so every
   column (the bootcamp terms' early-price dates above all) is in view without a swipe. */
.legal .dates-table th, .legal .dates-table td { white-space: normal; padding: 10px 12px; }

/* ---------- dark mode ----------
   TWO copies of the same rules, deliberately: the first follows the device (and yields
   to an explicit light choice from the toggle), the second is the toggle's explicit dark.
   Edit them TOGETHER or the toggle and the device preference drift apart. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --blue:       #6f8dff;   /* brightened so the wordmark + links read on dark */
    --blue-hover: #8aa2ff;
    --green:      #4da742;   /* the night green (Kevin, 26-09-22), replacing #35c243 */
    --green-soft: #4bd35a;
    --d-geometry: #6f8dff;   /* SAT_DOMAIN_GEOMETRY_ON_DARK */
    --ink:        #ecedf1;
    --ink-soft:   #a6a8b3;
    --paper:      #141518;
    --paper-card: #1e2025;
    --line:       #2f323a;
    --sheet-veil: rgba(20, 21, 24, 0.76);
    --field-veil: rgba(20, 21, 24, 0.20);
    /* White on near-black already reads heavy; the light-mode correction would be too much. */
    --copy:        1;
    --copy-weight: 400;
    --shadow:     0 10px 30px rgba(0, 0, 0, 0.4);
    --map-panel:   #1e2025;
    --map-lead:    #4a4e58;
    --map-locked:  #33363d;
    --map-locked-ink: #71747c;
    --shine-gold: 255 201 60;   /* saturated, so the band reads against near-white letters */
  }
  :root:not([data-theme="light"]) { --header-ground: rgba(20, 21, 24, 0.9); }
  :root:not([data-theme="light"]) .store-badge { background: #000; }
  :root:not([data-theme="light"]) .site-footer { background: #101114; }
  :root:not([data-theme="light"]) .btn-primary { color: #10131f; }
  :root:not([data-theme="light"]) .btn-ghost { color: var(--blue); }
  :root:not([data-theme="light"]) .btn-gold .gold-words { background-image: linear-gradient(180deg, #c99a2e, #f2d27a 50%, #c99a2e); }
  :root:not([data-theme="light"]) .btn-gold:hover .gold-words { background: none; color: #fff; }
}
:root[data-theme="dark"] {
  --blue:       #6f8dff;
  --blue-hover: #8aa2ff;
  --green:      #4da742;
  --green-soft: #4bd35a;
  --d-geometry: #6f8dff;
  --ink:        #ecedf1;
  --ink-soft:   #a6a8b3;
  --paper:      #141518;
  --paper-card: #1e2025;
  --line:       #2f323a;
  --sheet-veil: rgba(20, 21, 24, 0.76);
  --field-veil: rgba(20, 21, 24, 0.20);
  /* White on near-black already reads heavy; the light-mode correction would be too much. */
  --copy:        1;
  --copy-weight: 400;
  --shadow:     0 10px 30px rgba(0, 0, 0, 0.4);
  --map-panel:   #1e2025;
  --map-lead:    #4a4e58;
  --map-locked:  #33363d;
  --map-locked-ink: #71747c;
  --shine-gold: 255 201 60;   /* saturated, so the band reads against near-white letters */
}
:root[data-theme="dark"] { --header-ground: rgba(20, 21, 24, 0.9); }
:root[data-theme="dark"] .store-badge { background: #000; }
:root[data-theme="dark"] .site-footer { background: #101114; }
:root[data-theme="dark"] .btn-primary { color: #10131f; }
:root[data-theme="dark"] .btn-ghost { color: var(--blue); }
:root[data-theme="dark"] .btn-gold .gold-words { background-image: linear-gradient(180deg, #c99a2e, #f2d27a 50%, #c99a2e); }
:root[data-theme="dark"] .btn-gold:hover .gold-words { background: none; color: #fff; }

/* ---------- the theme toggle ----------
   THE RING IS ALWAYS THERE (Kevin, 26-09-12), in the menu's own ink rather than the hairline
   gray it used to borrow, so the control reads as a control before anyone points at it. It
   takes `--ink-soft`, which is the color "Philosophy" and "Mission" are set in, so it belongs
   to the row it sits in on both grounds — and it turns green on hover, which is the same
   green the Apps box is drawn in. */
.theme-toggle {
  width: 40px;
  height: 40px;
  border-radius: 999px;
  border: 1.5px solid var(--ink-soft);
  background: var(--paper-card);
  color: var(--ink-soft);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  padding: 0;
  transition: border-color 140ms ease, color 140ms ease;
}
.theme-toggle:hover { border-color: var(--green); color: var(--green); }
.theme-toggle:focus-visible { outline: 2px solid var(--green); outline-offset: 2px; }

/* ---------- motion / small screens ---------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .btn { transition: none; }
}
@media (max-width: 520px) {
  .brand-name { display: none; }
  .mission-statement { text-align: center; border-left: 0; padding-left: 0; border-top: 4px solid var(--green); padding-top: 18px; }
}

/* ===================================================================
   SAT page (sat.html) — components used only there
   =================================================================== */

/* ---------- SAT hero + countdown ---------- */
.sat-hero { text-align: center; padding: clamp(40px, 7vw, 72px) 20px clamp(24px, 4vw, 40px); max-width: var(--max); margin: 0 auto; }
/* "deltaPlus™ · SAT" BIGGER (Kevin, 10/9/2026: "Make deltaPlus TM and SAT at the top of the SAT page
   bigger"): from the eyebrow's fixed 15 to 18 on a phone, growing with the window to 24, as the home
   page's line grows (10/7/2026). deltaPlus™ is 1.15 times the line, so about 21 to 28, under "SAT Prep".
   The calendar's "Calendar" takes the same (Kevin, the same evening: "Make Calendar at the top the same
   size as top text on the SAT page; make things match!"). */
.sat-hero .hero-eyebrow,
.cal-hero .hero-eyebrow { margin-bottom: 10px; font-size: clamp(18px, 2.6vw, 24px); }
.sat-hero h1 { font-size: clamp(34px, 6vw, 52px); margin: 0 0 10px; color: var(--blue); }
.sat-hero h1 strong { color: var(--green); }
.sat-lead { font-size: calc(18px * var(--copy)); color: var(--ink-soft); max-width: 56ch; margin: 0 auto 26px; }
.countdown {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  background: var(--paper-card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 22px 34px;
  margin-bottom: 26px;
  position: relative;
}
/* THE WHOLE BOX GOES TO THE CALENDAR (Kevin, 10/10/2026: "Make clicking within this square go to
   the calendar page"). The link wraps the number and its label, and its ::after stretches over the
   entire box, so a click anywhere in it lands on the link. College Board's what-to-bring link is
   lifted above that layer, so it still opens College Board. A link cannot hold another link, which
   is why the box itself is not the link. */
.countdown_link {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  color: inherit;
  text-decoration: none;
}
.countdown_link::after { content: ""; position: absolute; inset: 0; }
.countdown_ready a { position: relative; z-index: 1; }
/* THE NUMBER ARRIVES WITH A SWELL, and with nothing else (Kevin, 26-09-11). It carried a
   gold band across it as well, the same shine the launch animation sweeps over "SAT", and
   that is gone: the page has the green button's shine in the top row, and a second one a few
   hundred pixels below it was one too many. The swell alone still says "look here", which was
   always the job — it replaced a calendar-page flip for exactly that reason.

   The swell plays when the number arrives, and again each time a pointer comes onto the box
   (Kevin, 10/10/2026: "Make hovering over the calendar square ... make the number do the bigger
   then back to normal animation again"): `is-swelling`, the same swell without the arrival's
   wait, added by the script in sat.html. It never repeats on its own. A number that kept
   swelling every five seconds would be a fidget rather than an arrival. */
.countdown-num {
  font-family: Georgia, serif;
  font-size: clamp(46px, 9vw, 68px);
  font-weight: 700;
  line-height: 1;
  color: var(--blue);
  display: inline-block;
}
.countdown-num.is-arriving {
  animation: countdown-swell 1100ms cubic-bezier(.34, .8, .3, 1) 450ms both;
}
.countdown-num.is-swelling {
  animation: countdown-swell 1100ms cubic-bezier(.34, .8, .3, 1);
}
@keyframes countdown-swell {
  0%   { transform: scale(1); }
  38%  { transform: scale(1.2); }
  100% { transform: scale(1); }
}
/* A reader who has asked for less movement gets the number and no swell. */
@media (prefers-reduced-motion: reduce) {
  .countdown-num.is-arriving,
  .countdown-num.is-swelling {
    animation: none;
    color: var(--blue);
  }
}
.countdown-label { font-size: calc(14px * var(--copy)); color: var(--ink-soft); }
.countdown-label strong { color: var(--green); }
/* THE TWO OCCASIONAL SENTENCES (Kevin, 26-09-12): the good luck above the number on a test
   day, in the green of the date beneath it, and the what-to-bring note below the label when
   the next SAT is three days away or fewer, as a small gray note like the rest. The width cap
   is what stops either one stretching the box; they wrap instead. Neither sets `display`, and
   the [hidden] rule is spelled out so a later one here can never quietly show them. */
.countdown_luck,
.countdown_ready {
  max-width: 30ch;
  margin: 0;
  text-align: center;
  text-wrap: balance;
}
.countdown_luck { margin-bottom: 10px; font-size: calc(15.5px * var(--copy)); font-weight: 600; color: var(--green); }
.countdown_ready { margin-top: 10px; font-size: calc(14px * var(--copy)); color: var(--ink-soft); }
.countdown_luck[hidden],
.countdown_ready[hidden] { display: none; }

/* ---------- dates table ---------- */
.table-wrap { overflow-x: auto; margin: 0 auto; max-width: 720px; }
.dates-table { width: 100%; border-collapse: collapse; font-size: calc(15.5px * var(--copy)); }
.dates-table th, .dates-table td { padding: 12px 16px; text-align: left; border-bottom: 1px solid var(--line); white-space: nowrap; }
.dates-table thead th { font-family: Georgia, serif; color: var(--blue); border-bottom: 2px solid var(--line); }
.dates-table tbody tr.is-next { background: rgba(7, 165, 22, 0.08); }
.dates-table tbody tr.is-next td:first-child { font-weight: 700; }
/* THE NEXT SAT WEARS A dP GREEN BORDER (Kevin, 10/10/2026: "make the next (11/7) standout more with a dP green
   border"), drawn on the row's cells because a collapsed table ignores a border on the row itself; where it
   meets a neighbor's 1px line, the wider green wins. The header's 2px underline is as wide as the green, though,
   and in a collapsed table a tie goes to the cell above, so when the next SAT is the table's first row (every
   summer, once the dates roll to a new school year) the header's gray would draw the frame's top edge; the :has
   rule below colors that underline green instead, which closes the frame at an even 2px. A browser without :has
   leaves that one edge gray. sat.html's own script marks the row each day, so the border moves to the following
   SAT on its own. --green is #07a516 by day (3.28:1 on the page) and #4da742 at night. */
.dates-table tbody tr.is-next td { border-top: 2px solid var(--green); border-bottom: 2px solid var(--green); }
.dates-table tbody tr.is-next td:first-child { border-left: 2px solid var(--green); }
.dates-table tbody tr.is-next td:last-child { border-right: 2px solid var(--green); }
.dates-table:has(tbody tr.is-next:first-child) thead th { border-bottom-color: var(--green); }
.dates-table .dates-tbd { color: var(--ink-soft); font-style: italic; }
.dates-subhead { font-family: Georgia, serif; font-size: 24px; margin: 40px 0 8px; text-align: center; }
.dates-note-left { text-align: left; max-width: 62ch; margin-left: auto; margin-right: auto; }
.dates-table tbody tr.is-past { color: var(--ink-soft); opacity: .55; }
.dates-table tbody tr.is-past td:first-child { text-decoration: line-through; }
.dates-note { text-align: center; font-size: calc(14px * var(--copy)); color: var(--ink-soft); max-width: 60ch; margin: 18px auto 0; }
/* THE BOOTCAMPS' EARLY-PRICE COUNTDOWN (26-10-05): the line above the groups' table and each
   group's "days left", both written by renderEarlyPrice() in sat.html. A group whose early
   price has ended goes quiet, like a past SAT date. */
.early-line { text-align: center; font-size: calc(16px * var(--copy)); margin: 18px auto 12px; color: var(--ink); }
.early-line .early-days { font-family: Georgia, serif; font-size: 1.6em; color: var(--green); margin-right: 4px; }
.early-table .early-left { display: block; font-size: 0.85em; font-weight: 600; color: var(--green); }
.early-table td.is-ended { color: var(--ink-soft); }
.early-table td.is-ended .early-left { color: var(--ink-soft); font-weight: 400; }
/* The photograph credit is a license obligation, not a message: it has to be present and
   readable, and it should not compete with the tagline under it. */
.footer-credit { font-size: 11px; line-height: 1.5; max-width: 1100px; margin-left: auto; margin-right: auto; opacity: .75; }

/* ---------- getting-started steps ---------- */
.steps { max-width: 720px; margin: 0 auto; padding: 0; list-style: none; counter-reset: step; }
.steps li {
  position: relative;
  padding: 4px 0 22px 52px;
  border-left: 2px solid var(--line);
  margin-left: 16px;
}
.steps li:last-child { border-left-color: transparent; padding-bottom: 0; }
.steps li::before {
  counter-increment: step;
  content: counter(step);
  position: absolute;
  left: -17px;
  top: 0;
  width: 32px;
  height: 32px;
  border-radius: 999px;
  background: var(--blue);
  color: #fff;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 15px;
}
.steps h3 { margin: 0 0 4px; font-size: 18px; }
.steps p { margin: 0; color: var(--ink-soft); font-size: calc(15.5px * var(--copy)); }

/* ---------- pricing ---------- */
.pricing {
  display: grid;
  gap: 20px;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  max-width: 900px;
  margin: 0 auto;
}
.price-card {
  background: var(--paper-card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 26px 24px;
  box-shadow: var(--shadow);
  display: flex;
  flex-direction: column;
}
/* THE THREE TILES MAGNIFY UNDER THE CURSOR (Kevin, 26-09-10). A card grows a little, lifts
   its shadow, and rises above its neighbors so the growth is never clipped by the card next
   to it — which is what `position: relative` and the raised z-index are for, since a grid item
   with no position cannot be stacked. Hover only, so a touch screen (which has no cursor and
   would fire this on tap) is left alone. */
.price-card { transition: transform 180ms ease, box-shadow 180ms ease; position: relative; z-index: 0; }
@media (hover: hover) {
  .price-card:hover {
    transform: scale(1.045);
    box-shadow: 0 18px 44px rgba(9, 39, 177, 0.18);
    z-index: 1;
  }
}
/* A reader who has asked their device for less movement gets the lift without the growth. */
@media (prefers-reduced-motion: reduce) {
  .price-card { transition: box-shadow 180ms ease; }
  .price-card:hover { transform: none; }
}
.price-card h3 { margin: 0 0 4px; font-size: 20px; color: var(--blue); }
/* The plans are not on sale yet (10/7/2026): a small green tag above the name, website only. */
.price-card .price-soon {
  display: inline-block; width: fit-content; align-self: flex-start;
  margin: 0 0 8px; padding: 2px 10px; border-radius: 999px;
  border: 1px solid var(--green); color: var(--green);
  font-size: 12px; font-weight: 600; letter-spacing: .4px; text-transform: uppercase;
}
.price-card .price { font-family: Georgia, serif; font-size: 26px; font-weight: 700; margin: 6px 0 2px; }
.price-card .price small { font-size: 14px; font-weight: 400; color: var(--ink-soft); }
.price-card .price-sub { font-size: calc(13.5px * var(--copy)); color: var(--ink-soft); margin: 0 0 12px; }
.price-card ul { margin: 0 0 4px; padding-left: 18px; color: var(--ink-soft); font-size: calc(15px * var(--copy)); }
.price-card li { margin-bottom: 5px; }
/* THE FREE DAY, AT THE FOOT OF THE PLUS CARD (Kevin, 10/10/2026: "there should be an option to try the
   trial at the botton of the Plus Plan card"). `margin-top: auto` pins it to the card's foot, level with
   the end of Premium's longer list. The card's own green: #07a516 on #ffffff is 3.28:1, and #4da742 on
   the night card's #1e2025 is 5.37:1, both above the 2:1 floor. */
.price-card .price-trial { margin: auto 0 0; padding-top: 12px; font-size: calc(14px * var(--copy)); font-weight: 600; color: var(--green); }
.price-note { text-align: center; font-size: calc(14px * var(--copy)); color: var(--ink-soft); max-width: 60ch; margin: 22px auto 0; }

/* ---------- FAQ (native <details>) ---------- */
.faq { max-width: 760px; margin: 0 auto; }
.faq details {
  border: 1px solid var(--line);
  border-radius: 10px;
  margin-bottom: 12px;
  background: var(--paper-card);
  overflow: hidden;
}
.faq summary {
  cursor: pointer;
  padding: 16px 20px;
  font-weight: 600;
  font-size: 16.5px;
  list-style: none;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; color: var(--green); font-size: 22px; line-height: 1; }
.faq details[open] summary::after { content: "\2212"; }   /* minus */
.faq details[open] summary { color: var(--blue); }
.faq .faq-body { padding: 0 20px 18px; color: var(--ink-soft); font-size: calc(15.5px * var(--copy)); }
.faq .faq-body p { margin: 0 0 10px; }
.faq .faq-body p:last-child { margin-bottom: 0; }

/* ---------- back-to-home link on subpage nav ---------- */
.site-nav a.nav-home { color: var(--blue); }
.site-nav a.nav-current { color: var(--ink); font-weight: 700; }

/* ===================================================================
   For Students (students.html + students/*) — the audience split: the
   homepage speaks to parents and prospects, this area to the students
   doing the work. Resource cards mirror the private hub's pattern:
   a live card is a whole-card link, a planned one is inert.
   PARKED 26-10-07 with those pages: these rules, down to the chart's, stay
   for their return (code/design/website/website_blueprint.md, section 2).
   =================================================================== */
.students-hero { text-align: center; padding: clamp(40px, 7vw, 72px) 20px 0; }
.students-hero h1 { font-size: clamp(34px, 6vw, 52px); margin: 0 0 10px; color: var(--blue); }
.resource-grid {
  display: grid;
  gap: 20px;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  max-width: 900px;
  margin: 0 auto;
}
.resource-card {
  display: block;
  background: var(--paper-card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 26px 24px;
  box-shadow: var(--shadow);
  text-decoration: none;
  color: var(--ink);
}
a.resource-card:hover { border-color: var(--green); }
.resource-card h3 { margin: 0 0 8px; font-size: 20px; color: var(--blue); }
.resource-card p { margin: 0; color: var(--ink-soft); font-size: calc(15.5px * var(--copy)); }
.resource-kicker {
  font-size: 12.5px !important;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  font-weight: 600;
  margin: 0 0 10px !important;
}
.resource-card[data-inactive] { opacity: 0.55; }
.section-enrolled { text-align: center; }

/* ---------- the Conjunctions versus Transitions chart ---------- */
/* The chart's own pedagogy colors — conjunction orange, transition gold —
   kept from the original, with the gold brightened on dark so it still reads. */
.chart-page { --conj: #ff8c00; --trans: #b8860b; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .chart-page { --trans: #d4a72c; }
}
:root[data-theme="dark"] .chart-page { --trans: #d4a72c; }
.chart-page .conj { color: var(--conj); }
.chart-page .trans { color: var(--trans); }
.chart-eyebrow { text-align: center; margin-bottom: 6px; }
.chart-title { text-align: center; font-size: clamp(28px, 5vw, 44px); margin: 0 0 12px; }
.chart-table { width: 100%; border-collapse: collapse; font-size: calc(15px * var(--copy)); max-width: 980px; margin: 0 auto; }
.chart-table th, .chart-table td {
  padding: 14px 16px;
  border: 1px solid var(--line);
  text-align: left;
  vertical-align: top;
  min-width: 150px;
}
.chart-table thead th { background: var(--paper-card); font-family: Georgia, serif; color: var(--ink); }
.chart-table td:first-child { font-weight: 700; }
.chart-table .chart-big { font-size: 19px; }
.chart-back { text-align: center; margin-top: 28px; }

/* ===================================================================
   The wordmark's first letter, on every page.

   The `d` of deltaPlus is DRAWN, not typed: Georgia's own lowercase delta
   leaned twelve degrees, lifted out of the font once by
   code/design/wordmark/build_delta.py and served as a mask so it takes the
   color of the text around it. A typed Greek delta would depend on the
   reader's device carrying Georgia's Greek, would be read aloud as a Greek
   letter, and would be copied into other people's documents as one; the
   plain `d` beside it, hidden from sight but not from a screen reader or
   from copying, is what keeps the name spelled the way it is spelled.

   THE BOX IS THE FONT'S OWN EM BOX. 1176 units wide (Georgia's `d`
   advance, so nothing after it moves), 2327 tall, sitting 449 below the
   baseline — all over the 2048 units that make one em. Writing it that way
   means the letter follows any type size with no second number to keep.
   =================================================================== */
/* The hero's first letter is an inline-block, and a line may break between one of those and
   the text after it. The hero DOES need to break between `deltaPlus` and `Learning`, so it
   keeps its company name together with `.dP-word` instead of forbidding the break outright;
   the other three wordmarks are plain text now and simply hold. */
.brand-name,
.footer-name,
.eyebrow-wordmark,
.dP-word { white-space: nowrap; }

/* THE REVOLVING DOOR, and the only drawn letter left on the site (Kevin, 26-09-12).
   deltaPlus is spelled with an ordinary `d` everywhere it comes to rest — the header, the
   footer, the SAT page's eyebrow, and this hero at every moment except one. On a first visit
   title.js carries this letter once around its own vertical axis where the shine reaches it,
   and Georgia's leaned delta is what the far side of the panel is painted with.

   --turn runs 0 → 1 and BOTH ENDS ARE THE `d`, so the resting state needs no guard, no
   override for reduced motion, and nothing in the <head> to stop a wrong letter flashing.

   The box is the `d`'s own em box, in `em`, so the letter follows any type size with no
   second number to keep: 1176 units wide, 2327 tall, sitting 449 below the baseline. The
   perspective is written the same way — a fixed pixel distance would turn the small
   wordmarks almost flat while the hero kept its depth. */
.dP-first {
  display: inline-block;
  width: calc(1176em / 2048);
  height: calc(2327em / 2048);
  vertical-align: calc(-449em / 2048);
  perspective: 19.5em;
}
.dP-pivot {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transform: rotateY(calc(var(--turn, 0) * 360deg));
}
.dP-first .dP-face-d,
.dP-first .dP-face-delta {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}
/* The near face is REAL TEXT, so the name is spelled with a `d` in the accessibility tree and
   in anything the reader copies. Line-height equal to the box puts its baseline exactly where
   Georgia's ascender does, which is where the mask's baseline is too. */
.dP-first .dP-face-d {
  line-height: calc(2327em / 2048);
  font-style: normal;
}
/* The far face, carried half a turn so it arrives the right way round rather than mirrored. */
.dP-first .dP-face-delta {
  transform: rotateY(180deg);
  background-color: currentColor;
  -webkit-mask: url(images/dP_delta.svg) no-repeat center / contain;
  mask: url(images/dP_delta.svg) no-repeat center / contain;
}

/* The runs the shine is painted into, and only while it is in flight: title.js sets the
   band and the run's own color as inline background layers, and clears both when the sweep
   lands. Clipping to the text is what keeps the gold ON the letters rather than over the
   page — the same thing the app does with a ShaderMask. */
.hero-title.is-shining .hero-run {
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}


/* ============================= SAT BREAKDOWN: THE WORKBOOK MAP =============================
   A leaded window, not a landscape and not a sky. Two panels, one per section, each 360 units
   wide (Reading & Writing 600 high, Math 676): a blue head rule across the head, the section's
   approach workbook spanning the window under it, and the other workbooks hanging below in two
   columns, joined by gray lead roads that each mean "learn this first" (26-09-22). Workbooks are
   tiles, the app icon's rounded square with the name inside, in the three states set out at
   .map-tile below. The panels are drawn by the script at the bottom of sat.html from the same
   geometry the app draws (mobile/dP_SAT/lib/data/workbook_map.dart); these rules only color
   them. Filtering by skill area DIMS rather than hides, so the shape of the whole test stays on
   screen while one part is picked out — hiding would answer "what is Algebra" and lose "how much
   of the SAT is Algebra", the more useful question.

   The panel's own inks: dP blue is the head rule, and the lead (--map-lead) is every road. A
   workbook takes its skill area's color (Kevin, 26-09-22), or the brand green for the three
   whole-section workbooks, which outlines it when a visitor can open it and fills it when tapped;
   the skill-area colors also mark the 8-unit circles. Everything a visitor cannot open takes
   --map-locked and says nothing at all. Georgia appears in exactly one place, the section name. */
.wb-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
  margin: 0 0 28px;
}
.wb-chip {
  font: inherit;
  font-size: 14.5px;
  font-weight: 600;
  cursor: pointer;
  border: 1.5px solid var(--line);
  background: var(--paper-card);
  color: var(--ink);
  border-radius: 999px;
  padding: 8px 16px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  transition: border-color .15s, background .15s;
}
.wb-chip:hover { border-color: var(--ink-soft); }
.wb-chip:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.wb-chip .dot {
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--c, var(--ink-soft));
  flex: none;
}
.wb-chip[aria-pressed="true"] { border-color: var(--c, var(--ink)); background: var(--paper); }
.wb-chip[aria-pressed="true"] .dot { box-shadow: 0 0 0 3px color-mix(in srgb, var(--c, #888) 25%, transparent); }
.wb-chip[data-domain="comp"]     { --c: var(--d-comp); }
.wb-chip[data-domain="english"]  { --c: var(--d-english); }
.wb-chip[data-domain="algebra"]  { --c: var(--d-algebra); }
.wb-chip[data-domain="geometry"] { --c: var(--d-geometry); }
.wb-chip[data-domain="data"]     { --c: var(--d-data); }
.wb-chip[data-domain="strategies"] { --c: var(--d-strategies); }

.wb-status {
  text-align: center;
  font-size: 14.5px;
  color: var(--ink-soft);
  margin: -12px 0 24px;
  min-height: 1.2em;
}

/* ---- the domino: which section's panel is in view ---- */
.map-band {
  display: flex;
  gap: 16px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  /* bleeds into the section's side padding, so on a phone a panel has room to center with
     its signpost strip unclipped */
  margin: 0 calc(-1 * clamp(20px, 5vw, 40px));
  padding: 2px clamp(20px, 5vw, 40px) 6px;
}
.map-band .map-panel:first-child { margin-left: auto; }
.map-band .map-panel:last-child { margin-right: auto; }
.map-panel {
  flex: none;
  width: min(360px, 92vw);
  height: auto;                     /* the viewBox sets the shape */
  scroll-snap-align: center;
  background: var(--map-panel);
  border: 1px solid var(--line);   /* square corners, as the app draws its panel */
}
.map-panel text {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  fill: var(--ink);
}
.map-panel .map-section { font-family: Georgia, "Times New Roman", serif; font-size: 24px; }
.map-panel .map-caps { font-size: 10px; font-weight: 500; letter-spacing: 1.8px; fill: var(--ink-soft); }
.map-panel .map-name { font-size: 16px; font-weight: 500; text-anchor: middle; }
/* the share hung under an open tile, and an approach workbook's line inside it; "free" in the
   green is kept for a free workbook, and none is free since 10/7/2026 */
.map-panel .map-state { font-size: 11px; text-anchor: middle; fill: var(--ink-soft); }
.map-panel .map-free { fill: var(--green); font-weight: 600; }

/* The head rule is the blue bar the whole panel hangs from — the mark that used to be a
   ground line at the foot, moved when the flow was turned over. A spine drops out of it 3
   wide; every other road is 1.5. Turns are square. The divider, buds, nodes, and gate of the
   ten-workbook map went with it on 26-09-22; commit 92fbb49 is the last that holds their rules. */
.map-panel .map-head { stroke: var(--blue); stroke-width: 4; }
.map-panel .map-lead { fill: none; stroke: var(--map-lead); stroke-width: 3; stroke-linecap: butt; stroke-linejoin: miter; }
.map-panel .map-road { stroke-width: 1.5; }

/* ---- PARKED (26-09-22) — the approach key, and its glitter ----
   Drawn by drawKey in sat.html, which nothing calls since the eighteen workbooks made the
   approaches tiles. Kept with it, not deleted, because the glitter is Kevin-approved work that
   could mark something later; commit 92fbb49 is the last where it ran.
   At rest the key is flat dP blue and nothing else is there. Once every five seconds a band
   travels right to left across it carrying two gold layers: a thick blurred stroke UNDER the
   key, which is the only thing here with enough area to catch an eye, and the key's own shapes
   ON it, so the mark itself turns gold as the light passes. Both are hidden outright between
   crossings — parked gold is how the first drawing left a permanent glint on every key.
   Each key's delay is set in JS to a random moment inside its own third of the period. */
.map-panel .map-bud-label { font-size: 11px; font-style: italic; fill: var(--ink-soft); }
.map-panel .map-key-bow { fill: none; stroke: var(--blue); stroke-width: 2; }
.map-panel .map-key-fill { fill: var(--blue); }
.map-panel .map-key-gold-bow { fill: none; stroke: #fff3c4; stroke-width: 2; }
.map-panel .map-key-gold-fill { fill: #fff3c4; }
.map-panel .map-key-glow { fill: none; stroke: #fff3c4; stroke-width: 7; stroke-linecap: round; opacity: .55; }
.map-panel .map-key-shine { animation: map-key-fade 5s linear infinite; }
.map-panel .map-key-band { animation: map-key-band 5s linear infinite; }
@keyframes map-key-band {
  0%   { transform: none; }
  24%  { transform: translateX(-88px); }
  100% { transform: translateX(-88px); }
}
@keyframes map-key-fade {
  0%, 23.8% { opacity: 1; }
  24%, 100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .map-panel .map-key-shine { animation: none; opacity: 0; }
  .map-panel .map-key-band { animation: none; }
}

/* A WORKBOOK HAS THREE STATES, in its own color, --c, which sat.html sets on each tile from
   its first question type's skill area, or the brand green on a whole-section workbook (Kevin,
   26-09-21: "for selection of workbooks in the
   map, given what we've done, these should be more neutral. Accessible should be the colors we
   decided, grayed out otherwise. The one that's being actively selected gets the colors
   switched color on white for example becomes white on color."):
     - one you can open is outlined in its color, 2 wide, with its name in full ink;
     - every other one is pale and silent, and carries no line beneath it at all;
     - is-focus, the tapped one, is filled with its color, and every word and circle on the
       fill is white, in both themes (Kevin, 26-09-22), an approach workbook's inside line included.
       The share lines under a tile hang on the panel's own ground, off the fill, so they keep
       their ink: white there would be 1:1 on the white panel.
   Until 26-09-22 an open tile was outlined in the keys' dP blue and the tapped one wore a dP
   green border 3 wide; commit 9d6c5bf is the last that holds those rules. is-dim is one
   outside the filter. */
.map-panel .map-tile { cursor: pointer; transition: opacity .18s; }
.map-panel .map-tile .tile { fill: var(--map-panel); stroke: var(--map-locked); stroke-width: 2; }
.map-panel .map-tile[data-open] .tile { stroke: var(--c); }
.map-panel .map-tile .map-name { fill: var(--map-locked-ink); }
.map-panel .map-tile[data-open] .map-name { fill: var(--ink); }
.map-panel .map-area { fill: var(--a); }
.map-panel .map-tile:hover .tile { stroke: var(--ink); }
.map-panel .map-tile.is-focus .tile { fill: var(--c); stroke: var(--c); }
.map-panel .map-tile.is-focus .map-name,
.map-panel .map-tile.is-focus .map-inside,
.map-panel .map-tile.is-focus .map-free,
.map-panel .map-tile.is-focus .map-area { fill: #fff; }
.map-panel .map-tile:focus-visible .tile { stroke: var(--ink); stroke-width: 4; paint-order: stroke; }
/* NO BROWSER FOCUS RING (Kevin, 10/7/2026: "white borders show when clicked; make these go
   away"). Chrome rings a clicked SVG shape that can take focus, as it never does a clicked
   button, so every click left a white box around the tile and its share line. A keyboard
   visitor keeps the rule above, which a click does not trigger. */
.map-panel .map-tile:focus { outline: none; }
.map-panel .map-tile.is-dim { opacity: .35; }

/* ---- the detail beneath the band: the tapped workbook's name, its share, how many official SAT
   skills it covers, and one sentence on what is in it (since 10/10/2026; sat.html, ABOUT). Its
   question types as chips in their skill-area colors are PARKED with `questionTypeChips` there,
   and their styles below are kept with them. ---- */
.map-detail {
  margin-top: 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper-card);
  padding: 14px 16px 18px;
  min-height: 92px;
  font-size: 14.5px;
  color: var(--ink-soft);
}
.map-detail b {
  display: block;
  font-family: Georgia, "Times New Roman", serif;
  font-size: 17px;
  color: var(--ink);
  margin-bottom: 2px;
}
.map-detail .map-skills { margin: 10px 0 0; font-weight: 600; color: var(--ink); }
.map-detail .map-about { margin: 4px 0 0; }
.map-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.map-chip {
  display: inline-block;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.25;
  padding: 5px 10px;
  border-radius: 8px;
  color: #fff;
  background: var(--c);
  transition: opacity .18s, filter .18s;
}
.map-chip[data-domain="comp"]     { --c: var(--d-comp); }
.map-chip[data-domain="english"]  { --c: var(--d-english); }
.map-chip[data-domain="algebra"]  { --c: var(--d-algebra); }
.map-chip[data-domain="geometry"] { --c: var(--d-geometry); }
.map-chip[data-domain="data"]     { --c: var(--d-data); }
.map-chip[data-domain="strategies"] { --c: var(--d-strategies); }
.map-chip.is-dim { filter: grayscale(1); opacity: .3; }

.wb-note {
  margin: 32px auto 0;
  max-width: 760px;
  border-left: 3px solid var(--line);
  padding-left: 18px;
}
.wb-note h3 { font-size: 19px; margin: 0 0 8px; }
.wb-note p { margin: 0 0 12px; color: var(--ink-soft); font-size: calc(15.5px * var(--copy)); }
.wb-note p:last-child { margin-bottom: 0; }
.wb-note strong { color: var(--ink); }

/* The official test, then how we sort it, above the filters (10/7/2026). */
.wb-official { max-width: 760px; margin: 0 auto 24px; text-align: center; }
.wb-official p { margin: 0 0 10px; color: var(--ink-soft); font-size: calc(16px * var(--copy)); }
.wb-official strong, .wb-official b { color: var(--ink); }
.wb-domains span { display: block; font-size: calc(14.5px * var(--copy)); }
.wb-domains b { margin-right: 6px; }

/* ===================================================================
   Calendar (calendar.html). ONE MONTH AT A TIME SINCE 10/7/2026 (Kevin: "rather than listing a
   ton of months, do something more elegant"; "look how small the numbers are with a big awkward
   rectangle around the number for today's date"; "The page space is also not being used enough";
   "More motion"). The month large on the left, its dates or the chosen day's beside it, across the
   width the SAT page's map uses; one stacked over the other on a phone. Today is a filled circle,
   the chosen day a soft wash in its first date's color. One color per kind of date, from the
   site's own tokens, every one above the 2:1 floor on both grounds; the chips at the top hide or
   show a kind. Until that day every month stacked in one 760-wide column (commit ee47a3db).
   =================================================================== */
/* `flow-root` keeps the lead's bottom margin INSIDE the veil (10/7/2026): without it the margin
   spilled below the opening and left a strip of bare photograph between it and the calendar. */
.cal-hero { text-align: center; padding: clamp(40px, 7vw, 72px) 20px 0; display: flow-root; }
.cal-hero h1 { font-size: clamp(34px, 6vw, 52px); margin: 0 0 10px; color: var(--blue); }
/* THE SEASON'S PHOTOGRAPH (Kevin, 10/9/2026), under the title on the calendar page only. calendar.html's
   <head> names the season on <html> before the page draws, on the day of each equinox and solstice, so
   only that season's file is ever downloaded. A small caption names the place. Under the pointer it
   drifts a little closer, the gentle cousin of the cards' growth. Licenses: images/boston/README.md. */
.season-photo {
  position: relative;
  max-width: 1120px;
  height: clamp(170px, 24vw, 300px);
  margin: 4px auto 30px;
  border-radius: 18px;
  overflow: hidden;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.18);
}
.season-photo::before {
  content: "";
  position: absolute;
  inset: 0;
  background: url(images/boston/boston_fall.jpg) center / cover;
  transition: transform 900ms ease;
}
/* THE PLACE ALONE, by its own name (Kevin, 10/9/2026: "just say Boston Commons Public Garden (or
   similar, whatever the official name is, no Fall upfront"): the season is the photograph's to show. */
.season-photo::after {
  content: "Boston Public Garden";
  position: absolute;
  right: 12px;
  bottom: 10px;
  padding: 4px 10px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.5);
  color: #fff;
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: .3px;
}
/* Winter and spring were replaced 10/13/2026 (Kevin: "Find better ones for winter and spring", then "Go with
   winter_02 and spring_18"): Boston Common under slush and Commonwealth Avenue's magnolias under a white sky gave way
   to the Public Garden in snow and its yellow tulips; images/boston/README.md has both, old and new. */
html[data-season="winter"] .season-photo::before { background-image: url(images/boston/boston_winter.jpg); background-position: center 78%; }
html[data-season="winter"] .season-photo::after  { content: "Boston Public Garden"; }
html[data-season="spring"] .season-photo::before { background-image: url(images/boston/boston_spring.jpg); background-position: center 45%; }
html[data-season="spring"] .season-photo::after  { content: "Boston Public Garden"; }
html[data-season="summer"] .season-photo::before { background-image: url(images/boston/boston_summer.jpg); background-position: center 45%; }
html[data-season="summer"] .season-photo::after  { content: "Charles River"; }
html[data-season="fall"]   .season-photo::before { background-position: center 55%; }
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .season-photo:hover::before { transform: scale(1.04); }
}
.cal-chips { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; max-width: 1120px; margin: 0 auto 26px; }
.cal-chip {
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.25;
  padding: 6px 11px;
  border: 0;
  border-radius: 999px;
  color: #fff;
  background: var(--c);
  cursor: pointer;
  transition: opacity .18s, filter .18s, transform .18s, box-shadow .18s;
}
.cal-chip:hover { transform: translateY(-2px); box-shadow: 0 6px 14px color-mix(in srgb, var(--c) 40%, transparent); }
.cal-chip.is-dim { filter: grayscale(1); opacity: .3; }
/* THE SAT IS THE ONE THING IN dP GREEN (Kevin, 10/10/2026: "nothing else is dP green, so switch the pill
   colors for SAT test days and Bootcamps, and make sure this matches on the website calendar"), as in the
   app (`CalendarKind.colorIn`). Until then the SAT days were dP blue and bootcamp registration dP green. */
.cal-k-sat      { --c: var(--green); }
.cal-k-deadline { --c: var(--d-data); }
.cal-k-scores   { --c: var(--sat-blue); }
.cal-k-mar_sw   { --c: var(--d-comp); }
.cal-k-mar_st   { --c: var(--d-english); }
.cal-k-may_sw   { --c: var(--pink); }
.cal-k-may_st   { --c: var(--d-algebra); }
.cal-k-enroll   { --c: var(--d-geometry); }

.cal-board {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  gap: 24px;
  max-width: 1120px;
  margin: 0 auto;
  align-items: start;
}
.cal-pane, .cal-side {
  background: var(--paper-card);
  border: 1px solid var(--line);
  border-radius: 18px;
  padding: 22px 22px 18px;
}
.cal-nav { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 0 0 10px; }
.cal-month-name { font-family: Georgia, "Times New Roman", serif; font-size: 30px; margin: 0; color: var(--ink); text-align: center; flex: 1; }
.cal-arrow {
  font: inherit; font-size: 26px; line-height: 1;
  width: 40px; height: 40px; border-radius: 50%;
  border: 1px solid var(--line); background: transparent; color: var(--ink);
  cursor: pointer; transition: background .18s, transform .18s, opacity .18s;
}
.cal-arrow:hover:not(:disabled) { background: var(--line); transform: scale(1.08); }
.cal-arrow:disabled { opacity: .25; cursor: default; }

/* The row of month names: the chosen one filled in dP blue, a month with dates underlined. */
.cal-strip { display: flex; flex-wrap: wrap; justify-content: center; gap: 4px; margin: 0 0 16px; }
.cal-month-pick {
  font: inherit; font-size: 13px; font-weight: 600;
  padding: 5px 10px; border-radius: 999px; border: 0;
  background: transparent; color: var(--ink-soft);
  cursor: pointer; transition: background .18s, color .18s, transform .18s;
}
.cal-month-pick.has-events { text-decoration: underline; text-decoration-color: var(--line); text-underline-offset: 4px; }
.cal-month-pick:hover { color: var(--ink); background: var(--line); transform: translateY(-1px); }
.cal-month-pick.is-on { background: var(--blue); color: #fff; text-decoration: none; }

.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
.cal-head { text-align: center; font-size: 12px; font-weight: 600; letter-spacing: .6px; text-transform: uppercase; color: var(--ink-soft); padding: 0 0 4px; }
.cal-day {
  font: inherit;
  aspect-ratio: 1 / 1;
  min-height: 48px;
  border: 0;
  border-radius: 14px;
  background: transparent;
  color: var(--ink);
  padding: 6px 2px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  cursor: pointer;
  transition: transform .18s, box-shadow .18s, background .18s;
}
.cal-day.is-blank { cursor: default; }
.cal-day:not(.is-blank):hover { background: color-mix(in srgb, var(--ink) 7%, transparent); transform: translateY(-2px); }
.cal-day.has-events:hover {
  background: color-mix(in srgb, var(--c) 16%, transparent);
  box-shadow: 0 8px 20px color-mix(in srgb, var(--c) 35%, transparent);
}
/* THE CHOSEN DAY WEARS A BORDER IN ITS OWN COLOR (Kevin, 10/10/2026: "using neither a white border or a
   colored fill in, but rather a colored border"), the color of its first date, or dP blue for a day with
   none; its lines in the list wear their own (`.cal-item.is-chosen`). Chosen from the square or from a
   line alike, and undone from either (calendar.html, `choose`). An outline drawn just inside the square,
   not a box-shadow, so the hover's glow (a box-shadow) never hides it; it follows the rounded corners. Until
   then a chosen square was filled with its color, and a line's click boxed the square in the ink. */
.cal-day.is-chosen { outline: 2.5px solid var(--c, var(--blue)); outline-offset: -2.5px; }
.cal-day.is-past { opacity: .4; }
.cal-num {
  font-size: 20px; font-weight: 500; line-height: 1;
  width: 36px; height: 36px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
}
.cal-day.is-today .cal-num { box-shadow: inset 0 0 0 2px var(--blue); color: var(--blue); font-weight: 700; }
.cal-bars { display: flex; justify-content: center; gap: 3px; height: 4px; }
/* FROM THE LIST (Kevin, 10/9/2026): pointing at a line swells its date the way the SAT page's
   countdown number swells (`countdown-swell`, the same curve, quicker here because it answers a
   pointer rather than greeting a visitor); clicking chooses its day, as a click on the square does
   (`.is-chosen`, 10/10/2026). */
.cal-day.is-swelling { animation: cal-swell 700ms cubic-bezier(.34, .8, .3, 1); position: relative; z-index: 1; }
@keyframes cal-swell {
  0%   { transform: scale(1); }
  38%  { transform: scale(1.28); }
  100% { transform: scale(1); }
}
.cal-item { cursor: pointer; }
.cal-item.is-chosen { outline: 2px solid var(--c); outline-offset: -2px; }
/* OFFICIAL TEST DAYS STAND OUT (Kevin, 10/7/2026: "the official test dates should stand out
   more"): the one filled circle on the page, in the SAT color with a white number, the word SAT
   under it, a tint behind, the month's name dotted, and the list's line bolder. Today is the
   lighter mark, a thin blue ring, so the two never look alike. */
.cal-day.is-sat { background: color-mix(in srgb, var(--c) 14%, transparent); }
.cal-day.is-sat .cal-num { background: var(--c); color: #fff; font-weight: 700; }
.cal-sat-word { font-size: 10px; font-weight: 800; letter-spacing: 1px; line-height: 4px; color: var(--c); }
.cal-month-pick.has-sat::after {
  content: ""; display: inline-block; width: 6px; height: 6px; border-radius: 50%;
  margin-left: 5px; vertical-align: 2px; background: var(--green);
}
.cal-item.cal-k-sat { background: color-mix(in srgb, var(--c) 12%, transparent); }
.cal-item.cal-k-sat .cal-what { color: var(--ink); font-weight: 700; }
.cal-bar { width: 12px; height: 4px; border-radius: 2px; background: var(--c); }

/* The month slides in from the side it was reached from. */
@keyframes cal-from-right { from { opacity: 0; transform: translateX(28px); } to { opacity: 1; transform: none; } }
@keyframes cal-from-left  { from { opacity: 0; transform: translateX(-28px); } to { opacity: 1; transform: none; } }
.cal-grid.cal-in-next { animation: cal-from-right .32s ease-out; }
.cal-grid.cal-in-prev { animation: cal-from-left .32s ease-out; }

.cal-side { max-height: 640px; overflow: auto; }
.cal-side-title { font-family: Georgia, "Times New Roman", serif; font-size: 22px; margin: 0 0 14px; color: var(--ink); }
.cal-list { list-style: none; margin: 0; padding: 0; }
@keyframes cal-rise { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.cal-item {
  display: grid;
  grid-template-columns: 6.6em 1fr;
  gap: 10px;
  padding: 7px 0 7px 12px;
  border-left: 4px solid var(--c);
  margin: 0 0 6px;
  font-size: calc(15px * var(--copy));
  animation: cal-rise .3s ease-out both;
  transition: transform .18s, background .18s;
  border-radius: 0 8px 8px 0;
}
.cal-item:hover { transform: translateX(3px); background: color-mix(in srgb, var(--c) 10%, transparent); }
.cal-item.is-past { opacity: .45; }
.cal-item.is-empty { border-left-color: var(--line); color: var(--ink-soft); grid-template-columns: 1fr; }
.cal-when { font-weight: 600; color: var(--ink); }
.cal-what { color: var(--ink-soft); }

@media (max-width: 860px) {
  .cal-board { grid-template-columns: 1fr; }
  .cal-side { max-height: none; }
}
@media (max-width: 520px) {
  .cal-pane, .cal-side { padding: 16px 12px; }
  .cal-month-name { font-size: 24px; }
  .cal-grid { gap: 3px; }
  .cal-day { min-height: 40px; border-radius: 10px; gap: 4px; }
  .cal-num { font-size: 16px; width: 28px; height: 28px; }
  .cal-bar { width: 8px; }
}

@media (prefers-reduced-motion: reduce) {
  .wb-chip, .map-tile, .map-chip, .cal-chip { transition: none; }
  .cal-arrow, .cal-month-pick, .cal-day, .cal-item { transition: none; }
  .cal-day:hover, .cal-chip:hover, .cal-month-pick:hover, .cal-item:hover { transform: none; }
  .cal-grid.cal-in-next, .cal-grid.cal-in-prev, .cal-item, .cal-day.is-swelling { animation: none; }
}

/* ===================================================================
   THE SIDE SPACE, ON A WIDE SCREEN ONLY (Kevin, 10/7/2026: "More use should be made of black
   space on the left and right of the other pages too"). From 1,100 pixels up, blocks that stood
   in one narrow column sit side by side across the sheet; sentences keep their reading length.
   Below 1,100 every page is exactly as it was. The calendar has its own width (above).
   =================================================================== */
@media (min-width: 1100px) {
  /* SAT page: the weekend dates beside the school-day windows */
  .dates-split { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: 40px; max-width: 1100px; margin: 0 auto; align-items: start; }
  .dates-col + .dates-col .dates-table th, .dates-col + .dates-col .dates-table td { white-space: normal; }
  .dates-split .table-wrap { max-width: none; }
  .dates-split .dates-subhead { margin-top: 0; text-align: left; }
  .dates-split .dates-note, .dates-split .dates-note-left { text-align: left; margin-left: 0; }

  /* SAT page: the official test beside how we sort it */
  .wb-official { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); gap: 40px; max-width: 1100px; text-align: left; align-items: start; }

  /* SAT page: the map's two panels grow with the sheet, to 520 each */
  .map-panel { width: min(520px, calc((var(--max) - 140px) / 2)); }

  /* SAT page: the steps as cards in one row, four since the free analysis joined (10/9/2026) */
  .steps { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; max-width: 1100px; }
  .steps li {
    margin: 0; border: 1px solid var(--line); border-radius: var(--radius);
    background: var(--paper-card); padding: 58px 22px 22px; box-shadow: var(--shadow);
  }
  .steps li:last-child { border-color: var(--line); padding-bottom: 22px; }
  .steps li::before { left: 22px; top: 18px; }

  /* SAT page: the groups' table, the price cards, and the questions across the sheet */
  #bootcamps .table-wrap { max-width: 1100px; }
  .pricing { max-width: 1100px; gap: 24px; }
  .faq { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 0 20px; max-width: 1100px; align-items: start; }

  /* Home page: more words to a row, the four values in one row, the four claims across */
  .word-grid { max-width: 1100px; }
  .values { grid-template-columns: repeat(4, 1fr); max-width: 1100px; }
  .app-claims { max-width: 1100px; }
  /* Mission in two or three long lines, not five short ones (Kevin, 10/7/2026: "don't cover up
     more of the images just to have super narrow wrapped text like for Mission") */
  .mission-statement { max-width: 46ch; font-size: 33px; }
}

/* ===================================================================
   THINGS ANSWER THE POINTER (Kevin, 10/7/2026: "more interactable elements too, they don't have
   to be super involved, like how the cards for offerings move in the SAT page, similar for cards
   in philosophy"). Each takes the price cards' own move, a small growth and a soft dP-blue shadow,
   or a smaller cousin of it, at every screen width that has a pointer. Colors change for everyone;
   movement only for a device that has not asked for less of it, and never on a touch screen,
   where a tap would set it off.
   =================================================================== */
.value, .steps li, .app-claims li, .app-claims strong, .word-grid a, .faq details, .faq summary,
.faq summary::after, .wb-chip, .map-chip, .resource-card, .value-mark i, .steps li::before {
  transition: transform 180ms ease, box-shadow 180ms ease, background-color 180ms ease,
    border-color 180ms ease, color 180ms ease;
}
.dates-table tbody tr { transition: background-color 150ms ease; }
@media (hover: hover) {
  /* Colors, for everyone with a pointer */
  .app-claims li:hover { border-left-color: var(--blue); }
  .app-claims li:hover strong { color: var(--blue); }
  .faq details:hover { border-color: var(--green); }
  .faq summary:hover { background: color-mix(in srgb, var(--green) 7%, transparent); }
  .dates-table tbody tr:hover { background: color-mix(in srgb, var(--blue) 8%, transparent); }
  .steps li:hover::before { background: var(--green); }
  .wb-chip:hover, .map-chip:hover { border-color: var(--ink-soft); }
}
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  /* The philosophy cards and the steps: the price cards' move */
  .value:hover { transform: scale(1.035); box-shadow: 0 18px 44px rgba(9, 39, 177, 0.18); position: relative; z-index: 1; }
  .value:hover .value-mark i { transform: translateY(-3px); }
  .value:hover .value-mark i:nth-child(2) { transition-delay: 40ms; }
  .value:hover .value-mark i:nth-child(3) { transition-delay: 80ms; }
  .value:hover .value-mark i:nth-child(4) { transition-delay: 120ms; }
  .steps li:hover::before { transform: scale(1.12); }
  /* The claims slide, the words and buttons lift, the questions and chips rise a little */
  .app-claims li:hover { transform: translateX(4px); }
  .word-grid a:hover { transform: translateY(-2px); box-shadow: 0 8px 18px rgba(9, 39, 177, 0.14); }
  .btn:hover { transform: translateY(-2px); box-shadow: 0 10px 22px rgba(9, 39, 177, 0.2); }
  .btn:active { transform: translateY(1px); }
  .faq details:hover { transform: translateY(-2px); box-shadow: 0 10px 24px rgba(9, 39, 177, 0.12); }
  .faq summary:hover::after { transform: rotate(90deg); }
  .wb-chip:hover, .map-chip:hover { transform: translateY(-2px); }
  .resource-card:hover { transform: translateY(-3px); box-shadow: 0 14px 30px rgba(9, 39, 177, 0.16); }
}
@media (min-width: 1100px) and (hover: hover) and (prefers-reduced-motion: no-preference) {
  /* The steps are cards on a wide screen, so they move as cards there */
  .steps li:hover { transform: scale(1.035); box-shadow: 0 18px 44px rgba(9, 39, 177, 0.18); }
}
