
@font-face{font-family:'Poppins';font-weight:400;font-style:normal;font-display:swap;src:url('/shared/fonts/poppins-400.woff') format('woff')}
@font-face{font-family:'Poppins';font-weight:500;font-style:normal;font-display:swap;src:url('/shared/fonts/poppins-500.woff') format('woff')}
@font-face{font-family:'Poppins';font-weight:700;font-style:normal;font-display:swap;src:url('/shared/fonts/poppins-700.woff') format('woff')}
@font-face{font-family:'InkGlyphs';font-weight:400;font-style:normal;font-display:swap;src:url('/shared/fonts/inkglyphs-400.woff') format('woff');unicode-range:U+0370-03FF,U+221D,U+2264-2265,U+00B2-00B3,U+00B9,U+2070-2079,U+207B,U+2080-2089,U+0127,U+2096,U+2192,U+207A,U+2153-2154,U+2713,U+2090}
@font-face{font-family:'InkGlyphs';font-weight:700;font-style:normal;font-display:swap;src:url('/shared/fonts/inkglyphs-700.woff') format('woff');unicode-range:U+0370-03FF,U+221D,U+2264-2265,U+00B2-00B3,U+00B9,U+2070-2079,U+207B,U+2080-2089,U+0127,U+2096,U+2192,U+207A,U+2153-2154,U+2713,U+2090}
:root{--ink-bg:#0D1B2A;--ink-text:#F5F5F5;--ink-muted:#84919F;--ink-stroke:#6E7D8C;--ink-cyan:#38B6FF;--ink-purple:#A56EFF;--ink-coral:#EF476F;--ink-amber:#FFB703;--ink-success:#2EA043;--ink-card:rgba(255,255,255,.035);--ink-edge:rgba(245,245,245,.13);--ink-hair:rgba(245,245,245,.09);--navy:var(--ink-bg);--ink:var(--ink-text);--cyan:var(--ink-cyan);--amber:var(--ink-amber);--coral:var(--ink-coral);--grey:var(--ink-stroke);--greytx:var(--ink-muted);--bg:var(--ink-bg);--card:var(--ink-card);--edge:var(--ink-edge);--hair:var(--ink-hair);--space-1:.25rem;--space-2:.5rem;--space-3:.75rem;--space-4:1rem;--space-5:1.5rem;--space-6:2rem;--space-7:3rem;--space-8:4rem;--subject-accent:var(--ink-muted)}[data-subject="physics"]{--subject-accent:var(--ink-cyan)}[data-subject="maths"]{--subject-accent:var(--ink-purple)}[data-subject="biology"]{--subject-accent:var(--ink-coral)}
:root{color-scheme:dark;--wtext:42rem;--wpanel:47rem;--wfig:58rem}
*{box-sizing:border-box;margin:0}
html{scroll-behavior:smooth}
body{background:radial-gradient(1100px 520px at 50% -180px,rgba(56,182,255,.055),transparent 62%) var(--bg);
/* 1.68, not 1.75. The brand guide fixes the body measure at 1.68 and
   biology was built to it, so the three libraries were setting the same
   prose at two different rhythms and a reader moving between them felt
   the page change under them. Approved by George on 13 August 2026;
   the practical effect is roughly one line saved per fifteen, which
   shortens every lesson slightly and changes nothing else. */
color:var(--ink);font-family:'Poppins','InkGlyphs',sans-serif;font-weight:400;line-height:1.68;font-size:1.05rem;
-webkit-font-smoothing:antialiased;display:flex;flex-direction:column;min-height:100vh}
main{flex:1 0 auto}
footer.site{flex-shrink:0}
.wrap{max-width:64rem;margin:0 auto;padding:0 1.2rem}
a{color:var(--cyan);text-decoration:none}
a:hover{text-decoration:underline;text-underline-offset:3px}
main p a,main li a,main dd a{text-decoration:underline;text-underline-offset:3px;text-decoration-thickness:1px}
:focus-visible{outline:2px solid var(--cyan);outline-offset:3px;border-radius:4px}
/* Hidden by clipping rather than by left:-999px. The old technique parks a
   real, laid-out box a thousand pixels to the left of the viewport, and a
   320px sweep for horizontal overflow reports it on every page, because it
   genuinely is a box outside the viewport -- there is no way for a probe to
   tell "deliberately parked offscreen" from "this element is why the page
   scrolls sideways". Clipping to a 1px box at the position it already
   occupies takes it out of that question entirely while keeping it in the
   accessibility tree, focusable, and in the tab order, which is the whole
   job of a skip link. width/height/overflow are the belt to clip-path's
   braces for anything that does not support clip-path. */
.skip{position:absolute;left:.5rem;top:0;width:1px;height:1px;overflow:hidden;
  clip-path:inset(50%);white-space:nowrap;background:var(--bg);padding:0;z-index:9}
.skip:focus{width:auto;height:auto;overflow:visible;clip-path:none;padding:.5rem 1rem}
header.site{border-bottom:1px solid var(--hair);padding:1rem 0;margin-bottom:2.6rem}
header.site .wrap{display:flex;align-items:baseline;gap:1.6rem;flex-wrap:wrap}
.wm{display:flex;align-items:center}
.wm:hover{text-decoration:none}
.wmsvg{height:1.6rem;width:auto;display:block}
.wmsvg path{fill:var(--ink)}
.wmsvg circle{fill:var(--cyan)}
nav.top{margin-left:auto;display:flex;gap:1.4rem;font-weight:500;font-size:.95rem}
nav.top a{color:var(--ink);opacity:.82}
nav.top a:hover{color:var(--cyan);opacity:1;text-decoration:none}
/* No aria-current rule here. There used to be one painting the current item
   cyan, from before the subject accents existed, and brand.SHELL_CSS now
   overrides its colour anyway -- the two selectors tie on specificity and the
   shared one is emitted later. All the old rule still contributed was its
   opacity:1, which the shared rule has taken over, so leaving it would be a
   dead declaration that looks like it decides the active colour. */
main{display:block}
h1{font-weight:700;font-size:clamp(2.05rem,5.4vw,2.9rem);line-height:1.14;letter-spacing:-.012em;text-wrap:balance;margin:.1rem 0 .55rem}
.flourish{margin:.15rem 0 1.15rem}
.flourish svg{width:200px;height:auto;display:block}
h2{font-weight:700;font-size:1.42rem;line-height:1.3;margin:2.9rem 0 .9rem;display:flex;align-items:center;gap:.6rem;text-wrap:balance}
h2::before{content:"";width:.48rem;height:.48rem;border-radius:50%;background:var(--cyan);flex:none}
p{margin:0 0 1.15rem}
strong{font-weight:700}
.lede{color:var(--greytx);font-weight:500;font-size:1.17rem;line-height:1.6;margin-bottom:1.3rem;text-wrap:pretty}
.crumbs{font-size:.85rem;font-weight:500;color:var(--greytx);margin:0 0 1.15rem;letter-spacing:.01em}
.crumbs a{color:var(--greytx)}

/* The same measure, for the pages that are not lessons.
   A lesson is built as <article class="lesson"> straight inside <main>, with
   no .wrap around it, and the six rules above give every one of its children
   a measure. Everything else on the site -- the unit hubs, the tool pages,
   the FAQ, how-to-revise, the exam-board pages, the required practicals --
   is built as <div class="wrap"> inside <main>, and .wrap is 64rem. So the
   same paragraph, written by the same person for the same reader, was set to
   42rem on a lesson page and to about 1024px on a hub page: roughly 2.4 times
   the measure the brand guide asks for, and about 150 characters a line on a
   laptop, which is where a reader starts losing their place returning to the
   left margin. .lede in particular is set at 1.17rem and was running the full
   64rem on every hub in the library.
   This is deliberately a list of element selectors rather than a blanket
   `main>.wrap>*`, because the things that must keep the full width are also
   direct children of .wrap: the card grids, the figure blocks, the table
   wrappers, the search box, the flashcard and question-practice bars, the
   board panels. Naming the prose is the version that cannot squeeze one of
   those by accident later.
   margin-right:auto with no margin-left keeps the text block against the left
   edge, lined up with the grids and figures below it. Centring it, the way a
   lesson centres its column, would leave a hub page with a centred paragraph
   sitting over a full-width row of cards, and the two would not share an
   edge anywhere. */
main>.wrap>p,main>.wrap>h1,main>.wrap>h2,main>.wrap>h3,main>.wrap>h4,
main>.wrap>ul,main>.wrap>ol,main>.wrap>dl,main>.wrap>blockquote,
main>.wrap>.hubguide,main>.wrap>.faq,
main>.wrap>.rpsec>p,main>.wrap>.rpsec>h2,main>.wrap>.rpsec>ul,
main>.wrap>.rpsec>.rpwarn,main>.wrap>.rpsec>.rpmark,
main>.wrap>section.qr>p,main>.wrap>section.qr>h2{max-width:var(--wtext);margin-right:auto}
/* Equations need more room than prose does, and get exactly the allowance a
   lesson gives them, for the same reason: a definition list of equations on a
   unit hub is the same content as .eqs inside a lesson. */
main>.wrap>dl.hubeq{max-width:var(--wpanel)}
/* Not prose, and squeezing them is a real cost rather than a neutral one.
   .jumps and .azjump are rows of pill links, .cklegend is a horizontal key,
   .chips is a row of specification chips, and ul.topics is an index whose
   rows carry a coverage tag after the title. All four are laid out across the
   width on purpose and wrap to more lines the narrower they get, which is
   worse to use, not better to read. They are <p> and <ul> elements, so the
   rule above catches them and this one lets them go again. */
main>.wrap>.jumps,main>.wrap>.azjump,main>.wrap>.cklegend,
main>.wrap>.chips,main>.wrap>ul.topics{max-width:none}

.cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(13.5rem,1fr));gap:.95rem;margin:1.5rem 0}
.card{border:1px solid var(--edge);background:var(--card);border-radius:20px;padding:1.05rem 1.15rem;color:var(--ink);display:block;transition:transform .18s cubic-bezier(.2,.9,.3,1.25),border-color .18s}
a.card:hover{transform:translateY(-2px);border-color:rgba(56,182,255,.5);text-decoration:none}
.card h3,.card h2{font-size:1.02rem;font-weight:700;margin:0 0 .25rem}
/* The first card or section on an index page is an H2, not an H3: a page that
   goes H1 -> H3 leaves a hole in the outline a screen reader has to guess at.
   The level is semantics; the size is this rule. Anywhere a card-level H2
   appears it must lose the section dot and the flex row an H2 normally gets. */
