/* ==========================================================================
   Atlas of Histology, static rebuild
   --------------------------------------------------------------------------
   Every value in :root is a DESIGN TOKEN. Change it once, it applies
   everywhere. Nothing below :root hard-codes a colour.

   The numbers here are NOT guesses any more. They were read from the live
   Webflow site through its Data API (site 66e09a88..., page Home), class by
   class, so this file is the real design rather than a reconstruction of it.
   The Webflow class each rule comes from is named in a comment, which is how
   you check a value against the source.

   The page is two full screens. The first is fixed at 90vh and never moves;
   the second has margin-top:90vh and slides up over it as you scroll, which
   is the effect the live site is built on.
   ========================================================================== */

/* The atlas's one webfont, and it is carried rather than linked: the file sits
   in assets/fonts, so the page still asks nothing of any CDN and still works
   with no network but its own. Great Vibes is under the SIL Open Font
   Licence, whose text travels beside it. The latin subset is all that is
   needed: it covers the accents and the oe ligature French wants.
   swap, so the title is readable in the fallback while the file arrives and
   nothing on the page waits for it. */
@font-face {
  font-family: "Great Vibes";
  src: url("/assets/fonts/great-vibes-latin.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

:root {
  /* Brand palette, sampled from the logo. The gradient lives in the logo
     artwork itself: no text on the live site is painted with it, the
     wordmark next to it is flat --ink. Kept as the accent palette. */
  --brand-pink: #d6006e;
  --brand-violet: #3a00c8;
  --brand-gradient: linear-gradient(135deg, var(--brand-pink), var(--brand-violet));

  /* Ink. --ink is Webflow's body colour, --ink-title the headings. */
  --ink: #333333;
  --ink-title: hsla(0, 0%, 20%, 1);
  --ink-muted: #838992;
  /* What a control is BEFORE the pointer reaches it, by request: the muted
     ink read at 3.3 against the pill's own ground, under what a small word
     needs to be read at all, so a button at rest looked switched off. This
     reads at 5.8, and the violet it turns is 5.9: the two weigh the same, so
     the hover is a change of COLOUR rather than a jump in brightness. */
  --pill-ink: #5c626e;
  --ink-strong: hsla(220, 31.43%, 20.59%, .8);  /* .bold-text-149 */
  --ink-systems: hsla(0, 0%, 20%, .5);
  --accent: hsla(256, 41.96%, 38.98%, 1);   /* .currentcolor, the active tab */

  /* --surface is every sheet the page is printed on: the body, the header,
     the tissue card, the diagram panel, the footer. --white stays literally
     white, for the few places that need white ink on a dark ground. */
  --surface: #ffffff;
  /* The wash over the two gradients under the sheet. His own is 95% white;
     the same number in the dark palette, over the dark surface. */
  --sheet-veil: rgba(255, 255, 255, .95);
  /* The sheet a figure is drawn on. The same white as the page here; in the
     dark it is lifted well clear of --surface, because a body diagram wants
     to read as a plate laid on the screen rather than a hole cut in it. */
  --figure: #ffffff;
  --white: #ffffff;
  --on-accent: var(--white);   /* ink that sits on --accent */
  --panel: rgba(255, 255, 255, .75);        /* .div-block-190 */
  --tab-bg: rgba(200, 200, 200, .15);       /* .tab-link-tab-2-2 */
  --tab-bg-active: hsla(0, 0%, 100%, .65);
  --tab-line: hsla(0, 0%, 0%, .1);
  /* The arrow on it is a white picture, so the DISC is what makes it
     visible: darker and more opaque than his Webflow value, by request,
     which takes the white from 5.7 against its ground to 7.2. */
  --disc: hsla(0, 0%, 34%, .86);            /* .div-block-202 */
  --dot: hsla(0, 100%, 50%, .3);            /* .red-dot-* */
  --dot-hover: hsla(0, 100%, 50%, .55);
  --tip-bg: hsla(0, 0%, 100%, .71);         /* .a-1 */
  --label-plate: hsla(0, 0%, 100%, .45);    /* behind a hovered tissue name */

  /* The organ page. --ground is the page the white sheet is laid on, --plate
     the tray a strip of slides sits in, --hairline the rule between the rows
     of a list, --rule the heavy line above and below a heading. */
  --ground: #f0f0f0;
  --hairline: hsla(0, 0%, 0%, .1);
  --rule: hsla(0, 0%, 0%, .88);
  --ink-heading: hsla(214, 46%, 26%, 1);
  --plate: #e3e3ec;
  --caption-bg: hsla(0, 0%, 100%, .72);
  --note-bg: hsla(266, 40%, 99%, .88);
  --card-bg: hsla(250, 50%, 98.2%, 1);
  --pip: hsla(0, 0%, 78%, 1);               /* a slide not being shown */
  --pip-mid: hsla(0, 0%, 58%, 1);           /* the one under the pointer: darker, still grey */
  --pip-on: hsla(0, 0%, 16%, 1);            /* the one being shown, and no other */

  /* Glass. Every panel that floats over something else on an organ page is
     made of the same four values, so they all read as one material: a pane
     you can see through, blurred behind, lit along its top edge and with a
     hairline of light around it.

     --glass-sheen is the light on the pane, painted as a gradient on a layer
     of the panel's own rather than composited with the page behind it, for
     the same reason the home page's screen blur is: a browser that falls back
     to software rendering drops backdrop-filter, and a panel that was ONLY a
     blur then has nothing left. Here the tint and the sheen stay. */
  --glass: hsla(0, 0%, 100%, .58);
  --glass-deep: hsla(0, 0%, 100%, .74);     /* where words have to be read */
  --glass-edge: hsla(0, 0%, 100%, .72);
  --glass-sheen: linear-gradient(160deg, hsla(0, 0%, 100%, .55), hsla(0, 0%, 100%, .06) 42%, hsla(0, 0%, 100%, 0) 70%);
  --glass-blur: 18px;
  /* Frosted rather than clear, for a panel that opens directly over the words
     it came out of: at 18px the row below still reads through it as a ghost of
     itself, which is worse than either seeing it or not. */
  --glass-blur-deep: 30px;

  /* The pens a mark is drawn with, and the colour of the word in the caption
     that names it. The values are the author's own list, given hex by hex.
     These do NOT appear in the dark palette, and that is on purpose: a mark
     is drawn on a photograph, and the photograph is the same picture
     whichever way the page around it is painted. A blue arrow on a pink
     field has to stay that blue. Violet happens to share --hover's number;
     it is a pen, not an answer to the pointer, so it is its own token. */
  --mark-blue: #0000ff;
  --mark-red: #db0000;
  --mark-green: #009900;
  --mark-violet: #4a3aff;
  --mark-yellow: #d0c019;
  --mark-black: #101014;
  --mark-white: #ffffff;
  /* The fold marks: the two that step a strip of slides. The author's own
     arrow from the Webflow site, his right-arrow / left-arrow pair, and these
     five colours were SAMPLED off that file down the length of the stroke. It
     is an even ramp, which is why their offsets in build.py's ARROW_DEFS are
     0, .25, .5, .75 and 1.

     Indigo first, because the arrow he asked for is that asset turned half a
     turn: navy at the top of the chevron and crimson at the bottom. The step
     back is the drawing MIRRORED, never turned, exactly as his own two files
     mirror each other: flipping about the vertical keeps navy at the top on
     both sides, where half a turn would stand the ramp on its head.

     They are painted from here because an SVG presentation attribute cannot
     read a custom property. --chevron is what a browser that cannot find the
     gradient falls back to, so an arrow is still an arrow.

     They are the same in the light and in the dark: this is artwork, not ink,
     which is why none of these appear in the dark blocks below. */
  --arrow-1: #45389a;
  --arrow-2: #6a3c86;
  --arrow-3: #994080;
  --arrow-4: #c44174;
  --arrow-5: #e8406c;
  /* The two on the table of content wear a different ink, the one he pointed
     at for that list: his chevron_12202462 - Copie, the same drawing again
     but heavier, running blue into magenta. These five were sampled down the
     length of THAT file, top to bottom, and the fold is mirrored so the blue
     stays at the top whichever way the arrow points. Artwork too. */
  --arrow-toc-1: #4c89f0;
  --arrow-toc-2: #7f33fa;
  --arrow-toc-3: #ed35c7;
  --arrow-toc-4: #e025d9;
  --arrow-toc-5: #d81ce4;
  --chevron: #8e3f85;

  /* The one purple every piece of text on the atlas turns when the reader
     points at it. --accent says what a thing IS (the tab you are on, the
     heading of a card); this says something is answering your pointer, and
     the two are deliberately not the same colour. */
  --hover: #4a3aff;

  /* Screen washes, straight off .section-20 and .section-26 */
  --wash-general: linear-gradient(180deg, hsla(0, 0%, 100%, 1), hsla(0, 0%, 100%, 0) 46%, hsla(0, 0%, 100%, 0));
  --wash-general-tiny: linear-gradient(180deg, hsla(0, 0%, 100%, 1), hsla(0, 0%, 100%, .15) 22%, hsla(0, 0%, 100%, .4));
  --wash-special: radial-gradient(circle farthest-corner at 0% 0%, hsla(0, 0%, 100%, .8), hsla(0, 0%, 100%, .6) 18%, hsla(0, 0%, 100%, 0) 74%);
  --wash-special-tiny: radial-gradient(circle farthest-corner at 0% 0%, hsla(0, 0%, 100%, .2), hsla(0, 0%, 100%, .1) 17%, hsla(0, 0%, 100%, 0) 30%);

  --shadow-nav: 0 6px 8px 0 rgba(0, 0, 0, .2);
  --shadow-screen: -7px 2px 20px 10px rgba(0, 0, 0, .2);
  --shadow-veil: -7px 1px 5px 3px rgba(0, 0, 0, .2);
  --shadow-list: -12px 0 20px -6px rgba(0, 0, 0, .2);
  --shadow-diagram: 5px 2px 20px -7px rgba(0, 0, 0, .2);
  --shadow-disc: 0 -5px 7px 2px rgba(0, 0, 0, .2);
  --shadow-tip: 0 2px 8px 0 rgba(0, 0, 0, .2);
  --shadow-title: 5px 8px 12px rgba(0, 0, 0, .2);
  --shadow-systems: 0 1px 4px rgba(0, 0, 0, .2);
  --shadow-footer: 0 1px 5px 0 rgba(0, 0, 0, .2);
  --shadow-dialog: 0 18px 50px rgba(0, 0, 0, .28);
  --shadow-toc: 5px 0 18px -6px rgba(0, 0, 0, .25);
  --scrim: rgba(18, 19, 26, .45);
  /* A row of the header's drawer, under a finger. */
  --panel-press: hsla(0, 0%, 0%, .06);
  /* Down the drawer's one free edge, which is its right one. */
  --shadow-panel: 12px 0 30px -8px rgba(18, 19, 26, .35);
  /* A pane of glass, lifted: a wide soft shadow for the depth and a tight one
     under the edge so it does not float free of the page. */
  --shadow-glass: 0 10px 30px -12px rgba(18, 19, 26, .38), 0 2px 6px -2px rgba(18, 19, 26, .18);
  --shadow-glass-lift: 0 18px 44px -14px rgba(18, 19, 26, .46), 0 3px 8px -3px rgba(18, 19, 26, .22);
  /* Under the half disc that steps a strip of slides: his own shadow, read off
     .div-block-126, small and plain. The lift is what answers the pointer now
     that nothing on that button is allowed to move. */
  --shadow-step: 0 2px 5px 0 rgba(0, 0, 0, .4);
  --shadow-step-lift: 0 4px 9px 0 rgba(0, 0, 0, .45);
  /* His phone tray, and nothing else uses either: a soft shadow all round the
     picture (.div-block-45-copy-3 at tiny) and a lighter one round the bar
     that carries the dots (.slide-nav at tiny). */
  --shadow-slide-phone: 0 0 3px 2px rgba(0, 0, 0, .2);
  --shadow-bar: 0 0 3px 1px rgba(0, 0, 0, .2);
  /* Under the bottom edge of a slide picture, and only the bottom: the spread
     pulls the shadow back inside by more than the blur lets out, so nothing
     of it can climb the sides. It lands on the tray's own strip below. */
  /* Down the two long sides of the white sheet, and nowhere else. Each half is
     a shadow whose spread pulls it back inside the box by more than the blur
     lets out, so nothing of it reaches past the top or the bottom edge: the
     sheet is lit from the sides, and its two ends stay the flush seams they
     are, one against the hero picture and one against the footer. */
  --shadow-sheet: -18px 0 24px -18px rgba(18, 19, 26, .34),
                   18px 0 24px -18px rgba(18, 19, 26, .34);

  /* Type. The live site sets Palatino Linotype for every heading and
     Avenir LT Std for body copy; both are named first, then real fallbacks,
     because the pages carry no webfont and no CDN link. */
  --font-display: "Palatino Linotype", "Book Antiqua", Palatino, "URW Palladio L", P052, Georgia, serif;
  --font-body: "Avenir LT Std", Avenir, "Avenir Next", "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
  /* The one hand written line on the site, the organ's name over its picture,
     and the only face the atlas carries itself: Great Vibes, served from
     assets/fonts and named alone, so the title is that hand on every machine
     rather than whichever cursive a reader happens to own. No CDN: the file
     ships with the site, as its licence allows and asks. */
  --font-script: "Great Vibes";

  /* THE ATLAS'S OWN MEASURE. Every length on a page that is a share of the
     WINDOW is a share of THIS instead, never of the window itself. Below
     --page-max the two are the same thing and every page is fluid exactly as
     it is now. Past it the page stops growing ALL AT ONCE rather than one
     piece at a time, and every length left on it is a plain number of pixels,
     so zooming out from there takes the whole atlas down together instead of
     leaving the pictures behind at their full size.

     1632px is not a new number: it is where the sheet already stops,
     --sheet-max over --sheet-col (1240/.76), so the page's spine keeps the
     width it has today and the type now stops where the column it is set in
     stops. 100vw and not 100%, because these replace vw values and have to
     go on meaning what they meant. */
  --page-max: 1632px;
  --page: min(100vw, var(--page-max));
  /* The air left at the two sides once the page has stopped growing, and
     nothing at all below --page-max. Anything pinned to an edge of the WINDOW
     that has to stand at the edge of the PAGE is moved in by this. Measured
     against the box it is asked in (100%, not 100vw) so it agrees with the
     layout, which a scroll bar has already narrowed; the max() is what keeps
     it at zero rather than half a scroll bar negative below the cap. */
  --page-gutter: max(0px, calc((100% - var(--page)) / 2));
  /* The same sentence for the other axis. 1080px is the commonest screen, so
     no window shorter than that is touched; past it the air between the
     sections stops opening as the type shrinks. Only the air reads this:
     what is ABOUT the screen (a 90vh screen, a sticky offset) stays vh. */
  --page-h: min(100vh, 1080px);

  /* The same coefficients Webflow gives, read against the page instead of
     against the window: below the cap they are identical to the vw they
     replace, above it they freeze with everything else. */
  --size-display: calc(.1 * var(--page));            /* .text-block-13 */
  --size-display-special: calc(.09 * var(--page));   /* .text-block-13-copy */
  --size-systems: calc(.055 * var(--page));          /* .text-block-32 */
  --size-tissue: calc(.02 * var(--page));            /* .text-block-15 */
  --size-tab: calc(.012 * var(--page));              /* .tab-link-tab-2-2 */
  --size-tip: calc(.01 * var(--page));               /* .red-dot-link-* */

  /* The organ page. Still floored, because a reading size has a smallest it
     may be, but no ceiling of its own any more: --page carries the ceiling
     for all of them at once, which is what stops the type drifting against
     the column it is set in. Four ceilings at 1600, 1714 and 1810px become
     one at 1632. */
  /* Between the atlas's first six hundredths and his Webflow ten, by
     request. data-fit on the title still trims a long name by the exact
     shortfall wherever it outgrows its plate. */
  --size-banner: max(38px, calc(.08 * var(--page)));
  --size-part: max(24px, calc(.021 * var(--page)));
  --size-note: max(15px, calc(.0105 * var(--page)));
  --size-caption: max(14px, calc(.0105 * var(--page)));
  --size-toc: 13.5px;
  --size-toc-title: 16px;
  --size-card: 12.5px;
  --size-library: 15px;
  /* The line that says where a slide came from and who it belongs to. It is
     the smallest reading size on the atlas, one step under the caption it
     stands over: --size-caption is 14px on every screen under 1381px, where
     this was 14.5px and so set the credit LARGER than the picture's own line.
     Same step as the Organs menu's --size-card. */
  --size-note-fact: 12.5px;

  --size-nav: 14.5px;
  --size-panel: 16px;            /* .bold-text-68, his phone menu */
  --size-brand: 17px;            /* .heading-24, a Webflow h4 */
  --size-footer-atlas: 18px;     /* .footer-text--atlas */
  --size-footer-name: 14px;      /* .footer-text--name */
  --size-footer-line: 13px;      /* .footer-text */
  --size-tools: 22px;            /* the maintenance mark */

  /* Line heights as ratios, not vw. The live site writes them in vw next to a
     vw font size, which is the same thing until a breakpoint changes the font
     size and the line height stays behind. Each ratio is the live pair:
     10/10, 10/9, 2.5/5.5, 1.5/1.2. */
  --line-display: 1;
  --line-display-special: 1.11;
  --line-systems: .45;
  --line-tab: 1.25;
  --line-brand: 1.33;            /* 24px on an 18px h4 */

  /* Geometry */
  --screen-general: 90vh;        /* .section-19 */
  --screen-special: 100vh;       /* .div-block-191 */
  --screen-special-inner: 110vh; /* .section-26 */
  --title-col: 50%;              /* .div-block-190 */
  --list-col: 35%;               /* .container-15 */
  --systems-col: 55%;            /* .div-block-205 */
  /* .div-block-201 is 38% on the live site, which is not enough room for its
     own title: "Histologie" at 9vw of Palatino Bold is 42.1vw and the column
     is 35.7vw, so on the live site the word hangs off the right of the page.
     Widened into the slack that space-around was handing out, which costs the
     systems block about 14px and covers all but the last 2.6vw. The fitter in
     site.js takes care of that remainder by size, so no word is ever broken.
     The only number on this page deliberately not Webflow's. */
  --special-title-col: 42%;
  --rail-col: 10%;               /* .section-29 */
  --tabs-col: 90%;               /* .tabs-2 */
  --tabs-menu-col: 25%;          /* .tabs-menu-2 */
  --tabs-body-col: 75%;          /* .tabs-content-2 */
  --tissue-row-h: calc(.038 * var(--page));   /* .div-block-194 */
  --tissue-gap: 5px;             /* .grid-48 row gap */
  --tissue-media-h: 130%;        /* the slide, open, as a share of its row */
  --label-inset: 2%;             /* .div-block-193 margin, at rest */
  --label-inset-open: 7%;        /* and hovered, which is what slides the title */
  /* A share of the page, not of the veil: .scrolldown is absolutely
     positioned against the full width veil, which a page cap does not
     narrow, so a percentage here would go on growing alone. */
  --disc-w: calc(.04 * var(--page));   /* .container-16 */
  --dot-w: 4%;                   /* .red-dot-* */

  /* The organ page's geometry. The sheet is the white page the notes are
     printed on; a heading, its notes and its slides all sit in the same
     column inside it, which is what lines their edges up down the page. */
  --sheet-col: 76%;
  --sheet-max: 1240px;
  --part-col: 70%;
  /* The list is narrow on purpose: it is fixed to the left edge and the page
     runs past underneath it, so it has to clear the column the words are set
     in at every width it is shown at. 22vw is what keeps it off the text on
     the narrowest screen that still shows it. */
  --toc-w: 240px;
  --toc-max: 62vh;               /* it scrolls inside itself past this */
  /* The hero is three to one and full page width, so this IS its height.
     A share of the page, so a parked list's no-script resting place stops
     where the picture now stops. */
  --hero-h: calc(var(--page) / 3);
  /* How far DOWN THE PAGE a parked list rests, measured from the top of the
     article: past the picture, then the author's own 30vh, the padding on the
     column his own list rides in. site.js measures the picture and writes
     --toc-top over this; this is the sum in CSS alone, for a page whose
     script never arrived, and it is a hair out only because a scroll bar
     narrows the picture and vw does not know it. */
  --toc-park: calc(var(--hero-h) + 30vh);
  /* Where the list holds while the page is read: 30% of the screen above it,
     by request. A plain length, so nothing has to measure anything: it does
     not depend on how tall the list is, only on how tall the window is, and
     the same number is what stands under the picture. */
  --toc-hold: 30vh;
  /* The six system drawings are 1000 by 1000, every one of them, so the box
     that holds one is square. Said here rather than left to the picture, so
     the box has its height from the first frame and the red dots have
     somewhere to be placed before a byte of the drawing has arrived. */
  --diagram-ratio: 100%;
  /* The hero picture is 1884 by 628, which is exactly three to one. Its box
     spans the window and its height is --hero-h, the page's own third, so
     below the cap the box is the picture's shape and nothing is cropped. */
  /* A slide is 1280 by 1000. The tray
     is that shape whatever the screen, so stepping through a strip never
     moves the page under the reader's hand. */
  --slide-ratio: 78.125%;
  --slide-inset: 8px;            /* the tray around a slide */
  /* The half disc that steps to the next slide. His own numbers, measured off
     the Webflow site's .div-block-126 and .right-arrow-slide: a 30 by 40 box
     with only its two outer corners rounded, 16px each, which is what makes
     it read as a bump on the tray's edge rather than a pill floating beside
     it. */
  --step-w: 30px;
  --step-h: 40px;
  --step-r: 16px;
  --step-gap: 6px;               /* the daylight between the tray and the bump */
  --pip-w: 7px;
  --bar-h: 25px;                 /* .slide-nav, his phone bar of dots */
  --badge-w: 26px;
  /* How much of the picture the source note lies across. Cut to the longest
     credit in the atlas at --size-note-fact: 52% of the picture is 358px of
     measure at 1440 and that line wants 345px, so it is read at a glance on
     one line and no more of the photograph is covered than has to be. */
  --note-w: 52%;
  /* The drawing at the right of a menu row. The names beside it ride
     --size-note up with the page, and a drawing held at 30px stood still
     while they grew, smaller beside its own organs on every big screen. So
     it takes the same shape the reading sizes do: a floor that keeps the
     narrowest menu exactly as it was, and a share of --page above it. */
  --icon-col: max(30px, calc(.027 * var(--page)));
  --chev-word: 11px;             /* a chevron standing beside a word */
  --library-gap: 5em;            /* an organ's name to its chevron */
  --nav-h: 44px;                 /* what the header measures, for what hangs off it */
  /* The header's menu below 992px: his own drawer, and its own numbers,
     because it is the one piece of the atlas laid out around a hand rather
     than around a page. A row in it is a target rather than a word, so a row
     has a height; the words stand at his own phone margin; and the whole
     list rests at the FOOT of the drawer, which is where a hand holding a
     phone already is. The 84% and the 65% are his, moved off the two
     literals that used to sit in place. */
  --panel-w: 84%;                /* his .nav-menu, the width of the drawer */
  --panel-row-h: 56px;           /* a row, and every row is a target */
  --panel-row-sub: 32px;         /* a language name's row: tight, by request */
  --panel-inset: 8%;             /* his own phone margin, .main-texr at tiny */
  --panel-rule-w: 65%;           /* his hairline, now started at the words */
  --panel-pad: 8px;
  --panel-top: 70px;             /* the air over the first row, clear of the button */
  --panel-indent: 1.5em;         /* a language name's step in from its heading */
  --panel-mark-w: 3px;           /* the bar beside the language being read */

  --radius-lg: 20px;
  --radius: 15px;
  --radius-sm: 10px;
  /* A strip of slides is squarer than the rest of the site: half the corner
     the tray would otherwise take, and half again on the picture inside it.
     Written as the fraction rather than as 7.5 and 5, so the two go on being
     half of whatever the radius above becomes. */
  --radius-tray: calc(var(--radius) / 2);
  --radius-slide: calc(var(--radius-sm) / 2);
  --radius-xs: 5px;
  --radius-pill: 500px;

  --blur-screen: 15px;
  --blur-panel: 17px;
  --blur-tissue: 4px;
  --blur-tip: 3px;

  /* Two filters that are not colours, so they cannot be tokens of one. Both
     are the identity here and do their work in the dark palette: the screen
     photographs are taken down, and the body diagrams, which are pure black
     line art on nothing, are turned inside out so the lines come out white. */
  --screen-tint: brightness(1);
  --diagram-invert: invert(0);

  --z-nav: 1000;
  --z-special: 1;
  --z-tip: 10;
  /* Inside the header's own stacking context, bottom to top: the page dimmed
     and put out of reach, the drawer standing over it, and the button that
     opened it, which stays lit above both because it is also the way out.
     .nav__inner takes no z-index of its own, so the button's number counts
     here rather than inside it, which is how it has always worked. */
  --z-scrim: 1;
  --z-panel: 2;
  --z-burger: 3;

  --ease: .25s ease;
  /* What a CONTROL answers a pointer in, by request, and it is deliberately
     not --ease: at a quarter of a second a button reads as fading rather than
     answering, and the hand is already on its way somewhere else by the time
     the colour has arrived. Kept off --ease so it changes the four buttons and
     the arrow on the front page and nothing else: a card lighting under the
     pointer or a tab settling is a different errand and keeps its own pace. */
  --ease-fast: .1s ease-out;
  /* The drawer's own pace. ease-out, because it arrives and settles rather
     than starting and stopping evenly, which is the sentence --menu-open and
     --tip-fade are already written in. Its own token because --ease is the
     whole site's: every nav hover, every dropdown, every cue and every step
     button reads it, and retiming a drawer must not retime all of them. The
     bare duration is needed as well, because the drawer's visibility is
     switched at 0s going in and DELAYED by exactly one slide coming out. */
  --panel-ms: 240ms;
  --panel-ease: var(--panel-ms) ease-out;
  --ease-mark: .125s ease;       /* a mark called by its word, at twice the pace */
  /* The tissue card's own timings, measured off the screen recording of the
     live site frame by frame: the slide and the title take 100ms, the white
     plate behind the title keeps fading for another 100ms after they land. */
  --hover-open: 100ms ease-out;
  --hover-plate: 200ms ease-out;
  --menu-open: 60ms ease-out;
  /* An organ name appears and goes almost on the instant, so it is never
     something you can move the pointer onto. The dot stays the only target. */
  --tip-fade: 30ms ease-out;
}


/* ---------- Dark ---------------------------------------------------------
   The page follows the reader's system on its own, and the mark in the header
   overrides that and is remembered. Only the palette changes: not one size,
   position or timing below is different in the dark.

   THE TWO BLOCKS BELOW MUST STAY IDENTICAL. CSS has no way to hand one set of
   declarations to both a media query and a selector, and the alternatives all
   cost more than the copy does: light-dark() is not carried by every browser
   the atlas has to work in, and driving it from JavaScript would leave the
   page the wrong colour for anyone with JavaScript off. Change one, change the
   other, exactly as with the two editors.
   ------------------------------------------------------------------------- */

/* Follow the system, unless the reader has asked for light. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
  /* Ink, lifted off the dark ground rather than simply inverted: pure white on
     near black glares, so the body text is a warm off white and the headings
     only a little brighter. The violet is the same hue as the light palette,
     raised in lightness until it reads on this ground. */
  --ink: #dcdae0;
  --ink-title: hsla(0, 0%, 93%, 1);
  --ink-muted: #949aa6;
  --pill-ink: #a8aeba;
  --ink-strong: hsla(220, 20%, 88%, .92);
  --ink-systems: hsla(0, 0%, 100%, .3);
  --accent: hsla(256, 62%, 74%, 1);
  --on-accent: #1b1527;
  /* The same purple, lifted until it reads against a dark page. */
  --hover: #9b8dff;

  --surface: #14151b;
  --sheet-veil: rgba(20, 21, 27, .95);
  --figure: #2c2d31;
  --panel: rgba(20, 21, 27, .72);
  --tab-bg: rgba(255, 255, 255, .05);
  --tab-bg-active: hsla(0, 0%, 100%, .12);
  --tab-line: hsla(0, 0%, 100%, .12);
  --disc: hsla(0, 0%, 36%, .88);
  --dot: hsla(0, 100%, 64%, .58);
  --dot-hover: hsla(0, 100%, 68%, .88);
  --tip-bg: hsla(230, 12%, 15%, .84);
  --label-plate: hsla(0, 0%, 100%, .1);

  /* The organ page. The sheet keeps its lift off the page under it and the
     tray round a slide stays a shade lighter than the sheet. Neither --chevron
     nor any of the pens a mark is drawn with changes: see :root. */
  --ground: #0d0e12;
  --hairline: hsla(0, 0%, 100%, .12);
  --rule: hsla(0, 0%, 100%, .6);
  --ink-heading: hsla(214, 40%, 84%, 1);
  --plate: #22232b;
  --caption-bg: hsla(230, 12%, 15%, .72);
  --note-bg: hsla(230, 14%, 13%, .9);
  --card-bg: hsla(230, 12%, 17%, 1);
  --pip: hsla(0, 0%, 34%, 1);
  --pip-mid: hsla(0, 0%, 56%, 1);
  --pip-on: hsla(0, 0%, 88%, 1);
  /* Glass in the dark is a smoked pane, not a white one: the same material,
     the same blur, the same sheen along the top edge, tinted the other way. */
  --glass: hsla(230, 14%, 14%, .62);
  --glass-deep: hsla(230, 14%, 14%, .8);
  --glass-edge: hsla(0, 0%, 100%, .16);
  --glass-sheen: linear-gradient(160deg, hsla(0, 0%, 100%, .14), hsla(0, 0%, 100%, .03) 42%, hsla(0, 0%, 100%, 0) 70%);

  /* The same washes, in the dark of the page instead of white. */
  --wash-general: linear-gradient(180deg, hsla(230, 15%, 9%, 1), hsla(230, 15%, 9%, 0) 46%, hsla(230, 15%, 9%, 0));
  --wash-general-tiny: linear-gradient(180deg, hsla(230, 15%, 9%, 1), hsla(230, 15%, 9%, .3) 22%, hsla(230, 15%, 9%, .55));
  --wash-special: radial-gradient(circle farthest-corner at 0% 0%, hsla(230, 15%, 9%, .85), hsla(230, 15%, 9%, .7) 18%, hsla(230, 15%, 9%, 0) 74%);
  --wash-special-tiny: radial-gradient(circle farthest-corner at 0% 0%, hsla(230, 15%, 9%, .35), hsla(230, 15%, 9%, .2) 17%, hsla(230, 15%, 9%, 0) 30%);

  /* A shadow has to bite harder when there is no white to lose. */
  --shadow-nav: 0 6px 8px 0 rgba(0, 0, 0, .5);
  --shadow-screen: -7px 2px 20px 10px rgba(0, 0, 0, .5);
  --shadow-veil: -7px 1px 5px 3px rgba(0, 0, 0, .45);
  --shadow-list: -12px 0 20px -6px rgba(0, 0, 0, .5);
  --shadow-diagram: 5px 2px 20px -7px rgba(0, 0, 0, .5);
  --shadow-disc: 0 -5px 7px 2px rgba(0, 0, 0, .45);
  --shadow-tip: 0 2px 8px 0 rgba(0, 0, 0, .5);
  --shadow-title: 5px 8px 12px rgba(0, 0, 0, .45);
  --shadow-systems: 0 1px 4px rgba(0, 0, 0, .5);
  --shadow-footer: 0 1px 5px 0 rgba(0, 0, 0, .5);
  --shadow-dialog: 0 18px 50px rgba(0, 0, 0, .6);
  --shadow-toc: 5px 0 18px -6px rgba(0, 0, 0, .55);
  --scrim: rgba(0, 0, 0, .6);
  --panel-press: hsla(0, 0%, 100%, .09);
  --shadow-panel: 12px 0 30px -8px rgba(0, 0, 0, .7);
  --shadow-glass: 0 10px 30px -12px rgba(0, 0, 0, .7), 0 2px 6px -2px rgba(0, 0, 0, .5);
  --shadow-glass-lift: 0 18px 44px -14px rgba(0, 0, 0, .78), 0 3px 8px -3px rgba(0, 0, 0, .55);
  --shadow-step: 0 2px 5px 0 rgba(0, 0, 0, .6);
  --shadow-slide-phone: 0 0 3px 2px rgba(0, 0, 0, .5);
  --shadow-bar: 0 0 3px 1px rgba(0, 0, 0, .5);
  --shadow-step-lift: 0 4px 9px 0 rgba(0, 0, 0, .68);
  --shadow-sheet: -18px 0 24px -18px rgba(0, 0, 0, .62),
                   18px 0 24px -18px rgba(0, 0, 0, .62);

  --screen-tint: brightness(.38) saturate(3.4);
  --diagram-invert: invert(1);
  }
  :root:not([data-theme="light"]) .theme__moon { display: none; }
  :root:not([data-theme="light"]) .theme__sun { display: block; }
}

