/* ===== MU-core, extracted VERBATIM from live (never hand-edit) ===== */
html { scrollbar-gutter: stable; }
/* Core layout alignment */
body {
 display: flex;
 flex-direction: column;
 align-items: center;
}
/* === Main container === */
.imt-room {
 --imt-blue: #003366;
 --imt-light: #f8fbff;
 --imt-border: #cfe3ff;
 --ok: #065f46;
 --ok-bg: #d1fae5;
 --warn: #b45309;
 --warn-bg: #fff1c2;
 --gold: #f59e0b;
 background: #fff;
 border-radius: 18px;
 padding: 28px;
 width: 100%;
 max-width: 1500px;
 margin: 32px 0;
 box-shadow: 0 1px 4px rgba(0,0,0,.08);
 flex-shrink: 0;
 box-sizing: border-box;
 transform: translateZ(0);
}
.imt-room * { box-sizing: border-box; }
/* Titles */
.imt-room h2 {
 color: var(--imt-blue);
 text-align: center;
 font-weight: 800;
 font-size: clamp(1.8rem,2.6vw,2.2rem);
 margin: 0 0 12px;
}
.imt-desc {
 background: var(--imt-light);
 border: 1.5px solid var(--imt-border);
 color: #334155;
 border-radius: 12px;
 text-align: center;
 padding: 12px;
 margin: 0 0 18px;
}
/* Room selectors */
.imt-rooms {
 display: flex;
 flex-wrap: wrap;
 gap: .6rem;
 justify-content: center;
 margin: 10px 0 20px;
}
.imt-roomBtn {
 border: 2px solid var(--imt-blue);
 color: var(--imt-blue);
 background: #fff;
 border-radius: 999px;
 padding: .6rem 1rem;
 min-width: 170px;
 font-weight: 800;
 cursor: pointer;
 transition: .15s;
}
.imt-roomBtn small { display:block; font-weight:600; opacity:.9; }
.imt-roomBtn:hover,
.imt-roomBtn.active {
 background: var(--imt-blue);
 color: #fff;
}
/* Stats */
.imt-stats {
 display: flex;
 flex-wrap: wrap;
 justify-content: center;
 gap: 12px;
 margin: 0 0 18px;
}
.imt-stat {
 display: flex;
 align-items: center;
 gap: 8px;
 justify-content: center;
 border: 1.5px solid #e3eefc;
 background: #fff;
 border-radius: 999px;
 padding: 8px 16px;
 min-width: 150px;
 height: 40px;
 font-weight: 800;
 font-size: .95rem;
 color: var(--imt-blue);
}
.imt-stat .ico { display:inline-block; margin-right:6px; }
.imt-stat .ico.ok { color:#16a34a; }
.imt-stat .ico.no { color:#dc2626; }
.imt-stat.correct { background: var(--ok-bg); border-color: var(--ok); }
.imt-stat.tries { background: var(--warn-bg); border-color: var(--warn); }
.imt-stat.streak {
 background: linear-gradient(180deg,#eaf2ff 0%,#dfeaff 100%);
 border-color: var(--imt-blue);
}
.imt-stat.best {
 background: linear-gradient(180deg,#fff7e6 0%,#ffefcc 100%);
 border-color: var(--gold);
}
/* Grid + columns */
.imt-grid {
 display: grid;
 grid-template-columns: repeat(4, minmax(0, 1fr));
 gap: 16px;
}
.imt-col { display: flex; flex-direction: column; gap: 16px; }
@media(max-width:1100px){ .imt-grid{ grid-template-columns:repeat(3,1fr); } }
@media(max-width:820px){ .imt-grid{ grid-template-columns:repeat(2,1fr); } }
@media(max-width:560px){ .imt-grid{ grid-template-columns:1fr; } }
/* Question cards */
.imt-card {
 background: var(--imt-light);
 border: 1.5px solid var(--imt-border);
 border-radius: 12px;
 padding: 14px 16px;
 min-height: 155px;
 display: flex;
 flex-direction: column;
 justify-content: space-between;
 transition: background .2s, border-color .2s, box-shadow .2s;
}
.imt-card.active {
 border-color: var(--imt-blue);
 box-shadow: 0 0 0 2px rgba(0,51,102,.15) inset;
}
.imt-card.correct {
 background: var(--ok-bg);
 border-color: var(--ok);
}
.imt-card.incorrect {
 background: var(--warn-bg);
 border-color: #fbbf24;
 font-style: italic;
}
/* Question line */
.imt-qrow {
 display: grid;
 grid-template-columns: auto 1fr;
 column-gap: 8px;
 align-items: start;
}
.imt-qno { color: var(--imt-blue); font-weight: 900; line-height: 1.35; }
.imt-qtext { color: #0f172a; font-weight: 700; line-height: 1.35; margin-top: 2px; }
/* Answer input */
.imt-answerRow {
 display: flex;
 align-items: center;
 justify-content: center;
 gap: .6rem;
 margin-top: 8px;
 margin-bottom: 8px;
}
.imt-answerBox {
 background: #fff;
 border: 1.5px solid #9fb7e8;
 border-radius: 8px;
 padding: .6rem .65rem;
 text-align: center;
 font-size: 1rem;
 width: 85%;
 max-width: 260px;
 transition: border-color .15s, box-shadow .15s;
}
.imt-answerBox:focus {
 border-color: var(--imt-blue)!important;
 box-shadow: 0 0 0 3px rgba(11,99,199,.15)!important;
 outline: none;
}
.imt-feedback {
 width: 28px;
 font-size: 1.6rem;
 line-height: 1;
 text-align: center;
 font-weight: 700;
}
.imt-feedback.ok { color:#16a34a; }
.imt-feedback.no { color:#dc2626; }

/* ===== site frame — MUST come after MU-core, see build-css.py ===== */
/* =============================================================================
   The Maths Room — site stylesheet for the static build.
   Replaces the 208 per-page WordPress/Elementor/SiteGround CSS files.

   Every value below was MEASURED from the live rendered page with headless
   Chrome (computed styles), not eyeballed. Do not "tidy" these numbers — they
   are what the live site actually renders, and matching them is what stops the
   design shifting at cutover.

   Re-measure with:
     node migration-evidence/tools/measure-frame.mjs <url> --width=N --out=f.json
   Captured runs live in migration-evidence/measurements/.

   Part 1  design tokens (measured)
   Part 2  frame: top bar, header, nav, drawer, page card, footer, typography
   Part 3  MU-core, extracted VERBATIM from live (never hand-edit; re-run
           migration-evidence/tools/extract-live-parts.py)
   ============================================================================= */

/* ---- Part 1: tokens, measured from live ---------------------------------- */
:root{
  --imt-bg:#2E86AB;            /* page background + top bar   */
  --imt-card:#fff;             /* content card                */
  --imt-card-border:#1f2937;   /* 2px card border             */
  --imt-ink:#1f2937;           /* body text                   */
  --imt-heading:#003366;       /* h1-h3                       */
  --imt-link:#2E86AB;          /* body links                  */
  --imt-nav:#004AAD;           /* top-level nav links         */
  --imt-nav-sub:#040404;       /* dropdown links              */
  --imt-accent:#FF6400;        /* orange accent               */
  --imt-footer-bg:#f4f4f5;     /* footer band                 */
  --imt-drawer-bg:rgba(18,21,25,.98);  /* mobile drawer panel */
  --imt-font:-apple-system,system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,
             "Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";

  /* The header/footer container is NOT a plain max-width. Measured across
     390/768/1024/1440/2560 it is exactly 80vw until it hits a 1900px cap:
       vw 390 -> 312    vw 768 -> 614.4   vw 1024 -> 819.2
       vw 1440 -> 1152  vw 2560 -> 1900 (capped, 80vw would be 2048)
     Writing it as max-width:1900px alone would put the logo ~100px off on every
     desktop screen. */
  --imt-shell:min(80vw,1900px);
  --imt-topbar-h:50px;         /* blue band above the header  */
  --imt-header-h:76px;         /* white header bar height     */
}

/* ---- Part 2: frame ------------------------------------------------------- */
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
/* display:block is deliberate. MU-core (extracted verbatim from live) sets a
   GLOBAL `body{display:flex;align-items:center}` which, outside WordPress's own
   layout, collapses the header to its content width. Cascade order alone does
   not fix it — this rule must restate `display`. Do not remove.
   This is also a LIVE bug we are fixing on the way past: on a 430px phone live
   renders the white header only 375px wide, with blue gutters either side.
   Screenshot evidence in the 2026-07-28 section of the evidence log. */
body{display:block;margin:0;background:var(--imt-bg);color:var(--imt-ink);
     font-family:var(--imt-font);font-size:16px;line-height:1.65}
a{color:var(--imt-link)}
img,svg,video{max-width:100%;height:auto}
/* FORM CONTROLS DO NOT INHERIT TYPE, AND EVERY WORDPRESS THEME QUIETLY FIXES IT.
   Without this the browser's own default wins and a <button> renders at 13.33px
   in the system UI font — measured on the home page's module tabs, which are
   16px/25.6px on live and were 13.33px/normal here. It was invisible to every
   byte check and to the frame checks, because the markup is identical: only the
   cascade differs, and only below the fold.

   Long-hand rather than `font:inherit`, which is the shorthand and would also
   reset weight, style and variant on controls that set them deliberately.
   Anything with its own font-size (the practice-room buttons, the search field)
   is more specific and unaffected — verified by measuring a room page either side
   of this rule. */
button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}

/* Top bar: 50px of page-blue above the white header. Desktop only — live hides
   the whole top row below the 1000px breakpoint. */
.imt-topbar{height:var(--imt-topbar-h);background:var(--imt-bg)}

.imt-header{background:#fff}
.imt-header-inner{width:var(--imt-shell);margin:0 auto;height:var(--imt-header-h);
  min-height:60px;display:flex;align-items:center;gap:1rem}

/* The logo is a 236x133 SVG drawn at the full header height (76px), so it has no
   surrounding padding. width/height attributes stay on the <img> for the aspect
   ratio, which is what stops it reflowing while the SVG loads. */
.imt-logo{display:flex;align-items:center;height:var(--imt-header-h);flex:none}
.imt-logo img{height:var(--imt-header-h);width:auto;display:block}

.imt-nav{margin-left:auto}
.imt-nav>ul{display:flex;list-style:none;margin:0;padding:0}
.imt-nav>ul>li{position:relative;display:flex;align-items:center;height:var(--imt-header-h)}
.imt-nav>ul>li>a,.imt-nav>ul>li>.imt-nav-label{padding:0 15.5px;display:flex;align-items:center;
  height:var(--imt-header-h)}
.imt-nav a,.imt-nav .imt-nav-label{color:var(--imt-nav);text-decoration:none;font-size:12px;
  font-weight:700;text-transform:uppercase;white-space:nowrap}
/* Top-level entries with no URL are section headings, not links. Live emits them
   as <a> with no href, which is a keyboard trap and reads as a broken link; here
   they are <span> and the dropdown opens on hover/focus-within of the <li>. */
.imt-nav .imt-nav-label{cursor:default}
.imt-nav-caret{display:inline-flex;align-items:center;margin-left:6px}
.imt-nav-caret svg{width:8px;height:8px;fill:currentColor;display:block}
.imt-nav ul ul .imt-nav-caret{margin-left:auto}
.imt-nav ul ul{display:none;position:absolute;left:0;top:100%;z-index:60;
  background:#fff;border-radius:0 0 10px 10px;min-width:200px;list-style:none;margin:0;padding:0}
.imt-nav li:hover>ul,.imt-nav li:focus-within>ul{display:block}
.imt-nav ul ul li{position:relative;display:block;height:auto}
/* flex, not block, so the submenu chevron can sit hard right of a wrapping label */
.imt-nav ul ul a,.imt-nav ul ul .imt-nav-label{display:flex;align-items:center;gap:8px;
  color:var(--imt-nav-sub);font-size:12px;font-weight:500;text-transform:none;
  padding:13px 19.5px;white-space:normal;line-height:19.8px;height:auto}
.imt-nav ul ul a:hover{background:#f4f4f5}
.imt-nav ul ul ul{top:0;left:100%;border-radius:10px}

.imt-social{display:flex;align-items:center;flex:none;margin-left:10px}
.imt-social svg{width:25px;height:25px;fill:#f00;display:block}

/* Drawer trigger + panel: hidden entirely on desktop. */
.imt-drawer-open,.imt-drawer{display:none}
.imt-drawer-state:checked~.imt-drawer{display:block}

/* CONTENT WIDTH IS NOT THE SHELL WIDTH. --imt-shell (min(80vw,1900px)) is the
   header/footer container; live sizes the content column by a different rule and
   the two genuinely disagree on live — at 1440 the header container is 1152px
   wide while the content card is 1309.6px. Reusing the shell here would narrow
   every page by ~270px, so main keeps its own max-width.

   Measured, for the record and for step 3: live's content card is
   `100% - 2*(22px + 2.5vw)` with no max-width at all
     vw  768 -> 685.5    vw 1024 -> 914.4    vw 1440 -> 1309.6   vw 2560 -> 2373.4
   The rule below is the previously-agreed static-site decision (cap at 1900px so
   text lines stay readable on ultrawide) and is deliberately NOT changed here —
   changing content width is a 106-page visual decision, not part of the frame. */
main{max-width:1900px;margin:0 auto;padding:25px clamp(.6rem,2.5vw,2.5rem) 3rem}
.imt-page{background:var(--imt-card);border:2px solid var(--imt-card-border);
  border-radius:25px;padding:20px clamp(1rem,2.4vw,2.2rem)}

/* SEPARATE CARDS NEED A GAP, AND LIVE'S GAP IS A SPACER WIDGET.
   Live puts a transparent spacer container between each painted container and
   that is what produces the space: measured on /set-notation/ at 1440, card one
   ends at y=7856.3 and card two starts at 7886.3 — 30px. Same 30px on
   number-8-compound-measures, algebra-9-algebraic-proofs and
   how-to-find-the-nth-term-igcse.

   30px is the DOMINANT value, not a universal one. vectors-igcse-maths measures
   [30, 45] across its three cards, so a few pages will sit 15px tighter here
   than on live. The exact height is NOT recoverable from the clone: the spacer
   widget's markup is an empty <div class="elementor-spacer-inner"> and its
   height lives only in Elementor's per-page stylesheet, which this build does
   not ship. Reproducing it exactly would mean measuring all 106 pages on live
   and baking a per-page number into the build data — disproportionate for a
   vertical-rhythm difference, when the fault being fixed was the card COUNT.
   Recorded as a deliberate deviation so the parity checker can allow it.

   Spacers are dropped at build time (they are Elementor's way of doing margins),
   so the gap has to be a margin here or the cards would touch. Margin, not gap
   on a flex parent: main is not a flex container and making it one to space two
   children would change how every page's content sizes itself.

   Sibling-only, so a page with a single card gets no stray trailing space and
   main's own padding still decides the space above the first card and below the
   last — which is what the 106-page-wide measurement of main was set from. */
.imt-page + .imt-page,.imt-page + .imt-band,.imt-band + .imt-page,
.imt-band + .imt-band{margin-top:30px}

/* A container that paints nothing on live gets no card here. It still needs the
   card's horizontal inset, or its text would sit ~2.2rem further left than the
   text in the cards above and below it and the page would read as misaligned.
   No background, no border, no radius: on live these are transparent and the
   blue shows through. */
.imt-band{padding:0 clamp(1rem,2.4vw,2.2rem)}
.imt-page>h1{color:var(--imt-heading);font-size:46px;font-weight:700;text-align:center;
  line-height:1.05;margin:.4rem 0 1.1rem}
.imt-page h2{color:var(--imt-heading);font-size:31.2px;font-weight:800;line-height:1.25}
.imt-page h3{color:var(--imt-heading)}

/* THE ONE THING THAT LEAKED INTO THE HOME PAGE, AND ONLY THAT.
   The home widget is self-contained (everything scoped under .imt-hp) and sets
   its own colour, size and weight for h2/h3 — measured identical to live either
   side of this. What it does NOT set is line-height, so `.imt-page h2` above
   applied unopposed: every h2 came out 42px against live's 50.4px and every h3
   26.88px against 25.2px, enough to re-wrap a heading. Found by diffing computed
   styles element by element between live and the built page; the two screenshots
   differ only in where one heading breaks.

   Written as a same-specificity override that wins on source order, NOT as
   `.imt-page:not(.imt-home-page) h2`. The :not() version was tried and is a trap:
   :not() carries its argument's specificity, so it lifts the shared rule from
   (0,1,1) to (0,2,1) and would start beating info-block rules like `.imt-ib h2`
   that currently win on source order — on all 101 topic pages, to fix one.

   1.5 is measured: h2 50.4/33.6, the smaller "Disclaimer" h2 27.6/18.4, h3
   25.2/16.8 and 26.4/17.6. NOT h1 — the widget sets its own (49.6px/53.568px)
   and it already matched. */
.imt-home-page h2,.imt-home-page h3{line-height:1.5}

/* Video embeds rebuilt from Elementor's widget (see lib/content.js). aspect-ratio
   rather than the old padding-top hack, so the box is correct before the iframe
   loads and nothing jumps. */
.imt-video{margin:1.4rem 0}
.imt-video iframe{display:block;width:100%;aspect-ratio:16/9;height:auto;border:0;
  border-radius:12px;background:#000}

/* ---- search -------------------------------------------------------------
   Live's header search is a 15x15 magnifier sitting immediately before the
   social icon (measured: 15x15 at x=1228.5 on a 1440 viewport), and the trigger
   below keeps that size and position. Everything past the button is ours: live's
   opened into a WordPress AJAX search that has no server here.

   Both triggers ship with the `hidden` attribute and search.js removes it, so a
   blocked or failed script leaves no dead button behind. */
.imt-search-btn{background:none;border:0;padding:0;margin:0 4px 0 0;cursor:pointer;
  display:flex;align-items:center;justify-content:center;width:15px;height:15px;
  color:#000;flex:none}
.imt-search-btn svg{width:15px;height:15px;fill:currentColor;display:block}
.imt-search-btn:focus-visible{outline:2px solid var(--imt-nav);outline-offset:4px;
  border-radius:2px}

/* Drawer trigger. Live hides its search below 1000px entirely; this keeps one,
   because a phone is exactly where hunting through a collapsed 104-link tree
   costs the most. Full-width and 44px tall so it is a comfortable tap target. */
.imt-drawer-search{display:flex;align-items:center;gap:.6rem;width:100%;
  min-height:44px;margin:.5rem 0 1rem;padding:.6rem .8rem;cursor:pointer;
  background:rgba(255,255,255,.1);border:1px solid rgba(255,255,255,.25);
  border-radius:10px;color:#fff;font:inherit;font-size:17px;font-weight:600}
.imt-drawer-search svg{width:16px;height:16px;fill:currentColor;flex:none}

/* The dialog. Pinned to the top rather than vertically centred: the result list
   grows downwards and a centred box would jump about as it does. */
.imt-search-panel{position:fixed;inset:0;z-index:1000000;display:flex;
  align-items:flex-start;justify-content:center;padding:clamp(1rem,6vh,5rem) 1rem 1rem}
.imt-search-panel[hidden]{display:none}
.imt-search-scrim{position:absolute;inset:0;background:rgba(18,21,25,.55);cursor:pointer}
.imt-search-box{position:relative;width:min(100%,640px);background:#fff;
  border-radius:16px;box-shadow:0 24px 70px rgba(0,0,0,.35);overflow:hidden;
  max-height:min(80vh,720px);display:flex;flex-direction:column}
.imt-search-row{display:flex;align-items:center;gap:.6rem;padding:.9rem 1rem;
  border-bottom:1px solid #e5e7eb;flex:none}
.imt-search-icon{fill:#6b7280;flex:none}
#imt-search-input{flex:1;min-width:0;border:0;outline:none;font:inherit;
  font-size:17px;color:var(--imt-ink);background:none;
  /* Safari draws its own clear button on type=search and it collides with ours. */
  -webkit-appearance:none;appearance:none}
#imt-search-input::-webkit-search-cancel-button{display:none}
.imt-search-close{background:none;border:0;font-size:26px;line-height:1;
  color:#6b7280;cursor:pointer;padding:0 .2rem;flex:none}
.imt-search-close:focus-visible,#imt-search-input:focus-visible{outline:2px solid var(--imt-nav);
  outline-offset:2px;border-radius:4px}
.imt-search-status{margin:0;padding:.55rem 1rem;font-size:14px;color:#6b7280;flex:none}
.imt-search-status:empty{display:none}
.imt-search-list{list-style:none;margin:0;padding:0 .5rem .5rem;overflow-y:auto}
.imt-search-list a{display:block;padding:.6rem .7rem;border-radius:10px;
  text-decoration:none;color:inherit}
/* is-active is the keyboard cursor; it must look the same as hover or arrow-key
   users get no feedback at all. */
.imt-search-list a:hover,.imt-search-list a.is-active,
.imt-search-list a:focus-visible{background:#eef5ff;outline:none}
.imt-search-t{display:block;color:var(--imt-heading);font-weight:700;font-size:16px;
  line-height:1.35}
.imt-search-d{display:block;color:#4b5563;font-size:14px;line-height:1.45;
  margin-top:2px;
  /* Two lines then ellipsis: descriptions run to 160 characters and a full one
     per result turns twelve results into a wall of text. */
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
/* Stop the page scrolling behind the open dialog. */
html.imt-search-on,html.imt-search-on body{overflow:hidden}

/* The projector's title is an <h1> here and an <h2> on live — the page had no
   top-level heading at all, so the tag is promoted at build time (see
   projector.11tydata.js). The widget's own rule sets colour and margin but no
   size, so without this the promoted tag would jump to the browser's default h1
   size. MEASURED on live: 35px / 700 / 52.5px line-height / #003366. The point of
   the promotion is the document outline, not a bigger heading. */
.imt-projector-page h1.imt-title{font-size:35px;font-weight:700;line-height:52.5px;
  color:#003366;margin:0 0 3.2px}

/* Elementor's divider widget. One page uses it (handling-data-4-sample-space, a
   trailing rule under the practice room) and it was the only thing keeping that
   page out of the build, so the rule is reproduced here rather than the page
   being held back for it.

   MEASURED on live, not taken from Elementor's defaults — those are 2px/#0c0d0e,
   and this theme renders 1px solid black with 15px of padding either side. The
   clean template ships the widget BODY without Elementor's wrapper, so the
   `--view-line` modifier class is gone: the border has to hang off
   .elementor-divider-separator itself.

   border-block-start, matching what live computes, so the line stays on the
   leading edge if this ever renders in a vertical writing mode. */
.imt-page .elementor-divider{display:flex;padding:15px 0}
.imt-page .elementor-divider-separator{display:flex;width:100%;
  border-block-start:1px solid #000}

/* ---- the two Elementor-designed pages -----------------------------------
   /igcse-maths-exam-paper-walk-throughs/ and /igcse-online-maths-tutoring/.
   Their layout comes from assets/elementor-pages.css, which is live's own CSS
   extracted verbatim. What is NOT in there is the THEME's contribution — Blocksy
   sets heading sizes, heading margins and link colours, and this build does not
   ship Blocksy. Without these four rules the h1 falls back to the browser's 32px,
   the card titles come out body-grey and underlined, and every heading gets the
   browser's default margin.

   THE HEADING SCALE IS COPIED, NOT INVENTED. Blocksy emits it as custom
   properties in an inline <style> in every page's head, so unlike the rest of the
   theme it is not in any file the extractor can read — but it is right there in
   the crawl snapshot, and these are its own declared values:

     h1 46px   h2 35px   h3 30px   h4 25px   h5 20px   h6 16px
     all weight 700, line-height 1.5

   and the heading margin is one formula, not six numbers:

     h1..h6            margin-block-end: calc(.3em + 10px)
     ...:last-child    margin-block-end: 0

   which is what produces the 19px under a 30px image-box title (.3x30+10) and the
   16px under a 20px icon-box title (.3x20+10) — two numbers that looked unrelated
   until the rule turned up. Elementor's per-page CSS overrides sizes and colours
   on top of this where the page sets them (the white h1 and h2 on the blue bands,
   the 27px image-box title on the walk-throughs page), and it is more specific, so
   it still wins.

   Scoped to .imt-elementor-page so none of it can reach the other 104 pages, and
   verify-frame is the proof that the header, nav and footer are untouched. */
/* overflow-x:clip because THE PAGE GENUINELY OVERFLOWS, on live too. The fourth
   icon-box card sits at x=1108.8 and is 351.2 wide, so it ends at 1460 in a
   1440 viewport — measured identical on live and here, to within 0.05px. Live
   hides it with `body{overflow-x:hidden}` (Blocksy's), which is why its own
   screenshot shows "Homework Help" clipped mid-sentence. Without the same clip
   the document is 20px wider than the viewport and the header and footer, which
   are 100% of the VIEWPORT, come up short with a blue strip beside them.

   Scoped to these two pages rather than added to `body` globally: live applies it
   everywhere, but the other 104 pages are already verified without it and a
   global clip would mask a real overflow bug on any of them. `clip`, not
   `hidden` — hidden on one axis forces the other to `auto` and turns main into a
   scroll container. */
main.imt-bleed{max-width:none;padding:0;overflow-x:clip}

/* NATIVE <details> NEEDS ITS MARKER TURNED OFF, AND ITS ICONS TURNING ON.
   The accordion is converted to <details>/<summary> in lib/content.js (Elementor
   opens it with JavaScript, which this site does not have). Two consequences the
   extracted CSS cannot know about:

     the browser draws its own disclosure triangle beside Elementor's +/- icon.
     Two markers, one of them wrong. list-style plus the WebKit pseudo-element,
     because Safari still uses ::-webkit-details-marker rather than ::marker.

     Elementor swaps + for - on `.elementor-tab-title.elementor-active`, a class
     its script adds. Nothing adds it here, so every row keeps its + even when
     open. Re-keyed onto [open], which the browser maintains itself.

   Written with Elementor's own four-class selector shape plus the page class, not
   the short `details[open] .icon-closed` version: Elementor's rule is
   `.elementor-accordion .elementor-tab-title .elementor-accordion-icon
   .elementor-accordion-icon-closed{display:block}`, four classes, and the short
   form loses to it. Caught by measuring, not by reading — the panel opened
   correctly and kept its + sign. */
.imt-elementor-page summary{list-style:none;cursor:pointer}
.imt-elementor-page summary::-webkit-details-marker{display:none}
.imt-elementor-page .elementor-accordion details[open] > .elementor-tab-title
  .elementor-accordion-icon .elementor-accordion-icon-closed{display:none}
.imt-elementor-page .elementor-accordion details[open] > .elementor-tab-title
  .elementor-accordion-icon .elementor-accordion-icon-opened{display:block}
.imt-elementor-page .elementor-accordion details:not([open]) > .elementor-tab-title
  .elementor-accordion-icon .elementor-accordion-icon-opened{display:none}
.imt-elementor-page h1{font-size:46px}
.imt-elementor-page h2{font-size:35px}
.imt-elementor-page h3{font-size:30px}
.imt-elementor-page h4{font-size:25px}
.imt-elementor-page h5{font-size:20px}
.imt-elementor-page h6{font-size:16px}
.imt-elementor-page :is(h1,h2,h3,h4,h5,h6){font-weight:700;line-height:1.5;
  color:var(--imt-heading);margin:0 0 calc(.3em + 10px)}
.imt-elementor-page :is(h1,h2,h3,h4,h5,h6):last-child{margin-bottom:0}
/* Elementor's own `.elementor-heading-title{line-height:1;margin:0;padding:0}` IS
   in the extracted stylesheet and is correct — it is just outranked by the rule
   above, because `:is(h1,...)` contributes an element's specificity on top of the
   page class and Elementor's is one bare class. Restated here rather than
   weakening the theme scale, which the box titles genuinely need at 1.5.
   Measured on live: every heading widget's title has line-height exactly equal to
   its font-size (59/59, 39/39, 35/35). */
.imt-elementor-page .elementor-heading-title{line-height:1;margin:0;padding:0}
/* The wave dividers are wider than the viewport on purpose — the middle one
   measures 1739.8px inside a 1425px page, which is how the crest sits off-centre.
   The frame's global `img,svg,video{max-width:100%}` was clamping it back to the
   page width and flattening the wave. */
.imt-elementor-page .elementor-shape svg{max-width:none}
/* Link colour is INHERITED here, not set: on live these titles are navy because
   the heading is navy, and the anchor simply does not recolour it. Ours would
   otherwise take the site link blue and the browser's underline. */
.imt-elementor-page a{color:inherit;text-decoration:none}
.imt-elementor-page a:hover{text-decoration:underline}
/* Paragraph rhythm, and it is not one number. Measured on live: inside the
   widget bodies every <p> is margin:0 (text-editor, and both box descriptions),
   while ordinary content paragraphs — the booking notice, which is a shortcode
   holding hand-written HTML — keep 0 top and 1.5em bottom. Its own inline styles
   win over this where it sets them. */
.imt-elementor-page p{margin:0 0 1.5em}
.imt-elementor-page .elementor-widget-text-editor p,
.imt-elementor-page .elementor-image-box-description,
.imt-elementor-page .elementor-icon-box-description{margin:0}

/* best-maths-websites-for-students ONLY, and the scoping is the point.
   The two rules above were measured on the first two designed pages, where the
   widget bodies are single paragraphs and an image-box title is a navy link that
   inherits its colour. This third page is prose: each website's description is
   two or three real paragraphs, and its body links are the site blue.

   Measured on live at 1440:
     text-editor <p>   margin-bottom 24px, except the last in each widget: 0
                       (observed 0,24,0,24,0,24,24,0 down the page)
     text-editor <a>   rgb(46,134,171) — which is exactly --imt-link, #2E86AB
     underline         none, same as the rules above already give

   Scoped to .elementor-816, the page's own Elementor root class, so it cannot
   reach the walk-throughs or tutoring pages. That is the same scoping live's own
   post-816.css uses, so the two agree rather than fight. */
.imt-elementor-page .elementor-816 .elementor-widget-text-editor p{margin:0 0 1.5em}
.imt-elementor-page .elementor-816 .elementor-widget-text-editor p:last-child{margin-bottom:0}
.imt-elementor-page .elementor-816 .elementor-widget-text-editor a{color:var(--imt-link)}

/* ---- category archive (/category/useful-information/) -------------------
   The ONE page here that is not Elementor. It is a WordPress category archive
   drawn by the Blocksy theme, so every class below is Blocksy's and every value
   below was measured off the live archive with headless Chrome:

     measure-selectors.mjs https://igcsemathstutor.com/category/useful-information/ \
       --sel='h1.page-title' --sel='.entries' --sel='article.entry-card' ...

   Measured at 1440 and at 390. What changes across the breakpoint is the grid
   (3 columns -> 1) and the card title (20px/26px -> 18px/23.4px). Everything else
   holds.

   NO WHITE CONTENT CARD HERE. This is the one page live does not put in one: the
   archive body sits directly on the blue page background and the POST cards
   supply the white. Wrapping it in `.imt-page` like every other page rendered a
   white card inside a white card, with the post card visible only by its own
   4%-opacity shadow — invisible to every byte-level check and obvious the moment
   the page was rasterised and looked at.

   THE ARCHIVE IS ON THE SHELL WIDTH, NOT THE CONTENT WIDTH. Live's `.ct-container`
   here is the same min(80vw,1900px) the header and footer use — which is why on
   live the archive's h1 lines up with the logo. The content pages use a wider
   rule (see the note above `main`), and inheriting that put the h1 100px left of
   the logo. The two genuinely differ on live and the archive follows the header.

   The 300px top and bottom is live's, not a typo: Blocksy's archive vertical
   spacing, and it measures 300px at 390px wide as well as at 1440. It is a lot of
   empty blue around one post, and it is what visitors see today, so it is
   reproduced rather than quietly tightened. Changing it is a content decision,
   not a migration one. (`main` adds its own 25px/3rem on top, so the built bands
   are 325px and 348px — a difference nobody can see inside 300px of nothing, and
   not worth a rule that reaches into the shared frame to cancel it.) */
.imt-archive-page{width:var(--imt-shell);margin:0 auto;padding:300px 0}
.imt-archive-page .hero-section{margin-bottom:40px}
.imt-archive-page h1.page-title{color:var(--imt-heading);font-size:30px;
  font-weight:700;line-height:45px;text-align:left;margin:0}
/* The "Category" label is part of the h1 on live and is styled identically to
   the rest of it — no separate colour, weight or size. Reset here only because
   the frame's own heading rules must not reach it. */
.imt-archive-page h1.page-title .ct-title-label{font:inherit;color:inherit}

/* Grid: live computes `364px 364px 364px` with a 30px gap inside its 1152px
   container, i.e. three equal columns. Written as fractions rather than the
   measured pixels so the card is not pinned to 364px inside a card of a
   different width — the RATIO is the measurement, the pixel value is a
   consequence of live's container. */
.imt-archive-page .entries{display:grid;gap:30px;
  grid-template-columns:repeat(3,minmax(0,1fr));margin:0;padding:0;list-style:none}
.imt-archive-page .entry-card{display:flex;flex-direction:column;background:#fff;
  padding:30px;box-shadow:rgba(34,56,101,.04) 0 12px 18px -6px;
  border-radius:0;margin:0}
/* Both meta rows (category above the title, byline below it) are one rule on
   live: 12px/19.8px, 600, uppercase, in the body ink colour. */
.imt-archive-page .entry-meta{display:block;margin:0 0 20px;padding:0;
  list-style:none;font-size:12px;font-weight:600;line-height:19.8px;
  text-transform:uppercase;color:var(--imt-ink)}
.imt-archive-page .entry-meta li{display:inline}
/* The slash between author and date is Blocksy's `data-type="simple:slash"`
   separator, drawn with a pseudo-element rather than shipped as text. */
.imt-archive-page .entry-meta li + li::before{content:"/";margin:0 8px;opacity:.6}
.imt-archive-page .entry-meta a{color:inherit;text-decoration:none}
.imt-archive-page .entry-meta a:hover{text-decoration:underline}
.imt-archive-page .entry-title{font-size:20px;font-weight:700;line-height:26px;
  color:var(--imt-heading);margin:0 0 20px}
.imt-archive-page .entry-title a{color:inherit;text-decoration:none}
.imt-archive-page .entry-title a:hover{text-decoration:underline}
.imt-archive-page .entry-excerpt{margin:0 0 20px;max-width:100%}
.imt-archive-page .entry-excerpt p{margin:0;font-size:16px;line-height:26.4px;
  color:var(--imt-ink)}
/* The byline is the last thing in the card and live pins it to the bottom, which
   is what keeps a row of cards of different excerpt lengths aligned. */
.imt-archive-page .entry-card > .entry-meta:last-child{margin:auto 0 0}

/* Footer is a light-grey band, NOT transparent-on-blue. Measured: background
   #f4f4f5, container 9px top/bottom, text 15px/19.5px in the body ink colour. */
.imt-footer{background:var(--imt-footer-bg)}
.imt-footer-inner{width:var(--imt-shell);margin:0 auto;padding:9px 0}
.imt-footer p{margin:0;font-size:15px;line-height:19.5px;color:var(--imt-ink)}
.imt-footer a{color:var(--imt-link)}

/* ---- responsive: live's breakpoint is exactly 1000px --------------------
   Found by binary search on the live page: the desktop header block is still
   hidden at 999px and shown at 1000px. The previous 1100px guess here would have
   put 1024px-wide tablets on the wrong header. */
@media (max-width:999.98px){
  .imt-topbar{display:none}
  .imt-header-inner{min-height:70px}
  .imt-nav,.imt-social{display:none}

  .imt-drawer-open{display:flex;align-items:center;justify-content:center;margin-left:auto;
    flex:none;cursor:pointer;padding:.4rem;color:#000}
  .imt-drawer-open svg{width:18px;height:14px;fill:currentColor;display:block}

  /* Panel geometry, measured with the live drawer open:
       vw 430 -> inner 387px (= 90vw)      vw 768 -> inner 499px (= the 500px cap)
     so the rule is min(90vw,500px). Inner padding measured 25px at 430 and 35px
     at 768; the clamp below reproduces both ends and interpolates between. */
  .imt-drawer{position:fixed;inset:0;z-index:999999}
  .imt-drawer-scrim{position:absolute;inset:0;display:block;cursor:pointer}
  .imt-drawer-inner{position:absolute;top:0;right:0;bottom:0;
    width:min(90vw,500px);background:var(--imt-drawer-bg);
    box-shadow:0 0 70px rgba(0,0,0,.35);overflow-y:auto;
    padding:0 clamp(25px,4.5vw,35px) 2rem}
  .imt-drawer-actions{display:flex;justify-content:flex-end;
    padding:clamp(20px,2.6vw,30px) 0 0}
  .imt-drawer-close{width:32px;height:32px;display:flex;align-items:center;
    justify-content:center;cursor:pointer}
  .imt-drawer-close svg{width:12px;height:12px;fill:#fff;display:block}
  .imt-drawer-nav ul{list-style:none;margin:0;padding:0}
  .imt-drawer-nav ul ul{padding-left:1rem}
  .imt-drawer-nav a,.imt-drawer-nav .imt-nav-label{display:block;color:#fff;
    text-decoration:none;font-size:20px;font-weight:700;line-height:33px;padding:5px 0}
  .imt-drawer-nav ul ul a,.imt-drawer-nav ul ul .imt-nav-label{font-size:17px;font-weight:600}
  .imt-drawer-nav ul ul ul a,.imt-drawer-nav ul ul ul .imt-nav-label{font-size:15px;font-weight:500}

  /* Collapsible groups. The sub-list is hidden until its checkbox is ticked, so
     the drawer opens on three headings instead of on 104 links. Parents that have
     their own page keep their link; the caret is a separate 44px hit target, so
     tapping the label still navigates and only the caret expands. */
  .imt-drawer-nav li{position:relative}
  .imt-drawer-nav li > ul{display:none}
  .imt-drawer-row{display:flex;align-items:center;justify-content:space-between;gap:.5rem}
  .imt-grp-toggle{display:flex;align-items:center;justify-content:center;
    width:44px;height:44px;flex:none}
  .imt-grp-toggle svg{width:15px;height:15px;fill:#fff;display:block;
    transition:transform .2s ease}
  /* The checkbox IS the control: stretched over the caret, transparent, and left
     focusable on purpose. `hidden`/display:none would take it out of the tab
     order and make the whole menu keyboard-dead. */
  .imt-grp-state{position:absolute;top:0;right:0;width:44px;height:44px;margin:0;
    opacity:0;cursor:pointer;z-index:1;-webkit-appearance:none;appearance:none}
  .imt-grp-state:checked ~ ul{display:block}
  .imt-grp-state:checked ~ .imt-drawer-row .imt-grp-toggle svg{transform:rotate(180deg)}
  .imt-grp-state:focus-visible ~ .imt-drawer-row .imt-grp-toggle{outline:2px solid #fff;
    outline-offset:-4px;border-radius:6px}
  @media (prefers-reduced-motion:reduce){.imt-grp-toggle svg{transition:none}}
  /* Locking the page behind the drawer needs no JS: the panel is fixed and the
     scrim covers the page, so only the panel scrolls. */

  .imt-page{padding:1.1rem 1rem;border-radius:16px}
  /* Matched to the card's mobile inset above, for the same alignment reason. */
  .imt-band{padding:0 1rem}
  .imt-page>h1{font-size:clamp(1.8rem,5.2vw,2.6rem)}
  /* Not excluded for the home page, and it does not need to be: the widget sets
     its own h2 font-size at every width, at equal specificity and later in the
     document, so this never reaches it. Measured, not assumed — the home page's
     h2 is 33.6px on live and here at both 1440 and 390. */
  .imt-page h2{font-size:clamp(1.35rem,3.6vw,1.95rem)}

  /* The archive's grid is on a DIFFERENT breakpoint from the header's, so it is
     stepped down here and again below rather than folded into one query.
     Measured: 3 columns at 1000+, 2 from 690 to 999.98, 1 below. */
  .imt-archive-page .entries{grid-template-columns:repeat(2,minmax(0,1fr))}
}

/* Blocksy's own mobile breakpoint, and it is NOT the 999.98px the header uses:
   binary-searched on live at 689.98px, where the grid drops to one column and the
   card title steps 20px/26px -> 18px/23.4px. The page h1 does not move (30px/45px
   at 390, 500, 600, 689, 999 and 1440), so it is not restated. */
@media (max-width:689.98px){
  .imt-archive-page .entries{grid-template-columns:minmax(0,1fr)}
  .imt-archive-page .entry-title{font-size:18px;line-height:23.4px}
}