.card h2,.rpcard h2,.cklesson h2,.ki h2,.eqgrp h2{display:block;line-height:1.3;gap:0;letter-spacing:normal}
.card h2::before,.rpcard h2::before,.cklesson h2::before,.ki h2::before,.eqgrp h2::before{content:none;display:none}
.card .meta{color:var(--greytx);font-size:.84rem;font-weight:500}

/* The Ink Learning page. Its own block because nothing else on the origin
   looks like it: a family hero with no subject in it, three explanation
   blocks, and three subject cards that have to sit at equal weight. Reusing
   .hero and .cards would have made the root the physics homepage with two
   extra cards, which is exactly what it must not be. */
.phero{text-align:center;padding:var(--space-7) 0 var(--space-4)}
.phero .bigwm{margin:0 0 var(--space-5);display:flex;justify-content:center}
.phero .bigwm .wmsvg{height:clamp(1.55rem,4.4vw,2rem)}
.phero .tag{font-weight:700;font-size:clamp(1.9rem,5vw,2.5rem);line-height:1.12;
letter-spacing:-.012em;margin:0 0 var(--space-4)}
.phero .lede{color:var(--greytx);font-size:1.06rem;max-width:34rem;margin:0 auto}
/* A heading the outline needs and the design does not: "How it works" and
   "Choose a subject" would be two more lines of furniture above blocks that
   already announce themselves, but without them the page runs H1 straight to
   the H3s inside the approach blocks. */