/* The reader has asked for dark, whatever the system says. */
:root[data-theme="dark"] {
  /* Ink, lifted off the dark ground rather than simply inverted: pure white on
     near black glares, so the body text is a warm off white and the headings
     only a little brighter. The violet is the same hue as the light palette,
     raised in lightness until it reads on this ground. */
  --ink: #dcdae0;
  --ink-title: hsla(0, 0%, 93%, 1);
  --ink-muted: #949aa6;
  --pill-ink: #a8aeba;
  --ink-strong: hsla(220, 20%, 88%, .92);
  --ink-systems: hsla(0, 0%, 100%, .3);
  --accent: hsla(256, 62%, 74%, 1);
  --on-accent: #1b1527;
  /* The same purple, lifted until it reads against a dark page. */
  --hover: #9b8dff;

  --surface: #14151b;
  --sheet-veil: rgba(20, 21, 27, .95);
  --figure: #2c2d31;
  --panel: rgba(20, 21, 27, .72);
  --tab-bg: rgba(255, 255, 255, .05);
  --tab-bg-active: hsla(0, 0%, 100%, .12);
  --tab-line: hsla(0, 0%, 100%, .12);
  --disc: hsla(0, 0%, 36%, .88);
  --dot: hsla(0, 100%, 64%, .58);
  --dot-hover: hsla(0, 100%, 68%, .88);
  --tip-bg: hsla(230, 12%, 15%, .84);
  --label-plate: hsla(0, 0%, 100%, .1);

  /* The organ page. The sheet keeps its lift off the page under it and the
     tray round a slide stays a shade lighter than the sheet. Neither --chevron
     nor any of the pens a mark is drawn with changes: see :root. */
  --ground: #0d0e12;
  --hairline: hsla(0, 0%, 100%, .12);
  --rule: hsla(0, 0%, 100%, .6);
  --ink-heading: hsla(214, 40%, 84%, 1);
  --plate: #22232b;
  --caption-bg: hsla(230, 12%, 15%, .72);
  --note-bg: hsla(230, 14%, 13%, .9);
  --card-bg: hsla(230, 12%, 17%, 1);
  --pip: hsla(0, 0%, 34%, 1);
  --pip-mid: hsla(0, 0%, 56%, 1);
  --pip-on: hsla(0, 0%, 88%, 1);
  /* Glass in the dark is a smoked pane, not a white one: the same material,
     the same blur, the same sheen along the top edge, tinted the other way. */
  --glass: hsla(230, 14%, 14%, .62);
  --glass-deep: hsla(230, 14%, 14%, .8);
  --glass-edge: hsla(0, 0%, 100%, .16);
  --glass-sheen: linear-gradient(160deg, hsla(0, 0%, 100%, .14), hsla(0, 0%, 100%, .03) 42%, hsla(0, 0%, 100%, 0) 70%);

  /* The same washes, in the dark of the page instead of white. */
  --wash-general: linear-gradient(180deg, hsla(230, 15%, 9%, 1), hsla(230, 15%, 9%, 0) 46%, hsla(230, 15%, 9%, 0));
  --wash-general-tiny: linear-gradient(180deg, hsla(230, 15%, 9%, 1), hsla(230, 15%, 9%, .3) 22%, hsla(230, 15%, 9%, .55));
  --wash-special: radial-gradient(circle farthest-corner at 0% 0%, hsla(230, 15%, 9%, .85), hsla(230, 15%, 9%, .7) 18%, hsla(230, 15%, 9%, 0) 74%);
  --wash-special-tiny: radial-gradient(circle farthest-corner at 0% 0%, hsla(230, 15%, 9%, .35), hsla(230, 15%, 9%, .2) 17%, hsla(230, 15%, 9%, 0) 30%);

  /* A shadow has to bite harder when there is no white to lose. */
  --shadow-nav: 0 6px 8px 0 rgba(0, 0, 0, .5);
  --shadow-screen: -7px 2px 20px 10px rgba(0, 0, 0, .5);
  --shadow-veil: -7px 1px 5px 3px rgba(0, 0, 0, .45);
  --shadow-list: -12px 0 20px -6px rgba(0, 0, 0, .5);
  --shadow-diagram: 5px 2px 20px -7px rgba(0, 0, 0, .5);
  --shadow-disc: 0 -5px 7px 2px rgba(0, 0, 0, .45);
  --shadow-tip: 0 2px 8px 0 rgba(0, 0, 0, .5);
  --shadow-title: 5px 8px 12px rgba(0, 0, 0, .45);
  --shadow-systems: 0 1px 4px rgba(0, 0, 0, .5);
  --shadow-footer: 0 1px 5px 0 rgba(0, 0, 0, .5);
  --shadow-dialog: 0 18px 50px rgba(0, 0, 0, .6);
  --shadow-toc: 5px 0 18px -6px rgba(0, 0, 0, .55);
  --scrim: rgba(0, 0, 0, .6);
  --panel-press: hsla(0, 0%, 100%, .09);
  --shadow-panel: 12px 0 30px -8px rgba(0, 0, 0, .7);
  --shadow-glass: 0 10px 30px -12px rgba(0, 0, 0, .7), 0 2px 6px -2px rgba(0, 0, 0, .5);
  --shadow-glass-lift: 0 18px 44px -14px rgba(0, 0, 0, .78), 0 3px 8px -3px rgba(0, 0, 0, .55);
  --shadow-step: 0 2px 5px 0 rgba(0, 0, 0, .6);
  --shadow-slide-phone: 0 0 3px 2px rgba(0, 0, 0, .5);
  --shadow-bar: 0 0 3px 1px rgba(0, 0, 0, .5);
  --shadow-step-lift: 0 4px 9px 0 rgba(0, 0, 0, .68);
  --shadow-sheet: -18px 0 24px -18px rgba(0, 0, 0, .62),
                   18px 0 24px -18px rgba(0, 0, 0, .62);

  --screen-tint: brightness(.38) saturate(3.4);
  --diagram-invert: invert(1);
}
:root[data-theme="dark"] .theme__moon { display: none; }
:root[data-theme="dark"] .theme__sun { display: block; }

