/* ============================================================================
   The public pages — landing, store, sign in, register.

   Tokens copied from src/parts/p01.html rather than reinvented, so a visitor
   who lands here and then opens the app is looking at one product. When a token
   changes there, change it here. (There is no honest way to share them: the
   app's CSS is assembled by a Python build, this is served by Apache.)

   Two themes, three states. An explicit choice stamps data-theme on <html>; the
   default "system" setting stamps nothing, so prefers-color-scheme is the only
   thing separating light from dark for most visitors. Every colour is a token
   defined on bare :root and redefined in both places, never declared only
   inside a media query.
   ========================================================================== */

:root{
  --f-ui:'Plus Jakarta Sans',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;
  --f-dis:'Outfit','Plus Jakarta Sans',system-ui,sans-serif;
  /* THE THIRD FACE, AND IT WAS MISSING FROM THIS FILE ENTIRELY until 24 August
     2026. Instrument Serif is one of the three the product is designed in, it
     is served from fonts/ beside this stylesheet, and src/parts/p01.html
     declares --f-ser for the app -- but the public pages never did. So every
     var(--f-ser) here resolved to nothing and fell back to the body font, and
     the one typographic contrast in the whole design silently did not happen:
     the spot-the-mistake sentence, the pull quotes and the stand-first line
     were all set in Plus Jakarta Sans and had been from the day they shipped.

     Found by reading a computed style rather than the stylesheet. It survived
     check-css-vars.py because that script pools every file it scans, and
     p01.html defining the token counted as this file defining it -- worth
     knowing before trusting that checker on a name used in two places. */
  --f-ser:'Instrument Serif','Iowan Old Style',Georgia,serif;

  --r-sm:12px; --r-md:16px; --r-lg:22px; --r-xl:28px; --r-2xl:36px;
  --ease:cubic-bezier(.22,1,.36,1);
  --spring:cubic-bezier(.34,1.46,.64,1);
  --maxw:1140px;

  --a1:#5A45FF; --a2:#A855F7; --a3:#FF3D9A;
  --grad:linear-gradient(135deg,var(--a1) 0%,var(--a2) 48%,var(--a3) 100%);
  /* THE BRAND ACCENTS AT FULL STRENGTH, and fixed rather than themed.
     Three attempts were made to darken this so white would clear 4.5 on every
     stop, and the owner rejected each one as losing its lightness. She is right,
     and the reason is worth writing down: #A855F7 is 3.96:1 under white and
     #FF3D9A is 3.29:1 -- so EVERY solid button on this site has been below the
     small-text floor since it shipped, Buy now included. Holding this one button
     to a stricter standard made it visibly duller than the buttons beside it,
     which is a real fault a reader can see, traded against one they cannot.
     THE CONTRAST IS A SEPARATE, SITE-WIDE DECISION -- either the accents move
     for every button at once, or the labels get a scrim. Fixing it on one
     button was the wrong shape of fix.
     WHY THE TOKEN STILL EXISTS: the app rewrites both --grad-brand and
     --grad-deep from the hero palette on every tick of its time-of-day theming
     (p09b.html), so a button using either drifts through the day and stops
     matching the public shop. This one is fixed and shared. */
  /* Her brand accents, at their own values and in their own places -- the same
     gradient every .btn-solid on the site carries. Fixed rather than themed:
     the app rewrites both --grad-brand and --grad-deep from the hero palette on
     every tick of its time-of-day theming (p09b.html), so a button built on
     either drifts through the day and stops matching the public shop. */
  /* THE GRADIENT WHITE TEXT MAY STAND ON. The same three colours as --grad
     with the lightness lowered until white clears 4.5:1 everywhere -- hue and
     saturation carried across untouched, so it is recognisably the same
     button. --grad is 3.96:1 at its middle stop and 3.29:1 at its end, and
     stays as it is: it has 43 uses and most are decoration. */
  --grad-btn:linear-gradient(135deg,#5A45FF 0%,#9D40F6 48%,#E4006D 100%);

  --bg:#F2F4FE; --surface:#FFFFFF; --surface-2:#F7F8FF; --surface-3:#EFF1FE;
  --line:#E2E6F7; --line-strong:#D2D8F0;
  --ink:#0F1130; --ink-2:#4A4E75; --ink-3:#7B7FA4; --ink-4:#A7ABC8;
  /* the quietest grey that is still readable as small text. --ink-3 is what
     the app sets its eyebrows in and it is 3.88:1 on white, under the 4.5 a
     small size needs; --ink-2 is 7.96 and reads as bold. This is 4.78 --
     within a shade of --ink-3 to look at, and above the line. */
  --ink-quiet:#6C7096;
  --brand:var(--a1); --brand-soft:rgba(90,69,255,.09); --brand-line:rgba(90,69,255,.22);
  --ok:#059669; --ok-soft:rgba(5,150,105,.1); --ok-line:rgba(5,150,105,.25);
  --no:#E11D6F; --no-soft:rgba(225,29,111,.09); --no-line:rgba(225,29,111,.24);
  /* THE SAME TWO, DEEP ENOUGH TO BE READ AS A SENTENCE. --ok is #059669, which
     is 3.77:1 on white -- fine as a tick or a bar, under the 4.5 a line of body
     text needs, and the store card sets a whole corrected sentence in it.
     These were used on that card and never defined: check-css-vars.py caught it,
     and until now both colours resolved to nothing and inherited whatever the
     card happened to be using. 5.48 and 5.45 on white. */
  --ok-ink:#047857; --no-ink:#C81E6B;
  --warn:#7C3AED; --warn-soft:rgba(124,58,237,.09); --warn-line:rgba(124,58,237,.24);
  --sh-1:0 1px 2px rgba(24,20,70,.05), 0 2px 6px rgba(24,20,70,.04);
  --sh-2:0 2px 4px rgba(24,20,70,.05), 0 10px 24px -6px rgba(24,20,70,.09);
  --sh-3:0 4px 8px rgba(24,20,70,.05), 0 22px 44px -12px rgba(30,22,86,.16);
  --sh-brand:0 12px 32px -8px rgba(90,69,255,.42);
  --focus:#5A45FF;
  --glass:rgba(255,255,255,.78);
  --aura:.5;
  /* AURORA, AT AFTERNOON. Copied out of the app's own palette in
     src/parts/p09b.html -- BASE.aura.light.afternoon. The pages were on
     aura.base, which is the app's midnight-to-anything default rather than a
     time of day. */
  --au1:#AFD8FF; --au2:#D6C4FF; --au3:#A9EFE0;
  /* THE EMPHASISED WORD ON A BRAND BAND, and its own token because the thing
     it needs is not the thing --au1 became.

     The rule using it says it wants "the aurora's pale blue", and in this
     theme --au1 IS pale blue. The dark theme flips the aurora trio to deep
     versions -- right for an aurora, which is a background -- so the same rule
     painted #3560CC on the dark band and measured 1.12:1. Not dim: very nearly
     invisible, and the owner reported it as unreadable before it was measured.

     Identical to --au1 here on purpose, so the light band does not move. */
  --band-em:#AFD8FF;
  /* THE HERO TRIO, which the pages did not have at all. BASE.hero.light
     .afternoon. In the app this is what .hero and .profhero are painted with,
     and it is the part of the palette that moves with the clock -- the brand
     trio above never does. So the bands take this and the buttons keep --grad. */
  --h1:#0E8FB8; --h2:#4B4BE8; --h3:#9B45E8;
  /* MIRRORED, and the colours are untouched. The app paints its hero
     h1 -> h2 -> h3 at 135deg, which puts #0E8FB8 -- the palest stop of the
     three -- in the top-left corner, and the top-left corner is where every
     eyebrow and heading on a band begins. White on that teal is 3.72:1 at
     best.
   *
     Measured across four pages at two widths, sampling all four corners of
     every line rather than one:
   *
         h1 -> h3 (the app's order)   34 lines under the bar, worst 3.84
         h3 -> h1 (this)               0 lines under the bar, worst 4.72
   *
     Same three hexes, same angle, same Aurora afternoon -- only which corner
     the pale one lands in. To go back to the app's exact order, swap --h3 and
     --h1 here and put the midpoint back to 52%. */
  /* THE BAND TRIO, and it is its own trio on purpose.
   *
     The bands used to be painted --a3 -> --h2 -> --h1: the button's own pink,
     the hero indigo, the hero teal. Three colours at three different DEPTHS --
     white sat on 3.29:1, then 6.05, then 3.72 -- so the band got lighter and
     darker as it crossed the screen, which is what read as restless whatever
     the hues were doing. Two of those numbers were also under the 4.5 small
     white text needs.
   *
     LEVELLED, NOT DRAINED, and the difference cost a round. The first attempt
     took a third of the saturation out as well and came back "now looks dull".
     The dullness was the saturation; the restlessness was the swing. Only the
     swing needed fixing.
   *
     Saturation is within a few points of the original at every stop. What
     changed is that all three now sit at the same depth, so the band no longer
     brightens and darkens across its own width:
   *
         pink    #FF3D9A s100 l62  3.29  ->  #DA217A s74 l49  4.69
         indigo  #4B4BE8 s77  l60  6.05  ->  #6464E4 s70 l64  4.68
         teal    #0E8FB8 s86  l39  3.72  ->  #127D9E s80 l34  4.72
   *
     AND IT CLEARS A CONTRAST FAULT. 3.29 and 3.72 are both under the 4.5 that
     small white text needs, so the pink and teal ends of every band were
     failing. Every stop clears it now.
   *
     THE BUTTONS ARE UNTOUCHED -- --grad, --a1/a2/a3 and the M. mark keep their
     full vibrancy, which is right: an accent can be louder than a field.
   *
     --h1/--h2/--h3 stay exactly as the app has them, because they are copied
     from its Aurora afternoon palette and are what keeps the two halves of the
     product looking related. The white buttons on a band still take their text
     colour from --h2.
   *
     135deg, the same diagonal the buttons and the M. mark run at, and the same
     pink-to-blue flow: it was horizontal for one round and is back on the
     diagonal. */
  --b1:#DA217A; --b2:#6464E4; --b3:#127D9E;
  --grad-hero:linear-gradient(135deg,var(--b1),var(--b2) 48%,var(--b3));
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --bg:#05071A; --surface:#0E1230; --surface-2:#141936; --surface-3:#1B2145;
    --line:rgba(140,155,255,.13); --line-strong:rgba(150,165,255,.24);
    --ink:#EFF1FF; --ink-2:#AFB6E4; --ink-3:#7A82B8; --ink-4:#565D8C;
    /* on the dark card --ink-3 is already 4.98:1, so this is the app's own value */
    --ink-quiet:#7A82B8;
    --brand:#8B7BFF; --brand-soft:rgba(139,123,255,.15); --brand-line:rgba(139,123,255,.34);
    --ok:#34E5B0; --ok-soft:rgba(52,229,176,.13); --ok-line:rgba(52,229,176,.32);
    --no:#FF5C93; --no-soft:rgba(255,92,147,.13); --no-line:rgba(255,92,147,.32);
    /* on a dark card the ordinary pair are already 11.31:1 and 6.28:1, so the
       deeper versions would be a step the wrong way -- they ARE the pair here */
    --ok-ink:#34E5B0; --no-ink:#FF5C93;
    --warn:#C084FC; --warn-soft:rgba(192,132,252,.13); --warn-line:rgba(192,132,252,.32);
    --sh-1:0 1px 2px rgba(0,0,0,.5);
    --sh-2:0 4px 18px rgba(0,0,0,.5);
    --sh-3:0 12px 40px rgba(0,0,0,.6);
    --sh-brand:0 12px 40px -10px rgba(139,123,255,.6);
    --focus:#A79BFF;
    --glass:rgba(10,14,38,.78);
    --aura:.34;
    --au1:#3560CC; --au2:#6A3BD1; --au3:#1B8F9E;
    /* Pale, where --au1 is deep. Measured 4.95:1 at the darkest stop of
       the band behind it, against 1.12:1 before. */
    --band-em:#BFE9FF;
    --h1:#123A72; --h2:#2C2585; --h3:#136273;
    --b1:#A81A5E; --b2:#4A4ADB; --b3:#0E6480;
  }
}
:root[data-theme="dark"]{
  --bg:#05071A; --surface:#0E1230; --surface-2:#141936; --surface-3:#1B2145;
  --line:rgba(140,155,255,.13); --line-strong:rgba(150,165,255,.24);
  --ink:#EFF1FF; --ink-2:#AFB6E4; --ink-3:#7A82B8; --ink-4:#565D8C;
  --ink-quiet:#7A82B8;
  --brand:#8B7BFF; --brand-soft:rgba(139,123,255,.15); --brand-line:rgba(139,123,255,.34);
  --ok:#34E5B0; --ok-soft:rgba(52,229,176,.13); --ok-line:rgba(52,229,176,.32);
  --no:#FF5C93; --no-soft:rgba(255,92,147,.13); --no-line:rgba(255,92,147,.32);
  --ok-ink:#34E5B0; --no-ink:#FF5C93;
  --warn:#C084FC; --warn-soft:rgba(192,132,252,.13); --warn-line:rgba(192,132,252,.32);
  --sh-1:0 1px 2px rgba(0,0,0,.5);
  --sh-2:0 4px 18px rgba(0,0,0,.5);
  --sh-3:0 12px 40px rgba(0,0,0,.6);
  --sh-brand:0 12px 40px -10px rgba(139,123,255,.6);
  --focus:#A79BFF;
  --glass:rgba(10,14,38,.78);
  --aura:.34;
  --au1:#3560CC; --au2:#6A3BD1; --au3:#1B8F9E;
  /* Pale, where --au1 is deep. Measured 4.95:1 at the darkest stop of
     the band behind it, against 1.12:1 before. */
  --band-em:#BFE9FF;
  --h1:#123A72; --h2:#2C2585; --h3:#136273;
  /* the band trio again, deeper, so a band still reads as a band against a
     #05071A page -- and level in the same way, white on about 7.5:1 at all
     three rather than swinging. Same saturations as the light set. */
  --b1:#A81A5E; --b2:#4A4ADB; --b3:#0E6480;
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *{animation-duration:.01ms !important;transition-duration:.01ms !important}
}
body{margin:0;background:var(--bg);color:var(--ink);font-family:var(--f-ui);
  font-size:16.5px;line-height:1.62;font-weight:450;-webkit-font-smoothing:antialiased;
  overflow-x:hidden;position:relative}
h1,h2,h3,h4{font-family:var(--f-dis);font-weight:800;line-height:1.08;
  letter-spacing:-.032em;margin:0;text-wrap:balance;color:var(--ink)}
p{margin:0}
a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer;
  -webkit-tap-highlight-color:transparent}
img,svg{display:block;max-width:100%}
:focus-visible{outline:2.5px solid var(--focus);outline-offset:3px;border-radius:8px}

.skip{position:absolute;left:-9999px;top:0;background:var(--surface);color:var(--ink);
  padding:12px 18px;border-radius:0 0 12px 0;z-index:200;font-weight:700}
.skip:focus{left:0}

.wrap{max-width:var(--maxw);margin:0 auto;padding:0 clamp(16px,4vw,32px)}

/* the aurora, dimmed — the same signature the app carries, calmer on a page
   that is mostly text */
body::before{content:"";position:fixed;inset:0;z-index:-2;background:var(--bg)}
body::after{content:"";position:fixed;inset:-20vh -10vw;z-index:-1;pointer-events:none;
  opacity:var(--aura);
  background:
    radial-gradient(42vw 42vw at 12% 8%, var(--au1), transparent 62%),
    radial-gradient(38vw 38vw at 88% 22%, var(--au2), transparent 60%),
    radial-gradient(36vw 36vw at 50% 96%, var(--au3), transparent 62%);
  filter:blur(60px)}

/* ------------------------------------------------------------------ topbar */
.topbar{position:sticky;top:0;z-index:80;background:var(--glass);
  backdrop-filter:blur(20px) saturate(160%);-webkit-backdrop-filter:blur(20px) saturate(160%);
  border-bottom:1px solid var(--line)}
.bar{display:flex;align-items:center;justify-content:space-between;gap:16px;height:70px}
.brand{display:flex;align-items:center;gap:10px;min-width:0}
.mark{flex:0 0 auto;border-radius:10px;overflow:hidden;box-shadow:var(--sh-brand);line-height:0}
.wordmark{display:flex;flex-direction:column;line-height:1.03;min-width:0}
.wordmark b{font-family:var(--f-dis);font-weight:600;font-size:12px;color:var(--ink-2);
  white-space:nowrap}
.wordmark i{font-family:var(--f-dis);font-weight:800;font-size:17.5px;font-style:normal;
  letter-spacing:-.03em;background:var(--grad);-webkit-background-clip:text;background-clip:text;
  color:transparent;white-space:nowrap;padding-right:.1em;margin-right:-.1em}
.wordmark.small i{font-size:16px}
.barnav{display:flex;align-items:center;gap:8px}
/* Same size and shape as the app's, so somebody moving between the two is
   pressing the same button. */
.iconbtn{width:38px;height:38px;flex:0 0 auto;border-radius:12px;display:grid;
  place-items:center;background:var(--surface);border:1px solid var(--line);
  color:var(--ink-2);transition:color .2s,border-color .2s,transform .2s var(--spring),
  box-shadow .2s}
.iconbtn:hover{color:var(--brand);border-color:var(--brand-line);transform:translateY(-2px);
  box-shadow:var(--sh-1)}
.iconbtn:active{transform:translateY(0) scale(.94)}
/* Light shows the moon — press it to GO dark. Dark shows the sun. The icon is
   what you are switching to, not what you are in; the other way round reads as
   a status light and people press it expecting nothing to happen. */
.i-sun{display:none}
:root[data-theme="dark"] .i-sun{display:block}
:root[data-theme="dark"] .i-moon{display:none}
/* the six site links. One rule for both shapes: a row inside the bar on a wide
   screen, a panel under it on a narrow one. */
.navlinks{display:flex;align-items:center;gap:2px}
.navlinks a{font-weight:650;font-size:14.5px;color:var(--ink-2);padding:8px 11px;
  border-radius:10px;transition:color .2s,background .2s;white-space:nowrap}
.navlinks a:hover{color:var(--brand);background:var(--brand-soft)}
.navlinks a[aria-current="page"]{color:var(--brand);background:var(--brand-soft)}
.navbtn{display:none}
/* the crossing's phone copy, which lives in the panel. Hidden until 560. */
.crossgo{display:none}

/* THE CROSSING, ONE SIZE, EVERY WIDTH -- AND THE PHONE'S IS THE ONE THAT WON.
   It is .btn-ghost for its colours and its hover, and it was also .btn for its
   SIZE: 46px tall with a 15px radius, which made it the largest thing in the bar
   and taller than the theme toggle beside it. On a phone it had been replaced
   outright by a smaller chip, so the same control was 46px in one layout and 38
   in the other and the owner had to point at each difference in turn.
   THE CHIP WAS THE FINISHED ONE, so it is the chip's measurements that stay and
   the desktop that comes to meet them: 38px and a 12px radius, which is what
   .iconbtn is, so the crossing and the toggle now agree with each other AND with
   themselves at every width. No media query touches how it looks -- the ones
   below buy horizontal room and change nothing else.
   The type is --brand rather than --ink: this is a doorway into the other half
   of the site, and the colour is what says so without a second line of text. */
.bar .crossbtn{height:38px;border-radius:12px;padding:0 14px;
  font-size:13.5px;color:var(--brand)}
.bar .crossbtn:hover{color:var(--brand);border-color:var(--brand)}




/* 1180px is where SEVEN links, the toggle and the button stop fitting beside
   the wordmark — it was 1100 for six, and Home was added on 24 August. Below
   it they fold into a panel. */
@media (max-width:1180px){
  .navbtn{display:grid}
  .navlinks{position:absolute;top:100%;right:clamp(16px,4vw,32px);left:clamp(16px,4vw,32px);
    flex-direction:column;align-items:stretch;gap:2px;padding:10px;
    background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);
    box-shadow:var(--sh-2);z-index:90}
  .navlinks a{padding:13px 14px;font-size:16px}
  /* page.js sets hidden on load. Without it the panel simply stays open, which
     is a tall header rather than an unreachable menu. */
  .navlinks[hidden]{display:none}
}
@media (min-width:1181px){
  /* the attribute is left on the element when the window is widened again */
  .navlinks[hidden]{display:flex}
}
.bar{position:relative}

@media (max-width:560px){
  .bar{height:62px}
  .wordmark b{font-size:10.5px}
  .wordmark i{font-size:15.5px}
  /* THE CROSSING LEAVES THE BAR AND JOINS THE PANEL. Measured: with it in the
     bar the wordmark had 41px of the 71 it needs at 375, and none at all at
     320 -- the brand was the only thing left that could shrink. It is the
     first item of the menu, above the links and ruled off from them. */
  /* `.bar` in front on purpose: .crossbtn is also .btn, and the .btn rules sit
     LATER in this file, so a bare `.crossbtn{display:none}` ties on specificity
     and loses. Measured -- it did lose, and both copies showed at once. */
  /* THE SAME BUTTON, AT EVERY WIDTH. It used to be hidden here and replaced by
     a second element -- a chip -- which then had to be talked into matching it
     and never quite did: same family and weight in the end, but 11.5px against
     14.8, purple against ink, 12px radius against 15, 38px tall against 46.
     Two elements for one control is two things to keep in step, and they drifted
     on every property nobody had thought to check.
     So there is one element now and it needs no keeping in step. What the phone
     changes is the room around the letters, which is the only thing the bar is
     actually short of. */
  /* THE MENU COPY GOES with it: the button beside Sign in is on screen at every
     width now, and two controls for one crossing is one too many. */
  .navlinks .crossgo{display:none}
}
@media (max-width:379px){
  /* THE WORDMARK'S TEXT PAYS FOR THE BUTTON. 71px of academy name against a
     crossing that must read the same here as it does on a desktop, and the logo
     mark beside it is not going anywhere -- the brand is still marked at every
     width, only its spelling goes and only on the narrowest phones. */
  .bar .wordmark{display:none}
  /* and it needs no narrower padding: 113px fits the 114 the hidden wordmark
     leaves at 320, measured. */
}
@media (max-width:465px){
  /* the wordmark, the app and the menu, and nothing else fits.
     465 AND NOT 420, corrected 12 September 2026 from two photographs of the
     same page. The owner's Galaxy S23 FE is 411 CSS px and read
     "Minati Ma'am's Academy"; an iPhone 16 Pro Max is 440 and read
     "Minati Ma'am / Acader". 420 was low enough to catch the Android phones
     and miss every large iPhone -- and large iPhones are most of the band:
     430 (15 Pro Max, 16 Pro), 428 (14 Plus), 440 (16 Pro Max).
     Measured against the live site, not guessed, by walking the viewport and
     comparing the wordmark's laid-out width to the box it was given:
         411  no button   needs 71  has 71   whole
         422  button      needs 71  has 31   cut to "Ma'am"
         440  button      needs 71  has 48   cut to "Acader"
         466  button      needs 71  has 71   whole
     466 is the first width where both fit, so the button goes at 465 and
     below. Re-measured with the rule in place: 421, 430 and 440 all whole.
     SHRINKING THE BUTTONS DOES NOT BUY IT. The rule below was tried across
     this band first, since it was already in this block; at 440 the wordmark
     was still 3px short. There is no arrangement where both fit, which is why
     this stays a choice rather than a layout tweak, and the choice is already
     made -- the button is what goes.
     WHAT IT COSTS: a visitor on a phone this wide cannot force the site light
     or dark by hand. Their phone's own setting still works -- page.css has a
     prefers-color-scheme block -- so the site still follows them. That was
     already true of every phone under 421px. */
  .barnav #themeBtn{display:none}
}
@media (max-width:420px){
  /* :not(.crossbtn) -- the crossing is 38px at every width by the rule near
     the top of this file, which is the whole point of it having one. Left in
     this selector it came out 40px on a phone and 38 on a desktop, which is
     precisely the inconsistency that rule exists to prevent. */
  .barnav .btn:not(.crossbtn){height:40px;padding:0 14px;font-size:13.5px}
}

/* ----------------------------------------------------------------- buttons */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:9px;
  height:46px;padding:0 22px;border-radius:15px;font-family:var(--f-dis);
  font-weight:750;font-size:14.8px;letter-spacing:-.008em;white-space:nowrap;
  transition:transform .3s var(--spring),box-shadow .3s var(--ease),background .2s,color .2s,border-color .2s}
.btn:active{transform:scale(.97)}
.btn-solid{background:var(--grad-btn);color:#fff;box-shadow:var(--sh-brand)}
.btn-solid:hover{transform:translateY(-2px)}
.btn-ghost{background:var(--surface);color:var(--ink);border:1px solid var(--line-strong);
  box-shadow:var(--sh-1)}
.btn-ghost:hover{border-color:var(--brand-line);color:var(--brand);transform:translateY(-2px)}
.btn-lg{height:54px;padding:0 28px;font-size:16px;border-radius:17px}
.btn-sm{height:38px;padding:0 15px;font-size:13.5px;border-radius:12px}
@media (max-width:560px){ .btn-lg{height:50px;font-size:15px;width:100%} }

/* ------------------------------------------------------------------ pieces */
.eyebrow{font-family:var(--f-dis);font-size:11.5px;font-weight:750;letter-spacing:.16em;
  text-transform:uppercase;color:var(--brand)}
.lede{color:var(--ink-2);font-size:clamp(16px,2.1vw,18.5px);line-height:1.6;max-width:56ch}
.card{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-xl);
  padding:clamp(18px,3vw,26px);box-shadow:var(--sh-1)}
.tag{display:inline-flex;align-items:center;gap:6px;height:27px;padding:0 11px;
  border-radius:99px;font-family:var(--f-dis);font-size:11.5px;font-weight:750;
  background:var(--surface-3);color:var(--ink-2);white-space:nowrap}
.tag.brand{background:var(--brand-soft);color:var(--brand)}
.tag.ok{background:var(--ok-soft);color:var(--ok)}
.tag.warn{background:var(--warn-soft);color:var(--warn)}

section{padding:clamp(52px,8vw,96px) 0}

/* ----------------------------------------------------------- brand band --
   THE PAGE ALTERNATES: light, coloured, light, coloured, top to bottom.

   var(--grad) is the M. mark's own background -- .port-seal fills the round
   plate with it, .wordmark i clips the letters out of it. A band painted in
   it is the logo at full width.

   THIS RULE REDEFINES NO TOKENS, AND THAT IS THE WHOLE POINT.

   The first version made --surface a pane darkened INTO the gradient. It
   measured well -- 7.44:1 for white text -- and it was wrong for a reason a
   contrast ratio cannot see: the card came out the same family as the band
   behind it. The owner: "I told you don't make foreground background same",
   "all cards inside all colored backgrounds should be light".

   So the band paints itself and says nothing else. Every card on it --
   .card, .hm-say, .vs-p, .ratepanel, .eq, .hm-plan -- is the same white card
   it is on a light page, because :root still says --surface is #FFFFFF and
   --ink is #0F1130 and nothing here argues with that. Which also means every
   drawn thing inside a card keeps the treatment it was designed with: the
   violet room board, the grey chevron, the gradient avatar, the white tick on
   green. None of them need a dark-band variant any more, and the ones written
   for the old model are gone rather than adjusted.

   WHAT IS LEFT TO DO BY HAND is only the text that sits directly ON the
   colour. That set was read off the rendered page rather than guessed, and it
   is small: a section head, the prose column of a two-column section, the
   points and steps lists, and the note under the rates.

   THE GRADIENT IS RAW -- no scrim, on purpose: "use this same background
   across all color backgrounds", meaning the one the solid button and the
   credential card have.

   IT USED TO BE DEEPENED 30%, and that is worth knowing before anyone changes
   it back or blames it. White on the pink stop #FF3D9A is 3.29:1, and a band
   is 1140px across where the button is about 180 -- so a band shows the pink
   end at full size where the button barely reaches it. A flat 30% of --ink
   kept every hue relationship and took white to 5.65:1.

   Putting it back is one line in each of the THREE rules that paint a band --
   .band-brand and .hm-band.band-brand just below, and .hm-close.band-brand
   further down. Missing one is visible: that is how the last one was caught.
       background:linear-gradient(rgba(15,17,48,.30) 0 100%),var(--grad);
   and SS11bp has the measured cost of not having it, band by band. */
.band-brand{
  position:relative;overflow:hidden;
  background:var(--grad-hero);
}
/* A section that is also .hm-band would otherwise paint itself --surface,
   which is white -- the band would come out white. */
.hm-band.band-brand{background:var(--grad-hero);border-top:0}
/* The soft light from the top-left that .fin::before draws, so a band has a
   light source and does not read as a flat slab. Behind everything. */
.band-brand::before{content:"";position:absolute;inset:-30% -8% auto -8%;
  height:150%;pointer-events:none;z-index:0;
  background:radial-gradient(58% 58% at 20% 0%, rgba(255,255,255,.15), transparent 62%)}
.band-brand > *{position:relative;z-index:1}

/* --- the text that stands on the colour, and nothing that stands on a card --
   Every selector below is anchored either to .sechead or to a direct child of
   .wrap, so it cannot reach inside a card. That is deliberate: a rule written
   as `.band-brand h2` would turn a heading white inside a white card. */
/* h1 AS WELL AS h2. /ebooks opens with a .sechead whose heading is an h1, so
   when that section became coloured its headline stayed --ink -- navy on
   violet. It measured about 3.1, over the 3.0 bar for large text and wrong all
   the same, which is why it was found by looking rather than by the numbers. */