.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);
clip-path:inset(50%);white-space:nowrap}
.approach{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
gap:var(--space-5);margin:var(--space-7) 0}
@media(max-width:760px){.approach{grid-template-columns:1fr;gap:var(--space-4)}}
.apr h3{font-size:1rem;font-weight:700;margin:0 0 .3rem}
.apr p{color:var(--greytx);font-size:.94rem;margin:0}
/* Three fixed tracks, not auto-fill: an auto-fill grid at this width opens a
   fourth empty column on a wide screen and leaves the three cards looking
   like a row that lost one. */
.scards{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
gap:var(--space-4);margin:var(--space-6) 0 var(--space-7)}
@media(max-width:860px){.scards{grid-template-columns:1fr}}
.scard{display:flex;flex-direction:column;gap:var(--space-3);
border:1px solid var(--edge);background:var(--card);border-radius:20px;
padding:var(--space-5);color:var(--ink);text-decoration:none}
.scard:hover,.scard:focus-visible{border-color:rgba(56,182,255,.5);text-decoration:none}
.scwm{display:flex;align-items:center;flex-wrap:wrap}
.scwm .wmsvg{display:block}
.scard .motif{display:block;width:100%;height:auto;max-width:132px;opacity:.9}
.scard .meta{color:var(--greytx);font-size:.92rem;flex:1}
.scgo{font-weight:600;color:var(--cyan)}
.scard:hover .scgo{text-decoration:underline;text-underline-offset:3px}
@media(prefers-reduced-motion:no-preference){.scard{transition:border-color 140ms ease}}
/* The list of libraries on the origin's About page. It needs its own rule for
   one reason: the global reset sets margin:0 on everything, and the only list
   styling on this site belongs to a component (.hubguide ul, ul.topics, the
   goals list). A bare <ul> in prose therefore sits flush against the sentence
   after it, which on the About page put "Each library has its own front
   door..." hard against the third list item with no gap at all. Scoped to a
   class rather than fixed on main>.wrap>ul, because that selector also
   catches ul.topics on twenty physics hub pages, where the spacing is already
   right and is set by the component. */