/* So that scrollbars, form controls and the dialog's own furniture follow.

   ONLY light, not light: the two are not the same sentence. "light" says the
   page is light; "only light" says the page is light and is not to be turned
   dark for it. Chrome carries an Auto Dark Theme, on by default in some places
   and standard on Android, which repaints any page that has not said the
   second one, and it repaints it by inverting colours rather than by using the
   palette below. That is how an atlas that ships light comes up dark on
   somebody's phone with nothing on this side to explain it.

   Where the reader is genuinely being offered the choice, :root keeps light
   dark, which tells the same browser the page handles its own dark and to
   keep out of it. */
:root { color-scheme: light dark; }
:root[data-theme="light"] { color-scheme: only light; }
:root[data-theme="dark"] { color-scheme: dark; }

*, *::before, *::after { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 14px;
  line-height: 20px;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

.no-select { user-select: none; -webkit-user-drag: none; }
.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* ---------- Navigation, .navbar ----------------------------------------- */

/* Thin. The row is as tall as the tallest thing standing in it, so the height
   comes off the padding above and below the buttons rather than off a height
   here: a fixed height would put the wordmark and the links over the edge of
   the bar instead of shortening it. --nav-h is what it then measures, for the
   two panels that hang off the bottom of it on a narrow screen. */
/* The BAR spans the window, whole, at every width: a header that stops short
   of the right edge reads as broken, which is exactly what a max-width here
   did the day someone zoomed out past it. Its CONTENTS keep the page's
   measure instead, on .nav__inner below, so the wordmark stands over the
   page's left edge and the links over its right. */
.nav {
  position: sticky; top: 0; z-index: var(--z-nav);
  padding: 0 calc(.01 * var(--page));
  background: var(--surface);
  box-shadow: var(--shadow-nav);
}
/* Relative, so the Organs menu can be pinned to the middle of the header
   without taking part in the row: brand at one end, links at the other, and
   Organs centred on the page rather than on whatever is left between them. */
.nav__inner {                                   /* .nav-container-div */
  position: relative;
  /* 98% of the page, not 100%: the row keeps the same 1% of air from the
     page's edge past the cap that the bar's own padding gives it below,
     so nothing in the header moves by a pixel as the window crosses it. */
  display: flex; width: 100%; max-width: calc(.98 * var(--page)); margin-inline: auto;
  align-items: center; justify-content: space-between;
}

/* The wordmark sits on the logo's baseline, both flush to the bottom of a
   30px box. The gradient belongs to the logo artwork, not to the letters:
   the live "istology" is flat --ink. Painting it as clipped gradient text
   also cut the descenders off, because the box a gradient fills is only as
   tall as the line, and "gy" hangs below it. */
.brand {                                        /* .container-12 */
  display: flex; width: 96px; height: 28px; margin-top: 2px;
  flex-direction: row; justify-content: flex-start; align-items: flex-end;
}
.brand__mark { width: 28px; height: 28px; }     /* .image-140 */
.brand__word {                                  /* .heading-24 + .text-span-10 */
  margin: 0; align-self: flex-end;
  font-family: var(--font-display);
  font-size: var(--size-brand); line-height: var(--line-brand); font-weight: 700;
  color: var(--ink);
}

.nav__links { display: flex; align-items: center; gap: 4px; }
.navitem { position: relative; }
.navitem__btn {
  display: flex; align-items: center; gap: 6px;
  padding: 7px 12px; border-radius: var(--radius-xs);
  font-family: var(--font-display);
  font-size: var(--size-nav); color: var(--ink);
  transition: color var(--ease);
}
.navitem__btn:hover, .navitem[data-open="true"] .navitem__btn { color: var(--hover); }
.icon-globe { width: 18px; height: 18px; }

/* Light or dark. The button holds both marks and the palette decides which one
   is showing, so it stays right even when the system flips underneath it and
   nothing has been clicked. */
.icon-theme { width: 18px; height: 18px; }
.theme__sun { display: none; }

.dropdown {
  /* Centred under the icon that opens it, not hung off one edge. Short: it
     holds two words, and a panel wide enough for six looked like a mistake. */
  position: absolute; top: calc(100% + 7px); left: 50%;
  min-width: 124px;
  padding: 5px;
  border: 1px solid var(--glass-edge); border-radius: var(--radius-sm);
  background-color: var(--glass-deep); background-image: var(--glass-sheen);
  backdrop-filter: blur(var(--glass-blur)) saturate(1.6);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(1.6);
  box-shadow: var(--shadow-glass);
  opacity: 0; visibility: hidden; transform: translate(-50%, -6px);
  transition: opacity var(--menu-open), transform var(--menu-open), visibility var(--menu-open);
}
.navitem[data-open="true"] .dropdown { opacity: 1; visibility: visible; transform: translate(-50%, 0); }
/* Centred under its icon while something stands to its right, and hung from
   its own right edge when nothing does. The language mark was in the middle
   of the row while Sources and About followed it; the day those two were made
   private it became the last thing in the header, and half a 124px panel
   centred on an icon that close to the edge put 27 pixels of it off the side
   of the window and gave the whole page a sideways scroll. Asked as "is
   anything after me", so it centres itself again the moment those two come
   back rather than needing to be remembered. */
.nav__links .navitem:last-child .dropdown {
  left: auto; right: 0; transform: translate(0, -6px);
}
.nav__links .navitem:last-child[data-open="true"] .dropdown { transform: translate(0, 0); }
.dropdown ul { list-style: none; margin: 0; padding: 0; }
.dropdown__link {
  display: block; padding: 5px 10px; border-radius: var(--radius-xs);
  font-family: var(--font-display); font-size: 13px; color: var(--ink);
  text-align: center;
  transition: background var(--ease), color var(--ease);
}
/* The language being read is a span, not a link: there is nothing to go to.
   It says so by staying violet and by not reacting to the pointer. */
.dropdown__link.is-current { color: var(--accent); cursor: default; }
.dropdown__link:hover { background: var(--tab-bg); color: var(--hover); }
.dropdown__link.is-current:hover { background: none; color: var(--accent); }

/* ---------- The header on a narrow screen -------------------------------- */

/* Below 992px the links are not a row any more. They are his own phone menu:
   a white panel that comes in over the page from the left, 84% of the window
   wide and the whole of it tall, with the languages under a heading of their
   own and a hairline between the groups. The wordmark stays where it is and
   the panel covers it; the only thing left in the header's row is the button.

   Three parts, and each has one job. The BUTTON is the header's, --nav-h
   square so that a thumb landing near it lands on it, wrapped round the same
   23 by 17 drawing, which does not grow. The SCRIM is the page, dimmed and
   put out of reach: it is what the dismissing tap lands on, so that tap
   cannot also open a slide's note or follow a link underneath, and it is
   what holds the reading still while the menu is over it. The PANEL is his,
   unchanged in shape, but its rows are targets rather than words: each one
   is the full width of the panel and --panel-row-h tall, and the list RESTS
   AT THE FOOT of the panel, because a hand holding a phone is at the bottom
   of the screen and the top of an 800px drawer is the one place it cannot go.

   All three live here rather than inside the media query because only their
   SHOWING depends on the width. What they look like does not, and a rule
   split across two places is a rule that will differ. */
.burger {
  display: none;                     /* a wide screen has the row instead */
  position: relative; z-index: var(--z-burger);   /* over the panel it opens */
  /* --nav-h square around a 23 by 17 drawing. The drawing does not grow: what
     grows is what a thumb can hit, which was 29 by 23 and missed by 18px. It
     is also what makes --nav-h true on a narrow screen, because the row is as
     tall as the tallest thing standing in it. Its right edge does not move,
     so it grows into the screen and away from the corner. */
  width: var(--nav-h); height: var(--nav-h);
  align-items: center; justify-content: center;
  padding: 0; border: 0; background: none;
  color: var(--ink);
  transition: color var(--ease);
}
.icon-burger {
  display: block; width: 23px; height: 17px;
  transition: transform var(--ease);
}
/* Open, the drawing takes a QUARTER TURN into three uprights and the purple
   that answers a pointer everywhere else on the atlas. One drawing turned,
   never a cross swapped in for it, exactly as every chevron here is. */
body.is-menu .burger { color: var(--hover); }
body.is-menu .icon-burger { transform: rotate(90deg); }

/* The page while the menu is over it. Dimmed with the same --scrim the
   sign-in dialog uses, and out of reach: touch-action stops a drag from
   scrolling the atlas away under the panel, and being the topmost thing
   under the panel is what stops a dismissing tap from also landing on a
   link, a slider or a slide's info badge, which is what it used to do. The
   16% of screen the panel leaves is now ONE full height dismiss target
   standing down the right edge, which is where a thumb already is, and that
   is why there is no close button anywhere: the way out is that whole edge,
   at any height, plus the drawing in the corner, plus Escape. */
.navscrim {
  position: fixed; z-index: var(--z-scrim); inset: 0;
  display: none;                     /* not on a wide screen at all */
  overflow: hidden; overscroll-behavior: contain; touch-action: none;
  background-color: var(--scrim);
  opacity: 0; visibility: hidden;
  transition: opacity var(--panel-ease), visibility 0s var(--panel-ms);
}
body.is-menu .navscrim {
  opacity: 1; visibility: visible;
  transition: opacity var(--panel-ease), visibility 0s;
}

/* His panel: 84% of the window, the whole of it tall, opaque, in from the
   left. Opaque, not glass: these are words read against whatever the page
   behind happens to be, and the table of content is opaque for the same
   reason. Past its own height it scrolls inside itself rather than putting a
   row out of reach, which is what lets a row be added to it one day. */
.navpanel {
  position: fixed; z-index: var(--z-panel); left: 0; top: 0; bottom: 0;
  display: none;                     /* not on a wide screen at all */
  width: var(--panel-w);
  flex-direction: column; align-items: stretch;
  padding: var(--panel-top) 0 var(--panel-pad);
  overflow-y: auto; overscroll-behavior: contain;
  background-color: var(--surface);
  box-shadow: var(--shadow-panel);
  /* visibility is what keeps the closed panel out of the tab order and out of
     hit testing while it can still slide. It is switched at 0s going in and
     DELAYED by the slide going out: written as a plain duration it computes
     hidden for the whole of the first frame, and focus() on something hidden
     is a no op, so nothing could put the reader inside it. */
  visibility: hidden; transform: translateX(-100%);
  transition: transform var(--panel-ease), visibility 0s var(--panel-ms);
}
body.is-menu .navpanel {
  visibility: visible; transform: translateX(0);
  transition: transform var(--panel-ease), visibility 0s;
}
/* site.js puts the reader inside the panel when it opens, so a screen reader
   is told what has appeared and the next Tab is the first row. A ring round
   the whole panel would say nothing that the panel arriving has not; the rows
   draw their own. */
.navpanel:focus { outline: none; }

/* The list stands at the TOP of the panel, by request, under the air
   --panel-top keeps clear of the button in the corner. */
.navpanel__group {
  display: flex; width: auto; padding: 0;
  flex-direction: column; align-items: stretch;
}

/* Language, Sources and About are ONE layout, by request: the same face, the
   same weight, the same row. Language is still a <p> and not a link, so it
   takes no hover, no press and no hand; the two language names under it are
   where the pressing happens. A ROW is the target, not the word in it: the
   full width of the panel and --panel-row-h tall, which clears Apple's 44
   and Android's 48, with the words at his own phone margin. A thumb that
   lands anywhere on the line gets the thing on that line, which is what a
   list on a phone is for. */
.navpanel__item, .navpanel__lang, .navpanel__label {
  display: flex; align-items: center;
  min-height: var(--panel-row-h);
  margin: 0; padding: 0 var(--panel-inset);
  font-family: var(--font-display); font-size: var(--size-panel);
  font-weight: 700; color: var(--ink-title); text-align: left;
  transition: color var(--ease);
}
/* The two language names are SUB-ROWS, one on top of the other, by request:
   a step in from the headings (their retraction), at the reading weight and
   the reading ink rather than the heading's, on the tight --panel-row-sub,
   so the pair sits close under Language with little air above and under
   each name. Still the full width of the panel, so a thumb that lands
   anywhere on the line gets the name on it. */
.navpanel__lang {
  min-height: var(--panel-row-sub);
  padding-left: calc(var(--panel-inset) + var(--panel-indent));
  font-weight: 400; color: var(--ink);
}

/* Hover is for a pointer that can hover. On a touch screen :hover sticks
   after a tap, and a whole row left lit --hover as the panel slides away is a
   lie about where the hand is. What a finger gets instead is :active, which
   lasts exactly as long as the finger does. */
@media (hover: hover) {
  a.navpanel__item:hover, a.navpanel__lang:hover { color: var(--hover); }
}
a.navpanel__item:active, a.navpanel__lang:active {
  background-color: var(--panel-press);
}
/* The language being read is a span, not a link: there is nothing to go to.
   It says so the way the dropdown says it, in --accent against the other
   one's --ink-title, and it says it a second way that is not a colour: a
   short bar of the same violet standing in the panel's leading edge, for a
   reader who cannot tell the two inks apart. */
.navpanel__lang.is-current { position: relative; color: var(--accent); cursor: default; }
.navpanel__lang.is-current::before {
  content: ""; position: absolute; left: 0; inset-block: 6px;
  width: var(--panel-mark-w);
  border-radius: 0 var(--radius-pill) var(--radius-pill) 0;
  background-color: var(--accent);
}
/* His hairline, his 65%, started at the words rather than centred on a panel
   whose rows now run edge to edge. Its margins are also what keeps daylight
   between one target and the next. */
.navpanel__rule {
  width: var(--panel-rule-w); height: 1.5px;
  margin: var(--panel-pad) var(--panel-inset);
  background-color: var(--hairline);
}

/* ---------- The Organs menu, on an organ page ---------------------------- */

/* Out of the header's row and pinned to the middle of it. It opens and closes
   on exactly the machinery the language menu uses, data-open on the .navitem,
   so there is no second dropdown to keep in step. */
.navitem--organs { position: absolute; left: 50%; transform: translateX(-50%); }
.navitem__chev { display: flex; line-height: 0; }
.icon-chev { width: 20px; height: 20px; display: block; }

/* The arrow beside Organs points down at a menu that is not open, and turns to
   point up at one that is, on the way, so it says which of the two is about to
   happen rather than only which has. It is one element turning, not two
   swapped: an arrow that changed drawing at the halfway mark would be a jump
   in the middle of a movement.

   -90 rather than +270, so closing it turns back the way it came. */
.navitem__chev .icon-chev {
  width: var(--chev-word); height: var(--chev-word);
  transform: rotate(90deg);
  transition: transform var(--ease);
}
.navitem[data-open="true"] .navitem__chev .icon-chev { transform: rotate(-90deg); }

/* Two chevrons on the site are NOT a gradient arrow: the one beside Organs in
   the header, and the one on an organ in the slides library. Those two stand
   beside a word and belong to it, so they answer the pointer with it: grey
   while its name is grey, purple while its name is purple. A stroke set in
   CSS beats the presentation attribute the drawing carries, so this is one
   rule over one drawing rather than a second drawing.

   The navy-to-crimson arrow is the pair that steps a strip of slides; the
   blue-to-magenta one, below, is the pair on the table of content. */
.navitem__chev .icon-chev path,
.library__chev .icon-chev path {
  stroke: currentColor;
  /* And a little heavier. His arrow is thin because it is drawn large, in a
     button of its own; beside a word at 14px that same weight is a hairline.
     Still one drawing: a stroke width set in CSS beats the attribute, exactly
     as the colour above does. */
  stroke-width: 2.7;
}

/* The gradients the author's own arrows are drawn with, painted onto the stops
   of the definitions build.py writes into every page. */
.ink-defs { position: absolute; width: 0; height: 0; overflow: hidden; }
#atlas-arrow stop:nth-child(1) { stop-color: var(--arrow-1); }
#atlas-arrow stop:nth-child(2) { stop-color: var(--arrow-2); }
#atlas-arrow stop:nth-child(3) { stop-color: var(--arrow-3); }
#atlas-arrow stop:nth-child(4) { stop-color: var(--arrow-4); }
#atlas-arrow stop:nth-child(5) { stop-color: var(--arrow-5); }
#atlas-arrow-toc stop:nth-child(1) { stop-color: var(--arrow-toc-1); }
#atlas-arrow-toc stop:nth-child(2) { stop-color: var(--arrow-toc-2); }
#atlas-arrow-toc stop:nth-child(3) { stop-color: var(--arrow-toc-3); }
#atlas-arrow-toc stop:nth-child(4) { stop-color: var(--arrow-toc-4); }
#atlas-arrow-toc stop:nth-child(5) { stop-color: var(--arrow-toc-5); }

.megamenu {
  position: absolute; top: calc(100% + 8px); left: 50%; z-index: 2;
  display: grid; width: 92vw; max-width: 1160px;
  /* A menu taller than the window scrolls rather than running off the bottom
     of it, and the page behind it does not scroll with it. This is not a way
     of rounding the panel: the cards inside are held clear of its corners by
     its own padding, and its shadow, which is what dims the page, is the
     panel's and not its content's, so nothing here is clipped by it. */
  max-height: calc(100vh - var(--nav-h) - 24px);
  overflow-y: auto; overscroll-behavior: contain;
  padding: 10px; gap: 8px;
  grid-template-columns: repeat(4, 1fr);
  border: 1px solid var(--glass-edge); border-radius: var(--radius);
  background-color: var(--glass-deep); background-image: var(--glass-sheen);
  backdrop-filter: blur(var(--glass-blur)) saturate(1.7);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(1.7);
  /* The page behind is dimmed by this panel's own shadow, spread wide enough
     to cover any screen. One element and one declaration, with nothing to
     appear, disappear or get left behind when the menu closes. */
  box-shadow: var(--shadow-dialog), 0 0 0 100vmax var(--scrim);
  transform: translateX(-50%);
  opacity: 0; visibility: hidden;
  transition: opacity var(--menu-open), visibility var(--menu-open);
}
.navitem--organs[data-open="true"] .megamenu { opacity: 1; visibility: visible; }

/* What the pointer SAYS over this menu. Everything in it is a title of some
   kind and only some of them go anywhere, so the pointer has to tell them
   apart: the hand over a name whose page is written, the plain arrow over
   every other title. The arrow is said once, here on the panel, because
   `cursor` inherits: a system's heading, a group's label and an organ that is
   not written yet all take it, and so will any title this menu is given
   later. Left alone they take `auto`, which over a run of words is the text I
   beam, and an I beam offers to select a heading rather than to follow it.
   This is the header's own menu, built once from content/organs.json, so it
   is the same menu with the same pointers on every organ page there will
   ever be. */
.megamenu { cursor: default; }
a.orgcard__organ { cursor: pointer; }

/* And what a DRAG across it does: nothing. This menu is a list of places to
   go, not a passage to quote, and dragging over it left a trail of headings
   and organ names in selection blue behind the pointer.

   `user-select: none` is refused everywhere else on the atlas, and that rule
   stands: a reader may well want to copy a caption or a paragraph, so only
   the DOUBLE click is taken away there, by one preventDefault in site.js,
   and a deliberate drag still selects. A menu is the exception, because
   there is nothing in it worth carrying off. Said on the whole item rather
   than on the panel, so the word that opens it and its chevron go quiet too
   and a drag begun on the button cannot select half of them; `user-select`
   inherits, so this one line covers every title inside. Nothing here is
   typed into and nothing here carries data-edit, so no field of either
   editor loses its cursor to it. */
.navitem--organs { user-select: none; -webkit-user-select: none; }

/* One system: a heading over a panel of its organs. The card stretches to its
   row, and the panel takes the slack, so four cards side by side finish level
   however many organs each of them holds. */
.orgcard { display: flex; height: 100%; flex-direction: column; gap: 6px; }
.orgcard__title {
  margin: 0; padding: 5px 8px;
  border: 1px solid var(--hairline); border-radius: var(--radius-sm);
  background-color: var(--surface);
  font-family: var(--font-display);
  font-size: var(--size-card); line-height: 1.25; font-weight: 700;
  color: var(--accent); text-align: center; text-transform: uppercase;
  letter-spacing: .02em;
}
.orgcard__body {
  display: flex; flex: 1; flex-direction: column; justify-content: center;
  gap: 7px; padding: 9px 6px;
  border: 1px solid var(--hairline); border-radius: var(--radius-sm);
  background-color: var(--card-bg);
  transition: box-shadow var(--ease), border-color var(--ease);
}
/* The card a reader's hand is over is lit, which is what tells twelve of them
   apart at a glance. Only the light changes: nothing moves, because twelve
   cards that each jumped a pixel as the pointer crossed them would be a page
   that will not sit still. */
.orgcard:hover .orgcard__body {
  border-color: var(--glass-edge); box-shadow: var(--shadow-glass);
}
/* A run of organs and the one drawing that belongs to all of them. The slot
   for the drawing is held open whether or not there is a drawing in it yet,
   so the day the artwork arrives nothing on this menu moves. */
.orgcard__row { display: flex; align-items: center; gap: 6px; }
.orgcard__names { flex: 1; min-width: 0; }
.orgcard__names ul { margin: 0; padding: 0; list-style: none; }
.orgcard__art { flex: none; width: var(--icon-col); }
.orgcard__icon { width: 100%; height: auto; filter: var(--diagram-invert); }
.orgcard__group {
  margin: 0 0 2px;
  font-family: var(--font-display); font-size: var(--size-card);
  font-weight: 700; line-height: 1.35; color: var(--ink-title); text-align: center;
}
.orgcard__organ {
  display: block; padding: 1px 2px;
  font-family: var(--font-display); font-size: var(--size-card);
  font-weight: 700; line-height: 1.4; color: var(--ink-title); text-align: center;
  transition: color var(--ease);
}
/* Every organ answers the pointer, whether or not its page is written yet:
   this list is what a reader runs their eye down, and a name that stayed dead
   under the pointer read as a name that was not there.

   The group headings do not, and that is the rule they mark: a line ending in
   a colon is the name of a run of organs, not an organ, and there is nowhere
   for it to go. */
.orgcard__organ:hover { color: var(--hover); }
/* An organ whose page is not written yet is text, not a link. */
.orgcard__organ.is-soon { cursor: default; }
.orgcard__organ--sub { font-size: calc(var(--size-card) - 1.5px); font-weight: 400; }
/* On a screen wide enough for the four column panel, an organ's name is set
   at the same size as the words on the page itself (--size-note), asked for
   so the menu reads like part of the atlas rather than small print beside
   it. The group headings stay at the card size: they are labels over a run
   of names, not names. Below 992px the menu is a drawer down a phone's
   screen, where the smaller size is what fits. */
@media (min-width: 992px) {
  .orgcard__organ { font-size: var(--size-note); }
  .orgcard__organ--sub { font-size: calc(var(--size-note) - 2px); }
}

/* ---------- The two screens --------------------------------------------- */

/* A block formatting context, so screen two's 90vh top margin pushes screen
   two down instead of collapsing out through main and dragging the whole
   page with it. The live site has no wrapper here, which is why it does not
   need this line. */
main { display: flow-root; }

/* Screen one is fixed and never scrolls, .section-19 */
.screen--general {
  position: fixed; width: 100%; height: var(--screen-general);
  background-position: 50% 50%; background-size: contain;
}

/* Screen two starts below it and slides up over it, .div-block-191 */
.screen--special {
  position: relative; z-index: var(--z-special);
  display: flex; width: 100%;
  height: var(--screen-special); min-height: var(--screen-special);
  margin-top: var(--screen-general);
  flex-direction: column; justify-content: flex-start; align-items: center;
  background-color: var(--surface);
  background-position: 50% 50%; background-size: contain;
  box-shadow: var(--shadow-screen);
}

/* The blur that turns each screen's photograph into a soft field.

   Webflow does it with backdrop-filter on the veil above (.section-20 and
   .section-26). The result here is the same, but it is painted rather than
   composited: backdrop-filter is the first thing a browser drops when it
   falls back to software rendering, on a phone in low power mode or with
   hardware acceleration off, and when it goes the background snaps sharp.
   A blurred copy of the picture cannot be dropped.

   The layer inherits the photograph from its screen, including the inline
   address the editor writes, so there is one place to change a background and
   it stays one. It reaches 60px past every edge, four times the blur radius,
   so no edge fades; the screen clips that surplus. It covers the screen
   completely and the picture is opaque, so the sharp original underneath is
   never seen. */
.screen { overflow: clip; }
.screen::before {
  content: ""; position: absolute; inset: -60px; z-index: 0;
  background-image: inherit;
  background-position: inherit; background-size: inherit;
  background-repeat: inherit;
  filter: blur(var(--blur-screen)) var(--screen-tint);
}

.veil {
  position: relative;
  display: flex; height: 100%;
  align-items: center;
}
/* The wash stays full width; only the air inside it grows, so the two
   screens hold their composition at --page and the photograph behind them
   still fills the window. The columns inside are percentages of the veil's
   CONTENT box, so capping the content box freezes all of them at once. */
.veil--general {
  padding: 0 calc(var(--page-gutter) + .07 * var(--page));
  flex-direction: column; justify-content: center;
  background-image: var(--wash-general);
}
.veil--special {
  width: 100%; height: var(--screen-special-inner);
  padding: 0 calc(var(--page-gutter) + .03 * var(--page));
  flex-direction: row; justify-content: space-around;
  background-image: var(--wash-special);
  box-shadow: var(--shadow-veil);
}

/* ---------- Screen one: General Histology -------------------------------- */

.hero {                                          /* .div-block-185 */
  display: flex; width: 100%;
  flex-direction: row; justify-content: space-around; align-items: center;
}

.hero__title {                                   /* .div-block-190 */
  position: relative; z-index: var(--z-tip);
  display: flex; width: var(--title-col); margin-right: 5%;
  justify-content: center; align-items: center;
  border-radius: var(--radius-lg);
  background-color: var(--panel);
  backdrop-filter: blur(var(--blur-panel));
  -webkit-backdrop-filter: blur(var(--blur-panel));
}

.display {
  margin: 0;
  font-family: var(--font-display);
  line-height: var(--line-display); font-weight: 700;
  color: var(--ink);
  text-shadow: var(--shadow-title);
}

/* Absolute, so the panel behind it collapses to nothing and its white never
   paints: on the live site the title floats free over the blurred field.
   Left and right pin it to the panel, the translate centres it on the
   panel's middle, which is where Webflow's flex static position puts it.

   The 6% is the one liberty: "Histologie" at 10vw of Palatino Bold is 46.8vw
   and the panel is 43vw, so the word is a shade too long for the line it is
   given. Reaching 6% past the panel on each side buys 5vw and keeps the text
   centred on exactly the same point, and since the panel is transparent and
   the title is out of flow, nothing on the screen moves. It is what lets this
   title stay at Webflow's full 10vw instead of being fitted down. */
.display--general {                              /* .text-block-13 */
  position: absolute; left: -6%; right: -6%; top: 50%;
  transform: translateY(-50%);
  font-size: var(--size-display); line-height: var(--line-display);
  text-align: center;
}

/* One line of text, not two: it wraps on its own because the column it sits
   in is only 38% wide. That is how the live site breaks it. */
.display--special {                              /* .text-block-13-copy */
  position: relative; width: 100%;
  font-size: var(--size-display-special); line-height: var(--line-display-special);
  text-align: left;
}

.hero__list { width: var(--list-col); max-width: var(--list-col); }  /* .container-15 */

.tissues {                                       /* .grid-48 */
  display: flex; width: 100%; padding: 1% 0;
  flex-direction: column; justify-content: center; align-items: flex-start;
  gap: var(--tissue-gap);
  border-radius: var(--radius);
  background-color: var(--surface);
  box-shadow: var(--shadow-list);
  /* Not clipped, as on the live site: an open slide on the first or last row
     stands about 6px proud of the card, and its own rounded corner reads as
     the card's. */
  overflow: visible;
}

.tissue {                                        /* .div-block-194 */
  position: relative; overflow: visible;
  display: flex; width: 100%; height: var(--tissue-row-h); padding: 1% 0;
  justify-content: flex-start; align-items: center;
  border-radius: var(--radius-sm);
  white-space: nowrap; cursor: pointer;
}

.tissue__label {                                 /* .div-block-193 + .text-block-15 */
  position: relative; z-index: 20;
  /* Hovered, this box loses --label-inset-open minus --label-inset off each
     side, so the fitter in site.js holds back twice that and sizes the name
     to the narrower of the two states. Change the insets, change this. */
  --fit-reserve: 10%;
  display: flex; height: 100%; margin: 0 var(--label-inset); padding: 0 5%;
  align-items: center;
  border-radius: var(--radius-sm);
  background-color: transparent;
  backdrop-filter: blur(var(--blur-tissue));
  -webkit-backdrop-filter: blur(var(--blur-tissue));
  font-family: var(--font-display);
  font-size: var(--size-tissue); line-height: 120%; font-weight: 700;
  color: var(--ink-title); text-align: left;
  transition: margin var(--hover-open), background-color var(--hover-plate);
}

/* Hovered, the plate slides 5% further in, carrying the name with it, and a
   white veil comes up behind it over twice as long. Behind (hover: hover),
   THE SAME question `canHover` asks in site.js and the slide badges and the
   red dots ask in theirs: a touch screen's sticky :hover would otherwise
   leave one row opened by the tap that was on its way to the organ. */
@media (hover: hover) {
  .tissue:hover .tissue__label,
  .tissue:focus-visible .tissue__label {
    margin: 0 var(--label-inset-open);
    background-color: var(--label-plate);
  }
}

/* The slide behind each tissue name is collapsed until you hover the row,
   which is the one interaction the live site runs on this card. It opens
   from the middle out to 130% of the row, which covers the white between the
   rows and the card's padding above the first one. */
.tissue__media {                                 /* .div-block-195 */
  position: absolute; left: 0; right: 0; z-index: 10;
  overflow: hidden;
  display: flex; width: 100%; height: 0; max-width: 100%;
  justify-content: center; align-items: center;
  border-radius: var(--radius-sm);
  transition: height var(--hover-open);
}
@media (hover: hover) {
  .tissue:hover .tissue__media,
  .tissue:focus-visible .tissue__media { height: var(--tissue-media-h); }
}
.tissue__media img {                             /* .image-325 */
  position: absolute; width: 100%; height: auto; object-fit: cover;
}

/* A screen with no pointer to hover with shows all seven slides at rest, by
   request: a picture kept behind an interaction the device cannot perform is
   a picture nobody there ever sees. Each fills ITS OWN row and no more: the
   hover state opens to 130% because one row at a time may lie over the white
   between the rows, and seven of them at 130% would lie over each other. The
   name keeps the inset and the white veil it wears open, or it would be read
   against a photograph; and it no longer narrows on hover, so the fitter is
   told to hold nothing back and the name is sized to the box it is really in. */
@media (hover: none) {
  .tissue__media { height: 100%; }
  .tissue__label {
    --fit-reserve: 0%;
    margin: 0 var(--label-inset-open);
    background-color: var(--label-plate);
  }
  /* With every row showing its picture, the card's own 1% of air is a white
     frame across the top and the bottom of the stack, so it goes, by request,
     and the first and last pictures run to the card's edges. The card is
     rounded, so its two top corners are handed to the first picture and its
     two bottom corners to the last: the radius goes on the thing that has to
     look rounded, never on a parent clipping what overflows it, and the card
     keeps `overflow: visible` for the hover state it still has elsewhere. */
  .tissues { padding: 0; }
  .tissue:first-child .tissue__media {
    border-top-left-radius: var(--radius); border-top-right-radius: var(--radius);
  }
  .tissue:last-child .tissue__media {
    border-bottom-left-radius: var(--radius); border-bottom-right-radius: var(--radius);
  }
}

.scrolldown {                                    /* .container-16 */
  position: absolute; bottom: 5%; width: var(--disc-w);
}
.scrolldown__disc {                              /* .div-block-202 */
  display: flex; width: 100%; height: 100%;
  justify-content: center; align-items: center;
  aspect-ratio: 1 / 1;
  border-radius: var(--radius-pill);
  background-color: var(--disc);
  box-shadow: var(--shadow-disc);
  transition: transform var(--ease-fast);
}
.scrolldown:hover .scrolldown__disc { transform: translateY(4px); }
.scrolldown img { width: 110%; max-width: 110%; aspect-ratio: 1 / 1; }  /* .image-326 */

/* ---------- Screen two: Special Histology -------------------------------- */

.systems { display: flex; width: var(--systems-col); justify-content: center; align-items: center; }  /* .div-block-205 */
.systems__inner { position: relative; display: flex; width: 100%; }   /* .div-block-208 */

.systems__rail {                                 /* .section-29 */
  display: flex; width: var(--rail-col); justify-content: center; align-items: center;
}
.systems__label {                                /* .text-block-32 */
  position: absolute; margin: 0 5% 0 0;
  display: flex; flex-direction: column-reverse;
  transform: rotate(-90deg);
  font-family: var(--font-display);
  font-size: var(--size-systems); line-height: var(--line-systems); letter-spacing: 2px;
  color: var(--ink-systems);
  text-shadow: var(--shadow-systems);
  white-space: nowrap;
}

.tabs { display: flex; width: var(--tabs-col); justify-content: center; align-items: center; }  /* .tabs-2 */

/* Webflow rounds this list and hides what overflows it, which rounds the
   corners by cutting them off, and cuts anything else that reaches them too:
   the editor's selection frame lost its corners to it. The corners belong to
   the tabs instead, below, which draws the same shape and crops nothing. */
.tabs__menu {                                    /* .tabs-menu-2 */
  position: relative;
  display: flex; width: var(--tabs-menu-col); max-width: var(--tabs-menu-col);
  margin: 0; padding: 0; list-style: none;
  flex-direction: column; justify-content: flex-start; align-items: stretch;
}
.tabs__link {                                    /* .tab-link-tab-2-2 */
  overflow: hidden;
  width: 100%; padding: 8px 8%;
  border: 0; border-bottom: 2px solid var(--tab-line);
  background-color: var(--tab-bg);
  font-family: var(--font-display);
  font-size: var(--size-tab); line-height: var(--line-tab); font-weight: 400;
  color: var(--ink); text-align: center; word-break: keep-all;
  transition: background-color var(--ease), color var(--ease);
}
/* One radius, --radius-sm, used the same way on every corner in the stack:
   the top of the first tab, the bottom of the last, and all four of whichever
   tab is open. Webflow rounds only the top two of the open tab and rules every
   tab underneath, the last one included, which left the list closed one way at
   the top and another at the bottom. */
.tabs__menu li:first-child .tabs__link {
  border-top-left-radius: var(--radius-sm);
  border-top-right-radius: var(--radius-sm);
}
.tabs__menu li:last-child .tabs__link {
  border-bottom: 0;
  border-bottom-left-radius: var(--radius-sm);
  border-bottom-right-radius: var(--radius-sm);
}
.tabs__link:hover { background-color: var(--tab-bg-active); }
.tabs__link[aria-selected="true"] {              /* Webflow's w--current */
  background-color: var(--tab-bg-active);
  color: var(--accent);
  border-radius: var(--radius-sm);
}

/* The same story as the tab list. Webflow rounds this panel by hiding what
   overflows it, and what overflows it is the organ name above a dot near an
   edge: on the narrower diagrams half the names were cut in two. The white
   card is rounded by its own corners now and nothing here clips, so a name can
   reach out over the edge of the panel, which is where it has to go. */
.tabs__content {                                 /* .tabs-content-2 */
  width: var(--tabs-body-col); height: auto;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-diagram);
}
.tabs__pane {                                    /* .tab-pane-tab-2-2 */
  display: none;
  width: 100%; height: auto;
  border-radius: var(--radius-lg);
  background-color: var(--figure);
}
.tabs__pane.is-active { display: block; }