.band-brand .sechead h1,
.band-brand .sechead h2,
.band-brand > .wrap > .hm-two > div > h2{color:#fff}
.band-brand .sechead .eyebrow,
.band-brand > .wrap > .hm-two > div > .eyebrow{color:#fff}
.band-brand .sechead .lede,
.band-brand > .wrap > .hm-two > div > .lede,
.band-brand > .wrap > .hm-note{color:#fff}
.band-brand > .wrap > .hm-two > div > .hm-points li,
.band-brand > .wrap > .hm-steps li{color:#fff}
.band-brand > .wrap > .hm-two > div > .hm-points li b,
.band-brand > .wrap > .hm-steps li b{color:#fff}
/* AND A BOLD WORD INSIDE A LEDE, which was the last thing on the site under the
   bar: it inherits the lede's .92 and lands at 18.5px, a sixth of a pixel short
   of the 18.66 that would let bold text be judged as large. 4.33 against 4.5.
   Solid white is what every other bold-on-colour here already is. */
.band-brand .sechead .lede b,
.band-brand > .wrap > .lede b,
.band-brand > .wrap > .herotext > .lede b,
.band-brand > .wrap > .hm-two > div > .lede b,
.band-brand > .wrap.hm-two > div > .lede b{color:#fff}
/* the little icon plates beside those lines, which were --brand-soft on white */
.band-brand > .wrap > .hm-two > div > .hm-points .hm-pi,
.band-brand > .wrap > .hm-steps .hm-si{
  background:rgba(255,255,255,.16);border-color:rgba(255,255,255,.40);color:#fff}
.band-brand > .wrap > .hm-two > div > .hm-more a{color:#fff}
/* THE AFFILIATE DISCLOSURE ON /ebooks, which stands on the colour rather than
   on a card -- deliberately, per the note in its markup: the first version was
   "four lines in a grey slab and read as a warning notice rather than as a
   courtesy". So it has no background to inherit, and on a band its --ink-3
   measured 1.46:1 and its bold 1.41:1. Invisible, on the one paragraph of that
   page that has to be readable for a reason other than design. Its rule at the
   very edge of the band is --brand-line, white in here already. */
.band-brand > .wrap > .eb-disc{color:#fff}
.band-brand > .wrap > .eb-disc b{color:#fff}
.band-brand > .wrap > .eb-disc .eb-dico{color:#fff}

/* A SOLID BUTTON STANDING ON THE COLOUR INVERTS. One inside a card does not --
   there it is the gradient on white, which is correct and is why this is
   anchored to a direct child of .wrap rather than to the band. */
.band-brand > .wrap > .herocta .btn-solid{background:#fff;color:var(--h2);
  box-shadow:0 14px 34px -10px rgba(0,0,0,.35)}
.band-brand > .wrap > .herocta .btn-solid:hover{background:#fff;transform:translateY(-2px)}
.band-brand > .wrap > .herocta .btn-ghost{background:rgba(255,255,255,.12);color:#fff;
  border-color:rgba(255,255,255,.42)}
.band-brand > .wrap > .herocta .btn-ghost:hover{background:rgba(255,255,255,.2);color:#fff;
  border-color:rgba(255,255,255,.6)}

/* ══════════════ two more shapes of section, now that the home page is inverted ══
   Everything above is anchored to `.band-brand > .wrap > ...` so it cannot reach
   inside a card. That is right, and it is why neither of the two shapes below is
   covered by any of it:

     the hero        puts .herotext between .wrap and its words
     .wrap.hm-two    is ONE element that is both, where every section coloured
                     before was .wrap CONTAINING a .hm-two. A child combinator
                     cannot match an element against itself.

   Measured before this block existed: --brand eyebrows at 1.32, 1.37 and 1.38
   and --ink-2 ledes between 1.89 and 2.05, against a bar of 4.5. The headings
   measured about 3.1 -- just over the large-text bar and navy on violet all the
   same, which is the difference between passing and looking right. */

/* --- the hero, when the hero is the coloured section --------------------- */
.band-brand > .wrap > .herotext > h1{color:#fff}
/* AND THE EMPHASISED WORD IN IT. .hero h1 em is painted with var(--grad-btn)
   through background-clip:text -- right on a white hero, and on a coloured one
   it is the gradient drawn on the gradient. Measured 1.11:1. A
   background-clip:text with no background paints nothing, so the clip comes off
   with the gradient. */
.band-brand > .wrap > .herotext > h1 em,
.band-brand .sechead h1 em,
.band-brand .sechead h2 em,
.band-brand > .wrap.hm-two > div > h2 em{background:none;
  -webkit-background-clip:border-box;background-clip:border-box;color:#fff}
.band-brand > .wrap > .herotext > .eyebrow{color:#fff}
.band-brand > .wrap > .herotext > .lede{color:#fff}
/* WHITE, NOT NEARLY WHITE. This and the two below sat at .90 and .92 alpha,
   which on the levelled band lands at 4.2-4.4:1 -- under the 4.5 small text
   needs, and for the sake of a softness nobody can see at 14px. */
.band-brand > .wrap > .herotext > .hm-note{color:#fff}
.band-brand > .wrap > .herotext > .herocta .btn-solid,
.band-brand > .wrap.hm-two > div > .herocta .btn-solid{background:#fff;color:var(--h2);
  box-shadow:0 14px 34px -10px rgba(0,0,0,.35)}
.band-brand > .wrap > .herotext > .herocta .btn-solid:hover,
.band-brand > .wrap.hm-two > div > .herocta .btn-solid:hover{background:#fff;
  transform:translateY(-2px)}
.band-brand > .wrap > .herotext > .herocta .btn-ghost,
.band-brand > .wrap.hm-two > div > .herocta .btn-ghost{background:rgba(255,255,255,.12);
  color:#fff;border-color:rgba(255,255,255,.42)}
.band-brand > .wrap > .herotext > .herocta .btn-ghost:hover,
.band-brand > .wrap.hm-two > div > .herocta .btn-ghost:hover{background:rgba(255,255,255,.2);
  color:#fff;border-color:rgba(255,255,255,.6)}

/* --- a .wrap that IS the .hm-two ----------------------------------------- */
.band-brand > .wrap.hm-two > div > h2{color:#fff}
.band-brand > .wrap.hm-two > div > .eyebrow{color:#fff}
.band-brand > .wrap.hm-two > div > .lede{color:#fff}
.band-brand > .wrap.hm-two > div > .hm-note{color:#fff}
.band-brand > .wrap.hm-two > div > .hm-more a{color:#fff}
.band-brand > .wrap.hm-two > div > .hm-points li{color:#fff}
.band-brand > .wrap.hm-two > div > .hm-points li b{color:#fff}
.band-brand > .wrap.hm-two > div > .hm-points .hm-pi{background:rgba(255,255,255,.16);
  border-color:rgba(255,255,255,.40);color:#fff}

/* --- the A/B/C picker, which stands on the band rather than in a card ----- */
/* the caption under it is --ink-2 and has nothing to sit on */
.band-brand .bt-cap{color:#fff}
.band-brand .bt-cap .ic{color:#fff}
/* THE CHIPS THEMSELVES STAY WHITE CARDS -- "Light cards on color background" --
   and only the picked one had to change: --brand-soft is violet at NINE PER
   CENT, so the gradient came straight through the one chip that is meant to
   look chosen. #F0EEFF and #DBD6FF are those tokens composited over white:
   what they already look like on a light page, made opaque. Same fault and
   same fix as .vs-p.is-on. */
.band-brand .bt-c.is-on{background:#F0EEFF;border-color:#DBD6FF}
.band-brand .bt-c.is-on::after{background:#F0EEFF;
  border-right-color:#DBD6FF;border-bottom-color:#DBD6FF}

/* --- shapes the homepage did not have, met on /about and /courses ---
   A closing section paints itself --surface, which is white, so one given a
   band came out white -- the same fix .hm-band already needed. And the closes
   put their eyebrow, heading and lede as DIRECT children of .wrap instead of
   inside a .sechead, so nothing above reached them. */
/* THE THIRD ONE, and it was missed when the scrim came off the other two --
   which is exactly how it was found: "in about last section ... does not have
   the same background as other color backgrounds". It was the only band left
   still 30% deeper than the rest. */
.hm-close.band-brand{background:var(--grad-hero);border-top:0}
.band-brand > .wrap > h2{color:#fff}
.band-brand > .wrap > .eyebrow{color:#fff}
.band-brand > .wrap > .lede{color:#fff}

/* /about's biography column is prose, not a card, so it stands on the colour. */
.band-brand .ab-prose p{color:#fff}
.band-brand .ab-prose .ab-open{color:#fff}
.band-brand .ab-prose b{color:#fff}
/* THE ONE THAT NO CONTRAST CHECK WOULD HAVE REPORTED. .ab-open b is
   var(--grad) clipped to the letters with color:transparent -- the band's own
   gradient, on the band. Invisible, and invisible while computing as
   `transparent` rather than as a colour anything could measure. */
.band-brand .ab-prose .ab-open b{background:none;
  -webkit-background-clip:border-box;background-clip:border-box;
  color:#fff;-webkit-text-fill-color:#fff}
.sechead{max-width:60ch;margin-bottom:clamp(24px,4vw,40px)}
.sechead h2{font-size:clamp(27px,4.4vw,40px)}
.sechead p{margin-top:12px}

/* ------------------------------------------------------------------- hero */
.hero{padding-top:clamp(36px,6vw,72px);padding-bottom:clamp(40px,6vw,80px)}
@media (min-width:940px){ .herogrid{grid-template-columns:minmax(0,1.05fr) minmax(0,1fr)} }
.hero h1{font-size:clamp(28px,6.4vw,52px);line-height:1.04}
.hero h1 em{font-style:normal;background:var(--grad);-webkit-background-clip:text;
  background-clip:text;color:transparent;padding-right:.08em;margin-right:-.08em}
.hero .lede{margin-top:18px}
.herocta{display:flex;gap:12px;flex-wrap:wrap;margin-top:28px}
.trust{display:flex;gap:18px;flex-wrap:wrap;margin-top:26px;color:var(--ink-3);font-size:13.5px}
.trust b{color:var(--ink);font-family:var(--f-dis);font-weight:800;font-size:19px;
  letter-spacing:-.03em;display:block;font-variant-numeric:tabular-nums}

/* ------------------------------------------------------- the live question */
.demo{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-2xl);
  padding:clamp(20px,3vw,28px);box-shadow:var(--sh-3);position:relative}
.demoq{font-family:var(--f-dis);font-weight:750;font-size:clamp(17px,2.4vw,20px);
  letter-spacing:-.02em;line-height:1.3;margin-bottom:16px}
.opts{display:flex;flex-direction:column;gap:9px}
.opt{display:flex;align-items:flex-start;gap:11px;width:100%;text-align:left;
  padding:14px 15px;border-radius:15px;background:var(--surface-2);
  border:1.5px solid var(--line);font-size:15px;line-height:1.45;
  transition:border-color .2s,background .2s,transform .25s var(--spring)}
.opt:hover:not([disabled]){border-color:var(--brand-line);transform:translateY(-1px)}
.opt .k{flex:0 0 auto;width:22px;height:22px;border-radius:7px;display:grid;place-items:center;
  background:var(--surface-3);color:var(--ink-3);font-family:var(--f-dis);font-weight:750;
  font-size:11.5px;margin-top:1px;transition:background .2s,color .2s}
.opt[data-state="right"]{border-color:var(--ok-line);background:var(--ok-soft)}
.opt[data-state="right"] .k{background:var(--ok);color:#fff}
.opt[data-state="wrong"]{border-color:var(--no-line);background:var(--no-soft)}
.opt[data-state="wrong"] .k{background:var(--no);color:#fff}
.opt[disabled]{cursor:default}
.why{margin-top:16px;padding:15px 16px;border-radius:15px;background:var(--brand-soft);
  border:1px solid var(--brand-line);font-size:14.5px;line-height:1.6;color:var(--ink-2)}
.why b{color:var(--ink);font-family:var(--f-dis)}
.why[hidden]{display:none}

/* ══════════════════════════════════ THE FIRST SCREEN ON A PHONE ══════════
   This block used to reorder a two-column hero — .herogrid, .demo, .lede-more
   — that no longer exists anywhere in the markup; /elearn's first screen is
   .tour .t-first now and carries its own rules further down. The dead
   selectors are gone.

   TWO OF THEM WERE NOT MERELY DEAD. `.hero .eyebrow{display:none}` and the
   pair of `.herocta` rules were written unscoped, so the moment the coaching
   pages arrived with heroes of their own they hid every eyebrow on a phone and
   forced three call-to-action buttons onto one 390px line — "Call 9117001777"
   squeezed into 110px. That is the argument for scoping a rule to the thing it
   was written for even when only one thing exists at the time. */
@media (max-width:939px){
  .trust{margin-top:14px;gap:14px}

  /* the question itself is tightened, because a question with its last two
     answers off the screen is not a question anybody will answer */
  .demoq{margin-bottom:11px}
  .opts{gap:7px}
  .opt{padding:10px 12px;font-size:14.4px;border-radius:13px}
  .why{margin-top:12px;padding:12px 13px;font-size:13.8px}
}


/* ══════════════════════════════════════════════════════ THE TOUR ══════════
   /elearn is the app, running, scrolled. Every beat is a real screen of the
   product with Pipo beside it saying one line - which is what he does inside
   it, and the reason he belongs here rather than being a mascot on a brochure.

   Two columns on a wide screen, alternating sides so the eye is not walking
   down a straight line. One column on a phone, and the app ALWAYS comes before
   the words there: a first screen that opens with a paragraph is a first screen
   somebody scrolls past. */
.tour{padding:clamp(34px,6vw,72px) 0}
/* ROOM ABOVE "MINATI MA'AM'S ACADEMY". This was clamp(18px,3vw,44px), and 3vw
   of a 390px phone is 11.7 -- so it sat at the 18px floor, which put the eyebrow
   almost against the header bar. The hero is a coloured band now, and a band
   that begins 18px above its own first word reads as cropped rather than as a
   band. The floor is what mattered; the ceiling barely moved. */
.t-first{padding-top:clamp(34px,3.4vw,48px)}
.tourgrid{display:grid;gap:clamp(20px,3.4vw,48px);align-items:center}
@media (min-width:980px){
  .tourgrid{grid-template-columns:minmax(0,1fr) minmax(0,1.02fr)}
  .tourgrid.alt .tourwords{order:2}
}

/* ON A PHONE THE BUTTONS COME AFTER THE LESSON, not before it.
   The first screen shows a real lesson running -- the mistake, the correction,
   Pipo -- and asking somebody to choose a course above it is asking before
   showing. Below 980px the grid is one column, so the demo lands under the
   headline and the two buttons follow it, which is the order the page argues in.

   HOW, because the buttons are not a sibling of the lesson: .herocta sits inside
   .tourwords and the lesson panel is that div's sibling, so no amount of `order`
   on either can interleave them. display:contents dissolves .tourwords for
   layout only -- its five children become grid items in their own right and can
   then be ordered against the panel. It carries no box styling of its own, so
   nothing is lost by dissolving it; checked before relying on it.

   row-gap:0 because the grid's gap is up to 48px and was written for the space
   BETWEEN THE TWO COLUMNS. Applied between five promoted children it would blow
   the headline apart. Each of them already carries its own margin-top, which is
   the spacing the page was designed with; the panel gets the column gap back
   explicitly. */
@media (max-width:979px){
  .t-first .tourgrid{row-gap:0}
  .t-first .tourwords{display:contents}
  .t-first .tourwords > .eyebrow{order:1}
  .t-first .tourwords > h1{order:2}
  .t-first .tourwords > .lede{order:3}
  .t-first .tourwords > .trust{order:4}
  .t-first .appwin{order:5;margin-top:clamp(20px,3.4vw,32px)}
  .t-first .tourwords > .herocta{order:6;margin-top:20px}
}

/* THE PINNED BAR IS .ctabar, A CHILD OF BODY, and it has to be.
   These rules were on .t-first .herocta first, and the bar scrolled behind
   every coloured section below it: .band-brand carries overflow:hidden, and
   overflow:hidden on an ancestor CLIPS a position:fixed descendant -- z-index
   cannot argue with that, because the element never gets out of the box.
   page_foot() emits .ctabar outside <main> instead, exactly where .callbar has
   always been, and these are .callbar's own measurements.

   The buttons still in the page keep their order:6 above: they are what a
   desktop shows, what a blocked page.js falls back to, and what a phone reads
   after the lesson if it scrolls that far. */
.ctabar{display:none}
@media (max-width:979px){
  .ctabar{display:flex;gap:9px;position:fixed;left:0;right:0;bottom:0;z-index:70;
    padding:10px clamp(12px,3.4vw,16px);
    /* the safe area, so the bar clears the home indicator on an iPhone */
    padding-bottom:calc(10px + env(safe-area-inset-bottom,0px));
    background:var(--glass);backdrop-filter:blur(18px) saturate(160%);
    -webkit-backdrop-filter:blur(18px) saturate(160%);
    border-top:1px solid var(--line)}
  .ctabar .btn{flex:1 1 0;min-width:0;height:46px;padding:0 12px;font-size:14.4px}
  /* the page gives back the room it takes, so the footer's last line is not
     sitting underneath it */
  body.has-ctabar{padding-bottom:74px}
  .ctabar.is-auto{transition:transform .28s var(--ease),opacity .2s var(--ease)}
  .ctabar.is-auto.is-off{transform:translateY(105%);opacity:0;pointer-events:none}
}

.tourwords h1{font-size:clamp(28px,6vw,52px);line-height:1.04;letter-spacing:-.03em}
/* ONE SENTENCE PER LINE, AT EVERY WIDTH. Two lines, and they are the two the
   headline is written as: "You're not bad at English." then "You're out of
   practice."
 *
 * At 6vw/52px the first sentence wrapped by itself as "You're not bad / at
 * English." -- a line ending on "at", which is the break that sends a reader
 * back to the start of it. Splitting each half deliberately was tried and
 * rejected; this is the version that keeps each sentence whole.
 *
 * Two changes, both small: the words take a slightly larger share of the row so
 * there is room for the longer sentence, and the type scales with the viewport
 * sooner so a 1024px screen is not still asking for 52px. Verified at eleven
 * widths from 1440 to 320. */
/* THE GAP UNDER "MINATI MA'AM'S ACADEMY", and it belongs here rather than in
   the phone block where I first wrote it. A desktop had the eyebrow sitting
   straight on top of the headline while a phone had 11px under it -- the same
   hero, two different spacings, which is exactly the kind of thing that reads
   as unfinished. The phone block still tightens it slightly; this is the rule
   both of them start from. */
.t-first .tourwords .eyebrow{margin-bottom:13px}
.t-first .tourgrid{grid-template-columns:minmax(0,1.1fr) minmax(0,.9fr)}
.t-first .tourwords h1{font-size:clamp(27px,4vw,48px)}
/* text-wrap:balance evens the two halves where a browser supports it, and does
   nothing at all where it does not -- the sizing above is what actually fixes
   the break; this only tidies it. */
.t-first .tourwords h1{text-wrap:balance}
@media (max-width:359px){
  /* the last width, and the same reason as all the others: at the clamp's 25px
     floor "You're not bad at English." needs 285px, and a 320px screen gives
     the text 273. It broke on "at", which is the worst word in the sentence to
     end a line on. 23px fits it.
     .tour.t-first, NOT .t-first: the phone block further down sets this same
     property on .t-first .tourwords h1, and being later in the file it was
     winning the tie and putting 25px back. Both classes are on the one element,
     so this is the same element at one more point of specificity. */
  .tour.t-first .tourwords h1{font-size:23px}
}
.tourwords h1 em{font-style:normal;background:var(--grad);-webkit-background-clip:text;
  background-clip:text;color:transparent}
.tourwords h2{font-size:clamp(24px,3.9vw,38px);line-height:1.08;letter-spacing:-.025em}
.tourwords .lede{margin-top:14px}
.tourwords .herocta{display:flex;gap:12px;flex-wrap:wrap;margin-top:24px}
/* ink-2, NOT ink-3. On /elearn the label under each number is a bare text node
   -- "lessons written", not <span>lessons written</span> -- so .trust span never
   reaches it and it inherits from here. At --ink-3 on the white tile that is
   3.80:1 at 13.5px, under the 4.5 small text needs. --ink-2 takes it to about 7.
   This was true before the tiles ever stood on a band; it was simply never
   measured, because the sweep only ever looked at text sitting on the colour. */
.tourwords .trust{display:flex;gap:18px;flex-wrap:wrap;margin-top:22px;
  color:var(--ink-2);font-size:13.5px}

/* ═════════════════════════════════════ the tour, standing on a band ══════
   FOUR OF /elearn's EIGHT SECTIONS ARE COLOURED NOW. The page was eight white
   sections in a row -- the one page an online student actually lands on, and
   the only public page here with no rhythm at all. Every other one alternates.

   The app window needs nothing: it is a white card, which is exactly what a
   band wants standing on it. What needs a white counterpart is everything the
   tour draws in ink beside it.

   .tourwords is a level deeper than .wrap, so none of the `.band-brand > .wrap >`
   rules further up reach it. These are its own. */
.tour.band-brand .tourwords .eyebrow{color:#fff;opacity:.85}
.tour.band-brand .tourwords h1,
.tour.band-brand .tourwords h2{color:#fff}
/* THE GRADIENT WORD GOES PALE RATHER THAN STAYING A GRADIENT. h1 em paints
   itself with --grad through background-clip:text; the button gradient laid over
   the band gradient is one saturated thing on another and the word disappears
   into it. --au1 is the aurora's pale blue, the light register of the same
   palette, so the emphasis still reads as a colour change and not as damage. */
.tour.band-brand .tourwords h1 em{background:none;color:var(--band-em);
  -webkit-text-fill-color:currentColor}
.tour.band-brand .tourwords .lede{color:#fff}
/* THE TRUST TILES ARE NOT INVERTED, and getting that wrong made them vanish.
   .trust > div is a WHITE CARD -- so on a coloured band the tiles are already
   light-cards-on-colour, which is the rule, and their text has to stay dark.
   Turning it white put white numerals on a white card: measured at 1:1, which
   is not low contrast, it is nothing at all. The owner found it before I did.
   So there is no rule here. The defaults -- ink for the number, ink-3 for the
   label -- are correct on the card, band or no band. */
.tour.band-brand .tourwords .herocta .btn-solid{background:#fff;color:var(--h2);
  box-shadow:0 14px 34px -10px rgba(0,0,0,.35)}
.tour.band-brand .tourwords .herocta .btn-solid:hover{background:#fff;
  transform:translateY(-2px)}
.tour.band-brand .tourwords .herocta .btn-ghost{background:rgba(255,255,255,.12);
  color:#fff;border-color:rgba(255,255,255,.42)}
.tour.band-brand .tourwords .herocta .btn-ghost:hover{background:rgba(255,255,255,.2);
  color:#fff;transform:translateY(-2px)}

/* .ctabar sits outside .band-brand, so its buttons are already .btn-solid and
   .btn-ghost as the rest of the site draws them. The override that used to be
   needed here is gone with the rules that made it necessary. */


/* ------------------------------------------------------------- the window */
/* The app's own frame. It is the product on the page rather than a picture of
   the product, which is the whole argument this page makes. */
.appwin{position:relative;background:var(--surface);border:1px solid var(--line-strong);
  border-radius:var(--r-2xl);box-shadow:var(--sh-3);overflow:hidden}
.aw-bar{display:flex;align-items:center;gap:10px;padding:11px 14px;
  background:var(--surface-2);border-bottom:1px solid var(--line);font-size:12.4px}
.aw-brand{display:flex;align-items:center;gap:7px;font-family:var(--f-dis);
  font-weight:800;color:var(--ink);letter-spacing:-.01em;white-space:nowrap}
.aw-brand i{width:17px;height:17px;border-radius:6px;background:var(--grad);flex:0 0 auto}
.aw-where{color:var(--ink-3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  flex:1 1 auto;text-align:center}
.aw-chips{display:flex;gap:6px;flex:0 0 auto}
.aw-chips b{font-family:var(--f-dis);font-weight:750;font-size:11.4px;padding:3px 8px;
  border-radius:999px;background:var(--brand-soft);color:var(--brand)}
.aw-body{padding:clamp(16px,2.4vw,24px);padding-bottom:74px}
.aw-k{font-family:var(--f-dis);font-weight:750;font-size:11px;letter-spacing:.15em;
  text-transform:uppercase;color:var(--ink-4)}

/* the lesson */
.aw-bad,.aw-ok{font-family:var(--f-dis);font-weight:750;letter-spacing:-.02em;
  line-height:1.32;font-size:clamp(18px,2.3vw,23px)}
.aw-bad{color:var(--ink-3);margin-top:10px}
.aw-bad span{position:relative;display:inline-block}
.aw-bad span::after{content:'';position:absolute;left:-.05em;right:-.05em;top:56%;height:2px;
  border-radius:2px;background:var(--no);transform:scaleX(0);transform-origin:left center}
.jsr .aw-bad span::after{transform:scaleX(0)}
.jsr .reveal.in .aw-bad span::after{animation:awStrike .42s .28s cubic-bezier(.4,0,.2,1) forwards}
.aw-ok{color:var(--ok);display:flex;align-items:baseline;gap:9px;margin-top:9px}
.jsr .aw-ok{opacity:0}
.jsr .reveal.in .aw-ok{animation:awUp .42s .72s var(--spring) forwards}
.aw-ok i{flex:0 0 auto;font-style:normal;width:20px;height:20px;border-radius:7px;
  display:inline-grid;place-items:center;background:var(--ok);color:#fff;font-size:11.5px;
  transform:translateY(2px)}
.aw-rule{margin-top:15px;padding:13px 14px;border-radius:14px;background:var(--brand-soft);
  border:1px solid var(--brand-line);font-size:14.2px;line-height:1.6;color:var(--ink-2)}
.jsr .aw-rule{opacity:0}
.aw-rule b{color:var(--ink);font-family:var(--f-dis)}
.jsr .reveal.in .aw-rule{animation:awUp .42s 1s var(--spring) forwards}
@keyframes awStrike{to{transform:scaleX(1)}}
@keyframes awUp{to{opacity:1;transform:none}}

/* the flashcards */
/* A DECK: one card, face up, and a way to the next. Three cards stacked in a
   column was a list of sentences - this waits, turns over, and moves on, which
   is the whole of what a flashcard does. */
.aw-deck,.aw-quiz{display:flex;flex-direction:column;gap:14px}
.qitem[hidden]{display:none}
.qitem .why{margin-top:14px}
.deck{position:relative}
.fcard{perspective:1100px;background:none;border:0;padding:0;text-align:left;
  cursor:pointer;width:100%;display:block}
.fcard[hidden]{display:none}
.fc-in{position:relative;display:block;min-height:132px;
  transition:transform .6s var(--spring);transform-style:preserve-3d}
.fcard.flip .fc-in{transform:rotateY(180deg)}
.fc-f,.fc-b{position:absolute;inset:0;display:flex;flex-direction:column;
  justify-content:center;gap:9px;padding:18px 20px;border-radius:18px;
  backface-visibility:hidden;-webkit-backface-visibility:hidden}
.fc-f{background:var(--surface-2);border:1.5px solid var(--line-strong);box-shadow:var(--sh-1)}
.fc-b{transform:rotateY(180deg);background:var(--ok-soft);border:1.5px solid var(--ok-line)}
.fc-k{font-family:var(--f-dis);font-weight:750;font-size:10.5px;letter-spacing:.15em;
  text-transform:uppercase;color:var(--ink-4)}
.fc-b .fc-k{color:var(--ok);opacity:.8}
.fc-f b,.fc-b b{font-family:var(--f-dis);font-weight:750;letter-spacing:-.02em;
  font-size:clamp(17px,2.2vw,21px);line-height:1.32}
.fc-f b{color:var(--ink-2)}
.fc-b b{color:var(--ok)}
/* the two behind it, so it reads as a deck rather than a single card */
.deck::before,.deck::after{content:'';position:absolute;left:9px;right:9px;bottom:-5px;
  height:16px;border-radius:0 0 18px 18px;background:var(--surface-2);
  border:1.5px solid var(--line);border-top:0;z-index:-1}
.deck::after{left:16px;right:16px;bottom:-10px;opacity:.6}
/* THE END OF THE QUESTIONS. It replaces the deck rather than sitting under it,
   so the panel does not grow a second screen's worth of content at the moment
   somebody finishes. Same white window, so it needs no colour of its own --
   the score is the only loud thing in it. */
.qsum{text-align:center;padding:6px 0 2px}
.qsum-n{margin-top:10px;font-family:var(--f-dis);font-weight:800;
  font-size:clamp(15px,2vw,17px);color:var(--ink-3);letter-spacing:-.01em}
/* same contract as .trust b above: one `color`, and it is transparent */
.qsum-n b{font-size:clamp(38px,6vw,52px);letter-spacing:-.04em;
  background:var(--grad);-webkit-background-clip:text;background-clip:text;
  color:transparent;display:block;line-height:1.02}
.qsum-l{margin-top:12px;color:var(--ink-2);font-size:14.6px;line-height:1.58;
  max-width:38ch;margin-left:auto;margin-right:auto}
.qsum-cta{display:flex;flex-wrap:wrap;justify-content:center;gap:9px;margin-top:18px}
.deckfoot[hidden]{display:none}
.qsum[hidden]{display:none}
.deckfoot{display:flex;align-items:center;justify-content:space-between;gap:12px}
.deckn{font-size:12.8px;color:var(--ink-3)}
.deckn b{font-family:var(--f-dis);font-weight:800;color:var(--ink);font-size:14px}

/* what it remembers */
.aw-stats{display:grid;gap:10px;grid-template-columns:repeat(3,minmax(0,1fr))}
.aw-stat{background:var(--surface-2);border:1px solid var(--line);border-radius:14px;
  padding:12px;text-align:center}
.aw-stat b{display:block;font-family:var(--f-dis);font-weight:800;font-size:clamp(19px,2.4vw,24px);
  color:var(--ink);letter-spacing:-.02em}
.aw-stat span{font-size:11.6px;color:var(--ink-3);line-height:1.3;display:block;margin-top:3px}
.aw-cert{grid-column:1/-1;margin-top:4px;padding:16px;border-radius:15px;
  background:var(--grad-btn);color:#fff;text-align:center;box-shadow:var(--sh-brand)}
.aw-cert .aw-k{color:rgba(255,255,255,.72)}
.aw-certname{font-family:var(--f-dis);font-weight:800;font-size:clamp(20px,2.6vw,26px);
  letter-spacing:-.02em;margin-top:5px}
.aw-certsub{font-size:12.4px;opacity:.86;margin-top:3px}

/* ------------------------------------------------------------- meet pipo */
/* A conversation, not a caption. Everywhere else on this page Pipo is pinned to
   the bottom of a window saying one line about what is above him; here he is
   the thing being shown, so the window is his and the 74px that was reserving
   room for that pinned line comes back as ordinary padding.

   .aw-body.aw-chat is two classes deliberately: it has to beat the mobile rule
   that sets padding-bottom on .aw-body, and doing it on specificity rather than
   source order means neither block can be moved and quietly break the other. */
.aw-body.aw-chat{padding-bottom:clamp(16px,2.4vw,24px)}
.aw-ex + .aw-ex{margin-top:16px;padding-top:16px;border-top:1px solid var(--line)}
/* The student's side: right-aligned and tinted, the arrangement every messaging
   app has already taught everybody to read without a legend. */
.aw-ask{display:flex;justify-content:flex-end}
.aw-ask span{max-width:86%;padding:9px 13px;border-radius:15px 15px 4px 15px;
  background:var(--brand-soft);color:var(--ink);font-size:13.4px;line-height:1.45}
.aw-say{display:flex;align-items:flex-start;gap:10px;margin-top:10px}
/* His face, borrowed from .tpipo-f and sized down - one gradient square in the
   stylesheet rather than two that can drift apart. */
.aw-say .tpipo-f{width:34px;height:34px;border-radius:13px}
.aw-say .tpipo-f svg{width:23px;height:23px}
.aw-say p{background:var(--surface-2);border:1px solid var(--line);
  border-radius:15px 15px 15px 4px;padding:10px 13px;
  font-size:13.2px;line-height:1.55;color:var(--ink-2)}
.aw-say p i{color:var(--ink);font-style:italic}

/* ------------------------------------------------------------------- pipo */
/* The same face and the same rounded square he wears in the app, in the corner
   he occupies there. A copy, not an extraction: his behaviour is a personality
   engine with no business on a marketing page, and his look is one SVG. */
/* row-reverse, so he is on the RIGHT with the bubble opening to his left -
   which is where he stands in the app and the only place he has ever stood.
   Putting him on the left made him a different character wearing the same
   face. */
.tpipo{position:absolute;left:14px;right:14px;bottom:12px;display:flex;
  flex-direction:row-reverse;align-items:flex-end;gap:10px;pointer-events:none}
.tpipo-f{flex:0 0 auto;width:46px;height:46px;border-radius:17px;display:grid;place-items:center;
  position:relative;
  background:linear-gradient(145deg,var(--a1),var(--a2) 55%,var(--a3));box-shadow:var(--sh-brand)}
.tpipo-f svg{width:31px;height:31px;position:relative;z-index:1}

/* HIS EYES WERE SHUT, AND THAT IS WHY HE DID NOT LOOK LIKE HIMSELF.
 *
 * The SVG is a byte-for-byte copy of the app's, and it carries two <rect
 * class="lid"> filled with the same near-white as his face -- one over each eye.
 * In the app, p06.html collapses them: `#pipoBtn .lid{transform:scaleY(0)}`, and
 * they only scale up for the length of a blink.
 *
 * That rule was never copied here. So on every public page both lids drew at
 * full size, directly over the pupils, and Pipo had a blank face. The markup was
 * right, the drawing was right, and one missing line took his eyes off him.
 *
 * NO IDLE BLINK, deliberately, and for the app's own stated reason: "Pipo sits
 * still. No breathing, no idle pulse -- hover, the celebration hop and the blink
 * are all deliberate reactions." There is nothing here for him to react to. */
.tpipo-f .lid{transform-origin:center;transform:scaleY(0)}

/* and the halo the app's button has behind it, which is the other half of why
   he read as flatter here than in the product */
.tpipo-f::before{content:'';position:absolute;inset:-6px;border-radius:21px;
  background:inherit;filter:blur(11px);opacity:.5;z-index:0;pointer-events:none}
/* the tail is on the right now, pointing at him */
.tpipo-b{background:var(--surface);border:1px solid var(--line);border-radius:16px 16px 4px 16px;
  padding:9px 13px;font-size:12.9px;line-height:1.45;color:var(--ink-2);box-shadow:var(--sh-2);
  transform-origin:bottom right}
.jsr .tpipo-b{opacity:0;transform:translateY(6px) scale(.96)}
/* HE SPEAKS ALMOST AT ONCE. This was 1.9s, sitting behind a chain of 0.5,
   1.05 and 1.5 - so on the first screen a visitor watched an empty corner for
   the best part of two seconds before the one voice on the page said anything.
   Half a second is long enough to read as arriving rather than as already
   there. Only the lesson window waits longer, and only because its own
   sentence is still correcting itself while he waits. */
.jsr .reveal.in .tpipo-b{animation:awPop .42s .5s var(--spring) forwards}
.jsr #awLesson.reveal.in .tpipo-b{animation-delay:1.25s}

/* AND THE FAILSAFE HAS TO FINISH THE JOB. .shown is what the script falls back
   to when the observer never fires, and it was revealing the window while
   leaving everything the window is FOR - the correction, the rule, Pipo's line -
   still at opacity 0, because those are revealed by animations keyed on .in.
   A rescue that leaves the page empty is not a rescue. */
.reveal.shown .aw-ok,.reveal.shown .aw-rule,.reveal.shown .tpipo-b{
  opacity:1;transform:none;animation:none}
.reveal.shown .aw-bad span::after{transform:scaleX(1);animation:none}
@keyframes awPop{to{opacity:1;transform:none}}

/* --------------------------------------------------------- arriving ------ */
/* Each window is still until it is on the screen, then it plays once. That is
   the delight on every scroll, and it is also why the animations above are
   written against .reveal.in rather than running on load: a screen that has
   already performed by the time you reach it has performed to nobody. */
/* NOTHING IS HIDDEN UNLESS THE SCRIPT IS RUNNING. The hiding is applied by the
   script's own class on <html>, so a blocked, failed or still-downloading
   page.js leaves every window plainly visible instead of leaving the landing
   page blank - which is what hiding by default and revealing by script would
   do, and it would do it silently. */
.jsr .reveal{opacity:0;transform:translateY(18px);
  transition:opacity .6s var(--ease),transform .7s var(--spring)}
.jsr .reveal.in,.reveal.shown{opacity:1;transform:none}

@media (prefers-reduced-motion:reduce){
  .reveal{transition:none}
  .jsr .reveal.in .aw-bad span::after{animation:none;transform:scaleX(1)}
  .jsr .reveal.in .aw-ok,.jsr .reveal.in .aw-rule,.jsr .reveal.in .tpipo-b{animation:none;opacity:1;transform:none}
  .fc-in{transition:none}
}

@media (max-width:979px){
  /* THE APP FIRST, ALWAYS. A first screen that opens with a paragraph is a
     first screen somebody scrolls past, and this page's whole argument is that
     you have used the product before you have read about it. */
  /* align-items:stretch is not decoration here. .tourgrid sets align-items to
     center for the two-column layout, where it means vertical; the moment this
     becomes a flex COLUMN that same property means horizontal, so the window
     shrank to its own content width while every other window on the page - all
     of them still grid - stayed full width. Same page, two widths. */
  .t-first .tourgrid{display:flex;flex-direction:column;align-items:stretch}
  /* The words, whole, and then the card - which is document order, so nothing
     needs reordering at all. It was tried the other way round twice: the card
     first, then the card wedged between the headline and the lede. The owner
     wants the section read as it is written. */
  .t-first .appwin{margin-top:30px}
  /* and the window itself stops holding its own contents so tightly */
  .aw-body{padding:18px 17px 74px}
  .aw-bad{margin-top:13px}
  /* ROOM TO BREATHE. The five blocks were 10, 16, 14 and 24px apart - close
     enough that the screen read as one dense slab rather than as a headline, a
     sentence, a choice and a demonstration. The rhythm now grows down the
     screen, 18 / 26 / 24 / 30, which is what makes it read as separate things
     said one after another instead of everything said at once. It costs about
     50px and the card still lands whole above the fold. */
  .t-first .tourwords h1{font-size:clamp(25px,7vw,33px);line-height:1.1}
  .t-first .tourwords .lede{font-size:15.3px;margin-top:18px;line-height:1.62}
  .t-first .tourwords .herocta{margin-top:26px;flex-wrap:nowrap;gap:10px}
  .t-first .tourwords .herocta .btn{flex:1 1 0;min-width:0;padding:0 12px;font-size:14.6px}
  /* THREE TILES, ONE ROW, by the owner's instruction. On a phone
     .tourwords .trust was a flex row that wrapped, so the tiles sized
     themselves to their own text: measured at 390, 126, 94 and 154px across two
     rows with a hole on the right of the first. Every other thing in this hero
     -- headline, lede, buttons, app window -- runs the full width, so three
     ragged boxes read as a mistake.
     Equal thirds now. A 343px screen gives each tile 108px, which is tight, so
     the padding and the label come down to fit it rather than the row breaking.
     The tiles stretch to the tallest of the three, so they stay square-edged
     however the labels wrap. */
  /* the text block above the tiles, loosened. A two-line headline at 1.04 is
     set for one long line, not for two short ones stacked, and the eyebrow was
     sitting right on top of it. */
  .t-first .tourwords h1{line-height:1.14}
  .t-first .tourwords .eyebrow{display:block;margin-bottom:11px}
  .t-first .tourwords .lede{margin-top:15px}
  .t-first .tourwords .trust{margin-top:24px;display:grid;
    grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;max-width:none}
  /* @media (max-width:420px) further down sets .trust>div:last-child to span
     the whole row, which is right for the home page's tiles and is exactly what
     put the third one on a line of its own here below 430px. Overridden by
     specificity rather than by moving either rule. */
  .t-first .tourwords .trust > div:last-child{grid-column:auto}
  .t-first .tourwords .trust > div{padding:10px 9px}
  .t-first .tourwords .trust b{font-size:19px}
  .t-first .tourwords .trust > div{font-size:11.6px;line-height:1.3}
  .aw-body{padding-bottom:70px}
  .tpipo-b{font-size:12.4px}
}
@media (max-width:400px){
  .aw-stats{grid-template-columns:repeat(2,minmax(0,1fr))}
}

/* ------------------------------------------------------- the mistake strip */
.mistakes{display:grid;gap:12px;grid-template-columns:minmax(0,1fr)}
@media (min-width:700px){ .mistakes{grid-template-columns:repeat(2,minmax(0,1fr))} }
@media (min-width:1000px){ .mistakes{grid-template-columns:repeat(3,minmax(0,1fr))} }
.mis{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:18px;box-shadow:var(--sh-1);text-align:left;width:100%;
  transition:transform .3s var(--spring),box-shadow .3s,border-color .2s}
.mis:hover{transform:translateY(-3px);box-shadow:var(--sh-2);border-color:var(--brand-line)}
/* One child, and everything laid out inside it. A <button> lays its contents
   out in an anonymous box of the browser's own making, and block children
   inside one do not behave the way they do anywhere else — a max-height reveal
   put directly on a button's child stayed at zero here even with an inline
   style, which is not a fight worth having. One plain <span> and the layout is
   ordinary again. */
.mis .bad{display:flex;align-items:flex-start;gap:9px;color:var(--no);font-size:15.5px;
  line-height:1.45;font-weight:600}
.mis .bad s{text-decoration-color:var(--no-line);text-decoration-thickness:1.5px}
/* Revealed by switching display, not by animating a height. The height version
   is prettier and it is the one that silently did nothing; this cannot fail.
   The fade is an animation on appear, which is safe because it runs on an
   element that is already in flow. */
.mis .good{display:none;align-items:flex-start;gap:9px;color:var(--ok);font-size:15.5px;
  line-height:1.45;font-weight:650;margin-top:10px}
.mis[aria-expanded="true"] .good{display:flex;animation:misIn .32s var(--ease) both}
@keyframes misIn{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}
/* .sig and not .mark: the brand lockup in the header already owns .mark, and a
   19px tick was quietly inheriting its border-radius and brand shadow. */
.mis .sig{width:19px;height:19px;flex:0 0 auto;border-radius:6px;display:grid;
  place-items:center;font-size:12px;font-weight:800;margin-top:1px;line-height:1}
.mis .bad .sig{background:var(--no-soft);color:var(--no)}
.mis .good .sig{background:var(--ok-soft);color:var(--ok)}
.mis .hintrow{display:block;margin-top:12px;font-size:12.5px;color:var(--ink-4);
  font-family:var(--f-dis);font-weight:650}
.mis[aria-expanded="true"] .hintrow{color:var(--brand)}

/* The "what's in" grid -- .feats and .feat -- was here. No page has written
   either class since that section was rewritten. */

/* ----------------------------------------------------------- course cards */
/* the grid moved to the shared-card block below, where it takes the app's
   own breakpoints. .course (the older card) still uses it on /elearn. */
.course{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-xl);
  overflow:hidden;box-shadow:var(--sh-1);display:flex;flex-direction:column;
  transition:transform .35s var(--spring),box-shadow .35s,border-color .25s}
.course:hover{transform:translateY(-4px);box-shadow:var(--sh-3);border-color:var(--brand-line)}
/* background-size:cover was missing here and the picture had just been wired
   in: a 1200x400 file in a 96px panel drew at its natural size and showed
   its top-left corner only. The shop's card had it; this one never needed
   it, because until now there was nothing here but a gradient. */
.course .art{height:96px;position:relative;background:var(--grad);
  background-size:cover;background-position:center}
.course .art.soon{filter:saturate(.3)}

/* IMMEDIATELY AFTER THE RULE IT OVERRIDES, and that is not a stylistic choice.
   Both overrides were written together up here, and .ccard .cart{height:128px}
   is declared eighty lines BELOW -- equal specificity, so the later rule won,
   the shop's hat stayed 128px at every width while /elearn's obeyed, and it
   measured as though the feature had simply not worked. */
@supports (aspect-ratio: 3 / 1){ .course .art{height:auto;aspect-ratio:3 / 1} }
.course .art .chip{position:absolute;top:12px;right:12px;height:26px;padding:0 11px;
  border-radius:99px;display:inline-flex;align-items:center;gap:5px;
  background:rgba(255,255,255,.2);border:1px solid rgba(255,255,255,.32);color:#fff;
  font-family:var(--f-dis);font-size:11px;font-weight:750;letter-spacing:.03em;
  backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}
.course .body{padding:18px;display:flex;flex-direction:column;flex:1 1 auto}
.course .meta{display:flex;align-items:center;justify-content:space-between;gap:10px;
  font-family:var(--f-dis);font-size:11.5px;font-weight:750;letter-spacing:.06em;
  text-transform:uppercase;color:var(--ink-3)}
.course .meta .price{color:var(--brand);letter-spacing:0;font-size:14px;text-transform:none}
.course h3{font-size:18.5px;margin-top:10px;letter-spacing:-.024em}
.course p{margin-top:8px;color:var(--ink-2);font-size:14.4px;line-height:1.55;flex:1 1 auto}
/* One line, three facts, dot-separated, identical on every card - which is what
   makes a column of them readable downwards instead of one at a time. */
.course .spec{display:flex;flex-wrap:wrap;align-items:baseline;gap:0 7px;
  margin-top:11px;font-size:12.9px;color:var(--ink-3);line-height:1.5}
.course .spec span+span::before{content:'·';margin-right:7px;color:var(--ink-4)}
.course .spec .saves{color:var(--ok);font-weight:700}
.course .facts{display:flex;gap:8px;flex-wrap:wrap;margin-top:14px}
.course h3 .tlink{color:inherit;text-decoration:none}
.course h3 .tlink:hover{color:var(--brand)}
/* The picture, as a link. Nothing about it looks different - it is a way in,
   not a control - so it is hidden from the screen reader and skipped by the
   keyboard, both of which get to the same page by the title link just below,
   properly labelled. Two visible affordances, one announced. */
.artlink{display:block;color:inherit}
.course .cta{margin-top:16px;display:flex;gap:9px;flex-wrap:wrap}
/* background-IMAGE, not the background shorthand. The shorthand resets
   background-size to auto, and .course .art above sets it to cover -- so with a
   picture wired into this panel the file drew at its natural 1200x400 inside a
   115px panel and showed its top-left corner. The same fault the course cards
   had, one rule further down and for a different reason. */
.course.bundle .art{background-image:linear-gradient(135deg,#0EA5E9,#5A45FF 60%,#A855F7)}
.savechip{background:var(--ok-soft);color:var(--ok)}

/* ═════════════════════ the course card, shared with the app ══════════════
   Copied from src/parts/p04.html (.ccard) and p03.html (.actrow), so the shop a
   customer meets before buying and the store they meet after are the same card:
   352x401 at three across, a 128px picture, the blurb held to a fixed depth so
   no two cards disagree about height.

   .courses is kept as the grid's name but takes the app's measurements --
   three columns from 1100px, not 1020, because that is where the app breaks and
   the two were landing on different column counts at 1024. */
.courses{display:grid;gap:16px;margin-top:16px;grid-template-columns:minmax(0,1fr)}
@media (min-width:640px){ .courses{grid-template-columns:repeat(2,minmax(0,1fr))} }
@media (min-width:1100px){ .courses{grid-template-columns:repeat(3,minmax(0,1fr));gap:18px} }

.ccard{position:relative;overflow:hidden;border-radius:var(--r-2xl);
  border:1px solid var(--line);background:var(--surface);box-shadow:var(--sh-1);
  display:flex;flex-direction:column;
  transition:transform .45s var(--ease),box-shadow .45s var(--ease),border-color .3s}
.ccard:hover{transform:translateY(-5px);box-shadow:var(--sh-3);border-color:var(--brand-line)}

/* THE PICTURE. 128px, and the element the course images will fill -- a
   background-image here needs nothing else changed. The glow is the app's, a
   light from the top left so a flat gradient is not a flat slab. */
/* THE PICTURE FILLS THE BAND. cover crops rather than squashes, and centre is
   the right anchor because the artwork is composed with its subject in the
   middle 70% for exactly this reason -- the card is 2.1:1 at its narrowest and
   3.9:1 at its widest, so the sides go on a narrow card and the top and bottom
   go on a wide one. */
/* THE HAT KEEPS THE PICTURE'S OWN SHAPE, so nothing is ever cropped out of it.
   It was a fixed 128px tall while the card is anywhere from 273 to 482 wide,
   which asked one 3:1 file to be everything from 2.13:1 to 3.77:1 and took up
   to 14.5% off EACH SIDE. Rendered and looked at: three of the six banners lost
   the first letter of their own title on a narrow phone -- "lunders",
   "unctuation", "ocabulary".
   Fixing the cards rather than the pictures fixes all seven at once, and the
   ones not drawn yet: at 3/1 the whole banner is always visible, whatever the
   card is doing. The hat is 91px on a small phone and 161px on a wide card
   instead of 128 everywhere -- and within any one row every card is the same
   width, so they still agree with each other.
   @supports because a browser without aspect-ratio must keep the fixed height;
   giving it height:auto unconditionally would collapse the hat to nothing. */
.ccard .cart{height:128px;position:relative;overflow:hidden;
  background:var(--grad);background-size:cover;background-position:center;
  background-repeat:no-repeat}
@supports (aspect-ratio: 3 / 1){ .ccard .cart{height:auto;aspect-ratio:3 / 1} }
.ccard .cart .glow{position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(320px circle at 22% 24%,rgba(255,255,255,.3),transparent 62%)}
.ccard.avail .cart{filter:saturate(.86)}
.ccard.avail:hover .cart{filter:saturate(1)}
.ccard.soon .cart{filter:none}

/* the state, worn on the picture rather than placed inside the card */
.ccard .statechip{position:absolute;top:14px;right:14px;display:inline-flex;
  align-items:center;gap:6px;height:29px;padding:0 12px;border-radius:99px;
  background:rgba(10,8,32,.5);color:#fff;border:1px solid rgba(255,255,255,.22);
  backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);
  font-size:11px;font-weight:750;font-family:var(--f-dis);letter-spacing:.06em;
  text-transform:uppercase}
.ccard .statechip .ic{font-size:13px}

.ccard .cbody{padding:18px;display:flex;flex-direction:column;flex:1 1 auto}
/* THE HEAD OF THE CARD IS THE APP'S, ELEMENT FOR ELEMENT. p12.html draws a
   .row.between.g8 of two .eyebrow spans above the title -- level left, price
   right in the brand colour -- and then the title. It is the shape that solves
   the problem the last three attempts kept re-creating: a level and a price set
   as CAPITALS AT 11.5px cannot be confused with a 17.5px sentence-case name, so
   nothing has to be shrunk, greyed, pilled or pushed onto the artwork to keep
   the three apart. And the level costs no height at all -- it shares a row the
   price needs anyway. */
.ccard .crow{display:flex;align-items:center;justify-content:space-between;gap:8px}
/* THE ROW IS SET AS QUIETLY AS THE APP SETS IT. This site's own .eyebrow token
   is 11.5px/750/.16em -- half a point bigger and a weight heavier than the app's
   11px/700/.14em -- and at this size the extra weight is what read as loud. The
   app's numbers, on this card only; the token is untouched everywhere else.
   The colour is the one divergence and it is forced: the app uses --ink-3,
   #7B7FA4, 3.88:1 on white, and this is small text, which needs 4.5.
   --ink-quiet is 4.78 -- within a shade of it to look at, above the line to
   read. On the dark card it IS the app's value, which passes there at 4.98. */
.ccard .crow .eyebrow{font-size:11px;font-weight:700;letter-spacing:.14em;
  color:var(--ink-quiet)}
/* after the rule above, so the brand colour wins on equal specificity --
   the same way p12.html sets it inline on the second span. */
.ccard .crow .cprice{color:var(--brand);font-variant-numeric:tabular-nums}
/* THE OLD PRICE, QUIETER THAN THE NEW ONE AND BESIDE IT. --ink-quiet rather
   than --ink-3, for the same reason the level uses it: this is small capitals
   and #7B7FA4 is 3.88:1 on white. A struck-through price still has to be
   readable -- it is the number the saving is measured against, and a customer
   who cannot read it cannot see the offer.
   The line is drawn in the same quiet ink rather than the brand colour, so the
   eye lands on what the course costs now. */
/* AND WHEREVER ELSE A STRUCK PRICE APPEARS. Added 10 September 2026: the
   eLearn landing page started printing <s class="cwas"> the same day and got
   nothing from this rule, because the selector reached only into the store's
   card. The class existed, which is what was checked; it was not styled,
   which is what mattered -- so the old price came out in the brand colour,
   the same as the new one, and the eye had nothing to land on.
   ONE DECLARATION, TWO PLACES, rather than a copy that can drift. */
.ccard .crow .cwas,
.price .cwas{color:var(--ink-quiet);font-weight:650;margin-right:1px;
  text-decoration-color:var(--ink-quiet);text-decoration-thickness:1.5px}
/* THE NAME, AT THE APP'S WEIGHT. The two files disagree about what a heading
   is before either of them mentions a card: the app's h1-h4 are 700/1.14, this
   site's are 800/1.08. So .ccard h3 could be copied across word for word -- and
   it was -- and still come out a whole weight heavier here than there.
   Set explicitly on this card, both properties, so it no longer depends on
   which stylesheet the rule happens to be living in. The site's own heading
   default is untouched; every other heading on every public page is still 800. */
.ccard h3{font-size:17.5px;font-weight:700;line-height:1.14;letter-spacing:-.026em;
  margin-top:8px}
.ccard h3 .tlink{color:inherit;text-decoration:none}
.ccard h3 .tlink:hover{color:var(--brand)}
/* min-height is the whole reason the blurb can come back. It was taken off
   these cards because a paragraph of a different length on each one meant no
   two were the same height; holding it to a fixed depth fixes that without
   losing the sentence that describes the course. */
/* .cdesc is gone with the description it held. The app's card still has one;
   this one does not, by the owner's instruction. */
/* THE FACTS, IN A BOX, ON ONE ROW, WITH THEIR ICONS -- and all three at once is
   the tight part.
 *
 * The box is what the old card had and the owner asked for back. It was never
 * designed: `.spec` at the bottom of this file belongs to the COURSE PAGE and
 * is unscoped, so it also caught the store card's row and gave it a surface, a
 * border and 14/15 of padding. It happened to look better than the bare line,
 * which is why it is deliberate now.
 *
 * WHAT IT COSTS. The row needed 330px at its worst and was given 295. A border
 * box takes another ~22 in padding, and the icons are 60 of the 330. So every
 * other number has to come down: the type from 12 to 11.4, the gap between
 * facts from 14 to 9, the icon and its gap from 20 to 16.
 *
 * AND THE CARD GETS NARROWER THAN THAT. At two across on a 640px screen the
 * card is 279 and the box has about 221 inside it -- less than three facts with
 * icons can hold at any readable size. Below 1000px the icons come off and the
 * words stay: the information survives, the decoration does not. Measured at
 * sixteen widths; the row is one row at every one of them. */
/* THE THREE FACTS TAKE THE WHOLE WIDTH: first against the left edge, last
   against the right, the middle one centred between them. space-between puts
   the outer two on the edges; the middle one is the piece that has to be told
   to centre, because a flex item is only as wide as its text and text-align has
   nothing to work with. Giving it flex:1 makes its box the space that is left,
   and justify-content:center puts the words in the middle of that box.
   It GROWS rather than shrinks, so this cannot squeeze the longest number.
   :not(:last-child) because a bundle has two facts, not three -- there the
   middle one IS the last one, and it belongs on the right edge with nothing
   stretched. */
/* THE BOX. One border round both compartments, because the sentence and the
   specification are one object: what the course does, and how much of it there
   is. Two bordered boxes stacked was tried and reads as the third band that
   made this area untidy in the first place. */
.ccard .cbox{margin-top:12px;border:1px solid var(--line);border-radius:var(--r-md);
  overflow:hidden;background:var(--surface-2)}
.ccard .cpair{padding:10px 11px;background:var(--surface)}
.ccard .cpair p{margin:0;display:flex;align-items:flex-start;gap:8px;
  font-size:13px;line-height:1.4;font-weight:600}
.ccard .cpair-bad{color:var(--no-ink)}
.ccard .cpair-bad s{text-decoration-color:var(--no-line);text-decoration-thickness:1.5px}
.ccard .cpair-ok{color:var(--ok-ink);font-weight:700;margin-top:6px}
/* THE PAIR ON A COURSE THAT IS NOT ABOUT MISTAKES. No strike, no red, and a
   quotation mark where the cross was: somebody said this, and the lesson has a
   better way to say it. Spoken English and Interview only -- see
   page_line_kind(). */
.ccard .cpair.usual .cpair-bad{color:var(--ink-2)}
.ccard .cpair.usual .cpair-bad .sig{background:var(--surface-3);color:var(--ink-3)}
/* THE BUNDLE'S LINE, where a course keeps its wrong/right pair. Same box, same
   padding, so the two cards agree band for band; one sentence rather than two,
   because a bundle has no mistake to correct -- what it has to answer is
   whether anything was trimmed to make it. */
.ccard .cbline p{margin:0;font-size:clamp(10.6px,3.75cqi,13px);line-height:1.45;
  font-weight:600;color:var(--ink-2)}

/* A BUNDLE'S LAST BUTTON TAKES A ROW OF ITS OWN. A course card runs to two
   rows because Start Free Trial claims one; a bundle cannot be tried, so its
   three buttons sat on a single row and the card came out 58px shorter than
   every card beside it. Giving the last one a full basis matches the shape
   without inventing a button that does nothing. */
/* AND THE SAME HEIGHT, not only the same width. The rule above gave it a full
   row and stopped there, so it came out 309x38 against Start Free Trial's
   309x51 on every card beside it -- the same width, visibly lighter, which the
   owner spotted on the desktop store on 13 September 2026. .btn-free's
   min-height is 48px, and two lines of content take it to 51 -- so copying the
   48 left the pair 3px apart, measured, not guessed. 51 is that rendered height
   and the two are now identical at 309x51.
   IT IS A NUMBER COPIED FROM A NEIGHBOUR, which is the weakness here: change
   .btn-free's padding or its second line and this drifts, silently, with
   nothing to catch it. Measure both together if either moves. */
.ccard.bundle .actrow > .btn:last-child{flex:1 1 100%;min-height:51px}
/* THE PAIR NEVER WRAPS. Each sentence is one line by design: the wrong one and
   the right one read as a matched pair, and a sentence spilling onto a second
   line makes one card taller than the card beside it for no reason a reader can
   see.
   Two halves to it. build.py picks a pair of 38 characters or fewer -- every
   course has one, checked course by course -- and the type here SCALES WITH THE
   CARD rather than stepping at a breakpoint.
   3.75cqi is 13px at the 347px card three-across gives, and cqi is a share of
   the CARD's width, not the window's: at 1024px the window is wide and the card
   is one of three, which is exactly the case a media query gets wrong. Stepping
   at a fixed width was tried first and still wrapped, because the box loses a
   constant 83px to padding and the tick, so the room for words falls away
   faster than the card does.
   Measured: the longest pair needs 92.7% of the room it has at 13px, so there
   is no headroom to spare and no guessing was possible. */
.ccard .cpair p{font-size:clamp(10.6px,3.75cqi,13px)}
.ccard .cpair .sig{width:clamp(14px,4.9cqi,17px);height:clamp(14px,4.9cqi,17px);
  font-size:clamp(9px,3cqi,10.5px)}
.ccard .cpair .sig{flex:0 0 auto;width:17px;height:17px;border-radius:6px;
  display:grid;place-items:center;font-size:10.5px;font-weight:800;line-height:1;margin-top:1px}
.ccard .cpair-bad .sig{background:var(--no-soft);color:var(--no-ink)}
.ccard .cpair-ok .sig{background:var(--ok-soft);color:var(--ok-ink)}
/* .lvlchip is gone with the level it carried -- see the note in the card. */

/* the spec sits under the proof, divided rather than boxed a second time */
.ccard .cbox .cmeta{margin-top:0;border:0;border-radius:0;background:transparent;
  border-top:1px solid var(--line)}

.ccard .cmeta{display:flex;gap:8px;margin-top:14px;flex-wrap:nowrap;
  align-items:center;justify-content:space-between;
  background:var(--surface-2);border:1px solid var(--line);
  border-radius:var(--r-md);padding:9px 10px}
/* THREE FACTS ARE A GRID, NOT A FLEX ROW, and the difference is 9px.
   space-between puts the outer two on the edges and leaves the middle wherever
   the leftover space falls -- and because the first fact is wider than the last,
   "660 questions" landed 9px right of the true centre. `1fr auto 1fr` makes the
   two outer columns equal by construction, so the middle column is centred on
   the box whatever is in the other two.
   Only when there ARE three: a bundle has two facts, and in a three-column grid
   the second of two would sit in the middle with an empty column beside it. The
   class is written by the card, which is the only thing that knows the count. */
.ccard .cmeta-3{display:grid;grid-template-columns:1fr auto 1fr;gap:8px}
.ccard .cmeta-3 i:nth-child(1){justify-self:start}
.ccard .cmeta-3 i:nth-child(2){justify-self:center}
.ccard .cmeta-3 i:nth-child(3){justify-self:end}
.ccard .cmeta i{display:inline-flex;align-items:center;gap:4px;font-size:11.4px;
  font-weight:650;color:var(--ink-2);font-style:normal;white-space:nowrap}
.ccard .cmeta .ic{font-size:11px;flex:0 0 auto;color:var(--ink-3)}
/* THE ICONS GO BY THE CARD'S WIDTH, NOT THE SCREEN'S -- and getting that wrong
   is why they disappeared on a phone.
 *
 * The first rule was `@media (max-width:999px)`, which treats the screen as a
 * stand-in for the card. It is not one. Measured at twenty-five widths:
 *
 *     screen 900px  -> card 410  icons fit with 95px to spare, and were hidden
 *     screen 375px  -> card 328  icons fit with 13px to spare, and were hidden
 *     screen 640px  -> card 279  icons do NOT fit, two columns of a tablet
 *
 * The card is what runs out of room, so the card is what should be asked. Every
 * card at 328px or wider fits three facts and their icons; the widest that
 * fails is 313. The line is drawn at 320, between the two.
 *
 * On a real phone the card is 328 or more, so the icons stay. They come off
 * only on a very small phone and in the narrow two-column band on a tablet --
 * the two places they genuinely do not fit. */
.ccard{container-type:inline-size}
@container (max-width:319px){
  .ccard .cmeta .ic{display:none}
}
@media (max-width:359px){
  /* 320px, where the card is 273 and the box has 249 inside it. Measured at
     the sizes above it came out 1px SHORT -- one row, but one character from
     not being one. The type and the gaps give that back rather than leaving it
     to luck about how long a course's name for its hours happens to be. */
  .ccard .cmeta{gap:7px;padding:8px 9px}
  .ccard .cmeta i{font-size:10.8px}
}
.ccard .cmeta .saves{color:var(--ok);font-weight:750}

/* The app's action row: every button shares the width, and .btn-free declares a
   100% basis of its own so it takes a line. The icons inside are hidden -- at
   this size the label is the whole of it. */
.ccard .actrow{display:flex;gap:8px;margin-top:16px;flex-wrap:wrap}
.ccard .actrow>.btn{flex:1 1 0;min-width:0;padding:0 9px;gap:0;
  font-size:12.6px;white-space:nowrap;overflow:hidden}
.ccard .actrow>.btn .ic,.ccard .actrow>.btn svg{display:none}
@media (max-width:360px){
  .ccard .actrow>.btn{font-size:12px;padding:0 7px}
}
/* the picture is a link, and announced by the title below it rather than twice */
.ccard .artlink{display:block;color:inherit}

/* ------------------------------------------------------------------- steps */
.steps{display:grid;gap:16px;grid-template-columns:minmax(0,1fr);counter-reset:s}
@media (min-width:800px){ .steps{grid-template-columns:repeat(3,minmax(0,1fr))} }
.step{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-xl);
  padding:24px;box-shadow:var(--sh-1);counter-increment:s;position:relative}
.step::before{content:counter(s);position:absolute;top:20px;right:22px;
  font-family:var(--f-dis);font-weight:800;font-size:44px;letter-spacing:-.05em;
  color:var(--ink);opacity:.07;line-height:1}
.step h3{font-size:18px;letter-spacing:-.022em}
.step p{margin-top:9px;color:var(--ink-2);font-size:14.6px;line-height:1.58}

/* ------------------------------------------------------------------ close */
/* the same object as a band -- a big coloured panel -- so the same trio */
.close{background:var(--grad-hero);border-radius:var(--r-2xl);padding:clamp(28px,5vw,54px);
  color:#fff;text-align:center;box-shadow:var(--sh-brand);position:relative;overflow:hidden}
.close h2{color:#fff;font-size:clamp(26px,4.2vw,40px)}
/* solid white, not .9 and .78. On the levelled band those land at 4.2-4.4:1,
   under the 4.5 small text needs, for a softness nobody can see. */
.close p{color:#fff;margin:14px auto 0;max-width:48ch;font-size:16.5px}
.close .btn{margin-top:26px;background:#fff;color:var(--h2);box-shadow:0 14px 34px -10px rgba(0,0,0,.35)}
.close .btn:hover{transform:translateY(-2px)}
.close .fine{margin-top:16px;font-size:13.5px;color:#fff}

/* ----------------------------------------------------------------- footer */
.foot{border-top:1px solid var(--line);padding:36px 0 26px;margin-top:20px}
.footgrid{display:grid;gap:22px;grid-template-columns:minmax(0,1fr)}
/* FIVE BLOCKS, AND THE GRID HAD FOUR COLUMNS. This is the second time the same
   fault has happened here and it is worth naming: the rule said four columns
   because there were four things in the footer, a fifth was added when the
   coaching pages arrived, and it wrapped onto a second row — which doubled the
   footer's height to 551px and made it, in the owner's words, eat the page.
   The wordmark block was 399px wide and so was the orphan, so the two rows
   looked deliberate enough that nothing screamed.

   Written so the count is not baked into a number, and NOT with auto-fit --
   that was the first attempt and it silently did nothing:
   `repeat(auto-fit, ...)` is only valid when every other track is definite, and
   the 1.55fr beside it is flexible, so the whole declaration was thrown away
   and the footer fell back to two columns and three rows. It got taller, not
   shorter, which is how an invalid value announces itself: it doesn't.

   One explicit column for the wordmark and grid-auto-flow:column for the rest,
   so every nav after it creates its own equal implicit column however many
   there are. */
/* TWO COLUMNS FROM 380px, not 640. Below that breakpoint all five blocks
   stacked and the footer came to 993px on a 390px phone — which is the same
   complaint as the desktop one, in the place it matters more. The nav links are
   short enough for a 168px column; the wordmark block takes the full width
   above them. */
@media (min-width:380px){
  .footgrid{grid-template-columns:repeat(2,minmax(0,1fr));gap:20px 18px}
  .footgrid>div:first-child{grid-column:1/-1}
}
@media (min-width:900px){
  .footgrid{grid-template-columns:1.6fr;grid-auto-flow:column;
    grid-auto-columns:minmax(0,1fr);gap:24px}
}
.foot h4{font-family:var(--f-dis);font-size:12px;font-weight:750;letter-spacing:.14em;
  text-transform:uppercase;color:var(--ink-3);margin-bottom:10px}
.foot nav{display:flex;flex-direction:column;gap:6px}
.foot nav a{color:var(--ink-2);font-size:14.5px;transition:color .2s;
  display:inline-flex;align-items:center;min-height:34px}
.foot nav a:hover{color:var(--brand)}
.footnote{color:var(--ink-3);font-size:14px;margin-top:10px;max-width:34ch}
.copy{margin-top:30px;padding-top:20px;border-top:1px solid var(--line);
  color:var(--ink-4);font-size:13px}

/* --------------------------------------------------------------- 404 page */
/* Two rules, and only because the page has no other home. Everything else it
   needs -- the section rhythm, .sechead, .eyebrow, .lede, .herocta -- it now
   borrows from the pages it sits beside. */
.p404-links{margin-top:6px;color:var(--ink-2)}
.p404-note{margin-top:clamp(28px,4vw,40px);max-width:56ch;
  color:var(--ink-2);font-size:15px;line-height:1.65}

/* ------------------------------------------------------------ legal pages */
/* /privacy, /terms, /refunds and /contact.
 *
 * Long prose, which nothing else on this site is -- the landing page and the
 * store are cards and headings, and their styles carry margins tuned for cards.
 * Reusing .authcard here was the first attempt and produced a 2,000-word privacy
 * policy inside a 420px sign-in box.
 *
 * The measure is capped at 68ch. A policy nobody can read is a policy nobody
 * reads, and full-width text on a laptop is about 140 characters a line. */
/* padding-block, NOT the padding shorthand.
 *
 * These pages carry class="wrap legal", and .wrap sets the side padding that
 * keeps text off the edge of a phone. The shorthand here was
 * "clamp(...) 0 clamp(...)", which comes later in the file at equal specificity
 * and therefore reset that side padding to zero: on a 375px screen the heading
 * started at x=0 and ran to x=375, touching both edges. */
.legal{padding-top:clamp(40px,7vw,72px);padding-bottom:clamp(56px,8vw,96px)}
/* THE MEASURE, AND WHY IT IS NOT IN ch.
 *
 * This was max-width:68ch, which is the unit the job calls for and was still
 * wrong: ch resolves against the font of the element it is written on, and
 * .lwrap inherits the 16.5px body font while the prose inside it is 15.5px.
 * 68ch measured 817px and gave lines of 95 characters -- half again as long as
 * intended, and past the point where the eye reliably finds the next line.
 *
 * A fixed measure cannot drift away from the text it is measuring. 39rem is
 * about 75 characters of this prose, which is the top of the comfortable band,
 * and it still scales if the reader has set a larger default font. */
.legal .lwrap{max-width:39rem}
.legal h1{font-family:var(--f-dis);font-size:clamp(28px,4.4vw,40px);line-height:1.15;
  letter-spacing:-.02em;margin-bottom:12px}
.legal .lupd{color:var(--ink-3);font-size:14px;margin-bottom:clamp(28px,4vw,40px)}
.legal h2{font-family:var(--f-dis);font-size:clamp(19px,2.4vw,23px);line-height:1.25;
  letter-spacing:-.01em;margin:clamp(30px,4vw,42px) 0 12px;padding-top:clamp(20px,3vw,28px);
  border-top:1px solid var(--line)}
.legal h2:first-of-type{border-top:0;padding-top:0}
.legal h3{font-family:var(--f-dis);font-size:16px;font-weight:750;margin:22px 0 8px}
.legal p{color:var(--ink-2);font-size:15.5px;line-height:1.68;margin-bottom:14px}
.legal ul{margin:0 0 16px;padding-left:20px}
.legal li{color:var(--ink-2);font-size:15.5px;line-height:1.62;margin-bottom:8px}
.legal li::marker{color:var(--ink-4)}
.legal a{color:var(--brand);text-decoration:underline;text-underline-offset:2px}
.legal strong{color:var(--ink);font-weight:650}
/* The plain-language line that opens most sections. It is not a summary of the
 * paragraph below it -- it is the same thing said once, shorter, for the reader
 * who is not going to read the paragraph. */
.legal .lplain{background:var(--surface-2);border-left:3px solid var(--brand);
  border-radius:0 10px 10px 0;padding:12px 16px;margin:0 0 18px;
  color:var(--ink);font-size:15px;line-height:1.55}
.legal .lbox{background:var(--surface-2);border:1px solid var(--line);border-radius:12px;
  padding:clamp(16px,3vw,22px);margin:0 0 18px}
.legal .lbox p:last-child,.legal .lbox ul:last-child{margin-bottom:0}
.legal .lmail{font-family:var(--f-dis);font-size:clamp(17px,2.4vw,20px);font-weight:750}
/* Two columns of short facts -- what is collected, what is not. */
.legal .lcols{display:grid;gap:16px;grid-template-columns:minmax(0,1fr);margin-bottom:18px}
@media (min-width:760px){ .legal .lcols{grid-template-columns:1fr 1fr} }
.legal .lalso{margin-top:clamp(36px,5vw,52px);padding-top:22px;border-top:1px solid var(--line);
  display:flex;flex-wrap:wrap;gap:8px 18px}
.legal .lalso span{color:var(--ink-3);font-size:14px}

/* ------------------------------------------------------------ empty state */
.empty{text-align:center;padding:48px 20px;color:var(--ink-3)}
.empty b{display:block;font-family:var(--f-dis);font-size:17px;color:var(--ink-2);
  margin-bottom:6px}

/* ============================================================================
   THE STORE — /elearn/store/

   Server-rendered like the rest of these pages, and for the same reason: this
   is the page that states a price, so it has to be reading the same catalogue()
   the checkout charges from. Anything built ahead of time can be wrong about
   money, and being wrong about money in public is the one mistake here that
   costs something.
   ========================================================================== */

/* --------------------------------------------------------------- the head */
/* A subtitle has a long form and a short one; the wide screen gets the long. */
.l-mini{display:none}

/* THE HERO IS A BAND, and it is the only coloured section on this page. That is
   deliberate: white cards standing on the brand gradient is the light-on-colour
   rule the rest of the site runs on, and it is what makes a shop's first screen
   land instead of merely open. It replaces a white page with a heading, a
   paragraph, two grey pills and NO CALL TO ACTION AT ALL -- the first thing a
   visitor could act on used to be a course card most of a screen further down. */
.sh-hero{padding:clamp(40px,5.6vw,76px) 0 clamp(34px,4.6vw,58px)}
.sh-hero-grid{display:grid;gap:clamp(26px,4vw,52px);align-items:center;
  grid-template-columns:minmax(0,1fr)}
.sh-hero .eyebrow{color:#fff;opacity:.85}
.sh-hero h1{font-size:clamp(31px,5.4vw,52px);line-height:1.03;letter-spacing:-.02em;
  color:#fff;max-width:15ch}
.sh-lede{margin:16px 0 0;color:#fff;line-height:1.6;max-width:46ch;
  font-size:clamp(15.5px,2vw,18px)}

/* Two buttons, which is the whole job of a shop's first screen: read one, or
   look at them all. The solid one inverts to white on the colour, the same way
   every other band on the site treats its primary. */
.sh-cta{display:flex;flex-wrap:wrap;gap:12px;margin-top:clamp(22px,3vw,30px)}
.sh-go{background:#fff;color:var(--h2);box-shadow:0 14px 34px -10px rgba(0,0,0,.35)}
.sh-go:hover{background:#fff;transform:translateY(-2px)}
.sh-see{background:rgba(255,255,255,.12);color:#fff;border:1px solid rgba(255,255,255,.42)}
.sh-see:hover{background:rgba(255,255,255,.2);color:#fff;transform:translateY(-2px)}
/* solid white, not .9 -- at 13.5px a translucent white on this band lands under
   the 4.5:1 small text needs, for a softness nobody can see */
.sh-fine{margin:14px 0 0;color:#fff;font-size:13.4px}

/* THE DECK. Three real wrong/right pairs out of three different courses, read
   from their own preview files -- not an illustration of the product, the
   product. Fanned and overlapping so one glance says "there are several of
   these, and this is what is inside them".
   The FRONT card is in normal flow and the two behind it are absolute, so the
   deck is exactly as tall as the card a reader actually reads. z-index has to
   be explicit: absolutely positioned siblings paint above an in-flow one
   whatever the source order says. */
.sh-deck{position:relative}

/* THE STAGE, AND IT IS A HAND OF CARDS.
 *
 * Every course's card lives in the SAME grid cell, so the deck is as tall as
 * the longest sentence in the catalogue and never resizes as it deals -- a
 * panel that grows and shrinks on a timer is the thing that makes a rotator
 * feel cheap.
 *
 * THREE POSITIONS, AND THEY DO NOT MOVE. p0 is the front, p1 sits behind and to
 * the right, p2 behind and to the left. They are the same three transforms the
 * static fan always had. What changes on each turn is WHICH card holds which
 * position, so the fan a visitor sees at rest is the fan they saw before.
 *
 * px is every other card: parked at p2's transform with no opacity, waiting.
 *
 * .sh-lift is the deal, in two moves. The front card is drawn down and out to
 * the right, still opaque and still on top; then the class comes off and it has
 * only .sh-px left, whose z-index is 0 -- so it drops behind the others and
 * travels back to the LEFT, sliding under the fan. Meanwhile p1 and p2 have
 * already transitioned forward. No jump and no reset: it ends where it belongs.
 *
 * z-index without position: grid items are painted in z-index order, so these
 * do not need position:relative and should not have it (it would break the
 * grid-area:1/1 stacking).
 */
.sh-stage{display:grid;position:relative;z-index:2}
.sh-card{grid-area:1 / 1;margin:0;background:var(--surface);border-radius:var(--r-xl);
  padding:clamp(18px,2.4vw,24px);border:1px solid rgba(255,255,255,.55);
  box-shadow:0 26px 60px -18px rgba(6,5,26,.5);pointer-events:none;
  opacity:0;z-index:0;transform:rotate(-5.2deg) translate(-15px,24px) scale(.95);
  transition:transform .52s var(--ease),opacity .4s var(--ease)}
.sh-p0{opacity:1;z-index:3;pointer-events:auto;transform:rotate(-1.2deg)}
.sh-p1{opacity:.94;z-index:2;transform:rotate(3.6deg) translate(16px,12px) scale(.975)}
.sh-p2{opacity:.72;z-index:1;transform:rotate(-5.2deg) translate(-15px,24px) scale(.95)}
/* THE DEAL, IN TWO MOVES, AND THE CARD NEVER LEAVES.
 *
 * It used to be flicked off to the right and faded out, which is a card being
 * thrown away rather than a card going to the back of the pile.
 *
 * MOVE ONE, .sh-lift: the front card is drawn down and out to the right, still
 * fully opaque and still ON TOP (z-index 4). This is the hand taking it off.
 *
 * MOVE TWO: the class comes off. It has nothing left but .sh-px, whose z-index
 * is 0 -- so it drops behind the others at once -- and whose transform is the
 * parked pose at the bottom of the pile. The base transition on .sh-card,
 * slower than the lift, carries it back left and UNDER the fan, shrinking and
 * fading as it tucks in.
 *
 * Two moves, no jump, no reset: the card is visible for the whole journey and
 * ends up underneath. */
.sh-lift{z-index:4;opacity:1;
  transform:translate(31%,15%) rotate(7deg) scale(.95);
  transition:transform .28s cubic-bezier(.32,0,.5,1),opacity .28s ease}
@media (prefers-reduced-motion:reduce){
  .sh-card,.sh-lift{transition:none}
}
.sh-card figcaption{font-family:var(--f-dis);font-weight:750;font-size:11px;
  letter-spacing:.14em;text-transform:uppercase;color:var(--ink-4);margin-bottom:13px}
.sh-bad,.sh-ok{display:flex;align-items:flex-start;gap:10px;margin:0;
  font-size:clamp(15px,1.9vw,17px);line-height:1.45;font-weight:600}
.sh-bad{color:var(--no)}
.sh-bad s{text-decoration-color:var(--no-line);text-decoration-thickness:1.5px}
.sh-ok{color:var(--ok);font-weight:700;margin-top:12px}
.sh-card .sig{flex:0 0 auto;width:21px;height:21px;border-radius:7px;display:grid;
  place-items:center;font-size:12px;font-weight:800;margin-top:1px;line-height:1}
.sh-bad .sig{background:var(--no-soft);color:var(--no)}
.sh-ok .sig{background:var(--ok-soft);color:var(--ok)}
/* The same pair, same rule, in the deck on the store's first screen. */
.sh-bad.usual{color:var(--ink-2)}
.sh-bad.usual .sig{background:var(--surface-3);color:var(--ink-3)}

/* Lessons and questions, which are facts about what one buyer gets. The count
   of courses that used to sit beside them is gone: no page here states how many
   things are on the shelf. */
.sh-meta{display:flex;flex-wrap:wrap;list-style:none;padding:0;
  margin:clamp(24px,3.2vw,34px) 0 0;color:#fff;font-size:14px}
.sh-meta li{padding:0 16px}
.sh-meta li:first-child{padding-left:0}
.sh-meta li + li{border-left:1px solid rgba(255,255,255,.45)}
.sh-meta b{font-family:var(--f-dis);font-weight:800;font-size:17px;letter-spacing:-.02em}

/* Sections inside the store sit closer together than the landing page's, which
   is paced for reading. Somebody on this page is comparing, not being told. */
.storesec{padding-top:clamp(26px,4vw,44px);padding-bottom:clamp(8px,2vw,16px)}
.storesec .sechead{margin-bottom:clamp(18px,3vw,26px)}
.storesec .sechead h2{font-size:clamp(23px,3.4vw,31px)}

/* Three actions, one row, at every width — the same rule the app's cards use.
   They used to be laid out at their natural widths with a 132px floor, so the
   third one wrapped to a line of its own the moment the labels grew. flex:1 1 0
   with min-width:0 divides the row instead of measuring it. */
/* wrap, not nowrap: .btn-free declares a 100% basis so it takes a row of its
   own by design. The two above it still divide their row equally, so nothing
   here can produce the ragged edge nowrap was guarding against. */
.course .cta{flex-wrap:wrap}
.course .cta .btn{flex:1 1 0;min-width:0;padding:0 9px;font-size:12.6px;
  white-space:nowrap;overflow:hidden}
.course .cta .btn.wide{flex:1 1 100%}
/* Removed with the block below: at 375 the two buttons measure 149 and 147 wide
   against a 147px need, so they were being shrunk to fit a row they already fit
   in. Kept as a guard well below any phone in use. */
@media (max-width:340px){ .course .cta .btn{padding:0 5px;font-size:11.5px;letter-spacing:-.012em} }
/* The art earns its height on a wide card; on a narrow one the sentence in it
   fits in less, and the body has no paragraph left to breathe around. */
/* A card is about 345px wide in the three-column desktop grid and about 343px
   on a phone - the same card, at the same size, reached by different routes. It
   was being drawn differently at each: smaller body padding, smaller spec,
   smaller buttons on the narrow one. Measured with the desktop values forced
   onto a 375px screen, nothing clipped, nothing overflowed and the card came
   out the same height, so the shrink was paying for nothing and costing a card
   that did not match itself between two screens. Only the taste line keeps a
   narrow-screen rule, because its padding-right clears the chip. */
@media (max-width:560px){
  .art.hastaste{min-height:100px;padding:46px 18px 18px}
  .course .taste{padding-right:18px}
}

/* ------------------------------------------------------------ the detail */
.crumbs{display:flex;align-items:center;gap:8px;padding-top:22px;font-size:13.4px;
  color:var(--ink-3)}
.crumbs a{color:var(--ink-2);font-weight:600;display:inline-flex;align-items:center;
  min-height:38px}
.crumbs a:hover{color:var(--brand)}
.crumbs i{font-style:normal;color:var(--ink-4)}

/* Document order is title, buy box, everything else — which is the order a
   phone reads, and the reason it is that way. With the buy box last in the
   markup a visitor on a phone met the price three and a half thousand pixels
   down, under the whole syllabus. Desktop puts it back on the right by naming
   rows and columns rather than by reordering, so nothing depends on the markup
   being in the "wrong" order for the wide layout to work. */
.detail{display:grid;gap:clamp(20px,3vw,30px) clamp(22px,3vw,38px);align-items:start;
  padding-top:clamp(20px,3vw,32px);grid-template-columns:minmax(0,1fr)}
@media (min-width:940px){
  .detail{grid-template-columns:minmax(0,1fr) 340px}
  .detail>.dhead {grid-column:1;grid-row:1}
  .detail>.buybox{grid-column:2;grid-row:1/span 2;align-self:start}
  .detail>.dbody {grid-column:1;grid-row:2}
}

.dhead h1{font-size:clamp(29px,4.8vw,44px);line-height:1.05}
.dhead .lede{margin-top:14px}
.dmeta{display:flex;flex-wrap:wrap;gap:8px;margin-top:18px}

/* THE PICTURE THE COURSE PAGE NEVER HAD. The store card, the /elearn shelf
   and the app all show it; this page showed a gradient tile in the buy box and
   nothing at all in the column a reader actually looks at.

   3/1 LIKE .ccard .cart, not a ratio of its own. These pictures are drawn to
   be cropped at 3/1, so a different frame here would show a different part of
   one picture on two pages about one course -- and the crop is where the
   subject is. @supports with the fixed height kept underneath for the reason
   the card's rule spells out: a browser that cannot do aspect-ratio must not
   be handed height:auto, or the picture collapses to nothing.

   The glow is the card's own, so the two read as the same object at two
   sizes rather than two treatments of one photograph. */
.dart{position:relative;overflow:hidden;height:230px;
  margin-top:clamp(18px,2.6vw,26px);border-radius:var(--r-lg);
  border:1px solid var(--line);box-shadow:var(--sh-1);background:var(--grad);
  background-size:cover;background-position:center;background-repeat:no-repeat}
@supports (aspect-ratio: 3 / 1){ .dart{height:auto;aspect-ratio:3 / 1} }
.dart .glow{position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(320px circle at 22% 24%,rgba(255,255,255,.3),transparent 62%)}
/* Off sale, the same drain the buy box's tile takes, so the two agree. */
.dart.soon{filter:saturate(.3)}

/* padding:0 IS THE POINT OF THIS RULE, not the margin.

   `section{padding:clamp(52px,8vw,96px) 0}` up at line 414 is the landing
   page's BAND spacing -- the air around a full-width stripe of colour. These
   are <section class="dsec"> too, so every one of them was carrying 96px of
   padding above its heading and 96px below its last line, at 1440. With the
   42px margin between them that is 234px of nothing between the end of one
   paragraph and the start of the next heading, four times down the page.

   The owner reported it as "lot of empty space" scrolling the left column and
   she was reading a real number: over 700px of it on a course page. Nothing
   chose that -- the margin above was written as though it were the only
   spacing there was, which is what it looks like in this file.
   6 September 2026. */
.dsec{margin-top:clamp(28px,4vw,42px);padding:0}
.dsec h2{font-size:clamp(20px,2.8vw,25px)}
.dsec p{margin-top:12px;color:var(--ink-2);font-size:15.6px;line-height:1.68;max-width:64ch}

.speclist{display:grid;gap:10px;margin-top:16px;grid-template-columns:minmax(0,1fr)}
@media (min-width:620px){.speclist{grid-template-columns:repeat(2,minmax(0,1fr))}}
.spec{display:flex;gap:11px;align-items:flex-start;background:var(--surface);
  border:1px solid var(--line);border-radius:var(--r-md);padding:14px 15px}
.spec .sig{flex:0 0 auto;width:20px;height:20px;border-radius:7px;display:grid;
  place-items:center;background:var(--brand-soft);color:var(--brand);font-size:12px;
  font-weight:800;margin-top:1px;line-height:1}
/* THE TITLE IS NOT THE CAPTION, and the selector could not tell them apart.

   The markup is .spec > span > (b + span): a wrapper holding the name and the
   line under it. `.spec span` was written for the line under it and matched the
   WRAPPER as well, so <b> inherited the caption colour and "Multiple choice"
   came out the same faint grey as the sentence beneath it. Six cards with no
   heading in them, which is what the owner was seeing as faded.

   ink-2 for the caption, not ink-3. Measured on the card it sits on: ink-3 is
   #7B7FA4 on white, which is 3.88:1, and 13.4px text needs 4.5. ink-2 is 7.26.
   This file has been caught by exactly this before -- see the note at .outline
   .on about 4.23:1 -- and the fix is the same one. */
.spec b{display:block;font-family:var(--f-dis);font-size:14.6px;letter-spacing:-.01em;
  color:var(--ink)}
.spec span{display:block;margin-top:3px;color:var(--ink-2);font-size:13.4px;line-height:1.5}

/* The two lessons anybody can read, named. "Two free lessons" is a claim; the
   titles of the two are evidence. */
.freelist{display:flex;flex-direction:column;gap:9px;margin-top:16px}
.freeitem{display:flex;align-items:center;gap:12px;background:var(--ok-soft);
  border:1px solid var(--ok-line);border-radius:var(--r-md);padding:13px 15px;
  color:var(--ink);font-size:14.8px;font-weight:600}
.freeitem em{font-style:normal;color:var(--ok);font-family:var(--f-dis);font-weight:800;
  font-size:12.5px;letter-spacing:.06em;text-transform:uppercase;margin-left:auto;
  flex:0 0 auto}

/* ------------------------------------------------------- what's in a bundle */
.inbundle{display:flex;flex-direction:column;gap:10px;margin-top:16px}
.inrow{display:flex;align-items:center;gap:13px;background:var(--surface);
  border:1px solid var(--line);border-radius:var(--r-md);padding:14px 15px;
  transition:border-color .2s,transform .2s var(--spring)}
.inrow:hover{border-color:var(--brand-line);transform:translateY(-2px)}
.inrow .grow{min-width:0;flex:1 1 auto}
/* The same two faults, in the same shape. .inrow is an <a>, and a{color:inherit}
   up at the top means the whole row took whatever the last rule said -- so the
   COURSE NAME in a bundle's contents was drawn in the caption grey, at 3.88:1.
   The name takes ink, the line under it and the price take ink-2. */
.inrow b{display:block;font-family:var(--f-dis);font-size:15.4px;letter-spacing:-.015em;
  color:var(--ink)}
.inrow span{display:block;margin-top:2px;color:var(--ink-2);font-size:13.2px}
.inrow .was{flex:0 0 auto;color:var(--ink-2);font-size:14px;font-weight:650}
.bundletot{display:flex;align-items:baseline;justify-content:space-between;gap:12px;
  margin-top:14px;padding-top:14px;border-top:1px dashed var(--line-strong);
  font-size:14.6px;color:var(--ink-2)}
.bundletot b{font-family:var(--f-dis);font-size:19px;color:var(--ok)}

/* -------------------------------------------------------------- the buy box */
.buybox{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-xl);
  padding:22px;box-shadow:var(--sh-2)}
@media (min-width:940px){.buybox{position:sticky;top:88px}}
.buybox .art{height:78px;border-radius:var(--r-md);background:var(--grad);margin:0 0 16px}
.buybox .art.soon{filter:saturate(.3)}
.bprice{display:flex;align-items:baseline;gap:10px}
.bprice b{font-family:var(--f-dis);font-weight:800;font-size:34px;letter-spacing:-.03em;
  line-height:1}
.bprice s{color:var(--ink-4);font-size:16px;text-decoration-thickness:1.5px}
/* ink-2 AND NOT ink-3. "One payment. Yours from then on" is the sentence that
   answers the one question a price raises, and at 13.4px --ink-3 measures 3.88
   against this white, where small text needs 4.5. It was recorded as measured
   and left in September and is fixed here; --ink-2 is 7.96. */
.bnote{margin-top:8px;color:var(--ink-2);font-size:13.4px;line-height:1.5}
.buybox .btn{width:100%;margin-top:12px}
.blist{margin-top:18px;padding-top:16px;border-top:1px solid var(--line);
  display:flex;flex-direction:column;gap:9px}
.blist div{display:flex;gap:9px;align-items:flex-start;color:var(--ink-2);font-size:13.8px;
  line-height:1.5}
.blist i{font-style:normal;color:var(--ok);font-weight:800;flex:0 0 auto}

/* Not on sale: the box says what is true instead of showing a dead button. */
.soonbox{background:var(--warn-soft);border:1px solid var(--warn-line);border-radius:var(--r-md);
  padding:14px 15px;margin-top:14px;color:var(--warn);font-size:13.8px;line-height:1.55;
  font-weight:600}

/* ------------------------------------------------------------ the syllabus */
/* Every lesson in the course, as the sentence it fixes. The corrections are not
   here — see page_outline() for why. Two columns once there is room, because
   fifty single-file rows is a scroll rather than a list. */
/* padding:0 GOES WITH list-style:none, and was missed. A <ol> carries the
   browser's own padding-inline-start:40px whether or not it has markers, so
   with the markers off this list still began 40px in -- every other block on
   the page starting at 16 on a phone and the syllabus at 56. Visible as a step
   in the left edge, reported by the owner scrolling a course on a phone,
   6 September 2026. */
/* margin, not margin-top: an <ol> carries the browser's own 1em BOTTOM margin
   as well, which is 16.5px nobody asked for. It was invisible while the list
   was the last thing in its section, and became a 25px seam across the middle
   of it the day the list was split in two. */
.outline{list-style:none;padding:0;margin:16px 0 0;display:grid;gap:8px;
  grid-template-columns:minmax(0,1fr);counter-reset:none}
@media (min-width:760px){.outline{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px 14px}}
.outline li{display:flex;gap:11px;align-items:flex-start;padding:11px 13px;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-sm)}
/* ink-2 rather than ink-3: at 11.5px on the dark surface-3 the lighter one
   measured 4.23:1, which is under the 4.5 small text needs. */
.outline .on{flex:0 0 auto;min-width:22px;height:22px;padding:0 5px;border-radius:7px;
  display:grid;place-items:center;background:var(--surface-3);color:var(--ink-2);
  font-family:var(--f-dis);font-weight:750;font-size:11.5px;margin-top:1px}
.outline .ot{min-width:0}
.outline .ot b{display:block;font-family:var(--f-dis);font-size:14.4px;letter-spacing:-.015em}
.outline .ot s{display:block;color:var(--no);font-size:13.6px;line-height:1.45;
  text-decoration-color:var(--no-line);text-decoration-thickness:1.5px;margin-top:2px}
.outline .ot b + s{margin-top:4px}

/* THE REST OF THE SYLLABUS, BEHIND THE BUTTON THE QUIZ ALREADY USES.

   Twelve lessons show and the rest sit in a <details>. quiz.css draws .seeall
   under the daily quiz's review list -- the site button, full width, 42px, a
   chevron -- and this is that button, with that button's numbers.

   A <details> and not a script: the lessons stay in the HTML for a search
   engine either way, and a <summary> is already a control to a keyboard and a
   screen reader with nothing wired. store.js animates the height on top of it
   and adds nothing the page depends on.

   Four things here are not obvious:

   ORDER. A <summary> is always the first child of its <details>, so open it
   would sit in the middle of the list with the second half beneath it. The
   flex column lets order put it after the content, visually only -- the markup,
   and the order a screen reader reads, is untouched.

   8PX EVERYWHERE, which is the gap between two rows of the list. The button
   belongs to the list rather than sitting under it, and the seam between the
   two halves has to be invisible: .outline's own 16px left a visible break
   across the middle at exactly the point the split happens to be. It also
   makes the two states the same shape -- with the second half folded to
   nothing, open and shut measure identically, so there is nothing to jump on
   the frame the element closes.

   --fold IS READ BY store.js. The chevron turns here and the list folds there,
   and they have to take the same time; declared once so they cannot part.

   .to-shut IS THE SAME BUTTON, PRESSED. [open] goes on at the start of an
   opening fold and cannot come off until the END of a closing one, because the
   list is still on screen collapsing -- so without this the chevron and the
   label changed instantly one way and a quarter of a second late the other.
   This says: still open, but going, so wear the shut face now. Nothing adds it
   without the script, and [open] alone is correct then. */
.outmore{--fold:260ms;display:flex;flex-direction:column}
.outmore .outline{margin-top:8px}
.outmore[open] > summary{order:2}
.outmore > summary{width:100%;height:42px;margin-top:8px;padding:0 18px;
  font-size:13.5px;color:var(--ink-2);cursor:pointer;list-style:none}
.outmore > summary::-webkit-details-marker{display:none}
.outmore > summary::marker{content:""}
.outmore > summary:hover{border-color:var(--brand);color:var(--brand);
  transform:translateY(-2px)}
.outmore > summary .ic{width:17px;height:17px;flex:0 0 auto;
  transition:transform var(--fold) var(--ease)}
.outmore[open] > summary .ic{transform:rotate(180deg)}
.outmore .om-less{display:none}
.outmore[open] .om-more{display:none}
.outmore[open] .om-less{display:inline}
.outmore.to-shut > summary .ic{transform:none}
.outmore.to-shut .om-more{display:inline}
.outmore.to-shut .om-less{display:none}
@media (prefers-reduced-motion:reduce){
  .outmore > summary:hover{transform:none}
  .outmore > summary .ic{transition:none}
}
/* THE SAME LINE, WHERE IT IS NOT A MISTAKE. Spoken English and Interview
   replace correct English with something better, so their lines are quoted
   rather than crossed out and take the ordinary reading colour instead of the
   error red -- see page_outline(). <q> draws its own quotation marks, which is
   the whole difference being made: somebody said this, and the lesson has a
   better answer. */
.outline.usual .ot q{display:block;color:var(--ink-2);font-size:13.6px;
  line-height:1.45;margin-top:2px;font-style:normal}
.outline.usual .ot b + q{margin-top:4px}


/* --------------------------------------------- the store's first screen, wide */
/* Words and buttons on the left, the deck on the right. The buttons come BEFORE
   the product here because the deck is already in view beside them -- the eye
   has taken it in before it reads a word. The phone rules further down reverse
   that, and the reason is written there. */
@media (min-width:940px){
  .sh-hero-grid{grid-template-columns:minmax(0,1.02fr) minmax(0,.98fr)}
  .sh-meta{grid-column:1 / -1;margin-top:clamp(30px,3.4vw,40px)}
}

/* The single specimen panel that stood here -- .shspec, its label, its one
   wrong/right pair and its three counts -- was replaced by the deck above.
   Three courses instead of one, on a coloured band instead of a white page,
   and with two buttons beside it that the old first screen did not have. */

/* On a phone the specimen sat between the heading and the shop, and the first
   course landed at 875px on an 812px screen — a whole screen of scrolling before
   anything was for sale. The panel earns its place on a wide screen, where it
   sits BESIDE the words and costs nothing. On a narrow one it is stacked, so it
   is cut down to the pair of sentences that make the argument and loses the
   three counts, which the cards repeat anyway. */
/* ══════════════════════════════ the store's first screen, on a phone ══════
   A DIFFERENT LAYOUT, NOT THE SAME ONE NARROWER. Three decisions, and each of
   them is a decision rather than a consequence of the width:

   ONE CARD, NOT THREE. The fan is the wide screen's idea. At 343px two cards
   peeking out from behind a third is not depth, it is clutter, and it steals
   width from the sentence that is the whole reason the card exists. The two
   behind are not shrunk -- they are not drawn.

   THE CARD COMES BEFORE THE BUTTONS, and on the wide screen it does not. Side
   by side, the deck is already in the eye before a word is read, so the buttons
   can sit straight under the sentence that earned them. On a phone nothing is
   visible beside anything: the product has to be shown before the ask, or the
   ask arrives on trust.

   THE BUTTONS GO FULL WIDTH AND STACK. 343px minus the page's own padding will
   not hold "Read two lessons free" and "See the courses" side by side without
   shrinking both past the size a thumb is confident about. Stacked and full
   width, the primary is the biggest thing on the screen after the heading.

   .sh-words is dissolved with display:contents so its children become items of
   the same flex column and can be ordered against the deck. */
@media (max-width:939px){
  .sh-hero{padding:clamp(30px,6.5vw,46px) 0 clamp(28px,5.4vw,40px)}
  /* align-items:stretch, and it is not decoration. The base rule centres the
     two columns against each other on a wide screen; turned into a COLUMN here,
     that same centring becomes horizontal, and every child shrinks to its own
     content width -- which is why the two buttons came out 204px inside a 343px
     screen instead of filling it. Measured before the line was added. */
  .sh-hero-grid{display:flex;flex-direction:column;align-items:stretch;gap:20px}
  .sh-words{display:contents}
  .sh-hero .eyebrow{order:1}
  .sh-hero h1{order:2;font-size:clamp(27px,7.4vw,34px);max-width:none}
  .sh-lede{order:3;margin-top:0;font-size:15px;max-width:44ch}
  .sh-deck{order:4}
  /* BOTH BUTTONS IN ONE ROW, by the owner's instruction. Stacked, they were two
     50px slabs one under the other and the hero read as a form.
     flex:1 1 0 with min-width:0 splits the row evenly however narrow it gets,
     and the two overrides under it are what let that work: .btn is nowrap and
     a fixed height, so a label too long for half a phone would have run out of
     its own box instead of taking a second line inside it. */
  .sh-cta{order:5;margin-top:0;flex-direction:row;gap:10px;max-width:none}
  .sh-cta .btn{flex:1 1 0;min-width:0;width:auto;
    white-space:normal;text-align:center;line-height:1.2;
    height:auto;min-height:50px;padding:9px 12px;font-size:14.5px}
  .sh-fine{order:6;margin-top:-2px}
  /* ONE LINE, by the owner's instruction -- it was a stacked column of three.
     It is a wrapping row, so it stays one line wherever the width allows and
     falls onto a second where it does not; the hairlines are kept because the
     items still read left to right. Type and padding come down so the line
     survives further: measured, all three hold one line to about 500px. */
  .sh-meta{order:7;margin-top:2px;font-size:12.6px}
  .sh-meta li{padding:0 10px}
  .sh-meta b{font-size:15px}

  /* NO FAN ON A PHONE, and the reason has not changed: two cards peeking out
     from behind a third at 343px is clutter, and it steals width from the
     sentence that is the reason the card exists. The deal still happens -- the
     front card is still flicked away and the next one arrives -- it simply
     arrives from directly behind instead of from a fan. */
  .sh-p1,.sh-p2{opacity:0;transform:scale(.97)}
  .sh-card{padding:20px;border-radius:var(--r-lg)}
  .sh-bad,.sh-ok{font-size:15.4px}
  .sh-ok{margin-top:11px}

  /* Both subtitles stay; each swaps to its short form. One line under a heading
     is support - three lines of small type is a second paragraph the reader did
     not ask for, and hiding it altogether leaves the phone without the one fact
     the section is there to give. */
  #courses .sechead .lede{margin-top:8px;font-size:14.6px}
  .l-full{display:none}
  .l-mini{display:inline}
}

/* ------------------------------------------------ a card that shows its wares */
/* The art was 96px of gradient and a chip, identical but for hue, so nine cards
   read as one card nine times. It now carries a sentence out of the course. */
/* Unscoped from .course on purpose: the course page's buy box shows the same
   pair, and a visitor arriving straight on that page from a search should meet
   the course the same way a visitor arriving through the shop does. */
/* The chip's band is reserved at the TOP, not carved out of the sentence's
   right-hand side. The sentence is bottom-aligned and grows upward, so a long
   one now makes the artwork taller rather than climbing into the chip - which
   is what the old 78px right gutter was there to prevent. It cost every card a
   lopsided line: text starting 18px from the left edge and stopping 78px short
   of the right, on cards whose sentences are mostly short enough not to need
   it. */
.art.hastaste{height:auto;min-height:118px;display:flex;align-items:flex-end;
  padding:46px 18px 18px;position:relative;overflow:hidden}
.art.hastaste::after{content:'';position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(0,0,0,.02),rgba(0,0,0,.26))}
.taste{position:relative;z-index:1;color:#fff;font-size:15.4px;line-height:1.4;
  font-weight:650;text-shadow:0 1px 12px rgba(0,0,0,.3)}
.course .taste{padding-right:18px}
.taste s{text-decoration-color:rgba(255,255,255,.6);text-decoration-thickness:1.5px}
/* <q> draws its own quotation marks, which is the whole difference being made
   on the two courses whose line is not a mistake. Italics are the browser's
   idea, not ours. */
.taste q{font-style:normal}
.art.soon .taste{opacity:.9}
/* THE PAIR COMPLETES ON THE PICTURE, not half on it and half on the card.
   Column rather than the shared flex row, so the two lines stack in the order
   they are read; the correction takes the white and the shadow the struck line
   already has, and its tick becomes a pane of the picture rather than the pale
   green chip that belongs on a white surface.

   The veil goes from .26 to .44 at the foot for one reason: there is now
   13.6px text down there as well as 15.4px, and small white lettering on a
   saturated gradient is exactly the case this file has been caught by before.

   CENTRED, NOT SITTING ON THE FLOOR. The base .art.hastaste rule pushes its
   one line to the bottom, which is right when a picture is behind it and the
   line is a caption lying on it. Here the tile IS the pair -- there is no
   photograph under it, only a gradient -- so a bottom-weighted pair left a
   band of empty colour above it and nothing below. Even padding and centred,
   so the two lines sit in the middle of their own tile. */
.buybox .art.hastaste{min-height:104px;margin-bottom:16px;
  flex-direction:column;align-items:stretch;justify-content:center;gap:7px;
  padding:16px}
.buybox .art.hastaste::after{background:linear-gradient(180deg,rgba(0,0,0,.02),rgba(0,0,0,.44))}
.buybox .art .fixline{position:relative;z-index:1;margin:0;color:#fff;
  font-size:13.6px;text-shadow:0 1px 12px rgba(0,0,0,.34)}
.buybox .art .fixline .tick{background:rgba(255,255,255,.24);color:#fff;
  border:1px solid rgba(255,255,255,.36)}

/* The correction, directly under the title — the pair completes on the card and
   a visitor has learnt something before deciding anything. */
.fixline{display:flex;align-items:flex-start;gap:8px;margin-top:9px;color:var(--ok);
  font-size:14.2px;line-height:1.45;font-weight:650}
.fixline .tick{flex:0 0 auto;width:18px;height:18px;border-radius:6px;display:grid;
  place-items:center;background:var(--ok-soft);font-size:11px;font-weight:800;margin-top:1px}
.course .fixline + p{margin-top:9px;font-size:13.8px}

/* --------------------------------------------------------------- free trial */
/* A row of its own, always. The two actions that cost money share the row above;
   this one spans the width beneath them, at every screen size, because a layout
   that is two rows here and three there is the thing that looks broken.

   The colour is the positive one — free is good news, not the option left over
   after the two that charge. The second line carries "no sign-up needed"
   because that is the objection somebody actually has: free usually means an
   email address and a password first, and saying otherwise removes the excuse
   more effectively than any adjective. */
/* THE BRAND var(--grad)IENT AS AN OUTLINE, not as a fill.

   It was green. The only green in this palette is --ok, which means "your
   answer was correct" - so a green button was borrowing the marking language to
   sell something, and that is why it read as foreign however it was arranged.
   Nothing here is green now. The gradient this whole product is built on draws
   the edge and the words, and the inside stays the surface it sits on: lighter
   than the two buttons above it, and unmistakably the same family.

   Two backgrounds do the border: the surface colour clipped to the padding box
   over the gradient clipped to the border box. That is the only way to put a
   gradient on a border, and it needs no image and no extra element.

   It is finished WITHOUT a mouse, which the old one was not - it spent its
   whole effect on hover, so on a phone it was a flat pale rectangle. The edge
   and the lettering are there at rest; hover fills it in. */
.btn-free{flex:1 1 100% !important;width:100%;
  height:auto !important;min-height:48px;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:1px !important;padding:8px 16px !important;text-align:center;
  color:#fff;border:0;
  position:relative;overflow:hidden;isolation:isolate;
  /* SOLID, AND THE LOUDEST THING ON THE CARD. It was a near-white outline
     between a white Add to cart and a solid Buy now -- the palest button on the
     card carrying the best offer on it. The weights are swapped: this one is
     filled and Buy now is outlined below.
     A DEEPER GRADIENT THAN --grad, and that is not taste. White on #A855F7 is
     3.96:1 and on #FF3D9A 3.29:1 -- both under the 4.5 a 14px label needs, and
     Buy now has been carrying that fault all along. #9E42F6 and #E4006D are the
     same two colours taken down until white clears 4.6 at every stop; only the
     pink moves much, and it is still plainly the brand pink. */
  /* AN ORDINARY SOLID BUTTON, AND THEN SOMETHING MOVES ACROSS IT. The gradient
     used to slide inside the button and the shadow used to breathe, so the
     button itself was never still -- it read as a special effect rather than as
     a button. It sits exactly like Buy now or Sign in now: one static gradient,
     one steady shadow. The only thing that moves is the shine crossing it,
     which is a layer on top and changes nothing underneath. */
  /* THE ORIGINAL GRADIENT, ONE STEP LESS BRIGHT. A flat 10% veil over it, which
     is the only way to take brightness off a fill without touching what it is
     made of: every hue keeps its own value and the three stay in exactly the
     relationship they had. Lightness, saturation and stop positions were each
     tried on the colours themselves and each one changed the gradient into a
     different gradient.
     ON TOP OF --grad-btn, NOT INSTEAD OF IT, so the token stays the site's own
     and Sign in still reads from the same definition. */
  /* NO VEIL ANY MORE, and the button is not brighter for it. The 10% black
     over the old stops was how "make it less bright" was delivered; the new
     stops are already that much deeper. Measured against what the owner
     approved: this is 1.2% off it, and keeping both would be 21.6% darker --
     which is the complaint she made five times. */
  background:var(--grad-btn);
  box-shadow:var(--sh-brand)}
/* AND SOMETHING CROSSES IT, left to right, over and over. White now, because
   the fill is saturated -- the tinted stripe that showed up on a near-white
   button is invisible on this one. Narrow, with long feathered edges, so at any
   instant it is over a fraction of the button and gone in half a second. */
.btn-free::after{content:'';position:absolute;inset:0;z-index:-1;pointer-events:none;
  background:linear-gradient(100deg,
    transparent 38%,
    rgba(255,255,255,.30) 47%,
    rgba(255,255,255,.42) 50%,
    rgba(255,255,255,.30) 53%,
    transparent 62%);
  transform:translateX(-115%);
  animation:freePass 2.8s cubic-bezier(.42,0,.3,1) infinite}
@keyframes freePass{
  0%,6%   {transform:translateX(-115%)}
  60%,100%{transform:translateX(115%)}
}
/* Anybody who has asked the system not to move things gets a button that does
   not move: the gradient sits where it starts and nothing crosses it. */
/* Anybody who has asked the system not to move things gets a plain solid
   button: the shine does not run and nothing else ever moved. */
@media (prefers-reduced-motion:reduce){
  .btn-free::after{animation:none;opacity:0}
}
.btn-free b{font-family:var(--f-dis);font-weight:800;font-size:15px;
  letter-spacing:-.012em;line-height:1.25;color:#fff}
/* The objection, answered on the button: free usually means an email address
   and a password first. It was --ink-3 on a white fill, about 3.7:1, which made
   the whole offer the faintest thing on the button making it. On the fill it is
   white at .92 -- 4.6:1 against the palest stop underneath. One line at every
   width, so two cards side by side are never different heights. */
.btn-free small{font-size:11.6px;font-weight:600;line-height:1.3;
  /* FULL WHITE, not .92 of it. Measured on the fill: .92 comes out 4.05:1
     against the palest stop, and this is 11.6px text, which needs 4.5. The
     eight per cent bought nothing and cost the line its legibility. */
  color:#fff;
  max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.btn-free:hover{transform:translateY(-2px);
  box-shadow:0 14px 34px -8px rgba(90,69,255,.55)}
@media (prefers-reduced-motion:reduce){ .btn-free:hover{transform:none} }

/* BUY NOW STEPS BACK SO START FREE TRIAL CAN LEAD. On this card the free entry
   is the offer worth shouting, and two solid buttons in one row shout over each
   other -- so this one is outlined in the brand colour instead of filled with
   it. Three weights in one family: Add to cart plain, Buy now outlined, Start
   Free Trial filled.
   BRAND TEXT ON WHITE IS 5.63:1, better than the 3.96 white-on-gradient it had.
   The swap costs it nothing in legibility; it only stops it competing.
   Scoped to the card's action row -- .btn-solid is the site's primary button
   everywhere else and is not touched. */
/* A REAL COLOURED OUTLINE, not --brand-line. That token is brand at 22% --
   #DAD6FF on a white button, 1.39:1, which is an edge you have to look for.
   Full brand is 5.63:1 and matches the button's own lettering, so the outline
   and the words read as one deliberate object rather than a box that happens to
   be near some purple text. It is also what keeps this distinct from Add to
   cart beside it, which keeps the grey --line-strong. */
/* A SUBTLE FILL UNDER THE OUTLINE, so it reads as a button rather than as a
   rectangle drawn on the card. --brand-soft is brand at 9% -- #F1EFFF on white,
   just enough to sit apart from Add to cart beside it, which stays plain.
   NOT .ccard .actrow. Scoped to .actrow alone, because the app repeats this row
   inside the What's inside modal and a Buy now that is outlined on the shelf
   and filled in the popup is two buttons wearing one name. .actrow exists in
   exactly two places in the app and one here -- checked before widening it. */
.actrow .btn-solid{background:var(--brand-soft);color:var(--brand);
  border:1.5px solid var(--brand);box-shadow:none}
/* .13, not .17. Brand text on a 17% tint is 4.37:1 -- a hover state is
   still text somebody reads, and it must not fall under 4.5 the moment a
   pointer touches it. */
/* MORE THAN SUBTLE, WITHOUT BECOMING A PURPLE SLAB. A 13% tint was a
   hover you had to look for; filling it solid brand was the other
   extreme and she said so -- "buy now hover is now too purple".
   What changes instead is the EDGE and the LIFT: the tint goes to 15%,
   which is as far as it can go with brand lettering on it (17% is
   4.37:1), the border doubles, and a brand shadow puts it above the
   card. Three things move at once, so it is unmistakable, and none of
   them is more colour. */
.actrow .btn-solid:hover{background:rgba(90,69,255,.15);
  border-color:var(--a1);color:var(--brand);transform:translateY(-2px);
  /* an inset ring rather than a thicker border: these buttons are
     border-box, so growing the border eats 1.5px of content on each
     side and the label shifts under the pointer. A shadow thickens the
     edge and moves nothing. */
  box-shadow:inset 0 0 0 1.5px var(--a1),0 8px 20px -8px rgba(90,69,255,.55)}

/* .btn-sm shrinks the shared button to 38px; this one carries two lines, so it
   keeps its own floor rather than being squeezed under its own text. */
.btn-free.btn-sm{min-height:46px;font-size:inherit}

/* --------------------------------------------------------------- in cart --
   A STATE, NOT AN ACTION, and it was painted like an action: soft violet beside
   the solid violet of Buy now, two strengths of one colour arguing. Once the
   course is in the cart there is nothing left to do here, so it goes quiet and
   the eye carries on to Checkout. Its own class rather than a repaint of
   .btn-g, which the certificate link and the read-aloud toggles also wear. */
.btn.incart{background:var(--surface-2);border:1px solid var(--line);
  color:var(--ink-3);box-shadow:none}
.btn.incart:hover{background:var(--surface-3);color:var(--ink-2);
  border-color:var(--line-strong);transform:none;box-shadow:none}
/* This is the ONLY definition of the state. There were two - the second, an
   --ok-soft green further down the file, was the original and won on order
   alone, so the quiet version above had no effect at all until it went.
   The app puts a real check icon in this button; this page has no icon set, so
   the tick is a character in front of the label. */
.btn.incart [data-lbl]::before{content:'✓';margin-right:7px}

/* ---------------------------------------------------------------- mini cart */
/* A bar that rises from the bottom the moment something is chosen, the way a
   music player does. It is only ever present when the cart has something in it:
   an empty cart bar is a permanent strip of nothing across the bottom of a
   phone, taking real estate from the thing being sold. */
.minicart{position:fixed;left:0;right:0;bottom:0;z-index:70;
  background:var(--glass);backdrop-filter:blur(18px);-webkit-backdrop-filter:blur(18px);
  border-top:1px solid var(--line-strong);box-shadow:0 -10px 34px -14px rgba(24,20,70,.28);
  transform:translateY(110%);transition:transform .34s var(--spring);
  padding-bottom:env(safe-area-inset-bottom,0)}
.minicart.up{transform:none}
.mc-in{max-width:var(--maxw);margin:0 auto;display:flex;align-items:center;gap:12px;
  padding:11px clamp(14px,4vw,32px)}
.mc-list{flex:1 1 auto;display:flex;align-items:center;gap:11px;min-width:0;text-align:left}
.mc-n{flex:0 0 auto;width:30px;height:30px;border-radius:10px;display:grid;place-items:center;
  background:var(--grad-btn);color:#fff;font-family:var(--f-dis);font-weight:800;font-size:14px;
  box-shadow:var(--sh-brand)}
.mc-t{min-width:0;display:block}
.mc-t b{display:block;font-family:var(--f-dis);font-weight:800;font-size:17px;
  letter-spacing:-.03em;line-height:1.1}
.mc-t i{display:block;font-style:normal;color:var(--ink-3);font-size:12.4px;margin-top:1px}
.mc-chev{color:var(--ink-4);font-size:13px;transition:transform .25s}
.minicart.on .mc-chev{transform:rotate(180deg)}
.mc-go{flex:0 0 auto}
.mc-open{max-width:var(--maxw);margin:0 auto;padding:0 clamp(14px,4vw,32px) 12px;
  max-height:40vh;overflow:auto}
.mc-row{display:flex;align-items:center;gap:10px;padding:9px 0;
  border-top:1px solid var(--line)}
.mc-row b{flex:1 1 auto;min-width:0;font-family:var(--f-dis);font-weight:650;font-size:14px;
  letter-spacing:-.015em}
.mc-row span{flex:0 0 auto;color:var(--ink-2);font-size:13.6px;font-weight:650}
/* A course a bundle in the cart already pays for. Struck through with the
   bundle named beside it, rather than dropped from the list — the total has to
   add up in front of the person reading it. */
.mc-row span.mc-was{text-decoration:line-through;color:var(--ink-4);font-weight:600}
.mc-row .mc-inc{display:block;font-style:normal;font-family:var(--f-txt,inherit);
  font-weight:600;font-size:11.8px;color:var(--ink-3);margin-top:1px}
.mc-x{flex:0 0 auto;width:26px;height:26px;border-radius:8px;display:grid;place-items:center;
  background:var(--surface-2);border:1px solid var(--line);color:var(--ink-3);font-size:15px;
  line-height:1}
.mc-x:hover{background:var(--no-soft);border-color:var(--no-line);color:var(--no)}

/* A cart open at the bottom must not hide the last card on the page. */
body:has(.minicart.up){padding-bottom:78px}

/* --------------------------------------------------------------- not found */
.notfound{text-align:center;padding:clamp(60px,10vw,110px) 20px}
.notfound h1{font-size:clamp(27px,4.4vw,38px)}
.notfound p{margin:14px auto 0;color:var(--ink-2);max-width:44ch;font-size:16px;line-height:1.6}
.notfound .btn{margin-top:26px}

/* ============================================================================
   SIGN IN AND REGISTER — /login and /register

   Two columns on a wide screen: the form, and the reason the form looks the way
   it does. On a phone the form comes first and the explanation follows it, which
   is the order somebody who already knows what they are doing needs.
   ========================================================================== */

/* padding-top/bottom and NOT the shorthand: this element is also a .wrap, and
   `padding: … 0 …` quietly resets the side gutter .wrap sets, which put the
   card hard against both edges of a phone. */
.authwrap{display:grid;gap:clamp(24px,4vw,52px);align-items:start;
  padding-top:clamp(28px,5vw,64px);padding-bottom:clamp(40px,6vw,80px);
  grid-template-columns:minmax(0,1fr)}
@media (min-width:900px){.authwrap{grid-template-columns:minmax(0,440px) minmax(0,1fr)}}

.authcard{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-2xl);
  padding:clamp(24px,3.4vw,34px);box-shadow:var(--sh-3)}
.authcard h1{font-size:clamp(26px,3.6vw,33px);line-height:1.08}
.authsub{margin-top:10px;color:var(--ink-2);font-size:15px;line-height:1.55}

/* ------------------------------------------------------------------ google */
.gbtn{width:100%;margin-top:22px;gap:11px}
.ordiv{display:flex;align-items:center;gap:14px;margin:20px 0 4px;
  color:var(--ink-4);font-size:12px;font-family:var(--f-dis);font-weight:700;
  letter-spacing:.14em;text-transform:uppercase}
.ordiv::before,.ordiv::after{content:'';flex:1 1 auto;height:1px;background:var(--line)}

/* ------------------------------------------------------------------ fields */
.authcard form{margin-top:18px}
.field{display:block;margin-top:16px}
.field>span{display:block;font-family:var(--f-dis);font-weight:700;font-size:13.4px;
  color:var(--ink-2);margin-bottom:7px}
.field>span i{font-style:normal;font-weight:600;font-size:12px;color:var(--ink-4);
  margin-left:6px;letter-spacing:.02em}
.field input{width:100%;height:50px;padding:0 15px;border-radius:var(--r-md);
  background:var(--surface-2);border:1.5px solid var(--line-strong);color:var(--ink);
  font:inherit;font-size:15.5px;transition:border-color .2s,background .2s,box-shadow .2s}
/* iOS Safari zooms the page whenever a focused input is smaller than 16px, so
   on a phone every tap on a field jolted the layout. Exactly 16, and only where
   it matters. */
@media (max-width:768px){.field input{font-size:16px}}
.field input::placeholder{color:var(--ink-4)}
.field input:hover{border-color:var(--brand-line)}
.field input:focus{outline:none;border-color:var(--brand);background:var(--surface);
  box-shadow:0 0 0 4px var(--brand-soft)}

/* A PASSWORD FIELD WITH A REVEAL, on sign-in and on Create an account.
   Dots cannot be proof-read, and on the create form the password is set blind
   and typed twice -- which is why it IS typed twice. Seeing it is the better
   answer to the same problem.

   The wrapper only positions; the input keeps every one of its own styles
   including the focus ring, so a field with a button in it and one without look
   identical. Named pwbox to match the dashboard, and NOT pwrap, which is
   already taken there by the Pricing card -- a lesson learned ten minutes
   earlier in this same afternoon.

   A <button> inside a <label> is safe: clicking an interactive element does not
   forward to the labelled control, so the eye does not also re-focus the field
   twice. */
.pwbox{position:relative;display:block}
.pwbox>input{padding-right:48px}
.peye{position:absolute;right:7px;top:50%;transform:translateY(-50%);
  width:36px;height:36px;display:grid;place-items:center;
  background:none;border:0;border-radius:10px;cursor:pointer;
  color:var(--ink-3);transition:color .18s,background .18s}
.peye:hover{color:var(--ink);background:var(--surface-2,var(--surface))}
.peye:focus-visible{outline:none;color:var(--brand);box-shadow:0 0 0 3px var(--brand-soft)}
.peye svg{width:19px;height:19px}
/* The struck-through eye is drawn rather than swapped, so the two states need
   only one icon in the markup and cannot fall out of step with each other. */
.peye.off::after{content:'';position:absolute;left:8px;top:50%;width:20px;height:2px;
  background:currentColor;transform:rotate(-45deg);border-radius:2px}
.field small{display:block;margin-top:7px;color:var(--ink-3);font-size:12.6px;
  line-height:1.5}
.field[hidden]{display:none}

/* Six digits, spaced out and large, because they are read off one screen and
   typed on another and a typo means asking for a new one. */
.codein{font-family:var(--f-dis);font-size:24px !important;font-weight:750;
  letter-spacing:.36em;text-align:center;height:58px !important}

.authcard .btn-lg{width:100%;margin-top:20px}
.authcard button[disabled]{opacity:.6;cursor:default}

/* ---------------------------------------------------------------- messages */
.saidno,.saidyes{margin-top:16px;padding:12px 14px;border-radius:var(--r-md);
  font-size:14px;line-height:1.5;font-weight:600}
.saidno{background:var(--no-soft);border:1px solid var(--no-line);color:var(--no)}
.saidyes{background:var(--ok-soft);border:1px solid var(--ok-line);color:var(--ok)}
.saidno[hidden],.saidyes[hidden]{display:none}

/* ------------------------------------------------- an address that moved --
   Deliberately NOT the red error box. Nothing has gone wrong and nobody is
   being refused — they are being asked which of two true things they meant. Red
   would tell them they had made a mistake, and then the button that keeps their
   courses would look like the risky one. */
.movedbox{margin-top:18px;padding:16px 18px;border-radius:var(--r-lg);
  background:var(--brand-soft);border:1px solid var(--brand-line)}
.movedbox b{display:block;font-family:var(--f-dis);font-size:15.4px;color:var(--ink);
  letter-spacing:-.015em}
.movedbox p{margin-top:6px;color:var(--ink-2);font-size:14px;line-height:1.55}
/* One under the other, on purpose and at every width. These two are sentences
   rather than the short equal actions .actrow is for, and side by side they
   would clip. Stacking them ALWAYS is the point: a row that fits at one width
   and wraps at another is the thing that looks broken. The one that keeps
   somebody's courses is on top. */
.movedacts{display:flex;flex-direction:column;gap:8px;margin-top:14px}
.movedacts .btn{width:100%}
.movedbox small{display:block;margin-top:12px;color:var(--ink-3);font-size:12.6px;
  line-height:1.5}

/* --------------------------------------------------------- the other ways */
/*
 * TWO OFFERS AND A LINK. Ten designs for this block were drawn against the real
 * card and photographed at phone width, and on 12 September 2026 she chose this
 * one: two plain outlined buttons with a gap, and "I already have a code" as
 * plain underlined text below them. No joined container, no tint, no icons, no
 * dividing rule.
 *
 * The three things that are hers and are not to be undone by a later tidy-up:
 *   - "1 row is mandatory, never 2 rows for 2 buttons" -- the pair are grid
 *     columns, so neither can ever drop below the other;
 *   - "the corners must match sign in button [...] I am not talking about
 *     height, I want the corners matched" -- .btn-lg is 17px, so these are;
 *   - "the gap should be same as the gap between no password and 2 buttons" --
 *     the two 15s below are that, and they move together or not at all.
 *
 * WHAT IT COSTS. Two separate buttons spend two borders and four paddings where
 * a joined control spends one border and one divider, so each label has roughly
 * 20px less to sit in than the design this replaced. That is paid for by the
 * type ladders further down, and it is why there are two of them.
 */

/* The three words that say who the buttons are for. Somebody who has a password
   was meeting three unexplained buttons under Sign in and had to read all three
   to find out that none of them was for them. */
.authaltlead{margin-top:18px;padding-top:18px;border-top:1px solid var(--line);
  color:var(--ink-3);font-family:var(--f-dis);font-weight:650;font-size:13px;
  letter-spacing:.01em}
.authalt{margin-top:15px}

.authseg{display:grid;grid-template-columns:1fr 1fr;gap:9px}
.authseg button{display:inline-flex;align-items:center;justify-content:center;
  text-align:center;border:1px solid var(--line);background:var(--surface-2);
  border-radius:17px;
  font-family:var(--f-dis);font-weight:650;font-size:13.4px;
  color:var(--ink-2);padding:9px 6px;min-height:38px;
  transition:background .2s,color .2s,border-color .2s;cursor:pointer}
.authseg button:hover:not([disabled]){color:var(--brand);background:var(--brand-soft);
  border-color:var(--brand-line)}

/* NOT ONE OF THE PAIR, and so not a third button: the two above send you
   something, this one is for the code already sitting in the inbox.

   THE 15 IS THE SAME 15 AS .authalt's, and that is its whole reason: "the gap
   should be same as the gap between no password and 2 buttons". It was 11 and
   read as crowding. Both land 17.5px of clear air between the ink above and the
   ink below -- the lead's line box overhangs its text by 2.6px at the bottom
   and this one's by 2.4px at the top, measured on the rendered page rather than
   assumed. Change one of the two and change the other. */
.authalt [data-act="havecode"]{display:block;width:100%;background:none;
  border:0;margin-top:15px;padding:0;color:var(--ink-3);font-size:13px;
  font-family:var(--f-dis);font-weight:600;cursor:pointer;
  text-decoration:underline;text-underline-offset:3px;transition:color .2s}
.authalt [data-act="havecode"]:hover:not([disabled]){color:var(--brand)}

/* ── the ladder, for the pair as they normally read ──────────────────────────
 *
 * "Email me a 6-digit code" needs 143px at 13.4px and each column gets about
 * half the card, so the type steps down as the phone narrows. Where the steps
 * go is not where they look like they should, and each of these was found by
 * driving a width rather than by reading the rule:
 *
 *   - 13.4px runs out at 405, not at 400. A first rung at 400 left a five-pixel
 *     dead zone where the label wrapped and the buttons grew from 41px to 63.
 *   - 12.5px still has 7px to spare at 400, so its rung is at 400.
 *   - 11.5px has 2.8px left at 360 -- a very common Android width and far too
 *     close to call -- so a fourth rung at 375 puts 10.8px back.
 *
 * MEASURED AFTER THE CHANGE at thirteen widths. The label width is taken from
 * an unconstrained nowrap clone: a button that is ALREADY wrapping reports its
 * wrapped width and flatters itself, which cost a round earlier the same day.
 *
 *   1280 / 768 / 470 / 430   13.4px   41.7px tall   one line
 *   414 / 411 / 402 / 400    12.5px   40.3px tall   one line
 *   399 / 390 / 375          11.5px   38.6px tall   one line
 *   360                      11px     38px tall     one line
 *   320                      11px     wraps inside, still one row
 *
 * 320 is a 2016 iPhone SE and it wrapped under the previous design too, so it
 * is not a regression. Nothing scrolls sideways at any of the thirteen. */
@media (max-width:429.98px){
  .authseg button{font-size:12.5px}
}
@media (max-width:399.98px){
  .authseg button{font-size:11.5px;padding:9px 4px;letter-spacing:-.004em}
}
@media (max-width:374.98px){
  .authseg button{font-size:11px;padding:9px 3px}
}

/* ── and the ladder for code mode ────────────────────────────────────────────
 *
 * Once a code has been asked for, "Use my password instead" takes the code
 * button's place. It is the longest label of the four -- 154.6px at 13.4px
 * against 143 -- and the columns are equal, so the longer one decides for both
 * and every rung has to move up.
 *
 * Driven at the same thirteen widths with the card put into code mode first,
 * which is the only reason this was found: with the ladder above alone, code
 * mode wrapped at 400 and 402 and had half a pixel to spare at 430.
 *
 *   1280 / 768 / 470   13.4px   41.7px tall   one line
 *   430                12.5px   40.3px tall   one line
 *   414 ... 390        11.5px   38.6px tall   one line
 *   375                11px     38px tall     one line
 *   360                10.5px   38px tall     one line
 *   320                10.5px   wraps inside, still one row
 *
 * Higher specificity than the ladder above, so source order does not matter. */
@media (max-width:444.98px){
  .authcard[data-mode="code"] .authseg button{font-size:12.5px}
}
@media (max-width:419.98px){
  .authcard[data-mode="code"] .authseg button{font-size:11.5px;padding:9px 4px;
    letter-spacing:-.004em}
}
@media (max-width:384.98px){
  .authcard[data-mode="code"] .authseg button{font-size:11px;padding:9px 3px}
}
@media (max-width:364.98px){
  .authcard[data-mode="code"] .authseg button{font-size:10.5px}
}

/* The swap itself. Exactly two are ever visible, so the pair is never one and
   never three; and "I already have a code" goes with it, because in code mode
   the code field is already on screen. */
.authseg .onlycode{display:none}
.authcard[data-mode="code"] .authseg .onlycode{display:inline-flex}
.authcard[data-mode="code"] .authseg [data-act="code"],
.authcard[data-mode="code"] .authalt [data-act="havecode"]{display:none}

.authfoot{margin-top:18px;color:var(--ink-3);font-size:13.8px;line-height:1.55}
.authfoot a{color:var(--brand);font-weight:650}
.authfoot a:hover{text-decoration:underline}

/* ------------------------------------------------------------- the aside */
.authside{padding-top:clamp(0px,1vw,10px)}
/* TWO LINES' WORTH, WHETHER OR NOT IT USES THEM. "same location on register":
   the sign-in heading wraps to two lines and the register one fits on a single
   line, so the three cards below started 31px apart -- exactly the difference.
   Nothing else above them differs. min-height and not height, so a narrow
   window may wrap either of them further without clipping. */
.authside h2{font-size:clamp(21px,2.8vw,27px);line-height:1.15;max-width:20ch;
  min-height:2.3em}
.authside>p{margin-top:14px;color:var(--ink-2);font-size:15.4px;line-height:1.65;
  max-width:52ch}
.authpts{display:flex;flex-direction:column;gap:11px;margin-top:22px}
.authpts>div{background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-md);padding:14px 16px}
.authpts b{display:block;font-family:var(--f-dis);font-size:14.8px;letter-spacing:-.015em}
.authpts span{display:block;margin-top:4px;color:var(--ink-3);font-size:13.6px;
  line-height:1.5}
.authside .btn{margin-top:20px}

/* ==========================================================================
   BOOKING  (/book)

   One column, one question at a time. Each step appears when the one above it
   is answered and then STAYS there - a wizard that swallows the last answer
   makes somebody who picked the wrong day start again, and the wrong day is
   the mistake people actually make.

   Written for a 375px phone first and widened from there. Every control below
   is at least 46px tall and every row stacks by default, because the desk
   layout is the easy half and the phone is where this will be read.
   ========================================================================== */
/* padding-block, NOT the shorthand. `padding: A 0 B` sets left and right to
   zero, which overrode the side margins .wrap gives every page on this site -
   so on a phone the whole booking flow sat against the glass. */
.bookwrap{padding-block:clamp(24px,5vw,56px) clamp(56px,9vw,110px);max-width:720px}
.bookwrap h1{font-size:clamp(25px,4.4vw,38px);line-height:1.1;letter-spacing:-.03em}
.bookwrap .lede{margin-top:10px;color:var(--ink-2);font-size:15.5px;line-height:1.6;
  max-width:54ch}

/* A rule above each step, so the eye can find where one question ends and the
   next begins without any colour being spent on it. */
.bk-step{margin-top:26px;padding-top:22px;border-top:1px solid var(--line)}
.bk-step[hidden]{display:none}
.bk-step:first-of-type{border-top:0;padding-top:0}
.bk-h{font-family:var(--f-dis);font-size:12.5px;letter-spacing:.09em;color:var(--ink-3);
  text-transform:uppercase;font-weight:750;margin-bottom:13px}
.bk-note{margin-top:12px;color:var(--ink-3);font-size:14.4px;line-height:1.6}
.bk-note.bad{color:#D1293D;font-weight:600}
.bk-one{font-size:15.5px;color:var(--ink);font-weight:600}
.bk-chosen{font-size:15.5px;color:var(--brand);font-weight:700;margin-bottom:16px}

/* ------------------------------------------------------- what to book --- */
/* Two across on a phone as well as a desk. One per row made a list of three
   sessions taller than the screen before anybody had chosen anything. */
.bk-cards{display:grid;gap:10px;grid-template-columns:repeat(2,minmax(0,1fr))}
@media (max-width:319px){ .bk-cards{grid-template-columns:1fr} }
/* Name, a line about it, and one line of facts pinned to the bottom - so
   cards beside each other line up along their prices however long the
   descriptions run. */
.bk-card{display:flex;flex-direction:column;gap:7px;text-align:left;width:100%;
  height:100%;background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-lg);padding:15px 16px 13px;cursor:pointer;
  transition:border-color .18s,box-shadow .18s,transform .18s}
.bk-card:hover{border-color:var(--brand);transform:translateY(-1px);
  box-shadow:0 6px 20px -12px rgba(20,20,60,.35)}
.bk-card.on{border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-soft)}
.bk-card-n{font-family:var(--f-dis);font-size:16.5px;letter-spacing:-.02em;color:var(--ink);
  line-height:1.25}
/* Three lines of description at most: past that a card of two hundred pixels
   is a paragraph, and the sessions beside it stop lining up. */
.bk-card-b{color:var(--ink-3);font-size:13.2px;line-height:1.45;display:-webkit-box;
  -webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
.bk-card-f{display:flex;align-items:baseline;justify-content:space-between;gap:8px;
  margin-top:auto;padding-top:10px;border-top:1px solid var(--line)}
.bk-card-t{font-size:12.4px;color:var(--ink-4);white-space:nowrap}
.bk-card-p{font-family:var(--f-dis);font-size:15.5px;letter-spacing:-.02em;
  color:var(--brand);white-space:nowrap}

/* ---------------------------------------------------------- how to meet -- */
/* CARDS, TWO ACROSS, and squarish - which is what they were before I widened
   them into bars while fixing the page margins. Two problems were live at once
   and I fixed the wrong one: the shape was never wrong, the missing margins
   were.
   Two columns at every width down to 320px. At 375 that is 166px each inside
   the page margins, which is a card; below 320 they stack rather than shrink
   to something nobody can read. */
/* However many ways there are, they share the full width in one row: two get a
   half each, three get a third, and the row lines up with the day strip and the
   times underneath it. grid-auto-columns rather than a fixed count, so adding
   "in person" to a session does not need a rule here. */
.bk-ways{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(0,1fr);gap:10px}
/* Under about 340px three of them stop being readable, so they stack. */
@media (max-width:339px){
  .bk-ways{grid-auto-flow:row;grid-auto-columns:auto}
}
.bk-way{display:flex;flex-direction:column;align-items:flex-start;justify-content:space-between;
  gap:10px;width:100%;min-height:118px;text-align:left;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:15px 16px;cursor:pointer;transition:border-color .18s,box-shadow .18s}
.bk-way:hover{border-color:var(--brand)}
.bk-way.on{border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-soft)}
.bk-way-n{font-weight:650;font-size:14.6px;color:var(--ink-2);line-height:1.35}
.bk-way-p{font-family:var(--f-dis);font-size:21px;letter-spacing:-.03em;color:var(--brand);
  white-space:nowrap}

/* --------------------------------------------------------------- when --- */
/* THE DAYS IN A LINE, with a button at each end that moves it. Only the days
   that have times are in it, so four weeks is a dozen or so pills rather than
   thirty-one.

   No scrollbar and no month: the horizon is four weeks, which arrives in one
   request, so paging by month was machinery for a range that never holds more
   than one. The arrows are the affordance, and they grey out at each end.

   Full width, so the strip and the times underneath it line up. */
.bk-cal{display:flex;align-items:stretch;gap:8px}
/* Quiet: a chevron and nothing else. They are a convenience beside a strip
   that swipes perfectly well on its own, and a bordered box each side made
   them look like the main event. */
.bk-nav{flex:0 0 auto;width:24px;border:0;background:transparent;
  font-size:23px;line-height:1;color:var(--ink-4);cursor:pointer;padding:0;
  transition:color .18s,opacity .18s}
.bk-nav:hover:not(:disabled){color:var(--brand)}
.bk-nav:disabled{opacity:.22;cursor:default}

.bk-days{display:flex;gap:8px;overflow-x:auto;flex:1 1 auto;min-width:0;
  scroll-snap-type:x proximity;-webkit-overflow-scrolling:touch;
  scrollbar-width:none;-ms-overflow-style:none}
.bk-days::-webkit-scrollbar{width:0;height:0}

/* Square. Three short lines - MON, 17, AUG - centred in a box the same width
   as it is tall. */
.bk-cell{flex:0 0 auto;scroll-snap-align:start;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:0;width:66px;aspect-ratio:1/1;
  padding:6px;cursor:pointer;background:var(--surface);
  border:1px solid var(--line);border-radius:var(--r-md);
  transition:border-color .16s,background .16s,color .16s}
.bk-cell:hover{border-color:var(--brand)}
.bk-cell.on{background:var(--brand);border-color:var(--brand)}
.bk-day-w{font-size:10px;font-weight:750;letter-spacing:.05em;text-transform:uppercase;
  color:var(--ink-4);line-height:1.4}
.bk-day-d{font-family:var(--f-dis);font-size:19px;letter-spacing:-.03em;color:var(--ink);
  line-height:1.25}
.bk-day-m{font-size:10px;color:var(--ink-4);text-transform:uppercase;letter-spacing:.04em;
  line-height:1.4}
.bk-cell.on .bk-day-w,.bk-cell.on .bk-day-d,.bk-cell.on .bk-day-m{color:#fff}

/* TWENTY-EIGHT TIMES IN ONE GRID IS A WALL, so they arrive in three named
   parts - morning, afternoon, evening - and each part is a short block with a
   quiet heading. Nobody scans 28 pills; everybody scans "evening". */
.bk-times{margin-top:18px}
.bk-part{margin-top:14px}
.bk-part:first-child{margin-top:0}
.bk-part-h{font-size:11.5px;font-weight:750;letter-spacing:.09em;text-transform:uppercase;
  color:var(--ink-4);margin-bottom:8px}
.bk-part-g{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}
@media (min-width:560px){ .bk-part-g{grid-template-columns:repeat(4,minmax(0,1fr))} }
.bk-time{min-height:46px;padding:0 10px;cursor:pointer;font-weight:500;font-size:14.5px;
  white-space:nowrap;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-md);
  transition:border-color .18s,background .18s,color .18s}
.bk-time:hover{border-color:var(--brand);color:var(--brand)}
.bk-time.on{background:var(--brand);border-color:var(--brand);color:#fff}

/* ---------------------------------------------------------------- who --- */
.bk-field{display:block;margin-bottom:12px}
.bk-field>span{display:block;font-size:13.5px;font-weight:650;color:var(--ink-2);
  margin-bottom:6px}
.bk-in{width:100%;height:50px;padding:0 15px;font:inherit;font-size:16px;
  color:var(--ink);background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-md);transition:border-color .18s,box-shadow .18s}
/* 16px and not 15px: anything smaller makes iOS zoom the whole page in when
   the field is tapped, and the page never zooms back out. */
.bk-in:focus{outline:none;border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-soft)}

/* A country and a number, side by side. Added 19 August 2026 with the country
   selector: before it, a student outside India had to know to type +44 in
   front, which is a thing people learn only by being caught out once.

   The select keeps .bk-in so it matches every other control on the form --
   same height, same border, same focus ring -- and only the width is
   overridden, because .bk-in is 100% and two of those side by side is not a
   row. Padding on the right leaves room for the arrow the browser draws.

   min-width:0 on the number is what stops flex refusing to shrink it: a flex
   item's default minimum is its content, so on a 320px screen the row would
   otherwise push past the edge and take the page with it. */
.bk-phone{display:flex;gap:8px;align-items:stretch}
.bk-phone>input{flex:1 1 auto;min-width:0}
.bk-ccwrap{position:relative;flex:0 0 auto}

/* The country button. Narrow on purpose -- it shows the code and nothing else,
   because the country's name is what you pick BY and the code is what you need
   to see afterwards. The full name is on the title attribute and on every row
   of the panel. */
.bk-cc{width:auto;min-width:86px;text-align:left;cursor:pointer;
  display:flex;align-items:center;gap:6px;padding-right:26px;position:relative}
.bk-cc::after{content:'';position:absolute;right:12px;top:50%;width:7px;height:7px;
  border-right:2px solid var(--ink-3);border-bottom:2px solid var(--ink-3);
  transform:translateY(-70%) rotate(45deg)}
.bk-cc[aria-expanded="true"]{border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-soft)}

/* THE PANEL, AND WHY IT IS NOT A <select>. A native select hands a phone's
   operating system the whole screen -- a wheel on iOS, a dialog down the middle
   on Android -- and fifty-seven rows in either is a lot of screen for one small
   answer. This stays anchored to the field, keeps the page visible behind it,
   and is capped at 40% of the viewport so it never becomes the screen.

   min() rather than a fixed width: 280px on anything with room, and never wider
   than the viewport less a margin, so it cannot push the page sideways on a
   narrow phone. */
.bk-ccpop{position:absolute;z-index:40;top:calc(100% + 6px);left:0;
  width:min(280px, calc(100vw - 40px));
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-md);
  box-shadow:0 12px 34px rgba(16,18,34,.16);overflow:hidden}
.bk-ccpop[hidden]{display:none}
.bk-ccq{width:100%;height:42px;padding:0 13px;font:inherit;font-size:16px;
  color:var(--ink);background:transparent;border:0;border-bottom:1px solid var(--line)}
/* The same ring .bk-in gets four lines up. Removing the outline and putting
   nothing back left the country-code search with no visible focus at all,
   which is the one control in this form a keyboard reaches by surprise. */
.bk-ccq:focus{outline:none;border-color:var(--brand);
  box-shadow:0 0 0 3px var(--brand-soft)}
.bk-cclist{max-height:min(40vh, 300px);overflow-y:auto;-webkit-overflow-scrolling:touch}
.bk-ccrow{padding:11px 13px;font-size:15px;color:var(--ink);cursor:pointer;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* .on is the keyboard's place in the list, .sel is what is currently chosen.
   They are different things and both need to be visible at once. */
.bk-ccrow.on{background:var(--brand-soft)}
.bk-ccrow.sel{font-weight:700}
.bk-ccnone{padding:14px 13px;font-size:14px;color:var(--ink-3)}
.bk-go{margin-top:18px}
.bk-go .btn{width:100%;min-height:52px}
@media (min-width:560px){ .bk-go .btn{width:auto;min-width:250px} }

/* --------------------------------------------------------------- done --- */
.bk-done-h{font-family:var(--f-dis);font-size:clamp(23px,3.6vw,31px);letter-spacing:-.03em}
.bk-done-w{margin-top:9px;font-family:var(--f-dis);font-size:clamp(19px,3vw,25px);
  letter-spacing:-.02em;color:var(--brand)}
.bk-done-s{margin-top:4px;color:var(--ink-2);font-size:15px}
#bkDone .btn{margin-top:20px;min-height:50px}
.bk-tz{margin-top:20px;color:var(--ink-4);font-size:12.8px}

/* ═══════════════════════════════════════════════════ the front page ═══
   academy.minnati.com/ — see server/pages/home/index.php.

   Everything below is built from the tokens above rather than from new ones.
   The first two drafts of this page invented their own palette and typeface
   and had to be thrown away: a landing page in a second visual language means
   a visitor changes companies on the way to the store. */

/* the line under the headline. Instrument Serif, because it is a person
   speaking rather than a claim being made, and the small-caps name inside it
   is set in the UI face so it reads as a name and not as prose. */
/* THE STAND-FIRST, AND WHY ITS NAME LOOKED WRONG.
 *
 * This rule was written while --f-ser was undefined in this file, so the line
 * silently rendered in the body sans and the <b> inside it was a slightly
 * smaller bold sans -- consistent, if not what was intended. The moment the
 * token was declared and Instrument Serif actually loaded, the sentence became
 * a serif with a smaller, letter-spaced, all-caps SANS name embedded in the
 * middle of it. The owner: "the font of taught mu minati mam is looking bad."
 *
 * ONE FAMILY, ONE SIZE, AND THE NAME STANDS OUT BY COLOUR. A name set in a
 * different family at 84% of the surrounding size reads as a sticker somebody
 * pasted into the sentence. Set in the same serif at the same size, in the
 * darkest ink against the line's --ink-2, it reads as emphasis -- which is all
 * it ever needed to be. The gradient is deliberately not used here: the h1
 * directly above already has a gradient word in it, and two in three inches is
 * one too many. */
/* A CARD, BECAUSE IT IS A CREDENTIAL AND NOT A SENTENCE SOMEBODY SAID.
 *
 * This line was set in --f-ser, and in this stylesheet the serif has exactly
 * one job: quoted speech and book titles -- .hm-sentence, .hm-say blockquote,
 * .ab-open, .eb-cover h3, .tr-a p, .cls-teach span. "Taught by Minati Ma'am --
 * twenty years reading the news" is a fact about the teacher, so it was the
 * only place in the product where the serif did something it does nowhere
 * else. The owner: "kinda not part of the seamless design system, looks odd",
 * and then "maybe you can give it a background kind of card".
 *
 * So it becomes the page's own furniture: --surface, --line, --r-lg, --sh-1,
 * lettered in the UI family, with the tv icon that this exact fact already
 * carries in .onair-src and on /about. The name is the darkest ink against the
 * line's --ink-2, which is the emphasis it had before and all it needed. */
.hm-stand{display:flex;align-items:flex-start;gap:11px;
  margin:0;max-width:31em;
  font-family:var(--f-ui);font-size:clamp(14.5px,1.85vw,16px);line-height:1.5;
  font-weight:450;color:var(--ink-2)}
.hm-stand .ic{flex:0 0 auto;font-size:19px;color:var(--brand);margin-top:1px}
.hm-stand b{font-family:var(--f-dis);font-weight:800;font-size:1.02em;
  letter-spacing:-.015em;color:var(--ink)}

/* ONE CARD INSTEAD OF FOUR. The credential sentence had its own card and each
   of the three numbers had another, so this column carried four white surfaces
   stacked with five different gaps between them. They are one object now: the
   sentence, a hairline, and the numbers under it.

   .trust IS SHARED WITH /elearn, so its own rule is untouched -- the tiles lose
   their chrome only in here. */
/* WHITE AGAIN. It was coloured for a while and is not any more: "make the
   taught by minati ma'am card the previous color" -- the colour moved across
   the hero to the class panel instead. The padding is 15/17/7 rather than
   16/18/8 because the coloured version had dropped the 1px border and was
   paying it back in padding. */
.hm-cred{max-width:31rem;background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-lg);box-shadow:var(--sh-1);padding:15px 17px 7px}
.hm-cred .trust{margin-top:13px;padding-top:12px;gap:8px;
  border-top:1px solid var(--line)}
.hm-cred .trust>div{background:none;border:0;padding:0 0 9px}
/* --ink-2, not the shared --ink-3. #7B7FA4 on white is 3.88:1 at 12px --
   under the bar, and a property of this palette rather than anything new,
   but there was no reason to inherit it into a card being built from
   scratch. 7.26:1, and still quieter than the numeral above it. The base
   .trust rule is left alone because /elearn uses it too.

   The white-on-colour overrides that used to sit here are DELETED rather than
   overridden, so the base rules do the work again: .hm-stand is --ink-2, its
   icon is --brand, its bold is --ink, and the numerals get back the var(--grad)
   background-clip that makes them gradient text on white. */
.hm-cred .trust span{color:var(--ink-2)}

/* THE SPACING IS A SCALE NOW, and it was not. Measured before touching it, the
   gaps down this column ran 0, 18, 14, 26, 29 -- five numbers in no order, with
   the tightest of them in the MIDDLE, so the two middle blocks read as one lump
   while the eyebrow was glued to the headline. It grows as the blocks get less
   related to each other: a label, its headline, the line under it, the proof,
   the thing to press. */
.hero .eyebrow{margin-bottom:10px}
.hero .lede{margin-top:18px}
.hero .hm-cred{margin-top:22px}
.hero .herocta{margin-top:28px}
.tag.no{background:var(--no-soft);color:var(--no)}

/* ----------------------------------------------------------- the test */
.hm-test{padding:0;overflow:hidden}
.hm-testgrid{display:grid}
@media (min-width:900px){ .hm-testgrid{grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr)} }
.hm-tl{padding:clamp(20px,3.2vw,32px)}
.hm-tl .eyebrow{margin-bottom:16px}
.hm-sentence{font-family:var(--f-ser);font-size:clamp(25px,4.6vw,37px);line-height:1.32;
  color:var(--ink);margin:0 0 18px}
/* a word is a button, so it must not look like one: no border, no background,
   only the underline that says it can be pressed */
.hm-w{display:inline;border:0;background:none;padding:2px 4px;margin:0;font:inherit;
  color:inherit;cursor:pointer;border-radius:7px;
  box-shadow:inset 0 -2px 0 var(--line-strong);
  transition:background .14s var(--ease),color .14s var(--ease),box-shadow .14s var(--ease)}
.hm-w:hover{background:var(--brand-soft);box-shadow:inset 0 -2px 0 var(--brand)}
.hm-w:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.hm-w.is-wrong{background:var(--no-soft);color:var(--no);box-shadow:none;
  text-decoration:line-through;text-decoration-thickness:2px}
.hm-w.is-done{cursor:default;box-shadow:none}
.hm-w.is-nope{animation:hmNudge .28s var(--spring)}
@keyframes hmNudge{25%{transform:translateX(-3px)}75%{transform:translateX(3px)}}
.hm-hint{font-size:14.5px;color:var(--ink-3);margin:0;min-height:1.6em}
.hm-reveal{background:var(--surface-2);border-top:1px solid var(--line);
  padding:clamp(20px,3.2vw,32px);animation:hmRise .32s var(--ease)}
@media (min-width:900px){ .hm-reveal{border-top:0;border-left:1px solid var(--line)} }
@keyframes hmRise{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.hm-after{margin:0;font-family:var(--f-dis);font-weight:750;font-size:14.5px}
.hm-after a{color:var(--brand);text-decoration:none}
.hm-after a:hover{text-decoration:underline}

/* -------------------------------------------------------- the chooser */
.hm-pick{padding:0;overflow:hidden}
.hm-q{padding:clamp(20px,3.2vw,28px)}
.hm-q + .hm-q{border-top:1px solid var(--line)}
.hm-qh{display:flex;gap:12px;align-items:baseline;margin:0 0 16px}
.hm-qn{font-family:var(--f-dis);font-size:11.5px;font-weight:750;letter-spacing:.14em;
  color:var(--ink-4);flex:none;padding-top:3px}
.hm-qt{font-family:var(--f-dis);font-size:17.5px;font-weight:800;letter-spacing:-.01em;
  color:var(--ink);margin:0;line-height:1.3}
.hm-opts{display:flex;flex-wrap:wrap;gap:9px}
.hm-opt{font-family:var(--f-ui);font-size:15px;font-weight:500;color:var(--ink);
  background:var(--surface-2);border:1px solid var(--line-strong);
  border-radius:var(--r-sm);padding:13px 17px;min-height:48px;text-align:left;
  line-height:1.35;cursor:pointer;transition:all .16s var(--ease)}
.hm-opt:hover{border-color:var(--brand-line);color:var(--brand);transform:translateY(-1px)}
.hm-opt:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.hm-opt[aria-pressed="true"]{background:var(--grad-btn);border-color:transparent;color:#fff;
  font-weight:700;box-shadow:var(--sh-brand)}

/* A PHONE IS A WIZARD AND A LAPTOP IS A PANEL, and that is a real difference
   rather than a stacked column. One question filling a phone screen gets
   answered; five options and a second question below the fold do not. */
@media (max-width:759px){
  .hm-opts{flex-direction:column}
  .hm-opt{width:100%}
  .hm-q{display:none}
  .hm-q.is-live{display:block}
  .hm-dots{display:flex}
}
@media (min-width:760px){
  .hm-q{display:block}
  .hm-dots{display:none}
}
.hm-dots{gap:7px;align-items:center;padding:16px 22px 0}
.hm-dot{width:7px;height:7px;border-radius:99px;background:var(--line-strong);
  transition:width .22s var(--ease)}
.hm-dot.is-on{background:var(--brand);width:22px}
.hm-back{margin-left:auto;background:none;border:0;font:inherit;font-size:14px;
  color:var(--ink-3);cursor:pointer;padding:5px 7px}
.hm-back:focus-visible{outline:2px solid var(--focus);outline-offset:2px}

.hm-out{border-top:1px solid var(--line);background:var(--surface-2);
  padding:clamp(20px,3.2vw,30px);animation:hmRise .32s var(--ease)}
.hm-out .eyebrow{margin-bottom:10px}
.hm-name{font-family:var(--f-dis);font-size:clamp(27px,4.6vw,38px);font-weight:800;
  letter-spacing:-.02em;line-height:1.04;color:var(--ink);margin:0 0 8px}
.hm-meta{font-family:var(--f-dis);font-size:11.5px;font-weight:750;letter-spacing:.14em;
  text-transform:uppercase;color:var(--ink-3);margin:0 0 14px}
.hm-why2{margin:0 0 22px;max-width:42em;font-size:15.5px;color:var(--ink-2)}
.hm-out .herocta{margin-top:0}
.hm-also{margin:20px 0 0;padding-top:16px;border-top:1px solid var(--line);
  font-size:14.5px;color:var(--ink-3)}

/* --------------------------------------------------- the front page, rest */

/* A full-bleed surface band, alternating, so twelve sections have a rhythm
   without twelve borders. On the tinted --bg with the aurora behind it, a plain
   white band separates far more cleanly than a rule does. */
.hm-band{background:var(--surface);border-top:1px solid var(--line);
  border-bottom:1px solid var(--line)}

/* words on the left, the review that proves them on the right. Each of the
   five reviews argues a different point, so they sit beside the claim they
   support rather than together in a testimonial block. */
.hm-two{display:grid;gap:clamp(26px,4vw,52px)}
@media (min-width:940px){
  .hm-two{grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr);align-items:start}
}
.hm-two h2{font-size:clamp(27px,4.4vw,40px)}
.hm-two .lede{margin-top:14px}
.hm-more{margin:20px 0 0;font-family:var(--f-dis);font-weight:750;font-size:14.5px}
.hm-more a{color:var(--brand);text-decoration:none}
.hm-more a:hover{text-decoration:underline}

.hm-say{margin:0;background:var(--surface-2);border:1px solid var(--line);
  border-radius:var(--r-xl);padding:clamp(20px,3vw,28px);position:relative}
.hm-band .hm-say{background:var(--surface-3)}
.hm-say blockquote{margin:0;font-family:var(--f-ser);color:var(--ink);
  font-size:clamp(18px,2.5vw,21px);line-height:1.46}
.hm-say figcaption{margin-top:16px;font-family:var(--f-dis);font-weight:750;
  font-size:13.5px;color:var(--ink-2)}
.hm-say figcaption::before{content:"— "}
.hm-say-wide{max-width:44em;margin-top:clamp(26px,4vw,40px)}

.hm-points{margin:20px 0 0;padding:0;list-style:none;display:grid;gap:14px}
.hm-points li{padding-left:22px;position:relative;color:var(--ink-2);font-size:15.5px}
.hm-points li::before{content:"";position:absolute;left:0;top:.62em;width:8px;height:8px;
  border-radius:99px;background:var(--grad)}
.hm-points b{color:var(--ink);font-weight:700}

/* ---------------------------------------------------------- seven classes */
/* ONE GRID OF SEVEN, on the front page and on /courses alike.
   Three groups as three columns came out 200, 410 and 129 pixels tall, which
   is a ragged bottom edge and two columns of white space. The group is a chip
   on each card now.

   THE LAST CARD SPANS ITS ROW when it would otherwise be alone at the start of
   one — :last-child:nth-child(3n+1) is true exactly then, and it turns an
   orphan into a deliberately wide final card. */
.hm-plans{display:grid;gap:10px;margin-bottom:clamp(26px,4vw,36px)}
@media (min-width:680px){ .hm-plans{grid-template-columns:repeat(2,minmax(0,1fr))} }
@media (min-width:1000px){ .hm-plans{grid-template-columns:repeat(3,minmax(0,1fr))} }
/* The last card used to span its row when it would otherwise be alone. That
   was my idea and it was a bad one: "Class 8 to 12 / Monthly or yearly"
   stretched across 1076px is not tidier than a short final row, which is what
   every card grid does and what nobody has ever called broken. */
/* A GRID WITH NAMED AREAS, because this card holds four things in a fixed
   arrangement and that is what grid is for. It was a flex row built for two
   things and then patched three times as more were added, and the last patch
   left the group chip flung to the far right of the card by a
   justify-content:space-between that nothing had removed:

       [icon]                                    [START HERE]
       Foundation
       3 MONTHS
       ------------------------------------------------------

   What it is now:

       [icon]  START HERE                            3 MONTHS
       [icon]  Foundation
       ------
*/
.hm-plan{background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-md);padding:15px 17px;
  display:grid;grid-template-columns:auto minmax(0,1fr) auto;
  grid-template-areas:
    "ico grp  dur"
    "ico name name"
    "bar bar  bar";
  column-gap:12px;row-gap:5px;align-items:center}
.hm-band .hm-plan{background:var(--surface-2)}
.hm-plan b{grid-area:name;font-family:var(--f-dis);font-weight:800;font-size:16px;
  color:var(--ink);letter-spacing:-.01em;line-height:1.22}

/* ----------------------------------------------------------- how it works */
.hm-steps{margin:0;padding:0;list-style:none;counter-reset:hm;display:grid;
  gap:clamp(14px,2vw,18px)}
@media (min-width:900px){ .hm-steps{grid-template-columns:repeat(4,minmax(0,1fr))} }
.hm-steps li{counter-increment:hm;position:relative;padding-top:44px;
  color:var(--ink-2);font-size:15px;line-height:1.55}
.hm-steps li::before{content:counter(hm);position:absolute;top:0;left:0;
  width:32px;height:32px;border-radius:99px;background:var(--grad-btn);color:#fff;
  font-family:var(--f-dis);font-weight:800;font-size:14px;
  display:flex;align-items:center;justify-content:center;box-shadow:var(--sh-brand)}
.hm-steps b{display:block;color:var(--ink);font-weight:700;margin-bottom:2px}

/* ------------------------------------------------------------ counselling */
.hm-rates{display:grid;gap:10px;margin-bottom:clamp(24px,3.4vw,32px)}
@media (min-width:760px){ .hm-rates{grid-template-columns:repeat(3,minmax(0,1fr))} }
.hm-rate{background:var(--surface-2);border:1px solid var(--line);
  border-radius:var(--r-md);padding:18px 20px;display:flex;flex-direction:column;gap:3px}
.hm-rate b{font-family:var(--f-dis);font-weight:800;font-size:16.5px;color:var(--ink)}
.hm-rate span{font-size:13.5px;color:var(--ink-3)}
.hm-note{margin:0;font-size:14px;color:var(--ink-3);max-width:44ch}

/* ------------------------------------------------------------------- FAQ */
.hm-faq{display:grid;gap:0;border:1px solid var(--line);border-radius:var(--r-xl);
  background:var(--surface);overflow:hidden}
.hm-faq details + details{border-top:1px solid var(--line)}
.hm-faq summary{cursor:pointer;list-style:none;padding:17px clamp(18px,2.6vw,24px);
  font-family:var(--f-dis);font-weight:750;font-size:16px;color:var(--ink);
  display:flex;align-items:center;justify-content:space-between;gap:16px}
.hm-faq summary::-webkit-details-marker{display:none}
.hm-faq summary::after{content:"+";font-weight:750;font-size:20px;color:var(--ink-3);
  flex:none;line-height:1}
.hm-faq details[open] summary::after{content:"\2212"}
.hm-faq summary:hover{color:var(--brand)}
.hm-faq summary:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.hm-faq p{margin:0;padding:0 clamp(18px,2.6vw,24px) 20px;color:var(--ink-2);
  font-size:15.5px;max-width:62ch}

/* ----------------------------------------------------------------- close */
.hm-close{background:var(--surface);border-top:1px solid var(--line)}
.hm-close h2{font-size:clamp(27px,4.4vw,40px);margin-top:12px}
.hm-close .lede{margin-top:14px}

/* ------------------------------------- the rest of the site: shared bits -- */

/* Announced but not shown. There was no helper for this in the file before
   /ebooks needed one — an "opens in a new tab" that only a screen reader hears. */
.vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}

/* Every page except the front one opens on a heading and not on an argument,
   so its hero is shorter. */
.hero-sm{padding-bottom:clamp(28px,4vw,48px)}
.hero-sm h1{font-size:clamp(30px,5.4vw,46px)}

/* A disabled button has to look disabled AND be disabled. See /ebooks. */
.btn:disabled,.btn[disabled]{opacity:.4;cursor:not-allowed;box-shadow:none;
  transform:none;pointer-events:none}

/* ------------------------------------------------- /courses  ------ */
.oc-list{display:grid;gap:10px;margin-bottom:clamp(26px,4vw,36px)}
.oc-list .eyebrow{margin:clamp(14px,2vw,20px) 0 2px}
.oc-list .eyebrow:first-child{margin-top:0}
.oc-item{background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-lg);padding:clamp(17px,2.4vw,22px)}
.oc-h{display:flex;flex-wrap:wrap;align-items:center;gap:8px 14px;margin-bottom:7px}
.oc-h h3{font-family:var(--f-dis);font-weight:800;font-size:clamp(18px,2.6vw,21px);
  letter-spacing:-.02em;color:var(--ink)}
.oc-item p{color:var(--ink-2);font-size:15.5px;line-height:1.58;max-width:66ch}

/* ------------------------------------------------------------ /about ------ */
.ab-prose{max-width:64ch}
.ab-prose p{color:var(--ink-2);font-size:clamp(16px,2.1vw,18px);line-height:1.68;
  margin-bottom:20px}
/* the first line is the one that has to land, so it is set like one */
.ab-prose .ab-open{font-family:var(--f-ser);font-size:clamp(20px,3vw,26px);
  line-height:1.4;color:var(--ink)}
.ab-open b{font-family:var(--f-ser);font-weight:400;
  background:var(--grad);-webkit-background-clip:text;background-clip:text;
  color:transparent}
.ab-prose b{color:var(--ink);font-weight:700}
.ab-cv{display:grid;gap:0;margin:0;border:1px solid var(--line);
  border-radius:var(--r-xl);background:var(--surface);overflow:hidden}
@media (min-width:820px){ .ab-cv{grid-template-columns:repeat(2,minmax(0,1fr))} }
.ab-cv>div{padding:16px clamp(18px,2.4vw,22px);border-top:1px solid var(--line)}
.ab-cv>div:first-child{border-top:0}
@media (min-width:820px){
  .ab-cv>div:nth-child(2){border-top:0}
  .ab-cv>div:nth-child(2n){border-left:1px solid var(--line)}
}
.ab-cv dt{font-family:var(--f-dis);font-weight:750;font-size:15.5px;color:var(--ink);
  letter-spacing:-.01em}
.ab-cv dd{margin:3px 0 0;font-size:14px;color:var(--ink-3)}

/* ----------------------------------------------------------- /ebooks ------ */
/* TWO ACROSS ON A PHONE, FOUR ON A WIDE SCREEN, by the owner's instruction.
   Both shelves on this page use the same counts so they stay in step. */
.eb-grid{display:grid;gap:clamp(14px,2.2vw,20px);
  grid-template-columns:repeat(2,minmax(0,1fr))}
@media (min-width:760px){ .eb-grid{grid-template-columns:repeat(4,minmax(0,1fr))} }
.eb-card{background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-xl);padding:clamp(18px,2.4vw,22px);display:flex;
  flex-direction:column;align-items:flex-start;gap:10px}
/* Standing in for a cover nobody has designed yet. The title set in the serif
   on the brand gradient reads as a placeholder on purpose — it should not be
   mistaken for artwork, and it should not look broken either. */
/* A REAL COVER NOW. This used to DRAW one -- a gradient block with the title
   set in the serif -- because nobody had designed one. Same box as the
   recommended shelf: 62% of the card, centred, at 7:10, which is the files'
   own ratio, so nothing is stretched or cropped again in the browser. */
.eb-cover{position:relative;width:62%;align-self:center;aspect-ratio:7/10;
  overflow:hidden;padding:0;border-radius:10px;
  background:#fff;border:1px solid var(--line);box-shadow:var(--sh-2)}
.eb-cover img{display:block;width:100%;height:100%;object-fit:cover;
  transition:transform .3s var(--ease)}
.eb-card:hover .eb-cover img{transform:scale(1.03)}
/* `.eb-cover span` USED TO BE HERE and had to go. It dressed the title back
   when the title was a <span> on the cover; the title has been an <h3> for a
   while, so the rule matched nothing that wanted it -- and then matched the
   "Coming soon" chip, which IS a span. Being one class plus one element it
   outranked .eb-soon, so the chip was drawn at 21px in the serif no matter what
   .eb-soon asked for. That is what "coming soon looks bad" actually was. */
.eb-card p{color:var(--ink-2);font-size:14.5px;line-height:1.55;margin-top:2px}
.eb-card .btn{margin-top:2px}

/* A COURTESY, NOT A WARNING NOTICE. The first version was four lines in a grey
   slab with a border all the way round, which is the shape of a hazard panel.
   Two sentences, no box, and one brand rule down the left — visible, which is
   the requirement, and quiet, which is the manners. */
/* THE SPACING IS ABOVE IT NOW, not below. It used to sit before the shelf, so
   its margin was underneath; below the shelf that same margin would push it
   away from the books it belongs to and leave it hanging on the section's own
   padding. Same measurement, other side. */
/* A FOOTNOTE UNDER THE SHELF, not a callout beside it. It used to sit ABOVE the
   books, where a 3px violet bar down its left edge read as an aside pointing at
   what came next. Underneath them the same bar reads as something left behind --
   "looks kinda messy there".
 *
   So the bar becomes a hairline ACROSS THE TOP: the shelf ends, a line closes
   it, and the note belongs to the section rather than hanging off it. Four
   competing weights in one small block -- violet bar, violet icon, grey body,
   black bold -- become three, and the left padding the bar needed goes with it. */
.eb-disc{max-width:72ch;margin:clamp(24px,3.2vw,30px) 0 0;
  padding:clamp(15px,2vw,19px) 0 0;
  border-left:0;border-top:1px solid var(--line);border-radius:0;background:none;
  font-size:13.8px;line-height:1.62;color:var(--ink-2)}
/* --ink-2, NOT the --ink-3 this used to be. On a coloured band it was given
   white and measured 5.01; the page inversion made this section light again and
   it fell back to --ink-3, which is 3.88:1 on white against a bar of 4.5. This
   is the affiliate disclosure -- the one paragraph here that has to be legible
   for a reason other than design -- so it does not get to inherit the faintest
   ink in the palette. 7.96 now, and the bold takes --ink to keep the hierarchy
   the two had. */
.eb-disc b{color:var(--ink);font-weight:750}

.eb-ext{font-size:15px;line-height:1}

/* The line on /book that says what booking is NOT. Deliberately quiet — it is
   a correction for the minority who arrived expecting to enrol here, not a
   banner for everybody who came to book. */
.bk-not{margin-top:14px;font-size:14.5px;color:var(--ink-3);max-width:56ch}
.bk-not a{color:var(--brand);text-decoration:none;font-weight:650}
.bk-not a:hover{text-decoration:underline}

/* Call-to-action rows on a phone.
 *
 * Three full-width stacked buttons cost 186px, which on the front page pushed
 * the one interactive thing on it — Spot the mistake — off the first screen.
 * A grid instead: an odd first button takes the whole row and the rest pair up,
 * so three read as "the main one, then two others" and two sit side by side.
 * :nth-last-child(odd) on the FIRST child is how the rule counts them without
 * a class per page. */
@media (max-width:560px){
  .herocta{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}
  .herocta .btn{width:100%;padding:0 12px}
  .herocta .btn:first-child:nth-last-child(odd),
  .herocta .btn:only-child{grid-column:1/-1}
}

/* ══════════════════════════════════════════════ THE COACHING PAGES, DRAWN ══
   The five pages at the top of academy.minnati.com were built out of headings
   and paragraphs and nothing else, and the owner's verdict was the right one:
   "so bland, so boring... no visual graphic". Everything below is the answer,
   and every piece of it is built from the tokens already at the top of this
   file — no new palette, no new type, no second design language. */

/* ------------------------------------------------------------------ icons */
/* One size, one colour, both inherited. See page_sprite() and ico(). */
.ic{width:1em;height:1em;flex:0 0 auto;fill:none;stroke:currentColor;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round;vertical-align:-.125em}

/* --------------------------------------------------------- the hero, wide */
/* Two columns from 1000px and one below it. The class panel is the second
   child in the markup, so on a phone it falls under the words with no ordering
   needed — and the words are what has to be read first on the smallest screen
   whatever else happens. */
/* THE THIRD BUTTON WAS WRAPPING, and by five pixels at 1440: the three came to
   529 and the text column was 524. Worse between 1000 and 1140, which is where
   most laptops sit — there the column falls to about 457 and the third button
   never had a chance.

   Shaving the gap would have bought four pixels and left it fragile. Three
   things instead, and none of them squeezes a button below its own text:
   the two-column hero starts at 1120 rather than 1000, so the band where the
   column is narrowest gets the full width; the text column takes a slightly
   larger share of what is left; and the hero's own buttons carry less
   horizontal padding than buttons elsewhere. */
.herowrap{display:grid;gap:clamp(28px,4vw,48px);align-items:center}
@media (min-width:1120px){
  .herowrap{grid-template-columns:minmax(0,1.08fr) minmax(0,.92fr)}
}
.hero .btn .ic{font-size:1.05em;margin-right:-2px}
.hero .herocta{gap:10px}
.hero .herocta .btn{padding:0 17px}

/* the three numbers, as tiles rather than a sentence with bold bits in it */
/* THE NUMERAL IS PAINTED BY THE GRADIENT, NOT BY `color`, and that is a
   contract rather than a flourish: background-clip:text shows the background
   THROUGH the letters, which only works while the text itself is transparent.
   Anything that sets `color` on .trust b switches the gradient off and leaves
   nothing behind -- which is exactly what happened on 26 August, when a band
   rule set it to #fff and the three hero tiles rendered white on white at
   1.00:1, blank. The rule used to declare `color` twice, ink and then
   transparent, so the dead first one made the contract look optional. One
   declaration now. */
.trust{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;
  margin-top:26px;max-width:31rem}
.trust>div{background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-md);padding:13px 14px;text-align:left}
.trust b{font-family:var(--f-dis);font-weight:800;
  font-size:clamp(20px,2.6vw,25px);letter-spacing:-.03em;display:block;
  font-variant-numeric:tabular-nums;line-height:1.1;
  background:var(--grad);-webkit-background-clip:text;background-clip:text;
  color:transparent}
.trust span{display:block;margin-top:3px;font-size:12px;line-height:1.3;color:var(--ink-3)}

/* ═══════════════════════════════════════════════════ a class in session ══
   The headline made visible. Nine people, and each one's turn lights up in
   sequence all the way round the room and starts again — which is the whole
   difference between this place and a lecture, drawn rather than claimed.

   THE SEAT NUMBER IS A CUSTOM PROPERTY and the delay is arithmetic on it, so
   the order round the room is one expression instead of eight rules. Change
   the count and only --cls-n below has to follow. */

/* ══════════════════════════════════════════ the Courses hero panel, drawn ══
   THE SEVEN, LIGHTING ONE AT A TIME. See page_courses_panel(). It wears the
   same shell as the class panel -- .crs is .cls with a different body -- so
   the two pages open with drawings from one product rather than two.

   THE TIMING IS ONE NUMBER SEVEN TIMES. A row is lit for a seventh of the
   cycle, and every row runs the SAME keyframes with its start pushed by --i.
   Nothing anywhere has to know that there are seven of them except the delay
   arithmetic below, so an eighth course is a row in the array and nothing
   else. 10.5s / 7 = 1.5s a course.

   THE BAR IS THE CARDS' BAR. Width is --m / 6, exactly as .hm-bar computes it
   further down the page, so the panel and the cards cannot disagree about how
   long a course is. */
.crswrap{position:relative}
.crswrap::before{content:"";position:absolute;inset:6% -4% -8% -4%;z-index:-1;
  background:radial-gradient(60% 60% at 50% 40%, var(--au1), transparent 70%);
  opacity:.85;filter:blur(6px)}
.crs{background:var(--surface);border:1px solid var(--line-strong);
  border-radius:var(--r-2xl);box-shadow:var(--sh-3);overflow:hidden}
.crs-bar{display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:11px 15px;background:var(--surface-2);border-bottom:1px solid var(--line);
  font-size:12.2px}
.crs-live{display:inline-flex;align-items:center;gap:7px;font-family:var(--f-dis);
  font-weight:800;color:var(--brand);letter-spacing:.02em}
.crs-live i{width:8px;height:8px;border-radius:99px;background:var(--brand);
  animation:crsPulse 2.4s var(--ease) infinite}
/* --ink-2, for the same reason as the two lines below: 3.88:1 against 7.26:1. */
.crs-where{color:var(--ink-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
@keyframes crsPulse{
  0%  {box-shadow:0 0 0 0 rgba(90,69,255,.45)}
  70% {box-shadow:0 0 0 7px rgba(90,69,255,0)}
  100%{box-shadow:0 0 0 0 rgba(90,69,255,0)}
}

.crs-list{display:grid;gap:1px;background:var(--line)}
/* --i is the row number and --m its months; both are set inline in the markup
   and declared here so neither resolves to nothing inside a calc(). */
/* ONE LINE A COURSE. It was two -- the name with its group underneath -- and
   at seven rows the panel stood 509px tall, which the owner called too long
   before an eighth was even added. The group was the line that cost the
   height, and the cards below already print it as a chip on every course, so
   it comes out rather than being shrunk. Eight rows now stand shorter than
   seven did. */
.crs-r{--i:0;--m:1;position:relative;background:var(--surface);
  padding:9px 14px;display:grid;align-items:center;gap:11px;
  /* THE LAST TWO COLUMNS ARE FIXED, AND THEY HAVE TO BE. Every row is its own
     grid -- they are separate boxes so each can light on its own -- so `auto`
     sized the duration column to THAT ROW's text and nothing lined up between
     rows. Measured across the eight: seven durations right-aligned at x=351
     with the bars running 362 to 458, and "Monthly or yearly" -- the one row
     with no bar -- starting at 358 and running to 447, straight through the
     column the bars live in. One row sticking 96px further right than the
     other seven is what read as messy.
     90px is the widest duration, measured, so nothing wraps; the bar column
     stays reserved on the row that has no bar, which is what keeps the edge
     straight. */
  grid-template-columns:auto minmax(0,1fr) 90px 96px;
  animation:crsRow 10.5s linear infinite;animation-delay:calc(var(--i) * 1.5s)}
@keyframes crsRow{
  0%  {background:var(--surface-2)}
  12% {background:var(--surface-2)}
  15% {background:var(--surface)}
  100%{background:var(--surface)}
}
.crs-ic{width:27px;height:27px;border-radius:9px;display:grid;
  place-items:center;background:var(--brand-soft);border:1px solid var(--brand-line);
  color:var(--brand);font-size:14px;
  animation:crsIc 10.5s linear infinite;animation-delay:calc(var(--i) * 1.5s)}
@keyframes crsIc{
  0%  {background:var(--grad-btn);border-color:transparent;color:#fff}
  12% {background:var(--grad-btn);border-color:transparent;color:#fff}
  15% {background:var(--brand-soft);border-color:var(--brand-line);color:var(--brand)}
  100%{background:var(--brand-soft);border-color:var(--brand-line);color:var(--brand)}
}
.crs-t{min-width:0;display:block;line-height:1.2}
.crs-t b{font-family:var(--f-dis);font-weight:750;font-size:13.5px;color:var(--ink);
  letter-spacing:-.01em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  display:block}
/* --ink-2 and not --ink-3, which is what .cls-where and .hm-dur use for the
   same small print. #7B7FA4 on white is 3.88:1 -- under the bar, and a known
   property of this palette rather than anything new. Written from scratch
   here, so there was no reason to inherit it: #4A4E75 reads 7.26:1 and still
   sits back from the #0F1130 course name. */
/* Right-aligned, so the eight durations make one straight edge instead of
   eight starts. */
.crs-d{font-family:var(--f-dis);font-weight:750;font-size:11px;
  color:var(--ink-2);white-space:nowrap;text-align:right}
.crs-track{flex:none;display:block;width:96px;height:4px;border-radius:99px;
  background:var(--surface-3);overflow:hidden}
.crs-track i{display:block;height:100%;border-radius:99px;background:var(--grad);
  width:0;animation:crsFill 10.5s linear infinite;animation-delay:calc(var(--i) * 1.5s)}
@keyframes crsFill{
  0%  {width:0}
  10% {width:calc(var(--m) / 12 * 100%)}
  13% {width:calc(var(--m) / 12 * 100%)}
  15% {width:0}
  100%{width:0}
}
/* THE BAR GOES BEFORE THE NAME DOES. At 375px a row cannot hold a 27px icon,
   a course called "Professional for Corporate", its length in words AND a 96px
   bar -- and what was being cut was the name, to an ellipsis. The bar is the
   one thing in the row whose information is also printed beside it in words,
   so on a narrow screen it is the bar that goes. Measured: two names clipped
   at 375 with the track in, none without it. */
@media (max-width:430px){
  .crs-track{display:none}
  /* and the column it was holding goes with it, or every row keeps 96px
     of reserved space for something that is not drawn. */
  .crs-r{grid-template-columns:auto minmax(0,1fr) auto}
}

/* Nothing moves, and every bar shows its real length instead of none of it --
   a stopped animation must not leave the drawing reading as empty rows. */
@media (prefers-reduced-motion:reduce){
  .crs-r,.crs-ic,.crs-track i,.crs-live i{animation:none}
  .crs-track i{width:calc(var(--m) / 12 * 100%)}
}

.clswrap{position:relative}
/* a soft glow behind the panel so it sits on the page rather than on top of it */
.clswrap::before{content:"";position:absolute;inset:6% -4% -8% -4%;z-index:-1;
  background:radial-gradient(60% 60% at 50% 40%, var(--au2), transparent 70%);
  opacity:.85;filter:blur(6px)}

.cls{background:var(--surface);border:1px solid var(--line-strong);
  border-radius:var(--r-2xl);box-shadow:var(--sh-3);overflow:hidden}

.cls-bar{display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:11px 15px;background:var(--surface-2);border-bottom:1px solid var(--line);
  font-size:12.2px}
.cls-live{display:inline-flex;align-items:center;gap:7px;font-family:var(--f-dis);
  font-weight:800;color:var(--no);letter-spacing:.02em}
.cls-live i{width:8px;height:8px;border-radius:99px;background:var(--no);
  box-shadow:0 0 0 0 var(--no);animation:clsPulse 2s var(--ease) infinite}
.cls-where{color:var(--ink-3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
@keyframes clsPulse{
  0%  {box-shadow:0 0 0 0 rgba(239,68,68,.5)}
  70% {box-shadow:0 0 0 7px rgba(239,68,68,0)}
  100%{box-shadow:0 0 0 0 rgba(239,68,68,0)}
}

/* her, across the top, because the room has one teacher and eight of everybody
   else and the drawing should say so before it says anything else */
.cls-teach{display:flex;gap:12px;align-items:flex-start;
  padding:clamp(15px,2.2vw,19px);border-bottom:1px solid var(--line)}
.cls-teach b{display:block;font-family:var(--f-dis);font-weight:800;font-size:14.5px;
  color:var(--ink);letter-spacing:-.01em}
.cls-teach span{display:block;margin-top:3px;font-family:var(--f-ser);
  font-size:clamp(14.5px,1.9vw,16.5px);line-height:1.4;color:var(--ink-2)}

/* the same initial-in-a-circle the dashboard draws for a student with no photo */
.cls-av{flex:0 0 auto;width:34px;height:34px;border-radius:99px;display:grid;
  place-items:center;font-family:var(--f-dis);font-weight:800;font-size:13px;
  color:#fff;background:var(--grad-btn);box-shadow:var(--sh-brand);letter-spacing:-.01em}
/* HER MARK, not two letters. The eight students are initials in circles
   because that is what a class register looks like; the person at the front is
   the M and the full stop off the top of every page. */
/* the mark fills its circle: the viewBox is cropped to the glyph, so 100% here
   is the right 100% — see page_mark_svg() */
.cls-av.is-t{position:relative;width:42px;height:42px;
  background:linear-gradient(145deg,var(--a3),var(--a1) 70%)}
.cls-mk{position:absolute;inset:0;width:100%;height:100%;display:block}

.cls-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:1px;
  background:var(--line)}
/* --i is the seat number and every seat sets its own inline, in the markup.
   It is declared here as well so the value has a home in the stylesheet: an
   undefined custom property resolves to nothing inside calc(), which would
   break every delay at once if a row ever shipped without its style attribute. */
.cls-p{--i:0;position:relative;background:var(--surface);padding:12px 13px;
  display:flex;align-items:center;gap:9px;min-width:0;
  transition:background .4s var(--ease)}
.cls-p .cls-av{width:27px;height:27px;font-size:11.5px;
  filter:grayscale(.85) opacity(.55);transition:filter .4s var(--ease)}
.cls-p b{flex:0 0 auto;font-family:var(--f-dis);font-weight:750;font-size:13px;
  color:var(--ink-3);white-space:nowrap;transition:color .4s var(--ease)}

/* THE BUBBLE SITS BESIDE THE NAME, NOT OVER IT.
   The first version was absolutely positioned across the row so that a name
   would not shuffle sideways when its turn came — a row that reflows every
   1.15 seconds reads as a fault. But its background is brand-soft, which is
   nine per cent opaque, so the name showed straight through the words: "Sunil"
   and "I went to…" on the same line, smeared together.
   A flex item at opacity 0 still holds its track, so the reflow it was avoiding
   cannot happen anyway. In the flow it goes, and the overlap is impossible
   rather than merely painted over. */
.cls-b{flex:1 1 auto;min-width:0;transform:translateX(-5px) scale(.94);
  font-style:normal;font-size:11.8px;line-height:1.25;color:var(--brand);
  /* WHITE, not the brand tint. The row itself goes brand-soft while somebody is
     speaking, and a brand-soft bubble on a brand-soft row is one flat lilac
     rectangle with no bubble visible in it. On the surface colour it lifts, and
     the small shadow is what makes it read as spoken rather than as a field. */
  background:var(--surface);border:1px solid var(--brand-line);
  box-shadow:0 2px 6px -2px rgba(24,20,70,.16);
  border-radius:11px 11px 11px 3px;padding:5px 8px;opacity:0;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transform-origin:left center}

/* --cls-n seats, --cls-t seconds each. Every seat runs the same animation and
   only the delay differs, so the turn goes round the room once per cycle. */
.cls-grid{--cls-n:8;--cls-t:1.15s}
.cls-p{animation:clsSeat calc(var(--cls-n) * var(--cls-t)) steps(1) infinite;
  animation-delay:calc(var(--i) * var(--cls-t))}
.cls-p .cls-av{animation:clsFace calc(var(--cls-n) * var(--cls-t)) var(--ease) infinite;
  animation-delay:calc(var(--i) * var(--cls-t))}
.cls-b{animation:clsBub calc(var(--cls-n) * var(--cls-t)) var(--spring) infinite;
  animation-delay:calc(var(--i) * var(--cls-t))}

@keyframes clsSeat{
  0%,11.6%  {background:var(--brand-soft)}
  12.5%,100%{background:var(--surface)}
}
@keyframes clsFace{
  0%,11.6%  {filter:none;transform:scale(1.06)}
  14%,100%  {filter:grayscale(.85) opacity(.55);transform:scale(1)}
}
@keyframes clsBub{
  0%        {opacity:0;transform:translateX(-5px) scale(.94)}
  1.6%,11%  {opacity:1;transform:none}
  12.5%,100%{opacity:0;transform:translateX(-5px) scale(.97)}
}

.cls-foot{display:flex;align-items:center;gap:9px;padding:13px 15px;
  border-top:1px solid var(--line);background:var(--ok-soft);
  font-size:12.8px;line-height:1.4;color:var(--ok)}
.cls-tick{font-size:16px;stroke-width:2.6}

/* A room where nobody is speaking, for anybody who asked not to be moved. The
   panel still says what it is; it simply stops taking turns. */
@media (prefers-reduced-motion:reduce){
  .cls-p,.cls-p .cls-av,.cls-b,.cls-live i{animation:none}
  .cls-p .cls-av{filter:none}
  .cls-b{opacity:1;transform:none}
}

/* ═══════════════════════════════════════════════════════════ the on-air plate ══
   Twenty years reading the news to a country, drawn. It was one clause inside a
   paragraph, which is a waste of the strongest fact this business owns.

   The bars are a CSS waveform and not an audio visualiser: eighteen of them,
   each with its own resting height and its own delay, so it reads as a voice
   rather than as a loading spinner. */
.hm-airside{display:grid;gap:14px;align-content:start}

/* LIGHT, AND IT WENT THROUGH THREE STATES TO GET HERE.
 *
 *   1. the brand gradient — a bright violet rectangle, "not visually impressive"
 *   2. near-black with a glowing waveform — "don't like the dark version"
 *   3. this
 *
 * The second was the better drawing and the worse page. A near-black slab
 * between white bands is a hole in the page whatever is drawn on it, and this
 * site has no other dark surface anywhere for it to belong to.
 *
 * SO THE PANEL IS LIGHT AND ONE ELEMENT INSIDE IT IS NOT: the lower third. That
 * bar is dark on every channel there has ever been, it is the thing that makes
 * the drawing read as television, and as the only strong contrast on a light
 * card it anchors the whole composition. Everything else that only existed
 * because the panel was dark — the scanlines, the vignette — goes with the
 * darkness. They are things you draw on a screen you are pretending emits
 * light. */
.onair{position:relative;overflow:hidden;border-radius:var(--r-xl);
  padding:clamp(18px,2.6vw,24px);color:var(--ink);
  background:
    radial-gradient(90% 130% at 50% 122%, var(--brand-soft), transparent 66%),
    linear-gradient(168deg,var(--surface-2) 0%,var(--surface-3) 100%);
  border:1px solid var(--line);box-shadow:var(--sh-2)}
/* THREE LAYERS, STATED. 1 the vignette, over the background and under
   everything drawn on it; 2 the content; 3 the scanlines, over the lot at
   three per cent. Left to paint order the vignette sat on the waveform and
   dimmed the bars it was supposed to be behind. */
.onair::after{content:"";position:absolute;inset:0;pointer-events:none;z-index:3;
  background:none}
.onair::before{content:"";position:absolute;inset:0;pointer-events:none;z-index:1;
  background:none}
.onair-top{display:flex;align-items:center;justify-content:space-between;gap:12px}
.onair-top{position:relative;z-index:2}
.onair-lamp{display:inline-flex;align-items:center;gap:8px;font-family:var(--f-dis);
  font-weight:800;font-size:12px;letter-spacing:.18em;
  background:rgba(229,52,43,.09);border:1px solid rgba(229,52,43,.26);
  color:#C0332B;padding:6px 11px;border-radius:999px}
.onair-lamp i{width:7px;height:7px;border-radius:99px;background:#E5342B;
  animation:airLamp 3.4s var(--ease) infinite}
/* the one thing left moving on this panel, and it is 7px across. Slower and
   shallower than it was: a lamp that is on, not a lamp that is flashing. */
@keyframes airLamp{0%,100%{opacity:1}50%{opacity:.62}}
.onair-yrs{font-family:var(--f-dis);font-weight:800;font-size:13px;color:var(--ink-3)}

/* THE WAVEFORM LIGHTS THE SCREEN. Mirrored about a centre line, each bar a
   column running pink at the top through purple to indigo at the foot, with a
   bloom around it. On near-black that reads as a signal rather than as a bar
   chart, which is what it was on violet.

   IT MOVES, GENTLY. First it swung between a third of its height and full on
   a 1.5s loop with every bar glowing, which the owner called distracting, and
   she was right. I then took the motion away entirely, which was taking away
   the wrong thing: "I like the animation but the design is too distracting."

   The problem was never the movement. It was everything painted around it --
   two bright washes inside a near-black box, a purple bloom under it at 75%
   alpha, a saturated pink-to-indigo gradient on every bar with two glows each,
   scanlines, a vignette and a haloed lamp. Any one of those is a nice touch.
   All of them at once is a slot machine.

   So the bars bob again on a 2.9s loop between 68% and full -- a breath rather
   than a bounce -- in quiet lavender with no per-bar glow at all. */
.onair-wave{position:relative;z-index:2;display:flex;align-items:center;gap:4px;
  height:clamp(56px,8vw,78px);margin:clamp(16px,2.4vw,22px) 0}
/* the line the signal sits on */
.onair-wave::before{content:"";position:absolute;left:0;right:0;top:50%;height:1px;
  background:linear-gradient(90deg,transparent,var(--line-strong) 12%,
    var(--line-strong) 88%,transparent)}
.onair-wave i{--h:.5;--d:0s;position:relative;flex:1 1 0;min-width:0;
  border-radius:99px;
  background:linear-gradient(180deg,var(--a2) 0%,var(--a1) 100%);opacity:.82;
  height:calc(14% + var(--h) * 86%);transform-origin:center;
  animation:airBar 2.9s var(--ease) infinite alternate;animation-delay:var(--d)}
/* 68% to 100%, not 34% to 100%. It reads as a signal that is live rather than
   as one that is being thrashed, which is the difference the owner was
   pointing at. */
@keyframes airBar{from{transform:scaleY(.68)}to{transform:scaleY(1)}}

.onair-src{position:relative;z-index:1;display:flex;flex-wrap:wrap;gap:8px 18px;
  font-family:var(--f-dis);font-weight:750;font-size:13px}
.onair-src span{display:inline-flex;align-items:center;gap:7px}
.onair-src .ic{font-size:16px}

@media (prefers-reduced-motion:reduce){
  .onair-wave i,.onair-lamp i{animation:none;transform:none}
}

/* ─────────────────────────────────────────────────────────────── a review ── */
/* An initial in a gradient circle and a serif quote mark. There are no
   photographs and there will not be; an initial is the same stand-in the
   dashboard draws for a student without one, and it cannot be mistaken for a
   picture of somebody. */
.hm-say{position:relative;padding-top:clamp(26px,3.4vw,34px)}
.hm-say::before{content:"\201C";position:absolute;top:2px;left:clamp(18px,2.6vw,24px);
  font-family:var(--f-ser);font-size:64px;line-height:1;color:var(--brand);opacity:.16}
.hm-av{position:absolute;top:clamp(-16px,-1.6vw,-14px);right:clamp(18px,2.6vw,24px);
  width:40px;height:40px;border-radius:99px;display:grid;place-items:center;
  font-family:var(--f-dis);font-weight:800;font-size:16px;color:#fff;
  background:var(--grad-btn);box-shadow:var(--sh-brand);border:3px solid var(--surface)}
.hm-band .hm-av{border-color:var(--surface)}
.hm-say figcaption{display:flex;align-items:center;gap:8px}
.hm-say figcaption::before{content:"";width:18px;height:2px;border-radius:2px;
  background:var(--grad)}

/* ─────────────────────────────────────────────── the three points, drawn ── */
.hm-points li{padding-left:42px}
.hm-points li::before{display:none}
.hm-pi{position:absolute;left:0;top:0;width:30px;height:30px;border-radius:10px;
  display:grid;place-items:center;background:var(--brand-soft);color:var(--brand);
  border:1px solid var(--brand-line)}
.hm-pi .ic{font-size:16px}

/* ──────────────────────────────────────────────── a programme, scannable ── */
.hm-plan{transition:border-color .2s,transform .2s var(--spring),box-shadow .2s}
.hm-plan:hover{border-color:var(--brand-line);transform:translateY(-2px);box-shadow:var(--sh-1)}
.hm-pico{grid-area:ico;align-self:center;width:34px;height:34px;border-radius:11px;
  display:grid;place-items:center;background:var(--brand-soft);color:var(--brand);
  border:1px solid var(--brand-line)}
.hm-pico .ic{font-size:17px}
.hm-dur{grid-area:dur;justify-self:end;font-family:var(--f-dis);font-weight:750;
  font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-3);
  background:var(--surface-3);border-radius:999px;padding:4px 9px;white-space:nowrap}
.hm-band .hm-dur{background:var(--surface)}

/* ────────────────────────────────────────────────── four steps, on a line ── */
.hm-steps li{padding-top:0;padding-left:60px;min-height:44px}
.hm-steps li::before{width:26px;height:26px;font-size:12px;top:9px;left:0}
/* the line that joins them. Vertical on a phone, horizontal from 900px, and it
   is what turns four paragraphs into one sequence. */
.hm-steps li::after{content:"";position:absolute;left:12px;top:38px;bottom:-22px;
  width:2px;background:linear-gradient(var(--brand-line),transparent);border-radius:2px}
.hm-steps li:last-child::after{display:none}
.hm-si{position:absolute;left:34px;top:5px;width:34px;height:34px;border-radius:11px;
  display:none;place-items:center;background:var(--brand-soft);color:var(--brand);
  border:1px solid var(--brand-line)}
.hm-si .ic{font-size:17px}
@media (min-width:900px){
  .hm-steps li{padding-left:0;padding-top:54px}
  .hm-steps li::before{top:0;left:0}
  .hm-steps li::after{left:38px;right:-18px;top:15px;bottom:auto;width:auto;height:2px;
    background:linear-gradient(90deg,var(--brand-line),transparent)}
  .hm-si{display:grid;left:auto;right:0;top:-1px}
}

/* ────────────────────────────────────────────── an appointment, three ways ── */
.hm-rate{gap:0}
.hm-rico{width:36px;height:36px;border-radius:12px;display:grid;place-items:center;
  background:var(--brand-soft);color:var(--brand);border:1px solid var(--brand-line);
  margin-bottom:11px}
.hm-rico .ic{font-size:18px}

/* ──────────────────────────────────────────────────── an eyebrow with a mark ── */
.eyebrow .ic{font-size:14px;margin-right:5px;vertical-align:-.18em;color:var(--brand)}
.hm-more .ic{font-size:15px;margin-left:3px;vertical-align:-.18em}

/* ────────────────────────────────────────── a programme card, /courses ── */
.oc-item{position:relative;padding-left:clamp(64px,7vw,76px);
  transition:border-color .2s,transform .2s var(--spring),box-shadow .2s}
.oc-item:hover{border-color:var(--brand-line);transform:translateY(-2px);
  box-shadow:var(--sh-1)}
.oc-ico{position:absolute;left:clamp(16px,2.2vw,20px);top:clamp(16px,2.2vw,21px);
  width:38px;height:38px;border-radius:12px;display:grid;place-items:center;
  background:var(--brand-soft);color:var(--brand);border:1px solid var(--brand-line)}
.oc-ico .ic{font-size:19px}
.hm-band .oc-item{background:var(--surface-2)}

/* ═══════════════════════════════════════════════════════ /about, the plate ══
   There is no photograph of her yet and this is deliberately not a box waiting
   for one. A monogram on the brand gradient with the broadcast motif behind
   it is a designed thing that is also honest, and when a photograph arrives it
   goes into the same frame with the same border and shadow. */
/* A CARD, NOT A PHOTO FRAME. The first version was a 320x400 gradient
   rectangle with MM in the middle of it — which is an empty picture frame
   however many rings are drawn behind the letters, and it read as one.

   A round seal, her name, what she is, and three lines: the shape a profile
   takes when there is deliberately no photograph, rather than the shape one
   takes when the photograph has not been uploaded yet. When a picture arrives
   it goes where the seal is and nothing else has to move. */
.portwrap{display:grid;place-items:center}
/* THE CARD IS ALWAYS THE OPPOSITE OF WHAT IT STANDS ON. These base rules are
   the light version -- white card, gradient seal. The coloured version is
   further down, under `section:not(.band-brand)`, so the card follows its
   section whichever way the page is flipped and nobody has to remember it.

   It was painted coloured outright for a while. Then /about was inverted, its
   hero went coloured, and 368x449 of gradient card was standing on a gradient
   section with no edge at all. */
.port{position:relative;width:100%;max-width:23rem;margin:0;text-align:center;
  background:var(--surface);border:1px solid var(--line-strong);
  border-radius:var(--r-2xl);padding:clamp(26px,3.4vw,34px) clamp(20px,2.8vw,26px);
  box-shadow:var(--sh-3);overflow:hidden}
/* a wash behind the seal so the card has a light source */
.port::before{content:"";position:absolute;left:50%;top:-38%;width:130%;
  aspect-ratio:1;transform:translateX(-50%);border-radius:50%;pointer-events:none;
  background:radial-gradient(circle, var(--brand-soft), transparent 62%)}

/* THE SEAL. A true circle — the rings in the old version were radial gradients
   on a 4:5 box, so every one of them was an ellipse. The halo is two rings
   painted with box-shadow rather than borders, which keeps the circle one
   element and lets the gap be the card's own surface colour in either theme. */
/* THE SEAL. A true circle -- the rings in the old version were radial gradients
   on a 4:5 box, so every one of them was an ellipse. The halo is two rings
   painted with box-shadow rather than borders, which keeps the circle one
   element and lets the gap be the card's own surface colour in either theme. */
.port-seal{position:relative;width:clamp(112px,17vw,136px);aspect-ratio:1;
  margin:0 auto;border-radius:50%;display:grid;place-items:center;overflow:hidden;
  background:var(--grad);
  box-shadow:var(--sh-brand),0 0 0 7px var(--surface),0 0 0 8px var(--brand-line)}
/* the photograph fills the circle. No sheen over it -- 34% white laid across a
   face is a smear, and the drawn mark it was polishing is gone. */
.port-photo{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  display:block}

.port figcaption{margin-top:clamp(18px,2.4vw,22px)}
.port figcaption b{display:block;font-family:var(--f-dis);font-weight:800;
  font-size:clamp(20px,2.8vw,24px);letter-spacing:-.02em;color:var(--ink)}
.port figcaption span{display:block;margin-top:5px;font-size:12.6px;
  font-family:var(--f-dis);font-weight:650;color:var(--ink-3);
  letter-spacing:.02em}

/* ───────────────── and the coloured version, for a LIGHT section only ─────
   "the card at right side should be colored but the circle should be opposite"
   -- the card takes the gradient and the seal turns white, with the M going
   violet. Everything here is the mirror of the block above, and it applies only
   while the section around it is light. Three of these would otherwise have
   gone invisible rather than merely dark: the M is white, the wash is white,
   and the caption is --ink.

   page_mark_svg() hard-codes stroke="#fff" and fill="#fff" and is SHARED with
   the class panel's avatar, so the function is left alone -- a presentation
   attribute loses to a rule, and this one is scoped to .port-mk. */
section:not(.band-brand) .port{background:var(--grad);
  border-color:rgba(255,255,255,.18)}
section:not(.band-brand) .port::before{
  background:radial-gradient(circle, rgba(255,255,255,.20), transparent 62%)}
section:not(.band-brand) .port-seal{background:#fff;
  box-shadow:0 10px 28px -10px rgba(0,0,0,.35),
    0 0 0 7px rgba(255,255,255,.16),0 0 0 8px rgba(255,255,255,.38)}
/* the sheen lays 34% white across the top-left of a COLOURED seal; on a white
   one it does nothing at all */
section:not(.band-brand) .port figcaption b{color:#fff}
section:not(.band-brand) .port figcaption span{color:#fff}

/* the three lines, as rows of a small table rather than a bulleted list */
.port-tags{display:grid;gap:0;margin-top:clamp(18px,2.4vw,22px);text-align:left;
  border:1px solid var(--line);border-radius:var(--r-md);overflow:hidden;
  background:var(--surface-2)}
.port-tags span{display:grid;grid-template-columns:auto minmax(0,1fr) auto;
  align-items:center;gap:10px;padding:11px 13px}
.port-tags span + span{border-top:1px solid var(--line)}
.port-tags .ic{font-size:17px;color:var(--brand)}
.port-tags i{font-style:normal;font-family:var(--f-dis);font-weight:750;
  font-size:13.4px;color:var(--ink)}
.port-tags em{font-style:normal;font-size:11.4px;color:var(--ink-3);
  font-variant-numeric:tabular-nums;white-space:nowrap}

/* the career line */
.ab-cv dt{display:flex;align-items:center;gap:10px}
.ab-ico{flex:0 0 auto;width:28px;height:28px;border-radius:9px;display:grid;
  place-items:center;background:var(--brand-soft);color:var(--brand);
  border:1px solid var(--brand-line)}
.ab-ico .ic{font-size:15px}
.ab-cv dd{margin-left:38px}

/* ═════════════════════════════════════════════════════════ /ebooks, covers ══ */
/* The spine, the sheen, the book icon, and the title and author that were set
   on the drawn cover all went with it. There is a photograph there now, and a
   fake spine drawn down the side of one reads as a printing fault. */
/* THE STATUS CHIP, AND IT IS MEANT TO BE QUIET. The first version was a solid
   white pill with a drop shadow and a clock icon, which on a bright cover read
   as a sticker slapped on top of the artwork -- "coming soon looks bad , it
   should be subtle".
 *
   It is a wash of the page's own ink instead: dark, translucent, no border, no
   shadow, no icon. It sinks into the cover rather than sitting on it, and
   because the wash DARKENS whatever is underneath, white on it measures better
   than white on the bare cover did -- subtle and legible are not opposites
   here, they are the same choice. */
.eb-soon{position:absolute;z-index:2;top:9px;right:9px;
  font-family:var(--f-dis);font-weight:700;font-size:8.4px;letter-spacing:.1em;
  text-transform:uppercase;color:rgba(255,255,255,.94);
  background:rgba(15,17,48,.38);border-radius:999px;padding:3px 8px}

.eb-buy{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:2px}
.eb-buy .tag .ic{font-size:13px;margin-right:-1px}

.eb-disc{display:flex;gap:11px;align-items:flex-start}
.eb-dico{flex:0 0 auto;color:var(--brand);line-height:1}
.eb-dico .ic{font-size:18px}

/* ═════════════════════════════════════ THE HERO'S FIRST SCREEN ON A PHONE ══
   Measured at 390x844 with the drawn class in place and the panel stacked
   under the words, as source order puts it: headline 136, stand-first 210,
   lede 276, three number tiles 353, buttons 466, and the class starting at
   604 — so two thirds of the one animated thing on the page was below the
   fold, and the thing above it was three static numbers.

   So on a phone the class comes UP, to sit directly under the sentence it
   illustrates, and the numbers go DOWN. Nothing is duplicated and nothing is
   hidden: display:contents dissolves the text column so its children become
   items of the same grid the panel is in, and order does the rest.

   WHY THE NUMBERS ARE THE ONES THAT MOVE. "Twenty years reading the news" is
   already in the stand-first line two inches above them, so the tiles are the
   only part of the first screen that repeats something. They are worth having
   and they are not worth the fold. */
@media (max-width:999px){
  .herowrap{gap:0}
  .herotext{display:contents}
  .hero .eyebrow{order:1}
  .hero h1{order:2}
  .hero .hm-stand{order:3}
  .hero .lede{order:4}

  /* THE CARD STAYS A CARD HERE, and it did not used to.

     "card is not having the same info as the mob version" -- it did not. This
     was display:contents at this width, so the coloured card was the SENTENCE
     ALONE and the three numbers were white tiles sitting below the buttons.
     The same four facts as the desktop card, in two places, only one of them
     coloured. So the box stops dissolving and takes the sentence's seat.

     THE FOLD IS STILL BOUGHT, DIFFERENTLY. The note at the top of this block
     is right: the tiles used to cost the class animation its first screen. But
     what cost the fold was their 2+1 GRID, which is 147px tall. Inside this
     card they are one row of about 60 (the rule is under <=420px, below), so
     bringing them home costs the animation far less than sending them away
     saved. Measured at 375x812: the animation still opens above the fold.

     .herotext is display:contents, so this card is a grid item and order
     works on it. */
  .hm-cred{order:3;margin-top:18px}
  /* The sentence is inside the card again, so it carries no chrome of its own
     -- the card's padding is its padding. */
  .hm-stand{margin:0;padding:0;background:none;border:0;box-shadow:none}
  /* The trust rules that used to be repeated here are gone with the card's
     colour. While it was a gradient they had to undo themselves at this width;
     now the card is white at every width and the desktop rules simply apply. */
  .clswrap,.portwrap{order:5;margin-top:clamp(20px,3.4vw,26px)}
  .hero .herocta{order:6}
  .hero .hm-note{order:7}
  .trust{order:8;max-width:none}
  /* THE COURSES PANEL, WHICH HAD NO ORDER AT ALL and therefore kept 0 and
     sorted ahead of every word on the page: measured at 375x812 it opened the
     hero at 99 and pushed the headline to 539, below the fold.

     It is not added to the order:5 rule above with .clswrap and .portwrap. On
     home that panel sits between the lede and the buttons because the animation
     is the argument and was worth the first screen. Here the ask was "the left
     section should come first and right section later", so it follows the whole
     text column -- buttons and note included. */
  .crswrap{order:9;margin-top:clamp(20px,3.4vw,26px)}

  /* and the panel itself gives back what it can without losing a seat. Eight
     students is the argument; the padding around them is not. */
  .cls-bar{padding:9px 13px;font-size:11.6px}
  .cls-teach{padding:13px;gap:10px}
  .cls-av.is-t{width:36px;height:36px;font-size:13px}
  .cls-teach span{font-size:14.5px;margin-top:2px}
  .cls-p{padding:9px 10px;gap:7px}
  .cls-p .cls-av{width:24px;height:24px;font-size:10.5px}
  .cls-p b{font-size:12.2px}

  /* A PHONE GETS THE DOTS, NOT THE SENTENCE. Two columns of a 358px panel is a
     178px cell, and after the avatar and the name that leaves about 85px of
     bubble — measured, and two of the eight were already ellipsising inside it.
     A sentence nobody can finish reading is worse than no sentence.
     So on a phone the bubble keeps its shape and its timing and says the one
     thing that matters here: this person is talking now. Three dots, drawn as
     background circles rather than as three more elements, and the full
     sentences come back the moment there is room for them. */
  .cls-b{flex:0 0 auto;width:34px;font-size:0;padding:0;height:19px;
    background-color:var(--surface);
    background-image:
      radial-gradient(circle 2px at 9px 50%,  currentColor 98%, transparent 100%),
      radial-gradient(circle 2px at 17px 50%, currentColor 98%, transparent 100%),
      radial-gradient(circle 2px at 25px 50%, currentColor 98%, transparent 100%);
    background-repeat:no-repeat}
  .cls-foot{padding:10px 13px;font-size:12px}
}
@media (max-width:420px){
  .trust{grid-template-columns:1fr 1fr}
  .trust>div:last-child{grid-column:1/-1}
  /* EXCEPT INSIDE THE CREDENTIAL CARD, where the 2+1 grid is what used to cost
     the class animation its place on the first screen -- 147px of tiles. Three
     in a row is about 60, and the labels wrap to two lines rather than the
     tiles wrapping to two rows. This is why the card can hold the same four
     things here as it does on a desktop. */
  .hm-cred .trust{grid-template-columns:repeat(3,minmax(0,1fr))}
  .hm-cred .trust>div:last-child{grid-column:auto}
}

/* ══════════════════════════════════════════════════ the phone bar, on a phone ══
   See ACADEMY_CALL_PAGES in page.php for which pages carry it and why.
   Below 760px only: above that the hero's own buttons are on the screen and a
   fixed bar would be an advertisement rather than a help. */
.callbar{display:none}
@media (max-width:759px){
  .callbar{display:flex;gap:9px;position:fixed;left:0;right:0;bottom:0;z-index:70;
    padding:10px clamp(12px,3.4vw,16px);
    /* the safe area, so the bar clears the home indicator on an iPhone */
    padding-bottom:calc(10px + env(safe-area-inset-bottom,0px));
    background:var(--glass);backdrop-filter:blur(18px) saturate(160%);
    -webkit-backdrop-filter:blur(18px) saturate(160%);
    border-top:1px solid var(--line)}
  .callbar .btn{flex:1 1 0;min-width:0;height:46px;padding:0 12px;font-size:14.4px}
  .callbar .btn-ghost{background:var(--surface);flex:0 1 42%}
  .callbar .ic{font-size:16px;margin-right:-2px}
  /* and the page gives back the room it takes, so the footer's last line is not
     sitting underneath it */
  body{padding-bottom:74px}
}

/* ═══════════════════════════════════════════════════════ /contact, the ways ══
   Three things to look at rather than one paragraph with links inside it. This
   page is a menu item now, and it is the only page on the site that says where
   the centre is — the owner's instruction, because half the business is online
   and a site built around one city throws that half away. */
/* ───────────────────────────── the booking page's head ──────────────────
   .bk-not is the "joining a course is not booked here" line, and its own
   comment calls it the most useful sentence on the page. It was --ink-3 on
   white; on the band it takes white, and the link inside it with it. */
/* A LITTLE SHORTER, and only the padding. It inherits the 96px a full
   marketing band gets top and bottom, which is 192 of a 433px head; 64 and 60
   takes it to about 365 without touching the headline, the alignment or the
   words. An earlier attempt cut it to 291 and centred it, and that was too far
   -- this is the small version. */
.bk-hero{padding-top:clamp(40px,5vw,64px);padding-bottom:clamp(38px,4.8vw,60px)}
.bk-hero .sechead{max-width:44rem}
.band-brand > .wrap > .bk-not{color:#fff;margin-top:16px}
.band-brand > .wrap > .bk-not a{color:#fff;text-decoration:underline;
  text-underline-offset:2px}
.bookwrap{padding-top:clamp(26px,4vw,40px)}

/* THE CLASS PHOTOGRAPH, one column wide and quiet. It was full width and
   919px across, which read as a banner across the section; at column width it
   sits beside the claims it supports instead of interrupting them. Lighter
   chrome to match: a hairline and a small shadow rather than the big one. */
/* THE PHOTOGRAPHS IN THE TWO-COLUMN SECTIONS -- her portrait beside "you have
   heard her read the news", and the class beside "you do not sit and listen".
   It was .hm-room-photo when the classroom was the only one using it; one of
   the two is a portrait of one person, so the name says photograph now. */
.hm-photo{margin:0 0 clamp(14px,2vw,18px);
  border-radius:var(--r-lg);overflow:hidden;line-height:0;
  border:1px solid var(--line);box-shadow:var(--sh-1)}
.hm-photo img{display:block;width:100%;height:auto}
/* the right-hand column of that section: her photograph, the class, the quote */
.hm-two-side{display:grid;gap:clamp(12px,1.8vw,16px);align-content:start}
/* HER PICTURE BEFORE THE RADIO ON A PHONE, by the owner's instruction. The
   radio lives in the LEFT column and her photograph in the right, so stacking
   them in source order puts the radio first -- which is the wrong way round.
   The left column stops existing at this width (display:contents), its children
   become items of the same grid, and order does the rest: words, her, radio.
   Scoped to .hm-two-news so no other two-column section is touched. */
/* THE RIGHT COLUMN NOW REACHES THE BOTTOM. .hm-two is align-items:start, so
   that column kept its own 487px against the left's 557 and stopped 70px
   short -- "right side is shorter". Stretching it and spacing its two cards
   apart puts the photograph at the top and the review at the foot, so both
   columns end on the same line. */
@media (min-width:940px){
  .hm-two-news .hm-two-side{align-self:stretch;align-content:space-between}
}
/* "How a class runs" gets the same treatment, and its own column split. The
   right side carries a photograph and a review against three short claims on
   the left, so the columns are 1.32 / 0.68 rather than 1.15 / 0.85 -- which
   also does what she asked of the picture: narrowing the column narrows the
   photograph WITH the review under it, so the two still share one edge. A
   photograph made smaller on its own leaves a strip of dead space beside it,
   which is what looked scattered the last time. */
@media (min-width:940px){
  .hm-two-room{grid-template-columns:minmax(0,1.32fr) minmax(0,.68fr)}
  .hm-two-room .hm-two-side{align-self:stretch;align-content:space-between}
}
/* 939, AND THE NUMBER IS NOT FREE. Both sections become two columns at
   min-width:940px, so every width below that is a single stack -- and this
   block is the whole reason the photograph comes before the panel in that
   stack. Stopping it at 860 left the 861-939 band ordered by source instead:
   measured at 900 and at 939, both sections read words -> panel -> photo,
   which is the order she asked twice to be rid of. The two numbers have to
   meet, so this one is 940 minus one. */
@media (max-width:939px){
  .hm-two-news > div:first-child{display:contents}
  .hm-two-news .eyebrow,.hm-two-news h2,
  .hm-two-news .lede,.hm-two-news .hm-more{order:1}
  .hm-two-news .hm-two-side{order:2}
  .hm-two-news .onair{order:3}

  /* and the same for "How a class runs": the photograph before the drawing.
     Both panels live in the LEFT column, so plain source order stacks them
     ahead of the picture on a phone -- which is backwards in both sections. */
  .hm-two-room > div:first-child{display:contents}
  .hm-two-room .eyebrow,.hm-two-room h2,.hm-two-room .hm-points{order:1}
  .hm-two-room .hm-two-side{order:2}
  .hm-two-room .vs{order:3}
}
/* THEY FILL THE COLUMN, and that is the fix for "everything looks here and
   there". At 290px inside a 435px column each photograph left a strip of dead
   space down its right-hand side, and the quote below it did not -- so the two
   things in the column disagreed about where the edge was, and the eye reads
   disagreeing edges as clutter rather than as a small picture.
   Same width as the quote beneath it now: one column, one edge, top to bottom. */
.hm-two-side .hm-photo{margin:0}
.hm-two-side .hm-say{margin:0}

.ct-ways{display:grid;gap:10px;margin:18px 0}
@media (min-width:620px){ .ct-ways{grid-template-columns:repeat(3,minmax(0,1fr))} }
/* AND THE SAME SURFACE AND PADDING as the card below them: they were
   var(--surface) at 16/17 against var(--surface-2) at 18/19, which is close
   enough to look like a mistake rather than a distinction. The icon stays on
   TOP here rather than beside the text -- these are a third of the width of
   "Where we are", and a 36px icon in the side of a 228px card leaves an email
   address nowhere to go. */
.ct-way{display:flex;flex-direction:column;gap:2px;padding:18px 19px;
  background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-lg);
  text-decoration:none;transition:border-color .2s,transform .2s var(--spring),box-shadow .2s}
.ct-way:hover{border-color:var(--brand-line);transform:translateY(-2px);box-shadow:var(--sh-1)}
.ct-way b{font-family:var(--f-dis);font-weight:800;font-size:16px;color:var(--ink);
  margin-top:10px}
.ct-way>span:last-child{font-size:13.6px;color:var(--ink-2);word-break:break-word}
.ct-ico{flex:0 0 auto;width:36px;height:36px;border-radius:12px;display:grid;
  place-items:center;background:var(--brand-soft);color:var(--brand);
  border:1px solid var(--brand-line)}
.ct-ico .ic{font-size:18px}

/* ── the contact page, now that it is a page and not a legal document ──── */
/* ONE EDGE FOR ALL FOUR CARDS. The row of three was capped at 58rem and the
   card under it at 44 -- 928px against 704 -- so the row overhung by 224px and
   Email, being the last of the three, was the one visibly hanging off the side
   of "Where we are". They share the cap now, and 44 is the one to keep: 58rem
   is a 928px measure, far past what a paragraph of address and directions
   should be read at. */
.ct-hero .ct-ways{margin:24px 0 12px;max-width:44rem}
.ct-hero .ct-where{max-width:44rem}
.ct-form .eq{max-width:44rem}
/* the email box: a light card on the colour, same as every other card on a band */
.ct-mail{max-width:38rem;background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-lg);padding:clamp(18px,3vw,24px);box-shadow:var(--sh-1)}
.ct-mail p{margin:0 0 10px;color:var(--ink-2);font-size:15.4px;line-height:1.62}
.ct-mail p:last-child{margin-bottom:0}
.ct-mail strong{color:var(--ink);font-weight:650}
.ct-mailto{font-family:var(--f-dis);font-weight:750;color:var(--brand);
  font-size:clamp(17px,2.4vw,20px);text-decoration:underline;text-underline-offset:3px}

.ct-where{display:flex;gap:14px;align-items:flex-start;padding:18px 19px;
  background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-lg);
  margin-bottom:8px}
.ct-where b{display:block;font-family:var(--f-dis);font-weight:800;font-size:16px;
  color:var(--ink)}
.ct-where address{font-style:normal;margin:6px 0 0;font-size:15px;line-height:1.6;
  color:var(--ink-2)}
.ct-where p{margin:11px 0 0;font-size:14.4px;color:var(--ink-3)}

/* The bar only slides once page.js has opted it in. See "the phone bar" there:
   default is pinned and visible, so a blocked script leaves a phone with the
   number rather than without it. */
@media (max-width:759px){
  .callbar.is-auto{transition:transform .28s var(--ease),opacity .2s var(--ease)}
  .callbar.is-auto.is-off{transform:translateY(105%);opacity:0;pointer-events:none}
}

/* ═════════════════════════════════════════ 2 · the correction, app-styled ══
   The reveal borrows .aw-bad / .aw-ok / .aw-rule wholesale from the tour above.
   That is the argument, not a shortcut: this card is the hook and the app is
   what is being sold, and drawing them as the same picture says so without a
   sentence. Only the strike needs help — the tour's is animated by an observer
   on .reveal.in, and this one is revealed by a tap. */
.hm-reveal .aw-bad span::after{transform:scaleX(1);animation:none}
.hm-reveal .aw-bad{margin-top:8px}
.hm-reveal .aw-k{margin:0}
.hm-reveal .aw-rule{margin-top:14px}
.hm-after{margin:16px 0 0;font-family:var(--f-dis);font-weight:750;font-size:14px}
.hm-after a{color:var(--brand);text-decoration:none;display:inline-flex;align-items:center;gap:5px}
.hm-after a:hover{text-decoration:underline}
.hm-after .ic{font-size:15px}

/* ═══════════════════════════════════════════════ 5 · which room, not a mark ══
   Three rooms and a marker on one of them. The fear that stops a beginner
   ringing is being the worst in the room, and "the test only decides which
   batch" is the sentence that answers it — so the picture has three rooms in
   it and no pass mark anywhere. */
.bt{margin-top:22px}
.bt-row{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:9px}
.bt-c{position:relative;display:flex;flex-direction:column;gap:5px;padding:14px 12px;
  border-radius:var(--r-md);background:var(--surface);border:1px solid var(--line);
  font-size:12.6px;color:var(--ink-3);font-family:var(--f-dis);font-weight:650;
  text-align:center;align-items:center}
.bt-c b{width:30px;height:30px;border-radius:99px;display:grid;place-items:center;
  font-family:var(--f-dis);font-weight:800;font-size:14px;
  background:var(--surface-3);color:var(--ink-3)}
.bt-c.is-on{border-color:var(--brand-line);background:var(--brand-soft);color:var(--brand);
  box-shadow:var(--sh-1)}
.bt-c.is-on b{background:var(--grad-btn);color:#fff;box-shadow:var(--sh-brand)}
/* the marker under the one it picked */
.bt-c.is-on::after{content:"";position:absolute;left:50%;bottom:-9px;width:12px;height:12px;
  transform:translateX(-50%) rotate(45deg);background:var(--brand-soft);
  border-right:1px solid var(--brand-line);border-bottom:1px solid var(--brand-line)}
.bt-cap{display:flex;align-items:flex-start;gap:8px;margin:20px 0 0;
  font-size:14px;line-height:1.5;color:var(--ink-2)}
.bt-cap .ic{font-size:17px;color:var(--ok);flex:0 0 auto;margin-top:1px;stroke-width:2.4}

/* ══════════════════════════════════════════════ 9 · the app, with a question ══
   The product's own chrome carrying a real question out of a real preview file.
   The options are shown and not answerable — this is a picture of the app, and
   a half-working quiz on a marketing page is worse than a still one. */
.hm-win{margin:0}
.hm-win .aw-body{padding-bottom:78px}
.hm-win .demoq{margin-bottom:14px}
.hm-win .opts{display:grid;gap:8px}
.hm-win .opt{display:block;padding:11px 13px;border-radius:14px;font-size:14.4px;
  background:var(--surface-2);border:1.5px solid var(--line);color:var(--ink-2)}
.hm-win .opt.is-right{background:var(--ok-soft);border-color:var(--ok-line);color:var(--ok);
  font-weight:650}

/* ═══════════════════════════════════════════════════ 12 · the last panel ══
   The page exists to produce one telephone call and it used to end on the
   quietest block in it. */
.fin{position:relative;overflow:hidden;border-radius:var(--r-2xl);
  padding:clamp(30px,5vw,56px) clamp(22px,4vw,48px);
  background:var(--grad-btn);color:#fff;box-shadow:var(--sh-brand)}
.fin::before{content:"";position:absolute;inset:-40% -10% auto -10%;height:150%;
  background:radial-gradient(60% 60% at 22% 0%, rgba(255,255,255,.26), transparent 62%)}
.fin-in{position:relative;max-width:44rem}
.fin .eyebrow{color:rgba(255,255,255,.72)}
.fin h2{color:#fff;font-size:clamp(28px,5vw,44px);margin-top:12px}
.fin .lede{color:rgba(255,255,255,.9);margin-top:14px;max-width:38em}
/* the number itself, big, and a link */
.fin-no{display:inline-flex;align-items:center;gap:12px;margin-top:clamp(22px,3vw,30px);
  padding:12px 20px 12px 16px;border-radius:999px;text-decoration:none;color:#fff;
  background:rgba(255,255,255,.16);border:1px solid rgba(255,255,255,.3);
  transition:background .2s,transform .2s var(--spring)}
.fin-no:hover{background:rgba(255,255,255,.26);transform:translateY(-2px)}
.fin-no .ic{font-size:22px}
.fin-no span{font-family:var(--f-dis);font-weight:800;letter-spacing:-.02em;
  font-size:clamp(24px,4.4vw,34px);font-variant-numeric:tabular-nums}
.fin .herocta{margin-top:22px}
.fin .btn-solid{background:#fff;color:var(--brand);box-shadow:none}
.fin .btn-solid:hover{background:#fff;box-shadow:0 10px 26px -8px rgba(0,0,0,.35)}
.fin .btn-ghost{background:transparent;color:#fff;border-color:rgba(255,255,255,.42)}
.fin .btn-ghost:hover{background:rgba(255,255,255,.14);border-color:rgba(255,255,255,.7);
  color:#fff}
.hm-close .hm-say-wide{margin-top:clamp(26px,4vw,38px)}

/* ═══════════════════ the closing block when the closing section is coloured ══
   .fin is a var(--grad) card with white text, and that is right on a white
   page: it is the one loud block, "Colored cards on light background".
   Inverting the home page put it ON a band, where a gradient card on a gradient
   section has no edge at all.

   So it turns over -- white card, dark text -- and the two things that carried
   the colour, the number pill and the primary button, take the gradient
   instead. "Light cards on color background", the other half of the same rule.

   Scoped to .band-brand, so the moment that section is light again this whole
   block stops applying and .fin is exactly what it was. .fin is on the home
   page and nowhere else. */
.band-brand .fin{background:var(--surface);color:var(--ink);
  box-shadow:0 26px 60px -22px rgba(15,17,48,.5)}
/* the light source was 26% white over the colour; over white it is the brand */
.band-brand .fin::before{background:radial-gradient(60% 60% at 22% 0%,
  rgba(90,69,255,.10), transparent 62%)}
.band-brand .fin .eyebrow{color:var(--brand)}
.band-brand .fin h2{color:var(--ink)}
.band-brand .fin .lede{color:var(--ink-2)}
/* the number is the loudest thing on the page and stays that way -- it simply
   swaps which side of the pair is coloured */
.band-brand .fin-no{background:var(--grad-btn);color:#fff;border-color:transparent;
  box-shadow:var(--sh-brand)}
.band-brand .fin-no:hover{background:var(--grad-btn)}
.band-brand .fin .btn-solid{background:var(--grad-btn);color:#fff;
  box-shadow:var(--sh-brand)}
/* IT LIFTS, IT DOES NOT BRIGHTEN. This used to swap to var(--grad) on hover,
   which is the decorative gradient: 3.96:1 at its middle stop and 3.29:1 at its
   end under white, against the 4.5 a label this size needs. It was the last
   place on the site putting white text on that gradient -- everything else moved
   to --grad-btn (5.63 / 4.67 / 4.65). .btn-solid:hover already lifts the button
   2px, which is the feedback; the colour change was the part that cost
   readability, and the owner picked losing it on 30 August 2026 after hovering
   both side by side. */
.band-brand .fin .btn-ghost{background:var(--surface);color:var(--ink);
  border-color:var(--line-strong)}
.band-brand .fin .btn-ghost:hover{background:var(--surface);color:var(--brand);
  border-color:var(--brand-line)}

/* ══════════════════════════════════════════════ 6 · how long, to scale ══
   --m is the number of months and the width is that against six, so the bars
   are comparable and not merely decorative: one month beside six is the thing
   a reader is actually weighing up, and it was two words in grey. */
/* the group it belongs to, as a chip, since the headings that used to say so
   were the thing breaking the grid */
/* the group it belongs to, as a chip, since the headings that used to say so
   were the thing breaking the grid */
.hm-grp{grid-area:grp;justify-self:start;font-family:var(--f-dis);font-weight:750;
  font-size:10px;letter-spacing:.15em;text-transform:uppercase;color:var(--brand);
  background:var(--brand-soft);border-radius:999px;padding:3px 9px;line-height:1.5}
.hm-plan{--m:1}
.oc-item{--m:1}
/* A BAR HAS A HEIGHT AND A WIDTH AND NO OPINION ABOUT ITS CONTAINER. It used to
   carry flex:1 0 100%, which is right in a row and catastrophic in a column: on
   /courses, which stacks, that made a 4px rule grow to fill the card
   and put a 177px grey slab under all seven of them. */
.hm-bar{grid-area:bar;display:block;width:100%;height:4px;border-radius:99px;
  background:var(--surface-3);overflow:hidden;margin-top:6px}
.hm-band .hm-bar{background:var(--line)}
.hm-bar i{display:block;height:100%;border-radius:99px;background:var(--grad);
  /* TWELVE, NOT SIX. Rebased 25 August 2026 when English Hons arrived at
     twelve months. At /6 it computed 200%, and overflow:hidden clipped it to
     a full bar -- so the longest course and the second longest would have
     drawn identically and nothing would have said so. */
  width:calc(var(--m,1) / 12 * 100%)}
.oc-item .hm-bar{flex:0 0 auto;align-self:stretch;margin-top:14px;max-width:16rem}

/* ═════════════════════════════════════════════ 5 · the chooser's answer ══
   Two questions and the answer used to arrive as three lines of text inside
   the same white box the questions were in — so the one interaction on the
   page changed nothing about what the reader was looking at. */
.hm-out{position:relative;overflow:hidden;border-top:0;
  background:var(--grad-btn);color:#fff;box-shadow:var(--sh-brand)}
.hm-out::before{content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(80% 90% at 12% 0%, rgba(255,255,255,.24), transparent 62%)}
.hm-out>*{position:relative}
.hm-out .eyebrow{color:rgba(255,255,255,.74)}
.hm-name{color:#fff}
.hm-meta{color:rgba(255,255,255,.82)}
.hm-why2{color:#fff}
.hm-out .btn-solid{background:#fff;color:var(--brand);box-shadow:none}
.hm-out .btn-ghost{background:transparent;color:#fff;border-color:rgba(255,255,255,.42)}
.hm-out .btn-ghost:hover{background:rgba(255,255,255,.14);color:#fff;
  border-color:rgba(255,255,255,.7)}
.hm-also{color:rgba(255,255,255,.78);border-top-color:rgba(255,255,255,.24)}

/* ══════════════════════════════════════════════ 10 · the FAQ, with company ══
   Six closed accordions is a lot of grey rules and nothing to look at, and the
   question somebody actually has is usually not one of the six. */
.faqwrap{display:grid;gap:clamp(16px,2.6vw,24px);align-items:start}
@media (min-width:940px){
  .faqwrap{grid-template-columns:minmax(0,1.45fr) minmax(0,.55fr)}
}
.faqask{background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-xl);padding:clamp(20px,2.8vw,26px);
  display:flex;flex-direction:column;align-items:flex-start;gap:0}
.hm-band .faqask{background:var(--surface-2)}
.faqask-i{width:42px;height:42px;border-radius:14px;display:grid;place-items:center;
  background:var(--grad-btn);color:#fff;box-shadow:var(--sh-brand);margin-bottom:15px}
.faqask-i .ic{font-size:21px}
.faqask b{font-family:var(--f-dis);font-weight:800;font-size:18px;color:var(--ink);
  letter-spacing:-.02em;line-height:1.24}
.faqask p{margin:9px 0 18px;font-size:14.6px;line-height:1.55;color:var(--ink-2)}
.faqask .btn{gap:8px}
.faqask .btn .ic{font-size:16px}
.faqask-w{margin-top:12px;font-size:13.4px;color:var(--ink-3);text-decoration:none;
  border-bottom:1px solid var(--line)}
.faqask-w:hover{color:var(--brand);border-color:var(--brand-line)}

/* the three ways sit in a panel rather than loose on the band */
.ratepanel{background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-xl);padding:clamp(14px,2vw,18px);gap:clamp(14px,2vw,18px)}
.ratepanel .hm-rate{background:transparent;border:0;padding:clamp(4px,1vw,8px)}
.ratepanel .hm-rate + .hm-rate{border-left:1px solid var(--line)}
@media (max-width:759px){
  .ratepanel .hm-rate + .hm-rate{border-left:0;border-top:1px solid var(--line);
    padding-top:16px}
}

/* ═════════════════════════════════════════════════════ /about, section two ══
   Three paragraphs and a quote was the longest run of unbroken prose anywhere
   on this site. The words now sit beside the thing they are about. */
.ab-two{display:grid;gap:clamp(26px,4vw,44px);align-items:center}
@media (min-width:960px){
  .ab-two{grid-template-columns:minmax(0,1.08fr) minmax(0,.92fr)}
}
.ab-side{display:grid;gap:clamp(14px,2vw,18px);align-content:start}
/* the testimonial now lives at the foot of the prose column rather than full
   width under both, so it needs the gap a paragraph would have had */

/* the before and after. The middle paragraph says the harder half of twenty
   years on air was finding the version everybody understands the first time —
   which is a before and an after, and therefore a picture. */
.tr{position:relative;background:var(--surface);border:1px solid var(--line-strong);
  border-radius:var(--r-xl);padding:clamp(19px,2.6vw,25px);box-shadow:var(--sh-2)}
.tr .aw-k{margin:0 0 15px}
.tr-a,.tr-b{border-radius:var(--r-md);padding:14px 15px}
.tr-a{background:var(--surface-3);border:1px solid var(--line)}
.tr-b{background:var(--ok-soft);border:1px solid var(--ok-line)}
.tr-tag{display:block;font-family:var(--f-dis);font-weight:750;font-size:10.5px;
  letter-spacing:.15em;text-transform:uppercase;color:var(--ink-4);margin-bottom:7px}
.tr-b .tr-tag{color:var(--ok);opacity:.85}
.tr-a p{margin:0;font-family:var(--f-ser);font-size:clamp(16px,2.2vw,19px);
  line-height:1.34;color:var(--ink-3)}
.tr-b p{margin:0;font-family:var(--f-ser);font-size:clamp(17px,2.4vw,21px);
  line-height:1.32;color:var(--ok)}
.tr-arrow{display:grid;place-items:center;width:30px;height:30px;border-radius:99px;
  margin:9px auto;background:var(--grad-btn);color:#fff;box-shadow:var(--sh-brand);
  transform:rotate(90deg)}
.tr-arrow .ic{font-size:16px;stroke-width:2.4}
.tr-note{margin:15px 0 0;font-size:13.4px;line-height:1.5;color:var(--ink-3)}

/* the three spans of years */
.ab-yrs{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:9px}
.ab-yrs>div{background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-md);padding:14px 12px;text-align:center}
.ab-yi{display:grid;place-items:center;width:30px;height:30px;border-radius:10px;
  margin:0 auto 9px;background:var(--brand-soft);color:var(--brand);
  border:1px solid var(--brand-line)}
.ab-yi .ic{font-size:16px}
.ab-yrs b{display:block;font-family:var(--f-dis);font-weight:800;
  font-size:clamp(20px,2.6vw,25px);letter-spacing:-.03em;line-height:1.1;
  background:var(--grad);-webkit-background-clip:text;background-clip:text;color:transparent}
.ab-yrs span:last-child{display:block;margin-top:3px;font-size:11.6px;line-height:1.3;
  color:var(--ink-3)}
.hm-band .ab-two + .wrap .hm-say{margin-top:clamp(24px,3.4vw,34px)}

/* ══════════════════════════════════════════════════ /ebooks, the shelf ══
   Three drawn books rather than three rows of text with a button beside each.

   The accent is picked by :nth-child so three cards on a shelf are three books
   instead of one card printed three times. It comes out of the existing
   --a1/--a2/--a3 and nothing new enters the palette. */
.shelf{position:relative;display:grid;gap:clamp(16px,2.4vw,22px);
  grid-template-columns:repeat(2,minmax(0,1fr));padding-bottom:22px}
/* 760, the same as .eb-grid above. It used to be 820, which meant that between
   760 and 819 her three were four across and the recommended three were two --
   the two shelves on one page disagreeing with each other. */
@media (min-width:760px){ .shelf{grid-template-columns:repeat(4,minmax(0,1fr))} }
/* the shelf they stand on */
.shelf-line{position:absolute;left:0;right:0;bottom:0;height:4px;border-radius:99px;
  background:linear-gradient(90deg,transparent,var(--line-strong) 8%,
    var(--line-strong) 92%,transparent)}

.bk{--bk-a:var(--a1);
  display:flex;flex-direction:column;align-items:flex-start;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-xl);
  padding:clamp(17px,2.4vw,21px);
  transition:border-color .2s,transform .2s var(--spring),box-shadow .2s}
.bk:nth-child(2){--bk-a:var(--a2)}
.bk:nth-child(3){--bk-a:var(--a3)}
.bk:hover{border-color:var(--brand-line);transform:translateY(-3px);box-shadow:var(--sh-2)}
.hm-band .bk{background:var(--surface-2)}

/* THESE ARE OTHER PEOPLE'S BOOKS AND MUST LOOK IT. The note that used to be
   here said the drawn covers were deliberately pale so that "nobody should come
   away thinking Wren & Martin is a Minati Ma'am ebook". The concern was right
   and the real covers serve it better than any drawing could -- an actual
   Wren & Martin cover cannot be mistaken for one of hers.

   aspect-ratio IS 7/10 BECAUSE THE FILES ARE. All three are 700x1000 -- ratio
   0.7000 exactly -- so the box matches the picture and nothing is stretched or
   cropped a second time in the browser.

   THEY ARE PADDED, NOT CROPPED. The three product shots arrived at 0.783,
   0.657 and 0.654: the Wren & Martin is a LARGE FORMAT edition and genuinely a
   different shape from the other two. Cropping the wide one to the narrow ones
   would have taken 16% off its width, straight through "WREN & MARTIN". So each
   is padded out to 7:10 with a colour sampled from its OWN four-pixel border --
   white for the Wren, #CD0000 for the Word Power, #027B98 for the 30 Days -- so
   the padding disappears into the cover instead of showing as a bar. Nothing is
   cropped, every title is whole, and the three boxes are identical.

   NO SPINE AND NO GLOSS ANY MORE. Both belonged to the drawn cover: a fake
   spine shading down the left of a real product photograph, one of which stands
   on white, reads as a printing fault. */
/* 62% AND CENTRED, not stretched across the card. Measured: the cover was 429
   of a 723px card, and it was full-bleed only because a drawn mock cover wanted
   to be. A shop shows the book smaller with air either side -- it reads as an
   object rather than a banner, and it is the largest saving available here
   without cropping a single cover. */
.bk-cov{position:relative;width:62%;align-self:center;
  aspect-ratio:7/10;overflow:hidden;padding:0;
  border-radius:10px;
  background:#fff;border:1px solid var(--line);box-shadow:var(--sh-2)}
.bk-cov a{display:block;width:100%;height:100%}
.bk-cov img{display:block;width:100%;height:100%;object-fit:cover;
  transition:transform .3s var(--ease)}
.bk:hover .bk-cov img{transform:scale(1.03)}

/* NO flex-grow ANY MORE. It was here to push the button to the bottom of the
   card so the three buttons lined up -- which mattered while the descriptions
   ran three, four and five lines and the blocks were different heights. Now
   every description is exactly two lines and every block is the same, so the
   button lines up on its own and the card is only as tall as what is in it.
   Left in, the grow would simply reopen the hole it used to close. */
.bk-txt{padding-top:14px;align-self:stretch}
/* The title used to sit here in a box that reserved two lines, so that one
   wrapping title could not push its card's button 44px lower than the other
   two. With the title gone the covers carry the names and the three blocks are
   the same shape again for free. */
/* centred under the cover, which is itself centred */
.bk-meta{margin-top:0;text-align:center;
  font-family:var(--f-dis);font-weight:750;font-size:10.5px;
  letter-spacing:.13em;text-transform:uppercase;color:var(--ink-3)}
.bk-meta .bk-sep{display:inline-block;margin:0 6px;color:var(--bk-a);opacity:.85}
/* :not(.bk-meta) matters. The author line IS a <p> inside .bk-txt, so a plain
   `.bk-txt p` rule outranks `.bk-meta` on specificity and was handing it the
   description's 13px top margin -- which is why closing the title-to-author gap
   had no effect twice. This rule is the DESCRIPTION's alone now.

   TWO LINES IS DONE IN THE WORDS, NOT HERE. There was a -webkit-line-clamp on
   this rule for a while; it cut the sentences and put an ellipsis on them,
   which is exactly what she did not want. The descriptions are written to
   about thirty characters instead -- the measured budget for two lines at the
   narrowest card -- so there is nothing to clamp. It is deliberately NOT left
   in as a safety net: a clamp would quietly truncate anything written longer
   later, and quietly is the problem. */
.bk-txt p:not(.bk-meta){margin-top:13px;color:var(--ink-2);font-size:14.2px;
  line-height:1.5}
.bk-buy{margin-top:14px;width:100%;align-self:stretch}
/* ═══════════════ the two places where a book card is NARROW ═════════════
   Cards are narrow twice, not once: two across a phone (up to 620), and four
   across as soon as four columns start at 760 but before the page is wide
   enough to make them roomy (up to 1080 -- measured: at 900 a cover was still
   only 92px and the titles were being cut). Everything below applies to both.

   THE BUTTON. Two across a phone leaves it about 120px and "Buy on Amazon"
   plus its arrow does not fit -- measured clipped at 375, 360 and 760. The
   label is NOT shortened, because "Buy on Amazon" is the whole point of the
   button; it is set smaller and tighter so the words survive.

   THE COVER. 62% of the card was chosen when a card was 344px wide and the
   cover came out 186. At 156px it comes out 74 -- a thumbnail, and small
   enough that the titles printed on her placeholder covers were being cut off
   by the overflow:hidden rather than fitting. On a narrow card the cover takes
   almost the whole width instead, which is what the space is for. */
@media (max-width:620px), (min-width:760px) and (max-width:1080px){
  .bk-buy{font-size:12.4px;padding:0 8px;letter-spacing:0}
  .bk-buy .eb-ext{font-size:13px}
  .bk-cov,.eb-cover{width:92%}
  /* THE AUTHOR LINE STOPS WRAPPING. "WREN & MARTIN / GRAMMAR" is 23 uppercase
     characters with .13em of tracking, and a half-width phone card gives the
     line 120px -- it went to two lines, and "NORMAN LEWIS / VOCABULARY" is
     longer still. No size small enough to fit 25 tracked capitals in 120px is
     worth reading, so the CATEGORY drops instead and the author stays: the
     author is the half somebody is looking for, and the category is already
     obvious from the cover. */
  .bk-meta{white-space:nowrap}
  .bk-meta .bk-sep,.bk-meta .bk-kind{display:none}
  .eb-soon{top:6px;right:6px;font-size:7.4px;padding:2px 6px;letter-spacing:.05em}
}

/* ════════════════════════════════════ the seven, side by side rather than a list ══
   Seven cards one under another is a scroll, and the owner's note was that it
   wastes the width the page already has. The group headings stay full-width so
   the three groups still read as three groups. */
@media (min-width:700px){ .oc-list{grid-template-columns:repeat(2,minmax(0,1fr))} }
@media (min-width:1020px){ .oc-list{grid-template-columns:repeat(3,minmax(0,1fr))} }
.oc-list{gap:12px}
.oc-item{display:flex;flex-direction:column}
.oc-item p{flex:1 1 auto}
/* align-self, because .oc-item is a COLUMN flex container and the default
   stretch turned this pill into a full-width bar 251px across. */
.oc-grp{align-self:flex-start;font-family:var(--f-dis);font-weight:750;font-size:10px;
  letter-spacing:.15em;text-transform:uppercase;color:var(--brand);
  background:var(--brand-soft);border-radius:999px;padding:3px 9px;margin-bottom:9px;
  line-height:1.5}

/* A page whose first section IS the top of the page — /ebooks has no hero.
   It needs the breathing room the hero used to give it above the fold, and its
   heading is an h1 rather than an h2, so .sechead has to dress both. */
.sec-first{padding-top:clamp(34px,5vw,60px)}
.sechead h1{font-size:clamp(28px,5vw,44px);line-height:1.05}

/* ══════════════════════════════════ the on-air plate, across the section ══
   It spent its first day in a 435px side column under a review — the single
   most persuasive fact this business owns, rendered small. Full width, a taller
   waveform, and the three facts inside the panel rather than beside it. */
/* ───────────────────── the on-air panel, quiet version ──────────────────
   It sits in a column again, so it has to be about a third of the height it
   was: 431px of panel in a 589px column left a 280px hole under the column
   opposite. Flat surface, hairline border, no brand wash and no inner shadow;
   the wave drops from 124px to 44 and from 32 bars to 24, at half the weight.
   The only thing that moves is the wave, and it moves slowly. */
.onair-wide{margin-top:clamp(18px,2.4vw,24px);padding:clamp(15px,2vw,19px)}
.onair.onair-wide{background:var(--surface);border:1px solid var(--line);
  box-shadow:var(--sh-1)}
.onair.onair-wide::before,.onair.onair-wide::after{display:none}
.onair-wide .onair-wave{height:44px;gap:3px;margin:13px 0}
.onair-wide .onair-wave i{opacity:.5}
.onair-wide .onair-lamp{font-size:10px;letter-spacing:.14em}
.onair-wide .onair-lamp i{width:6px;height:6px}
.onair-wide .onair-yrs{font-size:11.5px}
/* "Two channels · twenty years · one voice" ellipsised at 390px, and a line
   that ends in a row of dots says less than no line at all. The lamp carries
   the panel on a phone; the three facts are spelled out underneath it anyway. */
.onair-wide .onair-yrs{display:none;font-weight:750;font-size:12.4px;
  letter-spacing:.02em;opacity:.8;white-space:nowrap}
@media (min-width:620px){ .onair-wide .onair-yrs{display:inline} }
.onair-wide .onair-src{position:relative;z-index:2;display:grid;gap:12px;
  padding-top:clamp(16px,2.4vw,20px);border-top:1px solid var(--line)}
@media (min-width:820px){ .onair-wide .onair-src{grid-template-columns:repeat(3,minmax(0,1fr))} }
.onair-wide .onair-src span{display:grid;grid-template-columns:auto minmax(0,1fr);
  gap:4px 11px;align-items:center}
.onair-wide .onair-src .ic{grid-row:span 2;font-size:22px;color:var(--brand)}
.onair-wide .onair-src b{font-family:var(--f-dis);font-weight:800;font-size:14.5px;
  letter-spacing:-.01em;color:var(--ink)}
.onair-wide .onair-src em{font-style:normal;font-family:var(--f-ui);font-weight:400;
  font-size:12.2px;color:var(--ink-3)}

/* ═══════════════════════════════════════════ a lecture, and this class ══
   The sentence above this is a comparison, so it is drawn as one. Twelve
   silent circles round a single speaker, against eight that are all speaking.
   It had been asserted in three bullet points and never shown. */
.vs{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(10px,1.6vw,14px);align-content:start;
  margin-top:clamp(26px,4vw,40px)}
/* COOL AND GREY AGAINST WARM AND LIT. The two panels were the same white box
   twice with different-coloured dots in them; the difference between a lecture
   and this class should be felt before it is read. */
.vs-p{position:relative;overflow:hidden;border:1px solid var(--line);
  border-radius:var(--r-lg);padding:clamp(15px,2.2vw,20px);
  background:linear-gradient(180deg,var(--surface) 0%,var(--surface-3) 100%)}
.hm-band .vs-p{background:linear-gradient(180deg,var(--surface-2) 0%,var(--surface-3) 100%)}
.vs-p.is-on{border-color:var(--brand-line);
  background:linear-gradient(180deg,var(--surface) 0%,var(--brand-soft) 100%);
  box-shadow:var(--sh-1)}
.hm-band .vs-p.is-on{background:linear-gradient(180deg,var(--surface-2) 0%,var(--brand-soft) 100%)}
/* THE LIT CARD IS THE ONE TRANSLUCENT SURFACE ON A BAND, and on a band that
   is a hole rather than a tint. Its lower stop is --brand-soft,
   rgba(90,69,255,.09) -- ninety-one per cent see-through. Over the pale --bg
   of an ordinary section that reads as a soft violet wash; over a coloured
   band the gradient shows straight through the bottom half of the card, so
   the violet text and the drawn room inside it end up standing on the band
   instead of on white. The owner: "the text and graphic inside 'this class'
   card ... is not properly visible against the background."

   #F0EEFF and #DBD6FF are those two tokens composited over #FFFFFF -- the
   colours they already LOOK like on a light page, made opaque. Nothing about
   the card changes anywhere else.

   Found by listing every element on a band whose background has an alpha
   between 0.02 and 0.99. It is the only one on the page. */
.band-brand .vs-p.is-on{border-color:#DBD6FF;
  background:linear-gradient(180deg,var(--surface) 0%,#F0EEFF 100%)}
/* a pool of light on the floor of the room that is lit */
.vs-p.is-on::after{content:"";position:absolute;left:50%;bottom:52px;width:78%;
  height:74px;transform:translateX(-50%);pointer-events:none;border-radius:50%;
  /* Halved. It is the one device that says the room is lit, so it stays --
     but at .20 it was a haze sitting exactly where the figures stand. */
  background:radial-gradient(closest-side,rgba(168,85,247,.10),transparent 78%)}
.vs-k{display:block;font-family:var(--f-dis);font-weight:750;font-size:10px;
  letter-spacing:.16em;text-transform:uppercase;color:var(--ink-4);margin-bottom:14px}
.vs-p.is-on .vs-k{color:var(--brand)}
.vs-p p{margin:14px 0 0;font-size:13.4px;line-height:1.45;color:var(--ink-3)}
.vs-p.is-on p{color:var(--brand)}
@media (max-width:520px){ .vs{grid-template-columns:minmax(0,1fr)} }

/* ════════════════════════════════════════════ a solid glyph in the sprite ══
   Everything else in the sprite is a 2px outline because it is an icon beside
   a label. The figures are drawn nine at a time at 22px as a room full of
   people, and at that size an outline reads as noise. */
.ic.solid{fill:currentColor;stroke:none}

/* ═════════════════════════════════ section 3, and it is not a screen ══
   It was one: a gradient box with a bar chart on it, four viewfinder
   corners, a clock, and the red LIVE tab and caption bar every news
   bulletin on earth uses. The quiet redesign deleted all of that from
   page_onair_panel(), and the rules for it stayed behind here matching
   nothing -- .oa-c, .oa-tl/tr/bl/br, .oa-lower, .oa-live, .oa-cap and
   .oa-time, about thirty lines. They are gone now. What draws the panel
   is .onair-wide, in the section that follows. */
.onair-wide{padding-bottom:clamp(18px,2.6vw,24px)}

/* ═══════════════════════════════════════════════ section 4, as a room ══
   Not dots. Nine people, sitting in two rows facing a board, drawn with the
   sprite's filled figure. On the left one of them speaks and the other eight
   are grey; on the right all nine are lit and their bubbles pop in turn — on
   the same 1.15s clock the hero's class runs on, so the two drawings on this
   page are visibly the same room. */
.room{position:relative;display:flex;flex-direction:column;align-items:center;
  gap:clamp(9px,1.4vw,13px);padding:clamp(12px,1.8vw,16px) 0 4px}
/* the board at the front, with a stand under it */
.room-board{position:relative;width:min(70%,180px);height:9px;border-radius:3px;
  background:var(--line-strong);opacity:.65;margin-bottom:2px}
.room-board::after{content:"";position:absolute;left:50%;top:100%;width:2px;height:7px;
  transform:translateX(-50%);background:inherit;opacity:.8;border-radius:0 0 2px 2px}
/* NO GLOW UNDER THE BOARD. It was a 14px violet spread, and the board is
   already the only gradient in the drawing -- the glow added haze and said
   nothing the colour was not already saying. */
.vs-p.is-on .room-board{background:var(--grad);opacity:.9}

.fig{--i:0;position:relative;display:grid;place-items:center;
  width:clamp(22px,2.6vw,27px);aspect-ratio:1;color:var(--ink-4);line-height:0}
.fig .ic{width:100%;height:auto;display:block}
.fig-lead{width:clamp(32px,3.8vw,40px);color:var(--ink-3);margin-bottom:4px}
/* THE LIT ROOM WAS THE BLURRED ONE, which is the opposite of the point.
   The owner: "the shadow does not look nice in it, makes it look more blur
   than clear." Four things were fogging it at once -- a 10px pink
   drop-shadow behind the lead figure, a 7px violet one behind every figure
   in the room, a 14px glow under the board, and a purple pool over the floor
   -- on top of the figures being held at .55 opacity.

   Measured against the card, and these are GRAPHICS, so the bar is 3:1 and
   not 4.5:1:

       room figures at .55 opacity   2.31   under the bar, and blurred
       lead figure in --a3           2.88   the faintest thing in the drawing

   The blurs go, the room comes to full strength at 4.93, and the lead figure
   takes --no rather than --a3: 3.99 against 2.88, still unmistakably the pink
   one against a room of violet, and an existing token rather than a colour
   invented to pass a number. */
.vs-p.is-on .fig{color:var(--brand);opacity:1}
.vs-p.is-on .fig-lead{color:var(--no);opacity:1}

/* DEPTH. Two rows of identical figures is a chart; a back row that is smaller
   and dimmer than the front one is a room somebody is standing in. */
.room-rows{position:relative;display:grid;grid-template-columns:repeat(4,auto);
  gap:clamp(9px,1.4vw,14px) clamp(8px,1.3vw,13px);justify-content:center;
  align-items:end}
.room-rows .fig:nth-child(-n+4){width:clamp(24px,2.9vw,30px)}
.room-rows .fig:nth-child(n+5){width:clamp(19px,2.3vw,24px);opacity:.72}
.vs-p.is-on .room-rows .fig:nth-child(n+5){opacity:.44}

/* the little bubble over whoever is speaking. Three dots painted as background
   circles rather than three more elements, the same way the phone's class
   panel draws them. */
.fig-say{position:absolute;bottom:calc(100% + 3px);left:50%;
  width:22px;height:11px;margin-left:-11px;border-radius:6px 6px 6px 2px;
  background-color:var(--surface);border:1px solid var(--brand-line);
  background-image:
    radial-gradient(circle 1.3px at 6px 50%,  var(--brand) 98%, transparent 100%),
    radial-gradient(circle 1.3px at 11px 50%, var(--brand) 98%, transparent 100%),
    radial-gradient(circle 1.3px at 16px 50%, var(--brand) 98%, transparent 100%);
  background-repeat:no-repeat;opacity:0;transform:scale(.7);transform-origin:bottom left}
.hm-band .fig-say{background-color:var(--surface)}

/* THE LECTURE: only the one at the front ever says anything, and it never
   stops. That is the whole picture. */
.vs-p:not(.is-on) .fig-lead .fig-say{opacity:1;transform:none}

/* THIS CLASS: everybody, in turn, on the hero's clock. */
.vs-p.is-on .fig-lead .fig-say{opacity:1;transform:none}
.vs-p.is-on .room-rows .fig{animation:figLit calc(8 * 1.15s) steps(1) infinite;
  animation-delay:calc(var(--i) * 1.15s)}
.vs-p.is-on .room-rows .fig-say{animation:figSay calc(8 * 1.15s) var(--spring) infinite;
  animation-delay:calc(var(--i) * 1.15s)}
@keyframes figLit{
  0%,11.6%  {opacity:1;transform:translateY(-3px) scale(1.06)}
  14%,100%  {opacity:.55;transform:none}
}
@keyframes figSay{
  0%        {opacity:0;transform:scale(.7)}
  1.6%,11%  {opacity:1;transform:none}
  12.5%,100%{opacity:0;transform:scale(.8)}
}
@media (prefers-reduced-motion:reduce){
  .vs-p.is-on .room-rows .fig,.vs-p.is-on .room-rows .fig-say{animation:none}
  .vs-p.is-on .room-rows .fig{opacity:1}
  .vs-p.is-on .room-rows .fig-say{opacity:1;transform:none}
}

/* ═══════════════════════════════════════════════════════ the enquiry form ══
   Two required boxes and three optional ones, on the front page, on
   /courses and on /contact. See page_enquiry_form(). */
.eq{background:var(--surface);border:1px solid var(--line-strong);
  border-radius:var(--r-2xl);padding:clamp(22px,3.4vw,38px);box-shadow:var(--sh-2)}
.hm-band .eq{background:var(--surface-2)}
.eq-head{max-width:44ch;margin-bottom:clamp(20px,2.8vw,26px)}
.eq-head h3{font-family:var(--f-dis);font-weight:800;
  font-size:clamp(22px,3.4vw,30px);letter-spacing:-.02em;color:var(--ink);
  margin:10px 0 0;line-height:1.14}
.eq-head p{margin:9px 0 0;color:var(--ink-2);font-size:15.2px;line-height:1.55}

.eq-grid{display:grid;gap:clamp(12px,1.8vw,16px)}
@media (min-width:640px){ .eq-grid{grid-template-columns:repeat(2,minmax(0,1fr))} }
.eq-wide{grid-column:1/-1}
.eq-f{display:flex;flex-direction:column;gap:7px;min-width:0}
.eq-f>span{font-family:var(--f-dis);font-weight:750;font-size:12.5px;
  letter-spacing:.02em;color:var(--ink-2)}
/* "optional" reads as part of the label rather than as a second thing to read */
.eq-f>span i{font-style:normal;font-weight:650;font-size:11.5px;color:var(--ink-4);
  text-transform:lowercase;letter-spacing:.04em;margin-left:5px}

.eq input,.eq select,.eq textarea{width:100%;font:inherit;font-size:15.4px;
  color:var(--ink);background:var(--surface-2);border:1.5px solid var(--line);
  border-radius:var(--r-md);padding:12px 14px;
  transition:border-color .18s,box-shadow .18s,background .18s}
.hm-band .eq input,.hm-band .eq select,.hm-band .eq textarea{background:var(--surface)}
.eq textarea{resize:vertical;min-height:84px;line-height:1.5}
.eq select{appearance:none;-webkit-appearance:none;cursor:pointer;padding-right:38px;
  /* the chevron, drawn rather than fetched: an inline SVG in a data URI needs
     no request and no img-src allowance beyond the data: the policy already
     grants for icons */
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%237B7FA4' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 14px center}
.eq input:hover,.eq select:hover,.eq textarea:hover{border-color:var(--line-strong)}
.eq input:focus,.eq select:focus,.eq textarea:focus{outline:none;
  border-color:var(--brand);box-shadow:0 0 0 4px var(--brand-soft)}

/* OFF-SCREEN, NOT display:none. Some bots skip anything hidden, and the point
   of the field is that a bot fills it in. Kept out of the tab order and out of
   the accessibility tree by the wrapper's aria-hidden. */
.eq-hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}

.eq-foot{display:flex;flex-wrap:wrap;align-items:center;gap:12px 18px;
  margin-top:clamp(18px,2.4vw,24px)}
.eq-alt{margin:0;font-size:14px;color:var(--ink-3)}
.eq-alt a{color:var(--brand);font-weight:700;text-decoration:none}
.eq-alt a:hover{text-decoration:underline}

.eq-msg{margin:16px 0 0;font-size:14.6px;line-height:1.5;color:var(--ink-2)}
.eq-msg.is-no{color:var(--no)}
.eq-msg[hidden]{display:none}

/* what the form becomes once it has been sent */
.eq.is-done{text-align:center;padding-block:clamp(34px,5vw,54px)}
.eq.is-done h3{font-family:var(--f-dis);font-weight:800;
  font-size:clamp(22px,3.4vw,30px);letter-spacing:-.02em;color:var(--ok)}
.eq.is-done p{margin:10px auto 0;max-width:44ch;color:var(--ink-2);font-size:15.4px;
  line-height:1.55}