.libs{margin:0 0 1.15rem;padding-left:1.15rem}
.libs li{margin:0 0 .35rem;line-height:1.6}

.note{color:var(--greytx);font-size:.88rem;margin:2.4rem auto 0}
footer.site{border-top:1px solid var(--hair);margin-top:3.4rem;padding:1.5rem 0 2.3rem;color:var(--greytx);font-size:.86rem}
footer.site a{color:var(--greytx);text-decoration:underline;text-underline-offset:3px}
footer.site .reg{font-size:.78rem;opacity:.8}
/* animation, not just transition. This reset used to stop transitions alone,
   which meant a reader who had asked their operating system for no motion
   still got the 6px slide-and-fade of @keyframes exin on every worked-example
   step, and the 26px horizontal slide of stepr/stepl on every lesson-step
   move in the installed app. Both are keyframe animations, so neither was
   touched. Biology's sheet has had animation:none!important here all along;
   this brings physics into line with it.
   The educational animations inside diagrams are content rather than
   interface, and they are already handled separately and earlier, by the
   two rules on .figsvg g[class^="an-"] and .animfig * near the figure CSS.
   Those rules stop them under the same media query and keep them stopped for
   print, so this blanket rule agrees with them rather than overriding them --
   there is no case where a diagram animates for a reduced-motion reader
   before this change and stops after it, or the reverse. */