/* The diagram is as tall as its picture, which is square, so the box is
   square without being told to be. It used to be told, with aspect-ratio, and
   the dots inside took their squareness from the same declaration; a browser
   that resolves aspect-ratio differently on an out of flow flex box turned
   every dot into a lens. Nothing here depends on it now. */
.diagram {                                       /* .div-block-a */
  position: relative; overflow: visible;
  width: 100%;
  /* The box is the drawing's shape BEFORE the drawing arrives, and that is
     the whole of why the dots are where they belong from the first frame.
     Every dot is placed at a percentage of this box; a box that took its
     height from the picture had NO height until the picture landed, so every
     one of those percentages was a percentage of nothing and all 43 dots sat
     piled along the top edge until the bytes came and threw them outwards.

     A percentage padding, not aspect-ratio, and not the picture: it is
     measured against this box's own width in every browser there is, exactly
     as the hero and the slide trays are, and it costs nothing to compute. */
  height: 0; padding-bottom: var(--diagram-ratio);
}
/* The drawings are pure black line art on nothing, so in the dark they are
   turned inside out and come back as white lines. The dots are their own
   elements and keep their own colour.

   Laid INTO the box rather than making it: the box is already the right
   shape, so the picture fills it and changes nothing when it arrives.
   `contain` so that a drawing which is one day not square letterboxes inside
   its square rather than stretching, and takes the dots nowhere. */
.diagram__img {                                  /* .image-329 */
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: contain; object-position: 50% 50%;
  filter: var(--diagram-invert);
}

/* Hotspots. left/top are the dot's top-left corner as a percentage of the
   diagram, exactly as Webflow places them, so they hold at every width.

   A percentage padding is always measured against the container's width, so
   padding-bottom equal to the width makes a square in every browser, whatever
   shape the diagram is and whatever it thinks of aspect-ratio. A square with
   a 50% radius is a circle. */
.hotspot {                                       /* .red-dot-* */
  position: absolute;
  width: var(--dot-w); height: 0; padding-bottom: var(--dot-w);
}
.hotspot--hidden { display: none; }              /* the three unfinished duplicates */

/* The red circle, and the whole of it is the link, so the organ opens wherever
   you click it. The colour and the growth on hover belong to this element
   rather than to the box around it: the name is a child of that box too, and
   scaling the box scaled the name along with the dot. */
.hotspot__dot {                                  /* .red-dot-link-* */
  position: absolute; inset: 0;
  display: block;
  border-radius: 50%;
  background-color: var(--dot);
  transition: background-color var(--ease), transform var(--ease);
}
/* Only where pointing is real: a touch screen holds :hover on the last thing
   tapped, and a dot left lit after its name was put away would contradict the
   state, exactly as the slide's badge did. The same question canHover asks in
   site.js. */
@media (hover: hover) {
  .hotspot:hover .hotspot__dot {
    background-color: var(--dot-hover); transform: scale(1.1);
  }
}
.hotspot[data-open="true"] .hotspot__dot {
  background-color: var(--dot-hover); transform: scale(1.1);
}

/* The name, above the dot and clear of it. It appears and goes, and does
   nothing else: no growing, no shrinking. Quick on and quick off, by request,
   so it is gone before the pointer can reach it, which is what keeps the dot
   the only thing there is to click. */
.hotspot__tip {                                  /* .a-1 */
  position: absolute; bottom: 150%; left: 50%; z-index: var(--z-tip);
  transform: translateX(-50%);
  padding: 6px 10px;
  border-radius: var(--radius-xs);
  background-color: var(--tip-bg);
  box-shadow: var(--shadow-tip);
  backdrop-filter: blur(var(--blur-tip));
  -webkit-backdrop-filter: blur(var(--blur-tip));
  font-family: var(--font-display); font-size: var(--size-tip); line-height: 1.35;
  color: var(--ink-title); text-align: center; white-space: nowrap;
  pointer-events: none;
  opacity: 0; visibility: hidden;
  transition: opacity var(--tip-fade), visibility var(--tip-fade);
}
/* Pointing at the dot shows the name, and arriving on it by keyboard shows the
   name. Not merely holding focus: a middle click, a ctrl click or a right
   click reaches the link without leaving the page, and the dot keeps focus
   afterwards, which left the name standing there once the pointer had gone.
   :focus-visible is the distinction the browser already draws between the two,
   and the tip is the dot's next sibling, so no :has() is needed for it.
   The hover half sits behind (hover: hover), same as the dot's own. */
@media (hover: hover) {
  .hotspot:hover .hotspot__tip { opacity: 1; visibility: visible; }
}
.hotspot__dot:focus-visible + .hotspot__tip { opacity: 1; visibility: visible; }

/* On a touch screen the first tap on a dot opens the name instead of the page
   (site.js), and the name is then a WAY IN: a real link to the same page, in
   the same tab, so it takes the pointer back while it is open. The dot stays
   clickable too, so a reader who already knows where a dot goes never waits
   on the name. Everything else on the screen closes it, and follows nothing. */
.hotspot[data-open="true"] .hotspot__tip {
  opacity: 1; visibility: visible; pointer-events: auto;
}
/* The dots with no page yet say so, on a line of their own. */
.hotspot__soon { display: block; color: var(--ink-muted); }

.special__title {                                /* .div-block-201 */
  position: relative;
  display: flex; width: var(--special-title-col);
  flex-direction: column; justify-content: center; align-items: center;
}