@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important;scroll-behavior:auto}}
@media (max-width:680px){header.site .wrap{flex-wrap:wrap;row-gap:.4rem}nav.top{width:100%;margin-left:0;flex-wrap:wrap;justify-content:space-between;gap:.4rem .9rem;font-size:.9rem}}
/* The print block, whose core is brand.PRINT_CSS. It has to be concatenated
   in rather than referenced, because this region is also sliced out to build
   the origin's stylesheet and the origin needs the same treatment on paper.
   The hide list stays here: what to suppress when printing is genuinely per
   subject, and only physics has a video line. */
@media print{
:root{--bg:#ffffff;--ink:#15222e;--cyan:#0b6fae;--amber:#8a6100;--coral:#b3264e;--grey:#5a6570;--greytx:#5a6570;--card:#ffffff;--edge:#c9d1d8;--hair:#dfe5ea}
body{background:#fff;font-size:11pt;line-height:1.55}
a{color:inherit;text-decoration:none}
figure.fig,.panel,.eqs,.goals{break-inside:avoid;page-break-inside:avoid}
details{display:block}details summary{display:none}details>*{display:block}
.card{box-shadow:none}
header.site nav.top,.search,.searchbox,.videotop,.pn,footer.site .fgroups,.hero .links{display:none}
.wbblock .card.wb{box-shadow:none}
}
html{-webkit-text-size-adjust:100%}.brandrow{display:flex;align-items:center;gap:.6rem;min-width:0}.brandrow .wmsvg{display:block}.brandsub{display:flex;align-items:center;text-decoration:none;margin-right:.15rem}.brandpar{display:flex;align-items:center;text-decoration:none;padding-left:.6rem;border-left:1px solid var(--ink-edge);opacity:.85}.brandpar:hover,.brandpar:focus-visible{opacity:1}@media(max-width:420px){.brandpar{padding-left:.42rem}.brandpar .wmsvg{height:13px}.brandrow{gap:.42rem}}nav.top a[aria-current]{color:var(--subject-accent);opacity:1;border-bottom:2px solid var(--subject-accent)}nav.top [aria-disabled=true]{color:var(--ink-muted);cursor:default;display:inline-flex;align-items:baseline;gap:.32rem}nav.top [aria-disabled=true] .soon{font-size:.62rem;letter-spacing:.04em;text-transform:uppercase;border:1px solid var(--ink-edge);border-radius:999px;padding:.06rem .34rem;white-space:nowrap}.navbtn{display:none;align-items:center;justify-content:center;width:44px;height:44px;border:1px solid var(--ink-edge);border-radius:12px;background:transparent;color:var(--ink-text);cursor:pointer;flex:none}.navbtn svg{width:20px;height:20px}@media(max-width:720px){.navbtn{display:inline-flex}header.site nav.top{display:none;order:3;width:100%;flex-wrap:wrap;gap:.2rem .9rem;padding:.5rem 0 .1rem;border-top:1px solid var(--ink-hair);margin-top:.55rem}header.site nav.top.open{display:flex}header.site nav.top a{padding:.55rem 0;min-height:44px;display:inline-flex;align-items:center}}@media(prefers-reduced-motion:no-preference){.navbtn{transition:border-color 140ms ease}}.fgroups{display:grid;gap:var(--space-5) var(--space-6);grid-template-columns:repeat(4,minmax(0,1fr));margin:0 0 var(--space-5)}@media(max-width:900px){.fgroups{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:520px){.fgroups{grid-template-columns:1fr}}.fgroup h2{font-size:.74rem;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-muted);margin:0 0 .5rem;font-weight:600}.fgroup ul{list-style:none;margin:0;padding:0;display:grid;gap:.34rem}.fgroup a{color:var(--ink-text);text-decoration:none;font-size:.88rem}.fgroup a:hover,.fgroup a:focus-visible{text-decoration:underline;text-underline-offset:3px}.beta{display:inline-block;font-size:.66rem;letter-spacing:.06em;text-transform:uppercase;border:1px solid var(--ink-edge);border-radius:999px;padding:.1rem .42rem;color:var(--ink-muted);vertical-align:.14em;margin-left:.45rem;font-weight:600}