/* Calibration overlay, enabled with ?calibrate=1 */
.diagram.is-calibrating { cursor: crosshair; outline: 2px dashed var(--accent); }
.calibrate-readout {
  position: fixed; left: 16px; bottom: 16px; z-index: 200;
  padding: 10px 14px; border-radius: var(--radius-xs);
  background: var(--ink); color: var(--surface);
  font: 500 13px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* ==========================================================================
   The organ page
   --------------------------------------------------------------------------
   A different page from the home page and built the same way: a hero, a table
   of content pinned to the left of the screen, then one section after another,
   each a heading between two rules, a few notes, and a strip of slides with
   the drawings marked on them.

   Everything down the middle of the page sits in one column, --part-col of
   the sheet, so a heading, its notes and its slides all line up.
   ========================================================================== */

/* Relative, so a parked list's rail can be laid down the left of the article
   and measured from the article's own top. Nothing else hangs off it. */
/* Relative, so a parked list's rail can be laid down the left of the article
   and measured from the article's own top. Full width, deliberately: the
   hero is a bar of photograph across the whole window and the parked list
   stands at the window's left edge, so neither may live in a capped box.
   What stops growing past --page-max is everything WITH a measure: the sheet
   caps itself at --sheet-max, the hero's HEIGHT is --hero-h, a share of the
   page, and the plate on it is frozen type in frozen air. */
.organ { position: relative; background-color: var(--ground); }

/* ---------- The hero ----------------------------------------------------- */

/* The hero is a bar of photograph across the whole window, like the header:
   it never detaches from the sides, whatever the zoom. Its HEIGHT is the
   page's own third, --hero-h, a length known before the picture arrives (the
   same promise the old percentage padding made, without asking the picture),
   so below --page-max the box is exactly the picture's 3 to 1 and nothing is
   cropped; past the cap the height stops with the type while the width rides
   the window, and cover trims the picture's top and bottom evenly, exactly
   as his own Webflow hero does. Never aspect-ratio, as everywhere else. */
.banner {
  position: relative; overflow: clip;
  height: var(--hero-h);
  background-position: 50% 50%; background-size: cover;
}
/* A copy of the picture, taken down in the dark. The identity filter in the
   light palette makes this layer invisible there, so there is one rule rather
   than a rule and an exception. */
.banner::before {
  content: ""; position: absolute; inset: 0;
  background-image: inherit;
  background-position: inherit; background-size: inherit;
  filter: var(--screen-tint);
}
/* Centred on the picture, and taken out of the flow because the box around it
   is now a padding rather than a height and has nothing in it to centre. */
.banner__plate {
  position: absolute; left: 50%; top: 50%; z-index: 1;
  /* Shares of the page, not vw or %: the plate is the hero's CONTENT, and
     content keeps the page's measure. Its air must not go on growing inside
     a picture that has stopped, and its ceiling must not loosen either, or
     the fitter would size the title differently at different zooms. */
  display: flex; max-width: calc(.86 * var(--page));
  padding: calc(.02 * var(--page)) calc(.044 * var(--page));
  justify-content: center; align-items: center;
  border: 1px solid var(--glass-edge); border-radius: var(--radius-lg);
  background-color: var(--glass); background-image: var(--glass-sheen);
  backdrop-filter: blur(var(--glass-blur)) saturate(1.5);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(1.5);
  box-shadow: var(--shadow-glass);
  transform: translate(-50%, -50%);
}
.banner__title {
  margin: 0;
  font-family: var(--font-script);
  font-size: var(--size-banner); line-height: 1.15;
  color: var(--ink-title); text-align: center;
  text-shadow: var(--shadow-title);
  white-space: nowrap;
}

/* ---------- The table of content ----------------------------------------- */

/* Fixed to the left of the screen and centred on it, so it stays put while the
   page runs past. It folds away to the left and leaves a handle behind, which
   is the only thing on this page that remembers a choice.

   Below the nav, deliberately: the header has to stay reachable with the list
   open, and the two never meet, since one is at the top and this is halfway
   down. Its left corners are the screen's edge rather than its own, which is
   what an edge panel is: it is a tab on the side of the window, not a card. */
.toc {
  position: fixed; left: 0; top: 50%; z-index: calc(var(--z-nav) - 1);
  /* Narrow enough to clear the column the words are set in, at every width it
     is shown at. It is a tab on the side of the window, not a second column. */
  display: flex; flex-direction: column;
  width: min(var(--toc-w), 22vw); max-width: 78vw;
  max-height: var(--toc-max);
  /* Solid, not glass. It is a list of words read against whatever the page
     happens to be showing behind it, which on this page is a photograph, and
     words on a photograph seen through a pane are words you have to work at. */
  border: 1px solid var(--hairline); border-left: 0;
  border-radius: 0 var(--radius) var(--radius) 0;
  background-color: var(--surface);
  box-shadow: var(--shadow-glass);
  transform: translate(0, -50%);
  transition: transform var(--ease), opacity var(--ease), visibility var(--ease);
}
.toc[data-open="false"] { transform: translate(-100%, -50%); }

.toc__head {
  flex: none;
  display: flex; padding: 9px 10px 9px 16px;
  justify-content: center; align-items: center;
  border-bottom: 1px solid var(--hairline);
}
.toc__title {
  margin: 0; flex: 1;
  font-family: var(--font-display);
  font-size: var(--size-toc-title); line-height: 1.25; font-weight: 700;
  color: var(--ink-title); text-align: center;
}
.toc__fold, .toc__handle {
  flex: none; display: flex; padding: 4px;
  color: var(--chevron); line-height: 0;
  transition: transform var(--ease);
}
.toc__fold:hover, .toc__handle:hover { transform: scale(1.12); }
/* Mirrored, never turned: flipping the drawing about the vertical is what his
   own left and right arrow files do to each other, and it keeps the blue at
   the top whichever way the arrow points. Half a turn would put the magenta
   there. */
.toc__fold .icon-chev { transform: scaleX(-1); }
/* The list's own ink, the blue-to-magenta arrow he chose for it, and his
   weight: 2.94 is that file's stroke measured against this drawing's 24 box.
   The CSS stroke beats the attribute the drawing carries, exactly as it does
   on the two chevrons that stand beside a word; --chevron stays behind it as
   the flat colour for a browser that cannot find the gradient. */
.toc__fold .icon-chev path,
.toc__handle .icon-chev path {
  stroke: url(#atlas-arrow-toc) var(--chevron);
  stroke-width: 2.94;
}
/* Longer than the window, it scrolls inside itself rather than growing past
   the top and the bottom of the screen. The heading stays put while it does. */
.toc__list {
  margin: 0; padding: 0; list-style: none;
  min-height: 0; overflow-y: auto; overscroll-behavior: contain;
}
.toc__list li + li { border-top: 1px solid var(--hairline); }
.toc__link {
  display: block; padding: 7px 14px;
  font-family: var(--font-display);
  font-size: var(--size-toc); line-height: 1.35;
  color: var(--ink); text-align: center;
  transition: color var(--ease), background-color var(--ease);
}
.toc__link:hover { color: var(--hover); background-color: var(--tab-bg); }
/* The last row's own corner, so its background cannot paint over the panel's.
   The panel is not clipped, here or anywhere else. */
.toc__list li:last-child .toc__link { border-bottom-right-radius: var(--radius); }

/* The handle the folded list leaves behind, at the same height it was.
   One step BELOW the list, deliberately: the two overlap at the window's
   edge while the list is sliding out, and the list must ride over the
   fading handle, never the handle over the list. */
.toc__handle {
  position: fixed; left: 0; top: 50%; z-index: calc(var(--z-nav) - 2);
  padding: 13px 6px;
  /* Solid, like the list it belongs to. */
  border: 1px solid var(--hairline); border-left: 0;
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  background-color: var(--surface);
  box-shadow: var(--shadow-glass);
  opacity: 0; visibility: hidden;
  transform: translateY(-50%);
  transition: opacity var(--ease), visibility var(--ease);
}
.toc__handle:hover { transform: translateY(-50%) scale(1.12); }
.toc[data-open="false"] + .toc__handle { opacity: 1; visibility: visible; }

/* The rail and the box that rides it are NOT THERE, except where the rule
   below turns them on. `display: contents` leaves their children exactly
   where they were, fixed to the window, so every screen and every page that
   is not parked behaves as though neither element had ever been added. */
.toc__rail, .toc__stick { display: contents; }

/* PARKED. The list holds a third of the way down the screen for the whole of
 * the reading, and only lets go at the very top of the page.
 *
 * Two things had to be true at once, and one property does both. The
 * author's screen recording of his own atlas, read frame by frame, shows the
 * letting go: through 1025px of scrolling the list moved with the page to
 * within a fortieth of a pixel, the distance from the bottom edge of the
 * hero picture to the top of the list never left 381.4px, and reaching the
 * top of the page carried the list off the bottom of the window with
 * everything else. No easing, no lag, nothing catching up. And his Webflow
 * stylesheet gives that resting place in CSS: a column at `position:
 * absolute`, `left: 0; top: 0; bottom: 0`, 20% wide and capped at 300px,
 * with `padding-top: 30vh`, and inside it his card. But his recording never
 * scrolls deep enough to show the hold, and the hold is what the reading
 * actually needs: a list of contents you lose after a screenful is a list
 * nobody can use.
 *
 * A STICKY box is exactly those two behaviours and no code. The rail is his
 * column, down the left of the article and the whole height of it. The box
 * rests on it at --toc-top, his 30vh under the picture, and holds at
 * --toc-hold, 30vh down the screen. At the top of the page the resting place
 * is the lower of the two, so the box sits there and rides with the page,
 * which is his recording frame for frame. The moment the page carries that
 * place up to 30vh, the box stops and stays there, for the whole of the way
 * down. Nothing switches, nothing is timed, and there is no scroll listener
 * anywhere: the browser does all of it.
 *
 * The two are the SAME 30vh, and that is what keeps the list off the picture
 * without anything having to check. Holding means the resting place, which
 * is 30vh under the picture, has come up to 30vh from the top of the screen;
 * so the picture's bottom edge is exactly at the top of the window the
 * instant the list stops, and above it ever after. The list can no more
 * stand on the picture than it can be in two places.
 *
 * --toc-top is measured DOWN THE PAGE from the top of the article: the hero
 * picture's height plus 30vh, which is his own number, the `padding-top` on
 * that column. site.js measures the picture and writes it, because a scroll
 * bar narrows the picture and vw does not know; --toc-park is the same sum
 * in CSS alone, for a page whose script never arrived. --toc-hold needs no
 * measuring at all and is never written from anywhere.
 *
 * The list and the handle share ONE box, so folding swaps them in place. The
 * handle is taken out of the flow inside it and laid over the list's own top
 * corner, which is where it was when both were fixed at the same height.
 *
 * No transition carries top, here or anywhere: the only movement is the
 * scroll's own.
 *
 * And never a scroll bar inside it, by request: max-height is lifted, so a
 * list taller than the room simply runs past the bottom of the window, where
 * what cannot be seen is off the screen rather than behind a bar.
 *
 * Asked for by the page, in its own JSON, which is why this is an attribute
 * on .organ and not a slug anywhere. A page that does not ask for it is
 * untouched, and site.js does not mark a parked page is-atop at all, so the
 * hero rules below have nothing to say here.
 *
 * Only from 992px up. Below that the list would lie across the page wherever
 * it sat, so it goes back to folding away to its handle exactly as before.
 * His own page goes further and takes the column away entirely below its
 * small breakpoint; the atlas keeps it, as a drawer. */
@media (min-width: 992px) {
  .organ[data-toc="parked"] .toc__rail {
    display: block;
    position: absolute; left: 0; top: 0; bottom: 0;
    width: min(var(--toc-w), 22vw);
    /* The rail is empty space. Only what rides it answers the pointer, or a
       transparent column down the left of every organ page would swallow
       every click meant for the words behind it. */
    pointer-events: none;
  }
  .organ[data-toc="parked"] .toc__stick {
    display: block; position: sticky;
    /* Where it holds: 30% of the screen above it, which is where it is for
       the whole of the reading. */
    top: var(--toc-hold);
    /* Where it rests in the page: under the picture, at his own 30vh. It is
       only ever THERE at the top of the page, where that place is lower down
       the screen than the middle; from the moment the two meet it holds at
       the middle instead, which is the whole of the way down. */
    margin-top: var(--toc-top, var(--toc-park));
    /* His own card carries z-index 5 for the same reason this one does: the
       list's own number no longer reaches past this box, because a box with
       a z-index of its own is where the counting starts again. Without it
       the white sheet, two levels up, paints straight over a list that is a
       thousand. The list and its handle keep their two numbers inside here,
       which is what still holds the one over the other. */
    z-index: calc(var(--z-nav) - 1);
  }
  .organ[data-toc="parked"] .toc {
    position: relative; top: auto; left: auto;
    transform: none; max-height: none;
    pointer-events: auto;
  }
  .organ[data-toc="parked"] .toc__list { overflow-y: visible; }
  .organ[data-toc="parked"] .toc__handle {
    position: absolute; left: 0; top: 0;
    transform: none;
    pointer-events: auto;
  }
  /* Folded by hand it still slides out to the left and leaves its handle. */
  .organ[data-toc="parked"] .toc[data-open="false"] { transform: translateX(-100%); }
  .organ[data-toc="parked"] .toc__fold:hover,
  .organ[data-toc="parked"] .toc__handle:hover { transform: scale(1.12); }
}

/* Neither the list nor its handle may stand on the hero picture. The script
   marks the page while the picture is still on screen and both get out of the
   way; once it has been scrolled past they come back, and they stay for the
   rest of the page. Without the script nothing is ever marked and the list
   simply stays, which is the state this page shipped in.

   Specific enough to beat the rules it has to beat, which are the two that
   bring the handle out: the one on a wide screen when the list is folded, and
   the one on a narrow screen where it starts folded. Leaning on data-open
   would not do it, because the attribute is not on the page until something
   has been clicked. Not display:none, because it slides away and comes back,
   and a thing removed from the page can do neither. */
body.is-atop .toc,
body.is-atop .toc:not([data-open="true"]) + .toc__handle,
body.is-atop .toc + .toc__handle {
  opacity: 0; visibility: hidden; pointer-events: none;
  transform: translate(-100%, -50%);
}

/* ---------- The sheet, and one section of it ----------------------------- */

/* The white page the notes are printed on. It meets the hero picture exactly
   as it meets the footer: square, flush, with nothing of it lying over the
   photograph. Its top edge is where the picture stops.

   Its two long sides are lit, which is what lifts it off the grey the page is
   laid on. Only its sides: --shadow-sheet is built so that neither half of it
   can reach past the top or the bottom edge, where it would smear onto the
   photograph above or the footer below and spoil both seams. */
.sheet {
  /* One above the footer, deliberately: the two share a level otherwise, the
     footer comes later in the page, and a list of slides dropped from the
     bottom of the library was walking in under it. Everything that hangs off
     the sheet is inside the sheet's own layer, so this one number is what
     keeps all of it over the footer. */
  position: relative; z-index: calc(var(--z-special) + 1);
  width: var(--sheet-col); max-width: var(--sheet-max);
  margin: 0 auto; padding: calc(.06 * var(--page-h)) 0 calc(.02 * var(--page-h));
  background-color: var(--surface);
  box-shadow: var(--shadow-sheet);
  /* The author's own ground under the words, read off his page: two soft
     gradients, a blue and a pink, tiled at their natural size and almost
     entirely washed out by a veil of the surface colour, so what is left is
     the faintest colour moving under the white rather than a pattern anyone
     could name. His Webflow rule is a 95% white gradient over
     "Purple Gradient.png" and "Gradient.png" at auto size; these are those
     two pictures, stored small and scaled back up, which costs 80KB instead
     of three megabytes and looks the same because they are blurs. The veil
     is the surface token, so the sheet still goes dark with the palette. */
  background-image:
    linear-gradient(to bottom, var(--sheet-veil), var(--sheet-veil)),
    url("/assets/img/sheet-glow-blue.webp"),
    url("/assets/img/sheet-glow-pink.webp");
  background-position: 0 0, 0 0, 0 0;
  background-size: auto, 1220px 1200px, 1220px 1185px;
}
.part { scroll-margin-top: 70px; }
.part + .part { margin-top: calc(.12 * var(--page-h)); }
.part__title {
  width: var(--part-col); margin: 0 auto calc(.034 * var(--page-h));
  padding: 12px 0;
  border-top: 2px solid var(--rule); border-bottom: 2px solid var(--rule);
  font-family: var(--font-display);
  font-size: var(--size-part); line-height: 1.2; font-weight: 700;
  color: var(--ink-heading); text-align: center;
}

/* The notes. The number is drawn by a counter rather than by the list, so it
   is part of the heading and the underline runs under it too, exactly as the
   design has it. Nothing here carries its own number in the JSON, so an item
   can be moved without renumbering anything by hand. */
.notes {
  width: var(--part-col); margin: 0 auto; padding: 0 0 0 3.2%;
  list-style: none; counter-reset: step;
  font-family: var(--font-display);
  font-size: var(--size-note); line-height: 1.5; color: var(--ink);
}
.notes__step { counter-increment: step; }
.notes__step + .notes__step { margin-top: 14px; }
.notes__head {
  font-weight: 700; color: var(--ink-title);
  text-decoration: underline; text-underline-offset: 4px;
}
/* What stands at the head of a line, which the page chooses and a block may
   overrule: a number, a dot, a square, or nothing at all. Written on the step
   rather than in the CSS's own mind, so the same stylesheet serves a numbered
   page and a plain one. */
.notes__step--number .notes__head::before { content: counter(step) ". "; }
.notes__step--dot .notes__head::before { content: "\2022"; }
.notes__step--square .notes__head::before { content: "\25aa"; }
/* A dot or a square HANGS in the margin, the way a list marks its items, so
   the second line of a long phrase starts under its first word rather than
   under the bullet, and a heading with no mark at all stands further left
   than the marked ones. The NUMBER does not hang: the design runs the
   heading's underline under it, and an underline cannot reach a box that
   has been taken out of the line. */
.notes__step--dot, .notes__step--square { position: relative; padding-left: 1.15em; }
.notes__step--dot .notes__head::before,
.notes__step--square .notes__head::before {
  position: absolute; left: 0; text-decoration: none;
}
.notes__step--plain .notes__head { text-decoration: none; }
/* A heading whose weight comes from its own words rather than from this rule:
   in the author's pages a top line is a sentence with a few words emphasised,
   not a title. The mark in front of it goes light with it, so a numbered
   heading does not carry a bold number over regular words. */
.notes__step--light > .notes__head,
.notes__step--light > .notes__head::before { font-weight: 400; }
/* The starred runs, in every note. Darker as well as heavier, which is how
   they read on his own page. */
.notes strong { font-weight: 700; color: var(--ink-title); }
.notes__list { margin: 8px 0 0; padding-left: 1.5em; list-style: disc; }
.notes__list--square { list-style: square; }
.notes__list--number { list-style: decimal; }
.notes__list--none { list-style: none; padding-left: 0; }
.notes__item + .notes__item { margin-top: 4px; }
.notes__sub { margin: 4px 0 0; padding-left: 1.7em; list-style: square; }
.notes__sub-item + .notes__sub-item { margin-top: 4px; }

/* ---------- A strip of slides -------------------------------------------- */

.slider { position: relative; width: var(--part-col); margin: calc(.036 * var(--page-h)) auto 0; }
/* Reached by keyboard, so the left and right keys have somewhere to arrive.
   The ring is drawn outside the tray rather than on it, so it cannot be taken
   for part of the picture. */
/* And it draws NOTHING when it has focus, by request: a ring round the whole
   tray reads as a selection on the photograph, which is the one thing on this
   page nothing else may compete with. The keyboard loses nothing it had: the
   arrow keys already step the strip the reader is looking at without anything
   having been clicked, and the dots under the tray are each a button of their
   own. It is the same answer the dots were given, that focus never inks. */
.slider:focus, .slider:focus-visible { outline: none; }
.slider__plate {
  position: relative;
  padding: var(--slide-inset);
  border-radius: var(--radius-tray);
  background-color: var(--plate);
  background-image: var(--glass-sheen);
  box-shadow: var(--shadow-glass);
}
/* The tray is 1280 by 1000, the shape every slide in the atlas is, and it is
   that shape whatever the screen. The height is a percentage padding measured
   against its own width, not aspect-ratio, and not the tallest picture in the
   strip: the tray's size is now a promise the pictures are cut to keep, so
   stepping through a strip cannot move the page under the reader's hand. */
.slider__stage {
  position: relative;
  height: 0; padding-bottom: var(--slide-ratio);
}

/* One slide is shown and the rest are not. It appears and it goes, and it does
   nothing else on the way: no sliding and no zoom. Hidden rather than removed,
   so the frame keeps its size, and hidden is out of reach of the pointer and
   out of the tab order too, which is the whole difference from merely
   transparent. */
.slide { position: absolute; inset: 0; margin: 0; visibility: hidden; }
.slide.is-active { visibility: visible; }

/* What fills the tray when a slide is not the shape the tray is cut for: the
   same picture again, blurred well past recognition. It reads as the light of
   the photograph carrying on past its edge rather than as an empty grey band,
   and when the slide IS 1280 by 1000 there is no band and none of this is ever
   seen. The same address as the picture itself, so it is fetched once.

   It is not made bigger than the slide and nothing is clipped to hold it in:
   the slide carries the marks, and the round cap at the point of an arrow is
   meant to be able to reach past the picture's edge. A blur has nothing to
   sample beyond the box, so the layer softens away at its own edges, which
   over the tray reads as a glow and is the reason it is not given a hard one.

   Only on the slide being shown, and only where there is tray to fill.
   site.js marks a slide is-inset when its picture does NOT reach the edges of
   its frame; a slide cut to 1280 by 1000 never is, and on a page whose slides
   all are, this layer never paints at all. That matters twice: a blur this big
   is expensive, and around a picture that fills its frame the layer has nothing
   to be the continuation of and reads as a halo. */
.slide.is-active.is-inset::before {
  content: ""; position: absolute; inset: 0; z-index: 0;
  background-image: var(--slide-art);
  background-position: 50% 50%; background-size: cover;
  filter: blur(24px) saturate(1.3) brightness(1.05);
}
/* Shown whole inside the frame, never cropped to fill it. A slide cut to 1280
   by 1000 is the same shape as the frame and the two are exactly the same box;
   one that is not is shown entire, centred, with a little of the tray left
   over, because a photograph with a finding marked on it is the one thing on
   this page that may not have its edges taken off to make a layout tidy. */
/* The box the picture is given, and on a wide screen it is not a box at all:
   the tray is already cut to the slide's shape there, so the wrapper hands
   the picture straight to the slide and adds nothing. On a phone the tray has
   no shape of its own and this is what carries it, a percentage padding
   measured against its own width, so the room is reserved before the picture
   arrives and nothing moves when it does. */
.slide__pic { display: contents; }
.slide__img {
  position: relative; z-index: 1;      /* in front of the blurred fill */
  width: 100%; height: 100%;
  object-fit: contain; object-position: 50% 50%;
  border-radius: var(--radius-slide);
  /* No shadow under the picture, by request. The tray it sits in is already
     lifted off the sheet, and a second shadow inside that one read as a
     seam across the bottom of every slide. */
}

/* The name of the picture, on a pane of glass across the foot of IT: measured
   from where the picture lands rather than from the tray, so it never ends up
   on the tray below a slide that is not the tray's own shape. */
.slide__caption {
  position: absolute; z-index: 2;
  left: calc(var(--paint-x, 0px) + var(--paint-w, 100%) * .1);
  right: calc(var(--paint-x, 0px) + var(--paint-w, 100%) * .1);
  bottom: calc(var(--paint-y, 0px) + var(--paint-h, 100%) * .026);
  padding: 8px 14px;
  border: 1px solid var(--glass-edge); border-radius: var(--radius-sm);
  background-color: var(--caption-bg); background-image: var(--glass-sheen);
  backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  box-shadow: var(--shadow-glass);
  font-family: var(--font-display);
  font-size: var(--size-caption); line-height: 1.3;
  color: var(--ink-title); text-align: center;
}

/* Where the slide came from, and who it belongs to. The wrapper is as wide as
   the note so that the note can be pinned to it, and it is transparent to the
   pointer, so only the mark itself and the note that opens from it react. */
.slide__info {
  position: absolute; z-index: 4;
  top: calc(var(--paint-y, 0px) + var(--paint-h, 100%) * .018);
  right: calc(var(--paint-x, 0px) + var(--paint-w, 100%) * .016);
  display: flex; width: var(--note-w); justify-content: flex-end;
  pointer-events: none;
}
/* It does not grow when you point at it. A mark that changed size on a
   photograph pulled the eye off the photograph, which is the one thing on this
   page nothing else should be competing with. It answers in colour instead. */
.slide__badge {
  position: relative; z-index: 2;
  flex: none; display: flex;
  width: var(--badge-w); height: var(--badge-w); padding: 0;
  justify-content: center; align-items: center;
  border: 1px solid var(--glass-edge); border-radius: 50%;
  background-color: var(--caption-bg); background-image: var(--glass-sheen);
  backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  box-shadow: var(--shadow-glass);
  font-family: var(--font-display); font-size: 14px; line-height: 1;
  color: var(--ink-title);
  pointer-events: auto;
  /* Not a hand: on a pointer that can hover, this mark is not clicked, it is
     pointed at, and the note appears. The question-mark cursor says exactly
     that, and it stays over the note itself so the two read as one thing. */
  cursor: help;
  transition: color var(--ease), background-color var(--ease);
}
.slide__info[data-open="true"] .slide__badge { color: var(--hover); background-color: var(--glass-deep); }
.slide__note {
  position: absolute; top: 0; right: 0; width: 100%;
  padding: 12px 15px 14px;
  border: 1px solid var(--glass-edge); border-radius: var(--radius-sm);
  background-color: var(--note-bg); background-image: var(--glass-sheen);
  backdrop-filter: blur(var(--glass-blur)) saturate(1.6);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(1.6);
  box-shadow: var(--shadow-glass-lift);
  font-family: var(--font-display);
  font-size: var(--size-note-fact); line-height: 1.45;
  color: var(--ink-title); text-align: left;
  pointer-events: auto;
  cursor: help;
  opacity: 0; visibility: hidden;
  transition: opacity var(--tip-fade), visibility var(--tip-fade);
}
/* Arriving by keyboard opens the note, and on a touch screen a tap opens it,
   which is the whole reason there is any script behind this at all. */
.slide__info[data-open="true"] .slide__note,
.slide__badge:focus-visible + .slide__note { opacity: 1; visibility: visible; }

/* Pointing at the mark, or at the note itself, keeps it open so it can be read
   and copied. Only where pointing is real. A touch screen holds :hover on the
   last thing tapped, so an ungated hover rule keeps the note on the screen
   after the tap that asked for it to go, and what is painted then disagrees
   with data-open and with aria-expanded. This query is the same sentence as
   canHover in site.js, so the stylesheet and the script cannot part. */
@media (hover: hover) {
  .slide__info:hover .slide__note { opacity: 1; visibility: visible; }
  .slide__badge:hover { color: var(--hover); background-color: var(--glass-deep); }
}
.slide__fact { margin: 0; }
.slide__fact + .slide__fact { margin-top: 10px; }

/* The pens. x and y in the JSON are the point the mark indicates, as a
   percentage of the picture, and --mark-size is its width on the same scale,
   so a mark holds its place and its proportions at every size. This is the
   pattern that replaces Webflow's 1,255 arrow classes: one class per kind of
   mark, and the position in the data.

   A percentage margin is measured against the container's width in every
   browser, which is the same basis as the width itself, so half the width is
   exactly half the box on both axes. */
.mark {
  position: absolute; z-index: 3;
  width: var(--mark-size); line-height: 0;
  margin-left: calc(var(--mark-size) * -0.5);
  transform: rotate(var(--mark-turn, 0deg));
  pointer-events: none;
}
/* Let the round cap at the point of an arrow reach past the box rather than
   being cut off by it: an SVG clips to its own viewBox unless told otherwise. */
.mark svg { display: block; width: 100%; height: auto; overflow: visible; }
/* An arrow and a line are both turned about their point, which is the top
   middle of the box: they are drawn in the same 100 by 190 viewBox and a line
   is an arrow without its head. Leaving the line out of this turned it about
   its middle instead, and every angled one landed off its target. */
.mark--arrow, .mark--line { transform-origin: 50% 0; }
/* A star or a ring is turned about its middle, and its middle is the point
   the percentages name, so it is lifted by half its own height. */
.mark--asterisk, .mark--ring {
  transform-origin: 50% 50%;
  margin-top: calc(var(--mark-size) * -0.5);
}
/* The double arrow spans between two things, so its middle is the point too.
   Its height is its length times its width, 1.9 as drawn, and a mark whose
   shaft has been let out says its new reach in --mark-len, so half its own
   height is always half of size times length. */
.mark--dline {
  transform-origin: 50% 50%;
  margin-top: calc(var(--mark-size) * var(--mark-len, 1.9) / -2);
  --mark-w0: 14;
}
/* Thickness is a field on the mark. Written by build.py and the editor as
   --mark-weight, in the drawing's own viewBox units; left out, each style
   keeps its drawn weight through the fallback. A CSS stroke-width beats the
   attribute the drawing carries, exactly as the chevrons' inks do. The
   family class carries the family's weight, and a style that is drawn
   lighter says so after it, so the later rule wins at equal specificity. */
.mark svg [stroke] { stroke-width: var(--mark-weight, var(--mark-w0, 17)); }
.mark--ring { --mark-w0: 7; }
.mark--arrow-i, .mark--dline-i { --mark-w0: 7; }
.mark--arrow-d, .mark--arrow-e { --mark-w0: 11; }
.mark--line-f { --mark-w0: 15; }
.mark--line-d, .mark--line-e { --mark-w0: 9; }
.mark--dline-d, .mark--dline-e { --mark-w0: 10; }
.mark--ring-i { --mark-w0: 3.5; }
.mark--ring-d { --mark-w0: 4.5; }
/* The dotted ring's weight IS the diameter of its dots, since each dot is a
   round cap on a dash of no length. Drawn at 6.5, which leaves the same
   again of daylight between them at the spacing the drawing sets. */
.mark--ring-o { --mark-w0: 6.5; }
/* A halo is the same gesture in translucent white underneath the ink, and
   it rides a fixed step wider than whatever the ink's weight is, so it
   follows the thickness slider. Written after the rule above so it wins. */
.mark svg .halo { stroke-width: calc(var(--mark-weight, var(--mark-w0, 17)) + 7); }
.mark--blue { color: var(--mark-blue); }
.mark--red { color: var(--mark-red); }
.mark--green { color: var(--mark-green); }
.mark--violet { color: var(--mark-violet); }
.mark--yellow { color: var(--mark-yellow); }
.mark--black { color: var(--mark-black); }
.mark--white { color: var(--mark-white); }

/* The layer the marks are drawn on. It is the picture's own rectangle, so a
   percentage in the data means a percentage of the picture, and it is out of
   the pointer's way so the marks never take a click from the slide. */
/* The marks lie on the PICTURE, not on the tray around it. The two are the
   same box when a slide is the 1280 by 1000 the tray is cut for, which is the
   normal case; when it is not, the picture is shown whole inside the tray with
   a little of the tray left over, and a mark placed at 40% of the picture has
   to stay at 40% of the picture and not slide off it. --paint-* is where the
   picture actually lands, measured in the reader's own browser by site.js.
   Untouched, the four fall back to the whole tray, which is what they are. */
.slide__marks {
  position: absolute; z-index: 3;
  left: var(--paint-x, 0px); top: var(--paint-y, 0px);
  width: var(--paint-w, 100%); height: var(--paint-h, 100%);
  display: block; pointer-events: none;
}
.slide__marks .mark { transition: opacity var(--ease-mark); }

/* Read, a slide starts clean: every mark waits, unseen, for its word in the
   caption to be clicked, and nothing but that word ever shows or hides one.
   site.js writes is-quiet on the body ONLY when the page is being read with
   scripts running: without scripts nothing could ever call a mark, and in
   edit mode the editor must see every mark to hold one, so neither is quiet
   and both show them all. */
body.is-quiet .slide__marks .mark { opacity: 0; }
body.is-quiet .slide__marks .mark.is-shown { opacity: 1; }

/* A word in the caption that names a mark. On the quiet page, which is the
   page being READ with scripts running, it rests in the caption's own ink,
   bold, and nothing else: no underline, no colour. Switched on, it takes the
   mark's colour, which arrives as --cue-tone: the named pens set it here,
   class by class, and a colour of the editor's own is written inline on the
   button. One word can name several marks: three arrows on three acini are
   one word, not three, and a word said twice in one caption is two buttons
   with one key that switch together.

   THE COLOUR AND THE MARK ARE ONE STATE. Wherever every mark is shown, every
   word is already in its mark's colour, and that is the same `is-quiet` the
   marks are hidden by, one class deciding both. Which settles two pages at
   once: the editor, who must see the caption finished rather than a row of
   grey words that only come right for a reader, and a reader with no
   scripts, who has no way to call a mark and so is shown all of them at
   once. Change a mark's pen in the editor and its word follows at once,
   because painting a mark repaints its caption. */
.cue {
  padding: 0; border: 0; background: none;
  font: inherit; font-weight: 700; color: inherit;
  transition: color var(--ease);
}
body:not(.is-quiet) .cue,
.cue.is-on { color: var(--cue-tone, var(--mark-blue)); }
.cue--red { --cue-tone: var(--mark-red); }
.cue--green { --cue-tone: var(--mark-green); }
.cue--violet { --cue-tone: var(--mark-violet); }
.cue--yellow { --cue-tone: var(--mark-yellow); }
.cue--black { --cue-tone: var(--mark-black); }
.cue--white { --cue-tone: var(--mark-white); }

/* Which slide of how many, under the tray. */
.slider__dots {
  display: flex; justify-content: center; align-items: center;
  gap: var(--pip-w); padding: 9px 0 2px;
}
.slider__dot {
  flex: none;                          /* a dot is round, never squashed */
  width: var(--pip-w); height: var(--pip-w); padding: 0;
  border-radius: 50%;
  background-color: var(--pip);
  transition: background-color var(--ease);
}
/* ONE dot wears the ink, and it is the slide being shown: nothing else may
   say it in black. Hover answers in a darker grey, not the ink, or the dot
   under an idle pointer would stand as a second black dot; and the browser's
   own focus ring, a black circle round a dot, is simply refused. The strip
   itself keeps its focus ring and its arrow keys, so nothing is lost to the
   keyboard. */
.slider__dot.is-on { background-color: var(--pip-on); }
.slider__dot:hover:not(.is-on) { background-color: var(--pip-mid); }
.slider__dot:focus { outline: none; }

/* The step to the next slide: a half disc standing beside the tray, level
   with the middle of it. His own design (.right-arrow-slide and
   .div-block-126 on the Webflow site): flat side toward the tray, the two
   outer corners rounded, the tray's own grey behind the arrow, a small plain
   shadow under it, and a little daylight (--step-gap) between the two, so
   neither can be read as lying on the other.

   Never a negative z-index here: it would put the button under the
   transparent body of its own .part, which then swallows every click, and a
   button nobody can press is not a button. Nothing here moves on hover,
   asked for in so many words; the shadow deepens instead. */
.slider__step {
  position: absolute; top: 50%; z-index: 3;
  display: flex; width: var(--step-w); height: var(--step-h);
  padding: 0; justify-content: center; align-items: center;
  border: 0;
  background-color: var(--plate);
  box-shadow: var(--shadow-step);
  color: var(--chevron);
  transform: translateY(-50%);
  transition: opacity var(--ease), visibility var(--ease),
              box-shadow var(--ease);
}
.slider__step:hover { box-shadow: var(--shadow-step-lift); }
.slider__step--prev {
  left: calc(-1 * var(--step-w) - var(--step-gap));
  border-radius: var(--step-r) 0 0 var(--step-r);
}
.slider__step--next {
  right: calc(-1 * var(--step-w) - var(--step-gap));
  border-radius: 0 var(--step-r) var(--step-r) 0;
}
/* Mirrored, never turned, as his own left and right arrow files are of each
   other: the navy stays at the top on both sides of the tray. */
.slider__step--prev .icon-chev { transform: scaleX(-1); }
/* His chevron stands about 30px tall in the 40px shape, which is this box:
   the ink is 84% of it. The box is wider than the bump and that is fine,
   an SVG box clips nothing here and the ink itself is 18px wide. */
.slider__step .icon-chev { width: 36px; height: 36px; }
/* A strip is a ring, and BOTH steps are always drawn: past the last slide is
   the first again, and back from the first is the last, button and arrow key
   alike. Only a strip of one draws neither, because a ring of one has
   nowhere to go. */
.slider[data-count="1"] .slider__step {
  opacity: 0; visibility: hidden; pointer-events: none;
}

/* ---------- The slides library ------------------------------------------- */

.library { width: var(--part-col); margin: calc(.034 * var(--page-h)) auto 0; }
/* The row is the full column, so the list it drops can be centred on the page;
   the BUTTON inside it is only as wide as the three things that make it up.
   Clicking the empty margin an inch away from an organ's name used to open its
   list, which reads as the page reacting to nothing at all. */
.library__row { position: relative; display: flex; justify-content: center; }
.library__organ {
  display: flex; width: max-content; max-width: 100%; padding: 5px 0;
  justify-content: center; align-items: center; gap: 14px;
  font-family: var(--font-display);
  font-size: var(--size-library); line-height: 1.6; font-weight: 700;
  color: var(--ink-title);
  transition: color var(--ease);
}
.library__organ:hover,
.library__row[data-open="true"] .library__organ { color: var(--hover); }
.library__chev { display: flex; flex: none; line-height: 0; }
/* The same arrow as the one beside Organs in the header, turning the same
   way for the same reason, and at the same size: the two are one kind of
   thing, a chevron standing beside a word, so they share --chev-word. */
.library__chev .icon-chev {
  width: var(--chev-word); height: var(--chev-word);
  transform: rotate(90deg);
  transition: transform var(--ease);
}
.library__row[data-open="true"] .library__chev .icon-chev { transform: rotate(-90deg); }

/* The list of an organ's slides is lifted off the page and hangs under the
   organ's own name, so the rows below it do not move when it opens. It appears
   and disappears, and does not grow into place. */
.library__drop {
  position: absolute; top: calc(100% + 6px); z-index: 5;
  left: 50%;
  /* No padding of its own: the panel's room lives in the rows, all of it,
     so the first band is exactly as tall as every other and a pointed-at
     row is lit edge to edge. */
  min-width: 210px;
  border: 1px solid var(--glass-edge); border-radius: var(--radius-sm);
  background-color: var(--glass-deep); background-image: var(--glass-sheen);
  /* Frosted, not clear: this one opens straight over the organ names it came
     out of, and they may not read through it. */
  backdrop-filter: blur(var(--glass-blur-deep)) saturate(1.7);
  -webkit-backdrop-filter: blur(var(--glass-blur-deep)) saturate(1.7);
  box-shadow: var(--shadow-glass-lift);
  transform: translateX(-50%);
  opacity: 0; visibility: hidden;
  transition: opacity var(--menu-open), visibility var(--menu-open);
}
.library__row[data-open="true"] .library__drop { opacity: 1; visibility: visible; }
.library__drop ul { margin: 0; padding: 0; list-style: none; }
/* A thin rule between one slide and the next, so a list of names reads as a
   list rather than as a paragraph. Between them, never above the first or
   below the last, where it would be a line across the glass. */
.library__drop li + li { border-top: 1px solid var(--hairline); }
.library__slide {
  display: block; padding: 8px 20px;
  font-family: var(--font-display); font-size: var(--size-toc); line-height: 1.6;
  color: var(--ink); text-align: center;
  transition: color var(--ease), background-color var(--ease);
}
.library__slide:hover { color: var(--hover); background-color: var(--tab-bg); }
/* The outer rows wear the panel's own corners, so their lit band follows
   the glass instead of poking square through a rounded edge. The radius is
   on the row itself: the panel never clips what it holds. */
.library__drop li:first-child .library__slide {
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
}
.library__drop li:last-child .library__slide {
  border-radius: 0 0 var(--radius-sm) var(--radius-sm);
}

/* ---------- Back to the top ---------------------------------------------- */

/* Its place is its own; its clothes are the pill's, and it no longer lifts
   under the pointer. */
.totop { margin: 4vh 2.6% 0 auto; }
.icon-top { display: block; width: 22px; height: 22px; }

/* ---------- The one shape a control has ---------------------------------- */

/* Every button the atlas offers a reader is this pill, by request: a hairline
   round a card, the muted ink, and under the pointer the answering violet
   with the glass edge and the glass shadow coming up behind it. Four of them
   wear it, and they are the four things a reader can DO on a page rather than
   read: take the words away, step back to the top, open the Organs menu, open
   the editor. Written once, here, and put on by name in build.py, because
   these are one KIND of thing and the atlas keeps one class per kind.

   NOTHING MOVES. That is the whole of the animation and it is deliberate: the
   step back to the top used to lift three pixels and the editor's mark used to
   swell by a tenth, so three controls answered the same pointer three
   different ways. The colour, the edge and the shadow change together over
   --ease and the button stays where it is.

   Each keeps only what belongs to WHERE it stands: its own margin, and its own
   type where it carries words. */
.pill {
  display: flex; width: max-content; padding: 9px 16px;
  gap: 9px; align-items: center;
  border: 1px solid var(--hairline); border-radius: var(--radius-pill);
  background-color: var(--card-bg);
  color: var(--pill-ink);
  transition: color var(--ease-fast), border-color var(--ease-fast),
              box-shadow var(--ease-fast);
}
/* One with a mark and no words is the same pill with the same air on every
   side of it, so it comes out round rather than stretched. */
.pill--mark { padding: 9px; gap: 0; line-height: 0; }
@media (hover: hover) {
  .pill:hover {
    color: var(--hover); border-color: var(--glass-edge);
    box-shadow: var(--shadow-glass);
  }
}
.pill:focus-visible { color: var(--hover); border-color: var(--glass-edge); }

/* The way a reader takes the page's words with them: an arrow coming down
   into a line, and the word for what it does beside it. It stands at the END
   of the article, under the last thing there is to read, because that is
   where a reader who has finished is; the step back to the top keeps its own
   corner above it. Written as a plain link to a file that is already on the
   server, so it works with no scripts at all. */
.getpdf {
  margin: 5vh auto 0;
  font-family: var(--font-body); font-size: var(--size-note-fact);
}
.icon-get { display: block; width: 17px; height: 17px; flex: none; }

/* The Organs button wears the pill too, and one number of it differs: the
   header is a 34px bar and the item is pinned to its middle rather than
   standing in the row, so it never made the bar taller. At the pill's own
   9px of air it hung three pixels out of the bar at each end and the rounded
   ends came off against the edges. The air comes down to what the bar holds;
   the hairline, the card, the radius, the muted ink, the timing and
   everything that happens under the pointer are the others' exactly. Its
   type stays the header's own, because it stands beside Sources and About
   and has to read as their equal. */
.navitem--organs .navitem__btn { padding: 4px 14px; }

/* ---------- The pages that are only words -------------------------------- */

/* Sources and About. The third page type, and the plainest: no hero, no
   pictures, just the atlas's own sheet with words on it. Everything here
   borrows from the organ page rather than inventing a second look, so a
   reader crossing from one to the other does not feel they have left: the
   same sheet at the same measure, the same Palatino at the same reading
   size, and section headings between the same two rules the organ page sets
   its own headings between. */
.prose { background-color: var(--ground); }
.prose__sheet {
  position: relative; z-index: calc(var(--z-special) + 1);
  width: var(--sheet-col); max-width: var(--sheet-max);
  margin: 0 auto;
  padding: calc(.06 * var(--page-h)) 0 calc(.03 * var(--page-h));
  background-color: var(--surface);
  box-shadow: var(--shadow-sheet);
}
/* A step above the section headings under it: at the same size the two read
   as one rank and the page has no top. */
.prose__title {
  width: var(--part-col); margin: 0 auto calc(.02 * var(--page-h));
  font-family: var(--font-display);
  font-size: calc(var(--size-part) * 1.45); line-height: 1.15; font-weight: 700;
  color: var(--ink-title); text-align: center;
}
/* The one line that says what the page is for, set a step up from the
   paragraphs under it so the eye starts there. */
.prose__lede {
  width: var(--part-col); margin: 0 auto calc(.05 * var(--page-h));
  font-family: var(--font-display);
  font-size: calc(var(--size-note) * 1.14); line-height: 1.5;
  color: var(--ink-heading); text-align: center;
}
.prose__part + .prose__part { margin-top: calc(.05 * var(--page-h)); }
.prose__head {
  width: var(--part-col); margin: 0 auto calc(.028 * var(--page-h));
  padding: 12px 0;
  border-top: 2px solid var(--rule); border-bottom: 2px solid var(--rule);
  font-family: var(--font-display);
  font-size: var(--size-part); line-height: 1.2; font-weight: 700;
  color: var(--ink-heading); text-align: center;
}
.prose__body {
  width: var(--part-col); margin: 0 auto;
  font-family: var(--font-display);
  font-size: var(--size-note); line-height: 1.6; color: var(--ink);
}
.prose__body + .prose__body { margin-top: .9em; }
.prose strong { font-weight: 700; color: var(--ink-title); }
/* A run of named things, a collection and what it is. The name leads in the
   heading's ink and its note follows on the same line, which keeps a list of
   sources readable as a list rather than as a wall of paragraphs. */
.prose__list {
  width: var(--part-col); margin: calc(.022 * var(--page-h)) auto 0;
  padding: 0; list-style: none;
  font-family: var(--font-display);
  font-size: var(--size-note); line-height: 1.55; color: var(--ink);
}
.prose__item + .prose__item { margin-top: .7em; }
.prose__name { font-weight: 700; color: var(--ink-title); }
.prose__name:not(:empty) + .prose__note::before { content: " "; }

@media (max-width: 767px) {
  /* His phone sheet: edge to edge, the words 8% in, exactly as an organ page
     reads there. */
  .prose__title, .prose__lede, .prose__head,
  .prose__body, .prose__list { width: 84%; }
}

/* ---------- The page that is not there yet ------------------------------- */

/* Deliberately plain. It is the one page a reader should leave quickly, so it
   says what happened, offers the way back, and gets out of the way. */
.gone {
  /* Tall enough to put the footer on the bottom edge, whatever the screen:
     the header is about 48px and the footer about 130px. */
  display: flex; min-height: calc(100vh - 178px);
  padding: 8vh 7%;
  justify-content: center; align-items: center;
  background-color: var(--surface);
}
.gone__box { max-width: 34em; text-align: center; }
.gone__title {
  margin: 0 0 14px;
  font-family: var(--font-display);
  font-size: clamp(28px, 4vw, 52px); line-height: 1.15; font-weight: 700;
  color: var(--ink-title);
}
.gone__body {
  margin: 0 0 26px;
  font-family: var(--font-display);
  font-size: clamp(15px, 1.3vw, 18px); line-height: 1.55; color: var(--ink);
}
.gone__back {
  display: inline-block;
  padding: 10px 22px; border-radius: var(--radius-xs);
  background: var(--accent); color: var(--on-accent);
  font-family: var(--font-display); font-size: 15px; font-weight: 700;
  transition: opacity var(--ease);
}
.gone__back:hover { opacity: .88; }

/* ---------- Footer, .footer --------------------------------------------- */

.footer {
  position: relative; z-index: var(--z-special);
  padding: 20px 0 24px;
  align-items: flex-start;
  background-color: var(--surface);
  box-shadow: var(--shadow-footer);
  text-align: center;
}
/* Same sentence as the header: the bar spans the window, its words line up
   with the page. Below the cap the padding is the same 4% it always was. */
.footer__inner {                                 /* .footer-container-div */
  display: flex; justify-content: space-between; align-items: flex-end;
  gap: 24px; max-width: var(--page); margin-inline: auto;
  padding: 0 calc(.04 * var(--page));
}
.footer__text { display: flex; flex-direction: column; }

/* The way in for whoever maintains the atlas. Quiet until you point at it. */
/* The way into the editor, in the corner of the footer. The pill's clothes,
   and it no longer swells under the pointer. */
.maintain { flex: none; }
.icon-tools { width: var(--size-tools); height: var(--size-tools); display: block; }
.footer__atlas {                                 /* .footer-text--atlas */
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--size-footer-atlas); line-height: 100%; font-weight: 700;
  color: var(--ink-strong); text-align: left;
}
.footer__name {                                  /* .footer-text--name */
  margin: 8px 0 0;
  font-family: var(--font-display);
  font-size: var(--size-footer-name); line-height: 100%; font-weight: 300;
  color: var(--ink-muted); text-align: left;
}
.footer__line {                                  /* .footer-text */
  margin: 6px 0 0;
  font-family: var(--font-display);
  font-size: var(--size-footer-line); line-height: 100%; font-weight: 300;
  color: var(--ink-muted); text-align: left;
}

/* ---------- The sign in dialog ------------------------------------------ */

.signin {
  width: min(420px, calc(100vw - 32px));
  padding: 0; border: 0; border-radius: var(--radius);
  background: var(--surface); color: var(--ink);
  box-shadow: var(--shadow-dialog);
}
.signin::backdrop { background: var(--scrim); backdrop-filter: blur(2px); }
.signin__box { position: relative; padding: 26px 26px 22px; }

/* The dialog says who it is for and hands over. It asks for nothing itself:
   the address and the code are Cloudflare's business, on its own page. */
.signin__title {
  margin: 0 0 10px;
  font-family: var(--font-display);
  font-size: 21px; font-weight: 700; line-height: 1.2;
  color: var(--ink-title);
}
.signin__body {
  margin: 0 0 18px;
  font-family: var(--font-display);
  font-size: 14.5px; line-height: 1.5; color: var(--ink);
}
/* Who the deployment says you are, written by site.js when the dialog
   opens: the address Access verified, or "Not signed in". */
.signin__status {
  margin: -8px 0 16px;
  font-family: var(--font-display);
  font-size: 13.5px; line-height: 1.5; color: var(--ink-muted);
}
.signin__actions { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
/* The dialog carries the reason at every width and shows it only where the
   ways in are gone, below 992px. In the page rather than written by a script,
   for the same reason the Organs menu leaves in CSS: a reader with no scripts
   would otherwise keep a button that opens nothing. */
.signin__narrow {
  display: none;
  margin: 0;
  font-family: var(--font-display); font-size: 14.5px; line-height: 1.5;
  color: var(--ink-muted);
}
.signin__go {
  padding: 9px 20px; border-radius: var(--radius-xs);
  background: var(--accent); color: var(--on-accent);
  font-family: var(--font-display); font-size: 15px; font-weight: 700;
  transition: opacity var(--ease);
}
.signin__go:hover { opacity: .88; }
.signin__alt {
  font-family: var(--font-display); font-size: 14px; color: var(--ink-muted);
  text-decoration: underline; text-underline-offset: 3px;
}
.signin__alt:hover { color: var(--hover); }
.signin__close {
  position: absolute; top: 10px; right: 12px;
  padding: 4px 8px;
  font-size: 22px; line-height: 1; color: var(--ink-muted);
  transition: color var(--ease);
}
.signin__close:hover { color: var(--ink); }

/* The sentence site.js writes when ?edit=1 is asked for below 992px. Built
   from tokens the page already has, so it needs no new colour and therefore
   no new line in either dark block: light and dark are a palette, and this
   borrows the palette whole. Above the header, which is --z-nav. */
.editnote {
  position: fixed; top: 0; left: 0; right: 0; z-index: calc(var(--z-nav) + 1);
  display: flex; align-items: center; justify-content: center;
  gap: 14px; flex-wrap: wrap;
  padding: 12px 16px;
  background: var(--surface); color: var(--ink);
  box-shadow: var(--shadow-dialog);
  font-family: var(--font-display); font-size: 14.5px; line-height: 1.45;
  text-align: center;
}
.editnote__go {
  padding: 7px 16px; border-radius: var(--radius-xs);
  background: var(--accent); color: var(--on-accent);
  font-family: var(--font-display); font-size: 14px; font-weight: 700;
  transition: opacity var(--ease);
}
.editnote__go:hover { opacity: .88; }

/* ---------- Responsive --------------------------------------------------
   Webflow's own breakpoints: medium 991, small 767, tiny 479. The overrides
   below are the ones the live site declares, no more.
   ------------------------------------------------------------------------ */

@media (max-width: 991px) {
  :root {
    --size-tissue: 3vw;
    --size-tab: 2vw;
    --size-systems: 7vw;
    --size-tip: 1.8vw;
    --tissue-row-h: 6.5vw;
    /* A tablet has no margin to spare. The sheet runs edge to edge and the
       column inside it fills the sheet; the white space that framed the words
       on a desktop becomes the padding on the sheet itself, which is a good
       deal less of it. Nothing is narrowed twice. */
    --sheet-col: 100%;
    --part-col: 100%;
    --toc-w: 260px;
  }
  /* The margin the words keep is the sheet's own padding now, and it is also
     where the two buttons that step a strip of slides stand. 6vw is what
     leaves room for both down to a 500px screen; below that they come inside
     the tray instead. */
  .sheet { padding-inline: 6vw; }
  /* The Organs menu is not offered here at all: not as a panel, not as a
     drawer, and not as a button that would open one. Twelve cards four to a
     row is a desktop shape, and every narrower shape tried for it, two cards
     to a row and then one column down the left edge, was a wall of thirty
     names with nothing to hold on to. A reader who has arrived on a tablet
     or a phone is READING an organ, not choosing between all of them, and
     the way to another one is the atlas's front page.

     The whole item goes, in one declaration, because the panel is inside the
     button's own box: hiding the panel and leaving the word and its arrow
     standing in the header would leave a button that opens nothing. This is
     Webflow's own tablet width, which is where the author asked for it and
     where every other tablet rule on the site already starts. */
  .navitem--organs { display: none; }
  /* And the rest of the row becomes his phone menu: the button appears, the
     links go, and the panel is there to be slid in. Exactly his width for it,
     `.menu-button` and `.nav-menu` both start at Webflow's medium. */
  .nav__links { display: none; }
  .burger { display: flex; }
  .navscrim { display: block; }
  .navpanel { display: flex; }
  /* And editing is not offered here either, for the reason the menu is not:
     the panel is a desktop shape and a mark is placed by dragging it. The
     maintenance mark in the footer goes first, so the dialog it opens is
     never reached at all; the two rules under it keep that dialog honest
     anyway, should it ever be opened another way: the ways in leave together,
     because the Sign in button beside them signs you in TO edit and there is
     nothing here to edit with, and the sentence saying so takes their place. */
  .maintain { display: none; }
  .signin__actions { display: none; }
  .signin__narrow { display: block; }
  /* And the header keeps his own margin at this width: 5%, which is what puts
     the wordmark and the button the same distance in from the two edges. */
  .nav { padding: 0 5%; }
  /* The list would lie across the page at this width, so it starts folded and
     its handle is there to open it. A reader who opens it keeps it open. */
  .toc:not([data-open="true"]) { transform: translate(-100%, -50%); }
  .toc:not([data-open="true"]) + .toc__handle { opacity: 1; visibility: visible; }
  .hero { flex-direction: column; justify-content: center; }          /* .div-block-185 */
  .hero__title { width: 100%; margin-right: 0; margin-bottom: 10%; background-color: transparent; }
  /* The panel is transparent from here down, so the title can sit in the flow
     and claim its own height instead of overlapping the card beneath it. */
  .display--general { position: static; transform: none; }
  .hero__list { width: 50%; max-width: none; }                        /* .container-15 */
  .veil--special { padding: 0 7%; flex-direction: column; justify-content: center; }
  .systems, .special__title { width: 100%; }
  .special__title { margin-bottom: 4%; order: -1; }
  .display--special { text-align: center; }
}

@media (max-width: 767px) {
  :root {
    --size-tissue: 3.6vw;
    --size-tab: 2.4vw;
    --tissue-row-h: 8vw;
    --step-w: 28px;
    --badge-w: 24px;
  }
  .hero__title { margin-bottom: 20%; }
  .hero__list { width: 60%; }
  .navitem__btn { padding: 8px 10px; font-size: 14px; }
  /* The table of content is not offered at all from here down, and that is
     his: `.flex-item-20._40-percent` is display:none at Webflow's small and
     again at tiny, so neither the list nor a handle for it is anywhere on a
     phone. A list of a page's own headings laid OVER the page, on a screen
     this narrow, is a second page in front of the first; here the reader
     scrolls the page instead, and the way back to the top is the arrow above
     the footer. */
  .toc, .toc__handle, .toc__rail { display: none; }
}

@media (max-width: 479px) {
  :root {
    --size-tissue: 4.6vw;
    --size-tab: 3vw;
    --size-systems: 9vw;
    --size-tip: 3vw;
    --tissue-row-h: 10vw;
    --disc-w: 11%;
    /* A phone has no room for a note cut to the longest credit: it is read
       at 76% of the picture here, as it already was. */
    --note-w: 76%;
    --library-gap: 3em;
    --toc-max: 56vh;
    /* His own phone sizes, read off the page class by class: .header-2 for a
       section's heading, .list-12 for the words under it, .paragraph-pic-1
       for the line under a picture. */
    --size-part: 25px;
    --size-note: 16px;
    --size-caption: 14px;
  }
  /* .main-texr at his small, which his tiny keeps: the margin the words hold
     on a phone is 8% of the screen, and the sheet itself runs edge to edge. */
  .sheet { padding-inline: 8%; }
  /* .header-2 at tiny: 25 over 41, no padding of its own, and 20px of air
     above it instead of the desktop's twelfth of a screen. */
  .part__title { padding: 0; line-height: 41px; }
  /* 50px of air before the next heading, measured off his page: his own is
     .header-2's 20px on top of the margin under whatever came before, and his
     column is a flex box, where those two add instead of collapsing. Ours
     collapse, so the gap is said once, here, as the sum he ends up with. */
  .part + .part { margin-top: 50px; }
  /* .imagebox1 at tiny: a tenth of the column under a strip of slides, which
     is what puts the air between a tray and the heading that follows it. */
  .slider { margin-bottom: 10%; }
  /* His phone list, measured off the page: the words start 11px inside the
     sheet's own margin and the dot hangs back across it, which is what a
     browser's own list does and what he kept. The desktop's deeper indent
     would spend a fifteenth of a 360px screen on white before a word. */
  .notes { padding-left: 0; }
  .notes__step--dot, .notes__step--square { padding-left: .7em; }
  .notes__step--dot .notes__head::before,
  .notes__step--square .notes__head::before { left: -.32em; }
  .notes__list { padding-left: 2.5em; }
  /* An organ name and its note, on one line, centred on a dot near the edge
     of a phone-sized diagram, reaches past the screen. It wraps here instead.
     Words are still never broken: max-content keeps a short name on one line
     and only a long one takes a second. */
  .hotspot__tip { white-space: normal; width: max-content; max-width: 27vw; }
  .veil--general { padding: 0 1%; background-image: var(--wash-general-tiny); }
  .hero { margin-bottom: 25%; justify-content: flex-start; align-items: center; }
  .hero__title { width: 100%; margin-bottom: 10%; }
  .hero__list { width: 80%; max-width: 100%; }
  .veil--special { padding: 0 5%; background-image: var(--wash-special-tiny); }
  .screen--special { background-size: cover; }
  .systems__rail { display: none; }
  .tabs { width: 100%; }
  .tabs__menu { width: 32%; max-width: 32%; }
  .tabs__content { width: 68%; }

  /* ---- His phone tray ---------------------------------------------------
     On a phone the tray stops being a window cut to 1280 by 1000 and becomes
     a COLUMN, which is his design and not a fallback: the picture at its own
     shape across the top, both its top corners rounded and a soft shadow all
     round it, the line under it IN THE FLOW rather than on a pane of glass
     over it, and a bar across the foot of the tray carrying the dots and the
     step to the next slide. His .div-block-127, .main-img-block, .comment-img
     and .slide-nav at Webflow's tiny, each read off the page.

     Nothing a mark is placed against changes. --paint-* is still measured off
     the picture wherever the picture lands, and the picture now lands at the
     top left of the slide at its full width, so the marks, the words that
     call them and the info note all follow it up the tray without knowing
     anything about any of this. */

  /* One grid cell, so the tray is as tall as the tallest slide and never
     changes height as you step through: with every picture at its own shape
     there is no ratio left to hold it to. */
  .slider__plate { padding: 0; background-color: var(--surface); }
  .slider__stage {
    display: grid; height: auto;
    padding: 1.5vw 1vw 0;                    /* the white round the picture */
  }
  .slide {
    position: relative; inset: auto; grid-area: 1 / 1;
    display: flex; flex-direction: column;
  }
  /* The picture's box is given the slide's own shape here, as a percentage
     padding measured against its own width, so the tray is its full height
     from the first paint and nothing moves when the picture lands. It is the
     same promise --slide-ratio makes for the tray on a wide screen, kept in
     the one place that is left to keep it. */
  .slide__pic {
    display: block; position: relative;
    width: 100%; height: 0; padding-bottom: var(--slide-ratio);
    border-radius: var(--radius-slide) var(--radius-slide) 0 0;
    box-shadow: var(--shadow-slide-phone);
  }
  .slide__img {
    position: absolute; inset: 0;
    border-radius: inherit;
  }
  /* The blurred fill lies across the whole slide, which down here is the
     picture AND the words under it, so it is not painted at all: a slide of
     another shape shows its own white beside itself instead. Every slide in
     the atlas is 1280 by 1000 and none of this is ever seen. */
  .slide.is-active.is-inset::before { content: none; }
  /* The caption comes off the glass and into the column under the picture. */
  .slide__caption {
    position: static; z-index: auto;
    /* It stands in the middle of what the picture leaves, so a short line on
       a strip whose tallest slide set the height is not left hanging at the
       top of a white field. Auto margins on the flex ITEM, never a flex box
       round the words: the caption's own content is a run of text with bold
       and its cues inside it, and a flex box would make a line of each. */
    flex: 0 0 auto; margin: auto 0;
    padding: 5px 5px;
    border: 0; border-radius: 0;
    background: none; box-shadow: none;
    backdrop-filter: none; -webkit-backdrop-filter: none;
    line-height: 1.2;
  }
  /* The bar across the foot: the tray's own grey, the full width of it, the
     two bottom corners rounded because the tray's are, and its own small
     shadow, which is what separates it from the white above. */
  .slider__dots {
    position: relative;
    height: var(--bar-h);
    /* The dots keep the middle of the bar and the two steps stand at its
       ends, so neither arrow can ever come down on a dot. */
    padding: 0 34px;
    border-radius: 0 0 var(--radius-tray) var(--radius-tray);
    background-color: var(--plate);
    box-shadow: var(--shadow-bar);
  }
  /* And the two steps stand at the two ends of that bar: his own drawing for
     them, a bare arrow with no plate under it and no shadow. He asked for
     BOTH here, which is also the atlas's own rule everywhere else: a strip is
     a ring, and the way back from the first slide is the last. */
  .slider__step {
    top: auto; bottom: 1px;
    width: 30px; height: var(--bar-h);
    border-radius: 18px;
    background: none; box-shadow: none;
    transform: none; clip-path: none;
  }
  .slider__step:hover { box-shadow: none; }
  .slider__step--prev { left: 2px; right: auto; }
  .slider__step--next { right: 2px; left: auto; }
  .slider__step .icon-chev { width: 18px; height: 18px; }
}

/* Below about 500px the sheet's own margin is narrower than the button and
   its bit of daylight, so there is nowhere outside the tray for it to
   stand: it comes inside, over the edge of the picture. The flat side sits
   on the tray's edge and the bump swings inward, so it is a tab on that
   edge and not a shape floating on the photograph; only here is the shadow
   clipped at the flat side, so it cannot draw a dark line along the seam it
   now shares with the tray. Its own breakpoint, because this is arithmetic
   between two numbers and not a layout the design changes its mind about.

   It stops at 480, where the tray becomes his phone tray and the step is not
   a half disc at all any more: a bare arrow at the right end of the bar of
   dots, with no step back beside it. */
@media (min-width: 480px) and (max-width: 560px) {
  .slider__step--prev {
    left: 0;
    border-radius: 0 var(--step-r) var(--step-r) 0;
    clip-path: inset(-14px -14px -14px 0);
  }
  .slider__step--next {
    right: 0;
    border-radius: var(--step-r) 0 0 var(--step-r);
    clip-path: inset(-14px 0 -14px -14px);
  }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important; animation-iteration-count: 1 !important;
    /* A duration of nothing does not cancel a DELAY, and the drawer's
       visibility is delayed by a whole slide on the way out. There is no
       other transition-delay in this file, so this changes nothing else. */
    transition-duration: .001ms !important; transition-delay: 0s !important;
    scroll-behavior: auto !important;
  }
}
