/* ---- src/styles/tokens.css ---- */
/* Square9 design tokens. The only file allowed to hold raw values (px, hex, numbers for the photo filter).
   Everything else uses these names. tools/lint.js fails the build if a raw px, hex, rgb() or hsl() value appears in
   any other CSS file (the one breakpoint, 900px, is the only exception: a media query cannot read a custom property).
   tools/check.js reads this file to know the type scale, the spacing scale and the allowed colours, and measures the
   built pages against them. The reference page (/system/) is drawn from it, comments included.

   Source of every value: round 8 (~/steve-joabs/sandbox/square9-site-1023/make8.py), approved by Antony on 23 Oct
   2026 (topic 625 msg 4510). Where the system moved a round 8 value onto a scale, the comment says so.

   Phone first: the values in :root are for phones; the block at the bottom changes the few that differ from 900px.
   Read top to bottom: palette, colour roles, type, space, layout, edges, imagery, icons, motion. */

:root {
  /* ---------------------------------------------------------------- palette (16 colours, nothing else) */
  --ink: #121317;            /* text and every outline */
  --black: #16171C;          /* the hero and the footer */
  --white: #FBFAF7;          /* the page */
  --ink-2: #45464C;          /* secondary text on white */
  --ink-3: #6E6D69;          /* labels and meta on white */
  --rule: #E4E0D8;           /* hairlines on white */
  --on-black: #D2CEC6;       /* secondary text on black */
  --rule-on-black: #3F4043;  /* hairlines on black: round 8's white at 18% over black, made solid */
  --green: #6FB07D;          /* the lead colour: asterisk, kicker and main button on black (msgs 4485, 4492) */
  --green-deep: #367245;     /* green text on white */
  --ochre: #DCAB53;          /* section and disc colour */
  --ochre-deep: #855A00;     /* ochre text on white */
  --sky: #70B6D7;           /* section and disc colour (msgs 4551, 4552) */
  --sky-deep: #276A8A;      /* sky text on white */
  --red: #D3646B;           /* section and disc colour (msg 4541) */
  --red-deep: #B7343C;      /* red text on white */
  /* each colour 8% of the way to ink: the big asterisk on a colour band (msg 4904). Ink text on them stays AA:
     green 6.3, ochre 7.6, sky 7.2, red 4.54 to 1 */
  --green-shade: #68A375;
  --ochre-shade: #CC9F4E;
  --sky-shade: #68A9C8;
  --red-shade: #C45E64;

  /* ---------------------------------------------------------------- colour roles */
  --lead: var(--green);      /* the one lead colour */
  /* Each surface (styles/surfaces.css) sets the roles below for what sits on it. These are the white page's. */
  --fg: var(--ink);          /* text */
  --fg-2: var(--ink-2);      /* secondary text: intros, card text */
  --fg-3: var(--ink-3);      /* meta: the stack line */
  --kicker: var(--ink-3);    /* kicker labels */
  --line: var(--rule);       /* hairline rules between rows */
  --outline: var(--ink);     /* outlines and card top rules */
  --ring: var(--ink);        /* the focus ring */

  /* ---------------------------------------------------------------- type */
  --font-display: Tanker, Impact, sans-serif;                 /* uppercase display, buttons; Fontshare's CSS (layout.js) */
  --font-text: "General Sans", system-ui, sans-serif;         /* everything else; Fontshare's CSS (layout.js) */
  --w-text: 500;
  --w-strong: 600;
  --w-display: 400;

  /* The scale: 10 steps, nothing else. Four are fluid (min, viewport share, max); each is still one token. */
  --fs-label: 13px;          /* kickers (capitals, tracked) */
  --fs-small: 15px;          /* meta: the stack line, the proof line, the footer's legal line; the footer's links on a phone (round 8: 15 and 16) */
  --fs-body: 17px;           /* all running text, and the footer's links from 900px (round 8: 16, 17 and 18) */
  --fs-button: 19px;         /* button labels: 17px until 26 Oct (msg 4901: a tiny bit bigger); the header's menu since 27 Oct (msg 5168) */
  --fs-lead: 20px;           /* intro paragraphs (round 8: 19, 20 and 21) */
  --fs-title: 30px;          /* card titles (round 8: 28, 30 and 32) */
  --fs-title-l: 34px;        /* work tile titles from 900px */
  --fs-quote: clamp(40px, 4.6vw, 68px);       /* the quote */
  --fs-heading: clamp(44px, 5.4vw, 76px);     /* section headings */
  --fs-statement: clamp(48px, 6.4vw, 96px);   /* the band and the contact line (round 8 contact: 52px, 7vw, 104px) */
  --fs-hero: clamp(60px, 7.4vw, 112px);       /* the hero line, once a page */

  --lh-display: 0.92;        /* Tanker headings */
  --lh-quote: 1;             /* the quote: long display text needs a little air */
  --lh-button: 1;
  --lh-label: 1.2;
  --lh-body: 1.6;
  --lh-lead: 1.5;            /* round 8's hero line; its section intros were 1.6 */

  --ls-display: 0.01em;
  --ls-button: 0.08em;
  --ls-label: 0.14em;

  --measure-hero: 28em;      /* the hero paragraph */
  --measure-lead: 30em;      /* section intros */
  --measure-quote: 18em;     /* the quote */
  --measure-statement: 12em; /* the contact line */
  --measure-text: 36em;      /* running text: case studies, prose, the options (about 70 characters a line) */

  /* type roles: weight, size / line height, family. Components use these, never the steps directly. */
  --type-label: var(--w-strong) var(--fs-label) / var(--lh-label) var(--font-text);
  --type-small: var(--w-text) var(--fs-small) / var(--lh-body) var(--font-text);
  --type-small-strong: var(--w-strong) var(--fs-small) / var(--lh-body) var(--font-text);
  --type-body: var(--w-text) var(--fs-body) / var(--lh-body) var(--font-text);
  --type-body-strong: var(--w-strong) var(--fs-body) / var(--lh-body) var(--font-text);
  --type-lead: var(--w-text) var(--fs-lead) / var(--lh-lead) var(--font-text);
  --type-choice: var(--w-strong) var(--fs-lead) / var(--lh-lead) var(--font-text);   /* the Get in touch form's choices and its fields' names: the intro size, semibold (msg 5200) */
  --type-button: var(--w-display) var(--fs-button) / var(--lh-button) var(--font-display);
  --type-menu: var(--w-display) var(--fs-small) / var(--lh-button) var(--font-display);   /* the footer's links and its email, in capitals like the buttons (msg 5098): 15px on a phone, 17px from 900px. The header's menu used it until 27 Oct; it is at the buttons' --type-button since (msg 5168) */
  --type-title: var(--w-display) var(--fs-title) / var(--lh-display) var(--font-display);
  --type-tile: var(--w-display) var(--fs-title) / var(--lh-display) var(--font-display);  /* 34px from 900px */
  --type-quote: var(--w-display) var(--fs-quote) / var(--lh-quote) var(--font-display);
  --type-heading: var(--w-display) var(--fs-heading) / var(--lh-display) var(--font-display);
  --type-chapter: var(--w-display) var(--fs-title) / var(--lh-display) var(--font-display);   /* a chapter's name: the title
                                size, 30px, on a phone; the quote size from 900px (40px to 68px). Smaller than a section
                                heading since 29 Oct (topic 625 msg 5573: "each of the headers needs to be smaller"); the
                                heading size until then */
  --type-statement: var(--w-display) var(--fs-statement) / var(--lh-display) var(--font-display);
  --type-hero: var(--w-display) var(--fs-hero) / var(--lh-display) var(--font-display);
  --type-code: var(--w-text) var(--fs-label) / var(--lh-body) ui-monospace, Menlo, monospace;   /* the reference page only */

  /* ---------------------------------------------------------------- space: the only gaps allowed */
  --s1: 4px;
  --s2: 8px;
  --s3: 12px;
  --s4: 16px;
  --s5: 24px;
  --s6: 32px;
  --s7: 48px;
  --s8: 64px;
  --s9: 96px;
  --s10: 128px;

  /* space roles */
  --gutter: var(--s5);       /* page edge to content; 32px from 900px */
  --sec-pad: var(--s7);      /* top and bottom of every section: 48px, 64px from 900px (64px and 96px until 29 Oct, a step
                                less again: topic 625 msg 5573, "a little bit less space at the top and bottom of each of
                                the other sections"; 96px and 128px until 26 Oct, msg 4772) */
  --head-gap: var(--sec-pad);   /* a section head to what follows it: the same as the space above it, so a heading sits
                                with as much room below it as above (msg 5573: "the spacing in each of those sections above
                                the header to be the same as the spacing below the header"). Measured to the letters: the
                                head's words are trimmed to their capitals and baseline (text-box, section.css), so the gap
                                set is the gap seen. 32px and 64px until 29 Oct */
  --gap-split: var(--s8);    /* between the two halves of a two-column layout */
  --gap-cards: var(--s7);    /* between cards three or four across (round 8's How I work used 64px) */
  --chapter-pad: var(--s4);  /* above and below a chapter's name (the slim colour band that names a group: AUTOMATION),
                                to its letters: 16px, 24px from 900px. The same above as below, as every heading (msg 5573) */

  /* ---------------------------------------------------------------- layout */
  --wrap-max: 1248px;        /* the content column with its gutters: 1184px of content */
  --cols-hero: 1.1fr 1fr;    /* hero words, photo */
  --cols-head: 1fr 1.4fr;    /* section heading, intro */
  --cols-band: 1.6fr 1fr;    /* band heading, line */
  --case-row: 780px;         /* a case study's three parts stand side by side once its words' columns are this wide
                                (from about 1110px: 3 columns of 240px or more); under it, one under the other */
  /* the one breakpoint: 900px (round 8 had 900 and 1100; 1100 only moved Things I've built from 2 to 4 across) */

  /* ---------------------------------------------------------------- edges */
  --edge: 3px;               /* the ink outline: photos, the photo frame, buttons, outline icons, the round photo (the credit: --credit-line) */
  --hair: 1px;               /* hairline rules between rows */
  --rule-bold: 2px;          /* the rule over a card (round 8: 1.5px) */
  --underline: 2px;          /* text links */
  --underline-l: 3px;        /* a card title's underline on hover */
  --underline-offset: 6px;
  --radius: 12px;            /* every box corner: photos, frame, buttons, the credit stamp (round 8: 10, 12 and 14) */
  --radius-round: 50%;       /* discs and the round photo */
  --radius-focus: 4px;       /* the focus ring round a text link */
  --stamp: 6px;              /* the offset block behind a button, and how far it presses (round 8: 5px; the credit stamp's 6px) */
  --focus-w: 3px;
  --focus-offset: 4px;
  --focus-offset-stamp: 8px; /* clears the 6px block behind a button */

  /* ---------------------------------------------------------------- controls */
  --tap: 44px;               /* the smallest tap target */
  --radio: 24px;             /* the ring in a Get in touch choice that fills when it is picked, so a pick shows by more
                                than its colour (topic 625 msg 5200) */
  --radio-pip: 10px;         /* the ink pip that fills it */
  --nav-h: 64px;             /* the banner: the logo and the menu button on a phone (76px until 28 Oct, when it held the Get in
                                touch button too; topic 625 msg 5384: "less real estate on mobile"); the menu row, 96px, from 900px */
  --nav-line: var(--rule-bold);   /* the slim line under the banner, on every page at every width (msg 5384: "a slim dividing
                                line underneath it"): 2px of --nav-line-colour */
  --nav-line-colour: var(--white);   /* the line is white: it divides the black banner from what comes next, so it is measured
                                against the banner, where it stands at 17.15:1 (a graphic needs 3:1). Any of the four colours
                                would do on black, but each one also paints the pictures under the line, and they move
                                (rule 15); white is the banner's own words' colour, and stays put */
  --menu-type: var(--w-display) var(--fs-title) / var(--lh-button) var(--font-display);   /* the open menu's links on a phone:
                                Tanker capitals at the card titles' 30px, a phone menu's big targets (rule 5) */
  --menu-row: var(--s3);     /* above and below each open menu link: 30px words and 12px a side make a 54px row */
  --menu-pad: var(--s3);     /* the open menu's own padding, the same above its first row and below its last, so the
                                banner's line sits as far above WORK's letters as the menu's closing line sits below GET IN
                                TOUCH's (topic 625 msg 5573; 0 above and 24px below until 29 Oct, 15px and 40px to the
                                letters). With --menu-row: 24px and a half to the letters at each end */
  --menu-bars: var(--s5);    /* the menu button's three lines: 24px long, --edge (3px) thick, --s1 (4px) apart */
  --logo-h: 28px;            /* the logo in the menu and the footer; 36px from 900px (24px and 30px until 26 Oct, msg 4902: a little bigger) */
  --credit-face-h: 51px;     /* the credit stamp: a button's height (16 + 19 + 16), fixed on every site (topic 149 msg 6271) */
  --credit-text: 19.52px;    /* the credit stamp's label: Tanker at 19px with the buttons' 0.08em tracking (14.25px
                                capitals), drawn as an outline whose box is the logo's proportion (msg 6489) */
  --credit-logo: 26px;       /* the credit stamp's logo, tile and wordmark as drawn (msgs 6510 and 6513): its height,
                                bigger than the label (msg 6517, 4 Nov: "the logo needs to be bigger whilst keeping the
                                button the same"), its capitals 18.98px; this one number resizes it, and it stays centred
                                on the label's capitals */
  --credit-lift: 1.07px;     /* the credit stamp: moves its label up so the capitals sit on a button's, 17.75px to 32px down */
  --credit-logo-lift: calc(var(--credit-lift) + (var(--credit-logo) - var(--credit-text)) * 0.023);   /* the credit stamp:
                                moves its logo up so its capitals' middle is the label's, 24.88px down. Both drawings have
                                their capitals from 0.158 to 0.888 of their height, so their middle is 0.023 of the height
                                below the drawing's own middle, where flex centring puts it (1.22px at 26px) */
  --credit-line: var(--rule-bold);   /* the credit stamp: the thin ink line inside its white face (msg 6488), 2px */
  --credit-space: var(--s6); /* the credit stamp: clear space above its face and below its block, the same on every site
                                (topic 625 msg 6546, 4 Nov: "standardise spacing above and below the button so that it
                                sits perfectly"). Above, it counts from the host footer's last ink, so a host takes off any
                                room its footer leaves under that (padding, a link's tap area or line box) */

  /* ---------------------------------------------------------------- imagery */
  --portrait-w: min(74%, 352px);        /* the hero photo; min(470px, 34vw) from 900px. Capped at 352px so the
                                           asterisk (18% of it above the photo, --ast-top) never reaches the button row
                                           above: 0.18 x 352 = 63px, inside the 48px + 32px of space (64px + 32px until
                                           29 Oct; round 8: 74%, uncapped) */
  --ast-size: 80%;                      /* the asterisk, as a share of the photo's width (round 8 phone: 72%) */
  --ast-top: -18%;                      /* how far it comes out past the photo's top (27% until 26 Oct; moved down, msg 4773) */
  --band-ast: clamp(420px, 70vw, 960px);   /* the big asterisk on a colour band, its centre on the band's top right corner (msg 4904) */
  --band-ast-shift: 30%;                    /* how far it is moved out past the corner, as a share of its width: its centre sits inside the band, so the hub shows */
  --ast-right: -21%;                    /* how far it comes out past the photo's right edge (27% until 26 Oct, msg 4773) */
  --ast-clear: var(--s6);               /* the least room between the circle the hero asterisk's points sweep as it turns
                                           and any button, link or line of text: 32px at every width (topic 625 msg 5609:
                                           "the asterisk behind the photo touches the green background of the button ...
                                           as it rotates by"; until 29 Oct its points crossed the Get in touch button's
                                           block at 1440). The circle is measured from the asterisk's centre to its
                                           farthest point (51.3% of its width), not to its box's side */
  --about-photo-w: 200px;               /* the square photo in About me, where it is kept (the alternative to the drawing); 280px from 900px (msg 4880: smaller than the hero's) */
  --about-draw-w: min(100%, 320px);     /* the drawing in About me, bare on its band: under the text on a phone; 360px from 900px, under the heading */
  --tile-img: 104px;                    /* the work tile's square; clamp(150px, 14vw, 208px) from 900px */
  --case-img: 240px;                    /* a case study's drawing (Work, Free tools), or a product's tile (Products, 3 Nov,
                                           msgs 6128, 6137 and 6139: the same size as the tools' drawings): 240px square on a phone, beside the
                                           words at clamp(160px, 15vw, 224px) from 900px (topic 625 msg 5573: "all the
                                           animations need to be a bit ... smaller", so a piece's title and words show
                                           on a phone's first screen). The column's width (342px at 390) and a quarter of
                                           the piece (272px at 1440) until 29 Oct */
  --intro-photo: 240px;                 /* Antony's photo in Process's intro (components/intro.js, 3 Nov 2026, topic 625
                                           msg 6150), above his words on a phone: a case study's picture's size there, so
                                           the photo and the start of his words share a 390 by 844 phone's first screen.
                                           From 900px it fills the first of the steps' three columns instead (intro.css):
                                           247px at 900, 363px at 1440 */
  --avatar: 56px;                       /* the round photo in the quote */
  /* the brand filter (src/lib/filters.js reads these): grey, contrast, then four stops from ink to near-white */
  --filter-slope: 1.25;                 /* contrast boost (msg 4506) */
  --filter-intercept: -0.1;
  --filter-shade: 0.6;                  /* stop 2: ink mixed 60% toward the colour */
  --filter-tint: 0.32;                  /* stop 3: the colour mixed 32% toward white */
  --filter-light: 0.94;                 /* stop 4: the colour mixed 94% toward white */
  --filter-mono: 0.5;                   /* the neutral filter's base: halfway from ink to white */

  /* ---------------------------------------------------------------- icons */
  --disc: 64px;              /* the colour disc */
  --strip-logo-h: 24px;      /* a company logo in the logo strip; 32px from 900px (it shared --logo-h's name until 26 Oct, so the strip drew at 30px) */
  --disc-sm: 48px;           /* the small disc (free tools) */
  --icon: 32px;              /* the icon in a disc: half the disc (round 8: 30px); the footer's email icon (msg 5242: "bigger"; --icon-sm from msg 5201) */
  --icon-sm: 24px;           /* half the small disc (round 8: 22px) */
  --step-disc: var(--disc-sm);   /* a numbered step's disc (components/steps.js, Process): the small disc, 48px; the disc,
                                64px, from 900px. Its number is in Tanker at the title size */
  --mark: 27px;              /* another company's mark beside a --icon icon (the footer's LinkedIn [in] bug, msgs 5201 and 5242): the width a 32px Phosphor icon draws (27 of its 32), so the two read the same size (20px beside the 24px icon until msg 5242) */
  --dot: 10px;               /* the status label's dot: filled when the work is in use, an outline when it is not */

  /* ---------------------------------------------------------------- drawings (the work tiles' pictures: rule 8) */
  /* Each drawing is 200 units square and scales with its frame, so these are in drawing units: at 200px wide (a work
     tile from 1440) one unit is one pixel, and the line and the shadow match the 3px edge and the 6px stamp. */
  --draw-line: 3;            /* every outline and line in a drawing (a spot has its own, --spot-line) */
  --draw-drop: 3%;           /* the hard shadow under a drawn object: 3% of the drawing (6 units), down and right, no blur */
  /* a drawing is the size its place gives it: a work tile's square (--tile-img), or About me's (--about-draw-w) */

  /* ---------------------------------------------------------------- spots (the round drawings beside items: rule 9) */
  /* A spot is drawn on a 112-unit square (src/components/spot.js) and shown at --spot, so at full size a unit is a
     pixel. Since 26 Oct (msg 4985: "too busy... clunky") a spot is one object, small in its circle, in a lighter line;
     since 27 Oct (msgs 5094, 5121) every spot moves through the whole loop at a methodical pace, and sits optically
     centred. */
  --spot: 80px;              /* the stamp or colour disc beside its words; 112px from 900px (Antony, topic 625 msg 4947:
                                "a bit bigger" than the 64px and 96px discs it replaced) */
  --spot-line: 2.5;          /* every line in a spot, in its units: 2.5px at full size, 1.8px on a phone (the tiles'
                                --draw-line, 3, until 26 Oct: on a 112-unit circle that was twice the tiles' weight) */
  --spot-field: 64%;         /* the centred square a spot's drawing stays inside, line included, as a share of its circle
                                (72 of 112 units), so the circle frames the object and never crops it (until 26 Oct the
                                drawings ran to the edge: 58% to 94% of it) */
  --spot-pace: 30;           /* the fastest any point of a spot may move, in its units a second (30px a second at full size).
                                4 on the morning of 27 Oct (msg 5094: "slow and consistent"); 30 since msg 5121 asked for
                                strokes drawn by a pen and a bar that fills: a pen's tip is the fastest point, about 25
                                units a second over a 45-unit stroke in 1.75 seconds. Parts that move whole stay under 6 */
  --spot-rest: 2s;           /* the longest a spot may stand still in its loop: 1s on the morning of 27 Oct, 2s since msg
                                5121, so a drawn cross or tick can hold for a beat (1 to 1.5s) before it clears. The relay
                                before 27 Oct rested 10.5s of 12, then moved */
  --spot-centre: 1;          /* how far a spot's optical centre may sit from its circle's centre at rest, in its units:
                                under half its line, so no eye can see it, and above the measuring's own error (a fifth
                                of a unit) */

  /* ---------------------------------------------------------------- page headers (components/phead.js) */
  /* The top band of Work, Process, Free tools and Products (topic 625 msgs 5236, 5263, 5274, 5619): a black picture the
     build draws (src/components/headers/), lights of colour moving through it, round a white title box. It is drawn once
     for each size, for the widest screen of that size, and centred, so a narrower screen shows its middle and nothing is
     ever stretched. Every length here is on the 8px grid, and the constructions take their own numbers from these. */
  --phead-art-w: 960px;      /* the construction's width: wider than every screen under 900px; 2624px from 900px, wider
                                than a 2560px screen, with 1024px either side of the clear zone (2592px until 28 Oct, when
                                the zone came down to 576px wide). A construction's own sides are edges it builds to, so a
                                screen wider still sees it end cleanly */
  --phead-h: 384px;          /* its height: 144px of picture, the clear zone with the box in it (96px), 144px of picture;
                                320px from 900px, 96px of picture either side of a 128px zone (480px until 28 Oct, msg 5426:
                                "about 2 thirds the depth of it currently is on desktop"). On a phone it is the tallest the
                                frame grows to (--phead-frame), on a tablet held upright */
  --phead-frame: clamp(var(--phead-frame-min), calc((var(--screen-half) - var(--nav-h) - 2 * var(--nav-line)) * 3 / 5), var(--phead-h));
                             /* the frame's height on a phone: three fifths of what is left of half the screen once the
                                banner and the two slim lines are counted (212px on a 390 by 844 phone), so the banner, the
                                lines and the picture take about a third of the screen and a piece of work, a tool or a
                                product starts on the first screen (topic 625 msg 5573: "reduce the size a little bit of
                                the header"). Two thirds until 29 Oct (msg 5426), half the screen since msg 5384. Never
                                taller than the construction, never shorter than --phead-frame-min. The construction stays
                                centred in it, so a shorter frame crops it top and bottom as a narrower screen crops its
                                sides. From 900px it is 288px, the middle of the construction's 320px (320px until 29 Oct) */
  --phead-frame-min: 128px;  /* the shortest frame on a phone: the clear zone (96px) and 16px of picture above and below.
                                Only a phone on its side is that short (390px tall: the banner, its line and the frame are
                                half its height); 192px until 28 Oct, which took two thirds of it */
  --screen-half: 50vh;       /* half the screen: 50svh where the browser knows the small viewport (below), so the
                                browser's own bars never count */
  --phead-box-w: 192px;      /* the title box, centred on the picture at every width (msg 5426: "I want it to be always
                                centred, kind of like it is a desktop size even when it's a mobile size"), holding the title
                                only (msg 5619): 192px on a phone, RECENT WORK at 30px (146px) and 16px a side inside the 3px
                                edge; 224px from 900px, RECENT WORK at 34px (166px) and 24px a side. Until 29 Oct it was
                                160px on a phone and 512px from 900px, where it held a one-line summary under the title;
                                it spanned the content column on a phone until 28 Oct */
  --phead-box-h: 64px;       /* the title box's height at both sizes: the title (30px on a phone, 34px from 900px) inside
                                12px over and under and the 3px edge. 128px from 900px until 29 Oct, with the summary (224px
                                until 28 Oct, the title at the hero size) */
  --phead-title: var(--type-title);   /* the title on a phone: Tanker capitals at 30px, the card titles' size; 34px from
                                900px, the work tiles' titles' size (--type-tile) */
  --phead-pad: var(--s3) var(--s4);   /* inside the title box: 12px over and under, 16px a side on a phone; 24px a side
                                from 900px */
  --phead-clear: var(--s4);  /* the clear zone: black all round the box, where no colour ever shows, so the box sits in a
                                black frame on the picture: 16px on a phone (a 224 by 96px zone), 32px from 900px (288 by
                                128px; 576 by 192px until 29 Oct, round the bigger box) */
  /* under the frame: the banner's slim line again (--nav-line of --nav-line-colour), the header band's foot, so the
     picture sits between two lines of one weight and never touches the band after the header (rule 4; topic 625 msg
     5573: "there's just a thin divider white line below the design as well as above it"). 8px of black until 29 Oct;
     24px above and below on a phone, 32px from 900px, until 28 Oct */

  /* ---------------------------------------------------------------- motion */
  --t-press: 120ms;          /* the button press */
  --t-turn: 60s;             /* one turn of the asterisk */
  --t-logos: 40s;            /* the logo strip scrolls one full row */
  --t-draw: 12s;             /* one full cycle of every drawing; its parts move on halves, thirds, quarters, sixths and twelfths of it */
  --t-hue: 240ms;            /* a menu link's or the logo's colour change on hover and focus, both ways: two button presses
                                long, so it reads as soft (msg 5169: "nice and softly"). It was --t-grow until 27 Oct, when the
                                grow (--grow, 1.08) went (msg 5232: "I don't really like the expansion of the text") */
  --ease-soft: cubic-bezier(0.2, 0.8, 0.2, 1);   /* fast out of the start, settling slowly into the end: the colour change's ease */
  /* The page headers' loops (rule 15, components/headers/turn.js; topic 625 msgs 5619 and 5621: colour "you need to be
     able to follow", "it hypnotises you, but you want to watch it again and again"). Each picture's lights move on one
     clock, at one steady speed, and come back to where they started once a loop; the four colours follow each other a
     quarter of a loop apart, so any point of a picture lights up once a quarter loop at most (3s or more), far inside
     WCAG 2.3.1's three flashes a second. Each is the one number to change for its picture's speed. Until 29 Oct the
     colours flipped from shape to shape 3 times a second (--t-colours-rate) */
  --t-dazzle: 16s;           /* Work: the trains of colour run the ribbon one round of the four colours, a new colour
                                every 4s and a leg every 2.7s, at about 70px a second along the ribbon's middle from
                                900px (76px until 30 Oct: rounding the turns made the path shorter, and a leg still
                                takes 2.7s) */
  --t-stella: 12s;           /* Process: the waves of colour sweep in to the title, a new colour every 3s, at 64px a
                                second from 900px and 32px on a phone */
  --t-tunnel: 16s;           /* Free tools: the rings of colour recede into the tunnel, a new one every 4s, quick at the
                                tunnel's mouth (about 125px a second from 900px) and slowing as they go deep */
  --t-burst: 15s;            /* Products: the burst turns a quarter turn, so it turns once a minute, with the asterisk
                                (--t-turn), a new colour past any point every 3.75s */
}
@supports (height: 1svh) { :root { --screen-half: 50svh; } }

/* the one breakpoint: 900px and up */
@media (min-width: 900px) {
  :root {
    --gutter: var(--s6);
    --sec-pad: var(--s8);
    --chapter-pad: var(--s5);
    --type-chapter: var(--w-display) var(--fs-quote) / var(--lh-display) var(--font-display);
    --case-img: clamp(160px, 15vw, 224px);
    --spot: 112px;
    --strip-logo-h: 32px;
    --nav-h: 96px;
    --logo-h: 36px;
    --step-disc: var(--disc);
    --portrait-w: min(470px, 34vw);
    --about-photo-w: 280px;
    --about-draw-w: 360px;
    --tile-img: clamp(150px, 14vw, 208px);
    --type-tile: var(--w-display) var(--fs-title-l) / var(--lh-display) var(--font-display);
    --type-menu: var(--w-display) var(--fs-body) / var(--lh-button) var(--font-display);
    --phead-art-w: 2624px;
    --phead-h: 320px;
    --phead-frame: 288px;
    --phead-box-w: 224px;
    --phead-title: var(--type-tile);
    --phead-pad: var(--s3) var(--s5);
    --phead-clear: var(--s6);
  }
}

/* The homepage from 900px: a step looser than the other pages (topic 625 msg 5609, 29 Oct 2026: "in desktop view, I
   think it needs to be a tiny bit looser in terms of the elements, a tiny bit more spaced out"). Each space role it uses
   takes the next step of the scale, as each gap down the page between its parts does in its components' own files
   (hero, logos, items, work, quote, button and form): a band's padding and the room under a heading 96px (64px on the
   other pages). The gaps across stay as they are: they set how long the lines are, and wider ones would break the
   outcomes' lines into three at 1440 (Antony, msg 4777: two lines at most from 1280px). Phones and tablets, under 900px,
   are unchanged. The homepage's body is page--home (src/pages/home.js). --head-gap is set again here because a custom
   property takes its value where it is set: :root's --head-gap is already 64px by the time it reaches the homepage. */
@media (min-width: 900px) {
  .page--home {
    --sec-pad: var(--s9);
    --head-gap: var(--sec-pad);
  }
}

/* ---- src/styles/base.css ---- */
/* Base: the reset, the page, and the focus ring every control gets. */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; }
/* the footer sits at the foot of the screen on a page shorter than the screen (Get in touch, msgs 5180 to 5202): the
   band before the footer grows to fill the gap, so the bands still run from the top of the page to its bottom */
body { display: flex; flex-direction: column; min-height: 100vh; min-height: 100dvh; background: var(--white); color: var(--fg); font: var(--type-body); }
body > main { flex: 1 0 auto; }
main:has(> [data-bg]) { display: flex; flex-direction: column; }   /* a page's main, which holds bands; the reference page's main is itself one band */
main > [data-bg]:last-child { flex-grow: 1; }
img, svg { display: block; max-width: 100%; }
img { height: auto; }
a { color: inherit; }
ul { list-style: none; }
p { text-wrap: pretty; }   /* no one-word last lines */
/* every control shows the ring when reached by keyboard; its colour comes from the surface it sits on */
:focus-visible, .is-focus { outline: var(--focus-w) solid var(--ring); outline-offset: var(--focus-offset); }   /* .is-focus: the reference page's frozen specimens */

/* ---- src/styles/layout.css ---- */
/* Layout: the content column. Every section's content sits in one .wrap, so every page shares the same two edges:
   24px from the screen edge on a phone, 32px from 900px, and never more than 1184px of content. */
.wrap { max-width: var(--wrap-max); margin: 0 auto; padding: 0 var(--gutter); }
/* The page: the homepage's body is page--home (src/pages/home.js), which tokens.css makes a step looser from 900px
   (topic 625 msg 5609). It has no style of its own here; its roles are in tokens.css and its gaps in each component. */

/* ---- src/styles/surfaces.css ---- */
/* Surfaces: the six backgrounds a band can have. Each sets the colour roles for what sits on it, so a component
   never needs to know where it is: text, secondary text, kickers, rules, outlines, the focus ring, and the two
   buttons' faces and blocks. White and the four colours are light surfaces (ink on them); black is the dark one.
   A colour band also names its own colour (--band-colour: what a stamp on it is drawn in) and its shade (--band-shade:
   the big asterisk). White and black also name each palette colour as text on them (--hue-green and the rest: the
   deep shade on white, the colour itself on black), for the menu links' hover (components/mlink.css). The order rule (black first, then white and a colour in turn) is checked on the built page by
   tools/check.js.
   The buttons (rule 7, topic 625 msgs 5573 and 5574: "the black button on black background or black shadow is rubbish
   ... any colour on the same colour looks really weird"): a button's face, its block and the band are three different
   colours, and the face or the block stands 3:1 or more against the band, so the button's shape always shows (WCAG
   1.4.11). Primary on white: an ink face on a lead green block; on a colour band, an ink face on a white block; on black,
   a green face on a white block. Secondary on white: a green face on an ink block; on a colour band, a white face on an
   ink block; on black, a white face on a green block. Until 29 Oct primary on white and on the colours was ink on ink,
   and secondary on white a white face on the white band. The lint and the check hold every button to it. */
.bg-white, .bg-green, .bg-ochre, .bg-sky, .bg-red {
  color: var(--ink);
  --fg: var(--ink); --outline: var(--ink); --ring: var(--ink);
  --btn1-face: var(--ink); --btn1-text: var(--white); --btn1-block: var(--white);
  --btn2-face: var(--white); --btn2-text: var(--ink); --btn2-block: var(--ink);
}
.bg-white { background: var(--white); --btn1-block: var(--lead); --btn2-face: var(--lead); --fg-2: var(--ink-2); --fg-3: var(--ink-3); --kicker: var(--ink-3); --line: var(--rule);
  --kicker-green: var(--green-deep); --kicker-ochre: var(--ochre-deep); --kicker-sky: var(--sky-deep); --kicker-red: var(--red-deep);
  --hue-green: var(--green-deep); --hue-red: var(--red-deep); --hue-sky: var(--sky-deep); --hue-ochre: var(--ochre-deep); }
/* on a colour, every text is ink: grey text fails AA on them (round 8 used #1E1F24, a hair off ink) */
.bg-green, .bg-ochre, .bg-sky, .bg-red { --fg-2: var(--ink); --fg-3: var(--ink); --kicker: var(--ink); --line: var(--ink); }
.bg-green { background: var(--green); --band-colour: var(--green); --band-shade: var(--green-shade); }
.bg-ochre { background: var(--ochre); --band-colour: var(--ochre); --band-shade: var(--ochre-shade); }
.bg-sky { background: var(--sky); --band-colour: var(--sky); --band-shade: var(--sky-shade); }
.bg-red { background: var(--red); --band-colour: var(--red); --band-shade: var(--red-shade); }
.bg-black {
  background: var(--black); color: var(--white);
  --fg: var(--white); --fg-2: var(--on-black); --fg-3: var(--on-black); --kicker: var(--lead);
  --line: var(--rule-on-black); --outline: var(--white); --ring: var(--white);
  /* a coloured kicker on black takes the colour itself (Work's pieces on black, msg 5008) */
  --kicker-green: var(--green); --kicker-ochre: var(--ochre); --kicker-sky: var(--sky); --kicker-red: var(--red);
  --hue-green: var(--green); --hue-red: var(--red); --hue-sky: var(--sky); --hue-ochre: var(--ochre);   /* a menu link's hover (msg 5168) */
  --btn1-face: var(--lead); --btn1-text: var(--ink); --btn1-block: var(--white);
  --btn2-face: var(--white); --btn2-text: var(--ink); --btn2-block: var(--lead);
}

/* ---- src/components/kicker.css ---- */
.kicker { font: var(--type-label); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--kicker); }
.kicker__keep { text-transform: none; }
/* a coloured kicker, on white or black only: the surface gives the shade (styles/surfaces.css). On white the deep
   shade, 5.5:1 or more on white and under 3:1 on its own colour; on black the colour itself */
.kicker--green { color: var(--kicker-green); }
.kicker--ochre { color: var(--kicker-ochre); }
.kicker--sky { color: var(--kicker-sky); }
.kicker--red { color: var(--kicker-red); }

/* ---- src/components/button.css ---- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  min-height: var(--tap); padding: var(--s4) var(--s5);   /* the label dead centre (msg 4901, measured): Tanker's capitals sit only
                                                             half a pixel high in their line, so the padding is even (it was 16 over, 12 under,
                                                             which put the label 1.5px low), and the indent matches the tracking after the last letter */
  text-indent: var(--ls-button);
  font: var(--type-button); letter-spacing: var(--ls-button); text-transform: uppercase; text-decoration: none; white-space: nowrap;
  color: var(--btn-text); background: var(--btn-face); border-radius: var(--radius);   /* no outline since 26 Oct (msg 4900) */
  box-shadow: var(--stamp) var(--stamp) 0 var(--btn-block);
  transition: transform var(--t-press), box-shadow var(--t-press);
}
.btn--primary { --btn-face: var(--btn1-face); --btn-text: var(--btn1-text); --btn-block: var(--btn1-block); }
.btn--secondary { --btn-face: var(--btn2-face); --btn-text: var(--btn2-text); --btn-block: var(--btn2-block); }
/* colour faces, on white only: a palette colour with ink words and block (Get in touch, msgs 4893-4894;
   red for All the work, msg 4898) */
.btn--ochre { --btn-face: var(--ochre); --btn-text: var(--ink); --btn-block: var(--ink); }
.btn--red { --btn-face: var(--red); --btn-text: var(--ink); --btn-block: var(--ink); }
.btn--sky { --btn-face: var(--sky); --btn-text: var(--ink); --btn-block: var(--ink); }
/* the press: the face moves onto its block */
.btn:hover, .btn.is-hover, .btn:active, .btn.is-active { transform: translate(var(--stamp), var(--stamp)); box-shadow: 0 0 0 var(--btn-block); }
.btn:focus-visible, .btn.is-focus { outline: var(--focus-w) solid var(--ring); outline-offset: var(--focus-offset-stamp); }
@media (prefers-reduced-motion: reduce) { .btn { transition: none; } }

.tlink {
  display: inline-flex; align-items: center; min-height: var(--tap);
  font: var(--type-body-strong); text-decoration: underline; text-decoration-thickness: var(--underline); text-underline-offset: var(--underline-offset);
}
.tlink:hover, .tlink.is-hover { text-decoration-thickness: var(--underline-l); }
.tlink:focus-visible, .tlink.is-focus { outline: var(--focus-w) solid var(--ring); outline-offset: var(--focus-offset); border-radius: var(--radius-focus); }

/* the action row: under the words it follows */
.acts { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s4) var(--s6); margin-top: var(--s7); }
/* on the homepage from 900px a step looser down the page (topic 625 msg 5609: "a tiny bit more spaced out"): 64px under
   the words it follows, and 24px between its rows when it wraps (styles/tokens.css, page--home) */
@media (min-width: 900px) { .page--home .acts { row-gap: var(--s5); margin-top: var(--s8); } }
/* an action row with nothing before it in its column (the 404 page's way home) starts at the band's padding */
.wrap > .acts:first-child { margin-top: 0; }

/* ---- src/components/icon.css ---- */
/* Icon discs: an ink icon, half the disc's width, centred on a colour disc (or inside an ink outline). */
.ico { display: grid; place-items: center; flex: none; width: var(--disc); height: var(--disc); border-radius: var(--radius-round); color: var(--ink); }
.ico__svg { width: var(--icon); height: var(--icon); }
.ico--sm { width: var(--disc-sm); height: var(--disc-sm); }
.ico--sm .ico__svg { width: var(--icon-sm); height: var(--icon-sm); }
.ico--green { background: var(--green); }
.ico--ochre { background: var(--ochre); }
.ico--sky { background: var(--sky); }
.ico--red { background: var(--red); }
/* the outline disc takes its surface's outline and text colour: ink on white and colours, white on black */
.ico--line { border: var(--edge) solid var(--outline); color: var(--fg); }

/* ---- src/components/imagery.css ---- */
/* the filter definitions take no room */
.filters { position: absolute; }

/* a square photo in the ink outline */
.pic { aspect-ratio: 1; border: var(--edge) solid var(--outline); border-radius: var(--radius); overflow: hidden; background: var(--ink); }
.pic__img { width: 100%; height: 100%; object-fit: cover; }
.pic--green .pic__img { filter: url(#duo-green); }
.pic--ochre .pic__img { filter: url(#duo-ochre); }
.pic--sky .pic__img { filter: url(#duo-sky); }
.pic--red .pic__img { filter: url(#duo-red); }
.pic--mono .pic__img { filter: url(#duo-mono); }

/* the hero photo and its asterisk (rule 5): the asterisk is 80% of the photo's width, comes out 18% past its top
   and 21% past its right edge, behind the photo, and turns once a minute; it stands still for anyone who asks for less motion */
.portrait { position: relative; width: var(--portrait-w); }
.portrait__frame { position: relative; z-index: 2; border: var(--edge) solid var(--outline); border-radius: var(--radius); overflow: hidden; background: var(--white); }
.portrait__img { width: 100%; filter: url(#duo-mono); }
.portrait__ast {
  position: absolute; z-index: 1; top: var(--ast-top); right: var(--ast-right); width: var(--ast-size); aspect-ratio: 1;
  background: var(--lead);
  -webkit-mask: url(brand/asterisk.svg) center / contain no-repeat; mask: url(brand/asterisk.svg) center / contain no-repeat;   /* a vector since 26 Oct: the PNG blurred (msg 4904) */
  animation: s9-turn var(--t-turn) linear infinite;
}
@keyframes s9-turn { to { transform: rotate(1turn); } }
@media (prefers-reduced-motion: reduce) { .portrait__ast { animation: none; } }

/* the round photo in the quote */
.avatar { display: block; flex: none; width: var(--avatar); height: var(--avatar); border: var(--edge) solid var(--outline); border-radius: var(--radius-round); overflow: hidden; }
.avatar__img { width: 100%; height: 100%; object-fit: cover; }
.avatar--green .avatar__img { filter: url(#duo-green); }
.avatar--ochre .avatar__img { filter: url(#duo-ochre); }
.avatar--sky .avatar__img { filter: url(#duo-sky); }
.avatar--red .avatar__img { filter: url(#duo-red); }
.avatar--mono .avatar__img { filter: url(#duo-mono); }

/* ---- src/components/drawing.css ---- */
/* A drawing (rule 8): one of the set in assets/drawings/, inline, in a square frame of its colour. The frame is the
   photo's: the ink outline and the radius. Inside, the drawing is 200 units square and is painted only with the
   classes below, so every colour is a token. Each drawing's motion is in components/drawings/<name>.css.
   Bare (draw--bare): no frame at all, straight on a colour band, which is its background (About me's drawing). */
.draw { aspect-ratio: 1; border: var(--edge) solid var(--outline); border-radius: var(--radius); overflow: hidden; }
.draw--bare { border: 0; border-radius: 0; }
.draw--green { background: var(--green); }
.draw--ochre { background: var(--ochre); }
.draw--sky { background: var(--sky); }
.draw--red { background: var(--red); }

/* the drawing: ink lines of one weight, round ends and joins. Every part moves within the drawing's own square. */
.draw__svg {
  width: 100%; height: 100%; overflow: hidden;
  fill: none; stroke: var(--ink); stroke-width: var(--draw-line); stroke-linecap: round; stroke-linejoin: round;
}
.draw__svg * { transform-box: view-box; }

/* fills: flat, one colour each */
.draw__ink { fill: var(--ink); }
.draw__white { fill: var(--white); }
.draw__green { fill: var(--green); }
.draw__ochre { fill: var(--ochre); }
.draw__sky { fill: var(--sky); }
.draw__red { fill: var(--red); }
/* the one grey: a beard (About me's drawing), so the glasses stay the darkest thing on the face */
.draw__grey { fill: var(--ink-2); }
/* bands: a thick stroke drawn as a solid shape (a track, a belt), its width set in the drawing */
.draw__band-ink, .draw__band-white, .draw__band-green, .draw__band-ochre, .draw__band-sky, .draw__band-red { fill: none; }
.draw__band-ink { stroke: var(--ink); }
.draw__band-white { stroke: var(--white); }
.draw__band-green { stroke: var(--green); }
.draw__band-ochre { stroke: var(--ochre); }
.draw__band-sky { stroke: var(--sky); }
.draw__band-red { stroke: var(--red); }
/* a shape with no outline */
.draw__plain { stroke: none; }
/* the hard shadow: an ink copy of an object's outline, 3% down and right, behind it. The only shadow outside a
   button (rule 7), and never blurred. */
.draw__drop { fill: var(--ink); stroke: var(--ink); transform: translate(var(--draw-drop), var(--draw-drop)); }

/* with reduced motion every drawing stands still, on the frame its file is drawn at */
@media (prefers-reduced-motion: reduce) { .draw .draw__svg * { animation: none; } }

/* ---- src/components/spot.css ---- */
/* A spot (rule 9): a small round drawing beside a beside item's words: one object in a light line (--spot-line), small
   in its circle (inside --spot-field) and optically centred in it, drawn in one colour on a round ground, moving
   through the whole loop at a methodical pace, all the spots at once. Two grounds:
   - the stamp: a solid black circle on a colour band, the drawing in the band's own colour (Antony, topic 625 msgs
     4904 and 4947);
   - a colour disc on white, the drawing in ink (msg 4949).
   The circle clips, but the drawing stays well inside it. No outline, no shadow. Each spot's motion is in
   components/spots/<name>.css. */
.spot {
  display: block; flex: none; width: var(--spot); height: var(--spot); border-radius: var(--radius-round); overflow: hidden;
  background: var(--spot-ground);
}
.spot--stamp { --spot-ground: var(--ink); --spot-ink: var(--band-colour); }
.spot--green { --spot-ground: var(--green); --spot-ink: var(--ink); }
.spot--ochre { --spot-ground: var(--ochre); --spot-ink: var(--ink); }
.spot--sky { --spot-ground: var(--sky); --spot-ink: var(--ink); }
.spot--red { --spot-ground: var(--red); --spot-ink: var(--ink); }

/* the drawing: lines of one weight in the one colour, round ends and joins */
.spot__svg {
  display: block; width: 100%; height: 100%;
  fill: none; stroke: var(--spot-ink); stroke-width: var(--spot-line); stroke-linecap: round; stroke-linejoin: round;
}
.spot__svg * { transform-box: view-box; }
/* the only two paints: the ground (a shape that hides what is behind it, with its outline) and solid line colour,
   and a line cut into a solid shape, in the ground's colour */
.spot__ground { fill: var(--spot-ground); }
.spot__solid { fill: var(--spot-ink); }
.spot__cut { stroke: var(--spot-ground); }
/* a shape with no outline */
.spot__plain { stroke: none; }
/* bands: a thick stroke drawn as a solid shape (a tube's walls, then its hollow), its width set in the drawing */
.spot__band-line { stroke: var(--spot-ink); }
.spot__band-ground { stroke: var(--spot-ground); }

/* with reduced motion every spot stands still at rest, on the frame its file is drawn at */
@media (prefers-reduced-motion: reduce) { .spot .spot__svg * { animation: none; } }

/* ---- src/components/mlink.css ---- */
/* Menu link: the header's menu and logo, the footer's logo, links and two icon links (topic 625 msgs 5168, 5169, 5201
   and 5232). Never underlined. On hover (a tap on a touch screen) and keyboard focus it takes the colour named by
   data-hue (the build sets it by position; the page's script, mlink.js, moves it on through one rotation that runs on
   from page to page, msg 5314), fading in over --t-hue
   on --ease-soft. Colour only: nothing grows (msg 5232). The colour is the surface's --hue-*: the colour itself on
   black, its deep shade on white (no menu sits on white since 28 Oct, msg 5384). An icon is drawn in the link's colour (fill="currentColor"); so
   is the logo's tile (logo.css). */
.mlink {
  text-decoration: none;
  transition: color var(--t-hue) var(--ease-soft);
}
.mlink[data-hue="green"] { --mlink-hue: var(--hue-green); }
.mlink[data-hue="red"] { --mlink-hue: var(--hue-red); }
.mlink[data-hue="sky"] { --mlink-hue: var(--hue-sky); }
.mlink[data-hue="ochre"] { --mlink-hue: var(--hue-ochre); }
/* the current page: the lead, held (green on black, deep green on the banner); a hover still takes the rotation */
.mlink[aria-current="page"] { color: var(--hue-green); }
.mlink:hover, .mlink.is-hover, .mlink:focus-visible, .mlink.is-focus { color: var(--mlink-hue); }
/* an icon at --icon, 32px (msg 5242: "bigger"; 24px until then); a mark at --mark, 27px, the width a 32px icon draws
   (Phosphor draws inside about 84% of its box), so the two read the same size beside each other */
.mlink__svg { display: block; width: var(--icon); height: var(--icon); }
.mlink__svg[data-mark] { width: var(--mark); height: var(--mark); }
.mlink:focus-visible, .mlink.is-focus { outline: var(--focus-w) solid var(--ring); outline-offset: var(--focus-offset); border-radius: var(--radius-focus); }

/* ---- src/components/logo.css ---- */
/* The logo as the link home (logo.js, msg 5232): a menu link whose face is the logo. The wordmark is always the
   surface's text colour; the tile (the rounded square, the asterisk a hole in it) is drawn in the link's colour: the
   same white at rest, and on hover and focus the rotation's next colour, fading over --t-hue as every menu link does.
   The file's own fill (pure white) gives way to the palette's. */
.logo { display: inline-flex; align-items: center; min-height: var(--tap); color: var(--fg); }
.logo__svg { display: block; height: var(--logo-h); width: auto; }
.logo__svg * { fill: var(--fg); }
.logo__svg .logo__tile { fill: var(--fg); } /* no hover colour on the logo since msg 5631: it stays white */

/* ---- src/components/header.css ---- */
/* The header (components/header.js): the banner at the top of every page's first band, on black, and the slim line
   under it at every width (topic 625 msg 5384: "a slim dividing line underneath it, and then I want the design to
   start"). The line is --nav-line of --nav-line-colour, white: it marks where the banner ends, so it is measured against
   the banner's black (17.15:1); the four colours would pass too, but they also paint the pictures under it, and move. */
.nav { position: relative; z-index: 3; border-bottom: var(--nav-line) solid var(--nav-line-colour); }
.nav__bar { display: flex; align-items: center; justify-content: space-between; gap: var(--s6); height: var(--nav-h); }
/* the logo is the link home (logo.css: a 44px target, its tile in the rotation's colour on hover and focus); it never
   gives up width to the menu */
.nav__logo { flex: none; }

/* ---- below 900px: the logo and the menu button; the button opens the menu (msg 5384) */
/* The menu button: three lines, --menu-bars long and --edge thick (the system's line weight), --s1 apart, in the
   banner's white, centred in a 44px target. The target sits right by its spare width, so the lines end on the column's
   edge as the logo starts on it. Open, the lines turn into a cross, so the same button reads as the way to close it;
   with reduced motion they change without turning. */
.nav__toggle {
  position: relative; left: calc((var(--tap) - var(--menu-bars)) / 2);
  display: inline-flex; align-items: center; justify-content: center; width: var(--tap); height: var(--tap);
  background: none; border: 0; border-radius: var(--radius-focus); color: var(--fg); cursor: pointer;
}
.nav__lines { display: flex; flex-direction: column; gap: var(--s1); width: var(--menu-bars); }
.nav__line { height: var(--edge); background: currentColor; border-radius: var(--edge); transition: transform var(--t-hue) var(--ease-soft), opacity var(--t-hue) var(--ease-soft); }
/* (each open-state selector sits inside :is(), whose list a browser reads one part at a time, so a browser without
   popovers or :has() still reads the rest of the rule) */
:is(.nav__bar:has(.nav__end:popover-open), .nav--open) .nav__line:first-child { transform: translateY(calc(var(--s1) + var(--edge))) rotate(45deg); }
:is(.nav__bar:has(.nav__end:popover-open), .nav--open) .nav__line:nth-child(2) { opacity: 0; }
:is(.nav__bar:has(.nav__end:popover-open), .nav--open) .nav__line:last-child { transform: translateY(calc(-1 * (var(--s1) + var(--edge)))) rotate(-45deg); }
@media (prefers-reduced-motion: reduce) { .nav__line { transition: none; } }

/* The menu, open (a popover, so it sits over the page, in the top layer, where no band clips it). It drops from the
   banner's line across the screen's full width and is the banner's own black, so it reads as the banner opening:
   the five links stacked down the content column, one a row, a hairline (--line) between rows, each the column's full
   width and at least --tap tall, in Tanker capitals at --menu-type (30px), the size of a phone menu's big targets.
   Get in touch is last. The current page is held in the lead green (rule 5). The open menu ends on the same slim
   line as the banner, so its edge is as clear over a picture as the banner's is, and its padding is --menu-pad at both
   ends, so WORK's letters sit as far below the banner's line as GET IN TOUCH's sit above the closing one (topic 625
   msg 5573). The browser's own popover look
   (its border, padding, colours, centring) gives way to these. */
:is(.nav__end:popover-open, .nav--open .nav__end) {
  position: absolute; inset: auto; top: calc(var(--nav-h) + var(--nav-line)); left: 0; width: 100%; height: auto; margin: 0;
  display: block; padding: var(--menu-pad) var(--gutter); overflow: visible;
  color: var(--fg); background: var(--black); border: 0; border-bottom: var(--nav-line) solid var(--nav-line-colour);
}
.nav--open .nav__end { top: calc(100% + var(--nav-line)); }   /* a frozen specimen on the reference page: under its own banner's line */
/* A browser without popovers (Safari before 17, Firefox before 125) cannot open the menu, so below 900px it shows the
   logo and the button only; the footer's links, the same five, take the visitor on */
@supports not selector(:popover-open) { .nav__end { display: none; } }
.nav__end .btn { display: none; }
.nav__links { display: flex; flex-direction: column; }
/* a menu link (mlink.css: no underline, the rotation's colour, no grow). Open on a phone: one a row */
.nav__link {
  display: flex; align-items: center; min-height: var(--tap); padding: var(--menu-row) 0;
  font: var(--menu-type); letter-spacing: var(--ls-button); text-transform: uppercase;
}
.nav__link + .nav__link { border-top: var(--hair) solid var(--line); }

/* ---- from 900px: the menu in a row and the Get in touch button (round 8's header); no menu button */
@media (min-width: 900px) {
  .nav__toggle { display: none; }
  .nav__end, :is(.nav__end:popover-open, .nav--open .nav__end) {
    position: static; display: flex; align-items: center; gap: var(--s6); width: auto; height: auto; margin: 0; padding: 0;
    overflow: visible; color: inherit; background: none; border: 0;
  }
  .nav__end .btn { display: inline-flex; }
  .nav__links { flex-direction: row; gap: var(--s5); }   /* 24px plus each link's 4px either side: 32px between words, as round 8 */
  /* at the buttons' label size: Tanker capitals, --type-button, 19px (msg 5168: "the same size as it is on the buttons") */
  .nav__link { justify-content: center; min-width: var(--tap); padding: 0 var(--s1); font: var(--type-button); }
  .nav__link + .nav__link { border-top: 0; }
  .nav__link--more { display: none; }
}

/* ---- src/components/hero.css ---- */
.hero { overflow: hidden; }   /* the asterisk may reach past the photo; it never makes the page scroll sideways */
.hero__in { display: grid; }
/* the parts closer together since 29 Oct (topic 625 msg 5573: "less space above the eyebrow, less space below the
   eyebrow ... bringing the entire hero section a little bit closer in together"), each gap a step less, and measured to
   the letters: the kicker, the title and the lead are trimmed to their capitals and baseline (text-box) */
.hero__copy { padding: var(--s6) 0; }
.hero__copy > :is(.kicker, .hero__title, .hero__lead) { text-box: trim-both cap alphabetic; }
.hero__title { margin: var(--s5) 0; font: var(--type-hero); letter-spacing: var(--ls-display); text-transform: uppercase; text-wrap: balance; }
.hero__lead { max-width: var(--measure-hero); font: var(--type-lead); color: var(--fg-2); }
.hero__cta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s5); margin-top: var(--s6); }
.hero__photo { display: flex; justify-content: flex-start; padding: var(--s7) 0; }

/* the proof pairs */
.proof { display: grid; gap: var(--s4) var(--s7); padding: var(--s5) 0 var(--s6); border-top: var(--hair) solid var(--line); font: var(--type-small); color: var(--fg-2); }
.proof__item { display: grid; align-content: start; gap: var(--s1); }
.proof__key { font-weight: var(--w-strong); color: var(--kicker); }

/* the bare page hero: the header alone. Its h1 stays for screen readers and takes no space (Work, msg 5008) */
.hero--bare { position: relative; }
.hero__title--hidden { position: absolute; width: var(--hair); height: var(--hair); margin: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
/* the page hero: no photo, no proof; the words run the full column */
.hero--page .hero__copy { padding-bottom: var(--s8); }
.hero--page .hero__title { margin-bottom: var(--s5); }
.hero--page .hero__cta { margin-top: var(--s5); }

/* From 900px the homepage hero is a step looser than on a phone, each gap the next step up the scale (topic 625 msg
   5609: "in desktop view ... a tiny bit more spaced out"): down the page, the words 64px under the banner and 96px over
   the next band, 32px from the kicker to the title and from the title to the lead, 48px from the lead to the button
   (until 29 Oct the phone's 24px and 32px, and 48px and 64px round the words). The photo sits as low as its asterisk
   needs: its column has --s10 above it, so the circle the asterisk's points sweep keeps --ast-clear (32px) from the
   banner's Get in touch button and its menu at every width (msg 5609: it "touches the green background of the button
   ... as it rotates by"; 48px above the photo until 29 Oct, when the points crossed the button's block by 13px at
   1440). The page hero keeps its own gaps */
@media (min-width: 900px) {
  .hero__in { grid-template-columns: var(--cols-hero); gap: var(--s5); }
  .hero__copy { align-self: center; padding: var(--s8) 0 var(--s9); }
  .hero__copy > .hero__title { margin: var(--s6) 0; }
  .hero__cta { gap: var(--s6); margin-top: var(--s7); }
  .hero__photo { align-self: center; justify-content: flex-end; padding: var(--s10) var(--s6) var(--s9) 0; }
  .proof { grid-auto-flow: column; grid-auto-columns: 1fr; }
  .hero--page .hero__in { grid-template-columns: 1fr; }
  .hero--page .hero__copy { padding: var(--s8) 0 var(--s9); }
  .hero--page .hero__copy > .hero__title { margin: var(--s5) 0; }
}

/* ---- src/components/phead.css ---- */
/* The page header (components/phead.js; topic 625 msgs 5236, 5263, 5274, 5384, 5426, 5619 and 5621): the first band of
   Work, Process, Free tools and Products. Black, the banner at its top with its slim line, then the frame: a black
   picture the build draws (its rules are in src/components/headers/ and the registry), lights of colour moving through
   it, with the small white title box over its middle, in the black clear zone. The frame starts on the banner's line
   and ends on the band's foot, the same slim white line (--nav-line of --nav-line-colour), so the picture sits between
   two lines of one weight and its colours never touch the next band (rule 4; topic 625 msg 5573: "a thin divider white
   line below the design as well as above it"). On a phone the banner, the lines and the picture take about a third of
   the screen (--phead-frame). */
.phead { overflow: hidden; border-bottom: var(--nav-line) solid var(--nav-line-colour); }
.phead__frame { position: relative; height: var(--phead-frame); overflow: hidden; }

/* the construction: drawn for the widest screen of its size (--phead-art-w) and the tallest frame (--phead-h), centred
   both ways, never stretched, so a narrower screen shows its middle and a shorter frame its middle band; the phone one
   under 900px, the wide one from 900px */
.phead__art {
  position: absolute; top: calc(50% - var(--phead-h) / 2); left: calc(50% - var(--phead-art-w) / 2);
  width: var(--phead-art-w); height: var(--phead-h); max-width: none;
}
.phead__art--wide { display: none; }
/* its paints: the four colours for the lights, which move (components/headers/turn.js writes their motion), and black for
   the still parts over them, the band's own black, so they run on into the ground. A light that runs along a stroke
   (phead__run) and a black line (phead__line) are painted as strokes; a shape with holes in it (the stencil, a ring) is
   filled even-odd */
.phead__green { --phead-paint: var(--green); }
.phead__red { --phead-paint: var(--red); }
.phead__sky { --phead-paint: var(--sky); }
.phead__ochre { --phead-paint: var(--ochre); }
.phead__black { --phead-paint: var(--black); }
.phead__art path { fill: var(--phead-paint); stroke: none; }
.phead__art .phead__run, .phead__art .phead__line { fill: none; stroke: var(--phead-paint); stroke-linejoin: miter; stroke-miterlimit: 4; }
.phead__holes { fill-rule: evenodd; }

/* the title box: rule 7's edge (the 3px ink outline and 12px corners) round a white face, with no stamp and no shadow;
   in the frame's middle at every width, where the construction left the clear zone for it, holding the title only (msg
   5619): 192 by 64px on a phone, 224 by 64px from 900px, each the longest title (RECENT WORK) and the box's padding. The
   title sits in its middle in Tanker capitals (--phead-title: 30px on a phone, 34px from 900px). It never moves: the
   colour moves in the picture only */
.phead__box {
  position: absolute; top: calc(50% - var(--phead-box-h) / 2); left: calc(50% - var(--phead-box-w) / 2);
  width: var(--phead-box-w); height: var(--phead-box-h);
  display: flex; flex-direction: column; justify-content: center;
  padding: var(--phead-pad); border: var(--edge) solid var(--outline); border-radius: var(--radius);
}
.phead__title { font: var(--phead-title); letter-spacing: var(--ls-display); text-transform: uppercase; text-align: center; white-space: nowrap; }

@media (min-width: 900px) {
  .phead__art--phone { display: none; }
  .phead__art--wide { display: block; }
}

/* ---- src/components/headers/turn.js (the page headers' motion, written by the build) ---- */
/* The page headers' motion (components/headers/turn.js, written by the build; topic 625 msgs 5619 and 5621): lights
   of colour move through still black, each picture on its own loop, linearly and one way, each colour a quarter loop
   behind the one before. Paused where the loop begins with reduced motion. */
.phead__run, .phead__slide-r, .phead__slide-l, .phead__recede, .phead__turn { animation-duration: var(--phead-loop); animation-timing-function: linear; animation-iteration-count: infinite; transform-box: view-box; transform-origin: 50% 50%; }
.phead__lag-1 { animation-delay: calc(var(--phead-loop) * -0.25); }
.phead__lag-2 { animation-delay: calc(var(--phead-loop) * -0.5); }
.phead__lag-3 { animation-delay: calc(var(--phead-loop) * -0.75); }
.phead__lag-4 { animation-delay: calc(var(--phead-loop) * -1); }
.phead__lag-5 { animation-delay: calc(var(--phead-loop) * -1.25); }
.phead__lag-6 { animation-delay: calc(var(--phead-loop) * -1.5); }
.phead__lag-7 { animation-delay: calc(var(--phead-loop) * -1.75); }
[data-phead="dazzle-phone"] { --phead-loop: var(--t-dazzle); }
@keyframes phead-dazzle-phone-run {
  0% { stroke-dashoffset: 539.25; }
  100% { stroke-dashoffset: -333.85; }
}
[data-phead="dazzle-phone"] .phead__run { animation-name: phead-dazzle-phone-run; }
[data-phead="dazzle-wide"] { --phead-loop: var(--t-dazzle); }
@keyframes phead-dazzle-wide-run {
  0% { stroke-dashoffset: 877.19; }
  100% { stroke-dashoffset: -241.68; }
}
[data-phead="dazzle-wide"] .phead__run { animation-name: phead-dazzle-wide-run; }
[data-phead="stella-phone"] { --phead-loop: var(--t-stella); }
@keyframes phead-stella-phone-slide-r {
  0% { transform: translateX(0%); }
  100% { transform: translateX(80%); }
}
[data-phead="stella-phone"] .phead__slide-r { animation-name: phead-stella-phone-slide-r; animation-duration: calc(var(--phead-loop) * 2); }
@keyframes phead-stella-phone-slide-l {
  0% { transform: translateX(0%); }
  100% { transform: translateX(-80%); }
}
[data-phead="stella-phone"] .phead__slide-l { animation-name: phead-stella-phone-slide-l; animation-duration: calc(var(--phead-loop) * 2); }
[data-phead="stella-wide"] { --phead-loop: var(--t-stella); }
@keyframes phead-stella-wide-slide-r {
  0% { transform: translateX(0%); }
  100% { transform: translateX(58.5366%); }
}
[data-phead="stella-wide"] .phead__slide-r { animation-name: phead-stella-wide-slide-r; animation-duration: calc(var(--phead-loop) * 2); }
@keyframes phead-stella-wide-slide-l {
  0% { transform: translateX(0%); }
  100% { transform: translateX(-58.5366%); }
}
[data-phead="stella-wide"] .phead__slide-l { animation-name: phead-stella-wide-slide-l; animation-duration: calc(var(--phead-loop) * 2); }
[data-phead="tunnel-phone"] { --phead-loop: var(--t-tunnel); }
@keyframes phead-tunnel-phone-recede {
  0% { transform: scale(4); }
  2.0833% { transform: scale(3.7282); }
  4.1667% { transform: scale(3.4909); }
  6.25% { transform: scale(3.2821); }
  8.3333% { transform: scale(3.0968); }
  10.4167% { transform: scale(2.9313); }
  12.5% { transform: scale(2.7826); }
  14.5833% { transform: scale(2.6483); }
  16.6667% { transform: scale(2.5263); }
  18.75% { transform: scale(2.4151); }
  20.8333% { transform: scale(2.3133); }
  22.9167% { transform: scale(2.2197); }
  25% { transform: scale(2.1333); }
  27.0833% { transform: scale(2.0535); }
  29.1667% { transform: scale(1.9794); }
  31.25% { transform: scale(1.9104); }
  33.3333% { transform: scale(1.8462); }
  35.4167% { transform: scale(1.786); }
  37.5% { transform: scale(1.7297); }
  39.5833% { transform: scale(1.6769); }
  41.6667% { transform: scale(1.6271); }
  43.75% { transform: scale(1.5802); }
  45.8333% { transform: scale(1.536); }
  47.9167% { transform: scale(1.4942); }
  50% { transform: scale(1.4545); }
  52.0833% { transform: scale(1.417); }
  54.1667% { transform: scale(1.3813); }
  56.25% { transform: scale(1.3474); }
  58.3333% { transform: scale(1.3151); }
  60.4167% { transform: scale(1.2843); }
  62.5% { transform: scale(1.2549); }
  64.5833% { transform: scale(1.2268); }
  66.6667% { transform: scale(1.2); }
  68.75% { transform: scale(1.1743); }
  70.8333% { transform: scale(1.1497); }
  72.9167% { transform: scale(1.1261); }
  75% { transform: scale(1.1034); }
  77.0833% { transform: scale(1.0817); }
  79.1667% { transform: scale(1.0608); }
  81.25% { transform: scale(1.0407); }
  83.3333% { transform: scale(1.0213); }
  85.4167% { transform: scale(1.0026); }
  87.5% { transform: scale(0.9846); }
  89.5833% { transform: scale(0.9673); }
  91.6667% { transform: scale(0.9505); }
  93.75% { transform: scale(0.9343); }
  95.8333% { transform: scale(0.9187); }
  97.9167% { transform: scale(0.9035); }
  100% { transform: scale(0.8889); }
}
[data-phead="tunnel-phone"] .phead__recede { animation-name: phead-tunnel-phone-recede; }
[data-phead="tunnel-wide"] { --phead-loop: var(--t-tunnel); }
@keyframes phead-tunnel-wide-recede {
  0% { transform: scale(4); }
  2.0833% { transform: scale(3.7282); }
  4.1667% { transform: scale(3.4909); }
  6.25% { transform: scale(3.2821); }
  8.3333% { transform: scale(3.0968); }
  10.4167% { transform: scale(2.9313); }
  12.5% { transform: scale(2.7826); }
  14.5833% { transform: scale(2.6483); }
  16.6667% { transform: scale(2.5263); }
  18.75% { transform: scale(2.4151); }
  20.8333% { transform: scale(2.3133); }
  22.9167% { transform: scale(2.2197); }
  25% { transform: scale(2.1333); }
  27.0833% { transform: scale(2.0535); }
  29.1667% { transform: scale(1.9794); }
  31.25% { transform: scale(1.9104); }
  33.3333% { transform: scale(1.8462); }
  35.4167% { transform: scale(1.786); }
  37.5% { transform: scale(1.7297); }
  39.5833% { transform: scale(1.6769); }
  41.6667% { transform: scale(1.6271); }
  43.75% { transform: scale(1.5802); }
  45.8333% { transform: scale(1.536); }
  47.9167% { transform: scale(1.4942); }
  50% { transform: scale(1.4545); }
  52.0833% { transform: scale(1.417); }
  54.1667% { transform: scale(1.3813); }
  56.25% { transform: scale(1.3474); }
  58.3333% { transform: scale(1.3151); }
  60.4167% { transform: scale(1.2843); }
  62.5% { transform: scale(1.2549); }
  64.5833% { transform: scale(1.2268); }
  66.6667% { transform: scale(1.2); }
  68.75% { transform: scale(1.1743); }
  70.8333% { transform: scale(1.1497); }
  72.9167% { transform: scale(1.1261); }
  75% { transform: scale(1.1034); }
  77.0833% { transform: scale(1.0817); }
  79.1667% { transform: scale(1.0608); }
  81.25% { transform: scale(1.0407); }
  83.3333% { transform: scale(1.0213); }
  85.4167% { transform: scale(1.0026); }
  87.5% { transform: scale(0.9846); }
  89.5833% { transform: scale(0.9673); }
  91.6667% { transform: scale(0.9505); }
  93.75% { transform: scale(0.9343); }
  95.8333% { transform: scale(0.9187); }
  97.9167% { transform: scale(0.9035); }
  100% { transform: scale(0.8889); }
}
[data-phead="tunnel-wide"] .phead__recede { animation-name: phead-tunnel-wide-recede; }
[data-phead="burst-phone"] { --phead-loop: var(--t-burst); }
@keyframes phead-burst-phone-turn {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(90deg); }
}
[data-phead="burst-phone"] .phead__turn { animation-name: phead-burst-phone-turn; }
[data-phead="burst-wide"] { --phead-loop: var(--t-burst); }
@keyframes phead-burst-wide-turn {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(90deg); }
}
[data-phead="burst-wide"] .phead__turn { animation-name: phead-burst-wide-turn; }
@media (prefers-reduced-motion: reduce) { .phead__art * { animation-play-state: paused; } }

/* ---- src/components/section.css ---- */
.sec { padding: var(--sec-pad) 0; }
/* the big asterisk on a colour band (msg 4904): behind the content, cut off by the band, still with reduced motion.
   It turns with the hero asterisk's keyframes (s9-turn, imagery.css). */
.sec--ast { position: relative; overflow: clip; }
.sec--ast > .wrap { position: relative; z-index: 1; }
.sec__ast {
  position: absolute; top: 0; right: 0; width: var(--band-ast); aspect-ratio: 1; translate: var(--band-ast-shift) calc(-1 * var(--band-ast-shift));
  background: var(--band-shade); pointer-events: none;
  -webkit-mask: url(brand/asterisk.svg) center / contain no-repeat; mask: url(brand/asterisk.svg) center / contain no-repeat;
  animation: s9-turn var(--t-turn) linear infinite;
}
@media (prefers-reduced-motion: reduce) { .sec__ast { animation: none; } }
/* still: the asterisk stands still, as it does with reduced motion. A page of products, where nothing moves (topic 625
   msg 5303: "No animations in there. Brands or screenshots"; components/product.js) */
.sec--still .sec__ast { animation: none; }
/* the head: --head-gap below it, the same as the band's --sec-pad above it (topic 625 msg 5573), measured to the letters:
   its words are trimmed to their capitals' top and their baseline (text-box), so the space set is the space seen, and
   the gaps inside it are letters to letters too. A browser without text-box adds each line's half-leading back. */
.sec__head { display: grid; gap: var(--s5); margin-bottom: var(--head-gap); }
.sec__head:last-child { margin-bottom: 0; }   /* a head with nothing under it: a group header */
.sec__head :is(.kicker, .sec__title, .sec__intro), .sec__aside > .points li { text-box: trim-both cap alphabetic; }
.sec__head .kicker { display: block; margin-bottom: var(--s5); }
.sec__whole { white-space: nowrap; }   /* a hyphenated word in a heading, kept whole on a phone; from 900px the
                                          heading is wide enough, and nowrap there unbalances its lines */
.sec__title { font: var(--type-heading); letter-spacing: var(--ls-display); text-transform: uppercase; text-wrap: balance; }
.sec__intro { font: var(--type-lead); color: var(--fg-2); max-width: var(--measure-lead); }
.sec__aside > .points { max-width: var(--measure-lead); margin-top: var(--s5); font: var(--type-lead); }   /* the list finishes the intro's sentence: the same size and width */

/* split: heading left, body right from 900px */
.sec__split { display: grid; gap: var(--head-gap); }
.sec__split > .sec__head { display: block; margin-bottom: 0; }
.sec__body { min-width: 0; }

/* parts: two sections on one band */
.sec__part + .sec__part { margin-top: var(--sec-pad); }

/* the statement band */
.band__in { display: grid; gap: var(--s5); }
.band__title { font: var(--type-statement); letter-spacing: var(--ls-display); text-transform: uppercase; text-wrap: balance; }
.band__text { font: var(--type-lead); color: var(--fg-2); }

@media (min-width: 900px) {
  .sec__whole { white-space: normal; }
  .sec__head { grid-template-columns: var(--cols-head); gap: var(--gap-split); align-items: end; }
  .sec__head--list { align-items: start; }
  .sec__head--solo { grid-template-columns: 1fr; }   /* a heading with nothing beside it takes the full width (26 Oct) */
  .sec__split { grid-template-columns: var(--cols-head); gap: var(--gap-split); align-items: start; }
  .band__in { grid-template-columns: var(--cols-band); gap: var(--gap-split); align-items: end; }
}

/* chapter: a slim colour band that names the group below it (the Work page's types, each tool, each product, each part
   of Process). Each piece under it is a band of its own, white, black, white (black, white, black under AI tools and
   Integrations, msg 5178), with a section's full padding. Since 29 Oct (topic 625 msg 5573: "each of the headers needs to
   be smaller") its name is at --type-chapter, the title size on a phone, and it has --chapter-pad above and below its
   letters: 55px tall on a phone (72px until then; a step slimmer on 26 Oct, msg 5008) */
.sec--chapter { padding: var(--chapter-pad) 0; }
.sec--chapter .sec__title { font: var(--type-chapter); }

/* ---- src/components/logos.css ---- */
/* The logo strip: a slim band under the homepage hero (sec--strip), one row tall: the kicker rides in the row as its
   first item, then the logos, scrolling slowly left in the surface's text colour, faded at both ends. With reduced
   motion the row stands still and wraps. */
.sec--strip { padding: var(--s5) 0; }
.logos { display: block; }
.logos__view {
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, var(--white) var(--s8), var(--white) calc(100% - var(--s8)), transparent);
  mask-image: linear-gradient(90deg, transparent, var(--white) var(--s8), var(--white) calc(100% - var(--s8)), transparent);
}
.logos__track { display: flex; width: max-content; animation: logos-scroll var(--t-logos) linear infinite; }
.logos__row { display: flex; align-items: center; gap: var(--s8); padding-right: var(--s8); }
.logos__item { display: flex; flex: none; }
.logos__item--label { white-space: nowrap; }
.logos__svg { display: block; height: var(--strip-logo-h); width: auto; color: var(--fg); }
@keyframes logos-scroll { to { transform: translateX(-50%); } }
/* from 900px a step looser, 32px above and below (topic 625 msg 5609: the homepage "a tiny bit more spaced out"). The
   logos keep their 64px apart: the row's length sets how fast it scrolls */
@media (min-width: 900px) { .sec--strip { padding: var(--s6) 0; } }
@media (prefers-reduced-motion: reduce) {
  .logos__view { -webkit-mask-image: none; mask-image: none; }
  .logos__track { width: auto; animation: none; }
  .logos__row { flex-wrap: wrap; row-gap: var(--s5); }
  .logos__row--copy { display: none; }
}

/* ---- src/components/status.css ---- */
.status { display: flex; align-items: flex-start; gap: var(--s2); font: var(--type-small-strong); color: var(--fg); }
/* the dot sits on the middle of the first line, so a status that wraps keeps it beside its first words */
.status::before { content: ""; flex: none; width: var(--dot); height: var(--dot); margin-top: calc((var(--fs-small) * var(--lh-body) - var(--dot)) / 2); border: var(--rule-bold) solid var(--fg); border-radius: var(--radius-round); }
.status--live::before { background: var(--fg); }

/* ---- src/components/points.css ---- */
.points { display: grid; }
.points__item { padding: var(--s3) 0; border-bottom: var(--hair) solid var(--line); color: var(--fg-2); }
.points__item:first-child { border-top: var(--hair) solid var(--line); }

/* ---- src/components/prose.css ---- */
.prose { max-width: var(--measure-text); color: var(--fg-2); }
.prose > * + * { margin-top: var(--s4); }
.prose > .prose__title { margin-top: var(--s7); }
.prose__title { font: var(--type-title); letter-spacing: var(--ls-display); text-transform: uppercase; text-wrap: balance; color: var(--fg); }
.prose > .prose__title + * { margin-top: var(--s5); }
.prose--lead { max-width: var(--measure-lead); font: var(--type-lead); }

/* ---- src/components/rows.css ---- */
.rows { display: grid; }
.rows__item { display: grid; gap: var(--s3); padding: var(--s5) 0; border-bottom: var(--hair) solid var(--line); }
.rows__item:first-child { border-top: var(--hair) solid var(--line); }
.rows__title { font: var(--type-title); letter-spacing: var(--ls-display); text-transform: uppercase; text-wrap: balance; }
.rows__text { max-width: var(--measure-text); color: var(--fg-2); }
@media (min-width: 900px) {
  .rows__item { grid-template-columns: var(--cols-head); gap: var(--gap-split); padding: var(--s6) 0; }
}

/* ---- src/components/work.css ---- */
.work { display: grid; gap: var(--s8); }
.tile { display: grid; grid-template-columns: var(--tile-img) minmax(0, 1fr); column-gap: var(--s5); align-items: start; }
.tile > .pic, .tile > .draw { grid-row: 1 / 3; }
.tile .status { margin-bottom: var(--s3); }
.tile__title { margin-top: var(--s3); font: var(--type-tile); letter-spacing: var(--ls-display); text-transform: uppercase; text-wrap: balance; }
.tile__text { grid-column: 1 / -1; margin-top: var(--s5); color: var(--fg-2); }
.tile__stack { grid-column: 1 / -1; margin-top: var(--s3); font: var(--type-small); color: var(--fg-3); }
/* from 900px a step looser down the page (topic 625 msg 5609: the homepage "a tiny bit more spaced out"): the tiles
   128px apart down, and the title 24px from its kicker and its words (96px and 16px until 29 Oct). Across, the picture
   32px from its words and --gap-split between the two, as they were */
@media (min-width: 900px) {
  .work { grid-template-columns: 1fr 1fr; gap: var(--s10) var(--gap-split); }
  .tile { grid-template-rows: auto auto 1fr; column-gap: var(--s6); }
  .tile > .pic, .tile > .draw { grid-row: 1 / 4; }
  .tile__title { margin: var(--s5) 0; }
  .tile__text, .tile__stack { grid-column: 2; }
  .tile__text { margin-top: 0; }
}

/* ---- src/components/items.css ---- */
.cols { display: grid; gap: var(--s7); }
.step > .ico { margin-bottom: var(--s6); }
.term > .ico { margin-bottom: var(--s5); }
.step__title { margin: var(--s4) 0 var(--s3); }   /* kicker above: 16px */
.term__title { margin: var(--s3) 0; }
.outcome { display: grid; grid-template-columns: auto 1fr; gap: var(--s5); align-items: start; }
.outcome__words { min-width: 0; }
.outcome__title { margin: var(--s4) 0 var(--s3); }   /* kicker above: 16px */
.outcome__words > .outcome__title:first-child { margin-top: var(--s3); }   /* no kicker: the title starts near the disc's top */
.step__title, .term__title, .outcome__title { font: var(--type-title); letter-spacing: var(--ls-display); text-transform: uppercase; text-wrap: balance; }
.step__text, .term__text, .outcome__text { max-width: var(--measure-text); color: var(--fg-2); }
.term > .tlink { margin-top: var(--s2); }
/* from 900px a step looser down the page (topic 625 msg 5609: the homepage "a tiny bit more spaced out"): a beside
   item's kicker 24px over its title and its title 16px over its words (16px and 12px until 29 Oct), and two across 96px
   apart down the rows (64px). Across, the spot 32px from its words and --gap-split between the two, as they were: they
   set the lines' length, two lines at most from 1280px (msg 4777) */
@media (min-width: 900px) {
  .outcome { gap: var(--s6); }
  .outcome__title { margin: var(--s5) 0 var(--s4); }
  .outcome__words > .outcome__title:first-child { margin-top: var(--s4); }
  .cols { grid-template-columns: repeat(3, 1fr); gap: var(--gap-cards); }
  .cols--2 { grid-template-columns: repeat(2, 1fr); gap: var(--s9) var(--gap-split); }
}

/* ---- src/components/intro.css ---- */
/* The intro (components/intro.js; Process since 3 Nov, topic 625 msgs 6150 and 6334): the words that open a section on
   the white band under its chapter band. The words at the lead size, trimmed to their letters (rule 6), --head-gap above
   the section's parts. With a photo, phone first: the photo above the words, --intro-photo wide, the words --s6 under
   it. From 900px the steps' three columns, --gap-cards apart: the photo in the first, still --intro-photo wide (about
   two-thirds of the column, msg 6634), the words from the second, level with the photo's middle. A title in Tanker
   stands --s5 over the words, and paragraphs --s6 apart, measured from one's baseline to the next one's capitals. */
.intro { display: grid; gap: var(--s6); }
.intro:not(:last-child) { margin-bottom: var(--head-gap); }
.intro__photo { width: var(--intro-photo); max-width: 100%; }
.intro__title { max-width: var(--measure-lead); font: var(--type-title); letter-spacing: var(--ls-display); text-transform: uppercase; text-wrap: balance; color: var(--fg); text-box: trim-both cap alphabetic; }
.intro__title + .intro__text { margin-top: var(--s5); }
.intro__text + .intro__text { margin-top: var(--s6); }
.intro__text { max-width: var(--measure-lead); font: var(--type-lead); color: var(--fg); text-box: trim-both cap alphabetic; }
@media (min-width: 900px) {
  .intro--photo { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--gap-cards); align-items: center; }
  .intro--photo .intro__words { grid-column: 2 / -1; }
}

/* ---- src/components/divider.css ---- */
/* The dividing line (components/divider.js; topic 625 msg 6457: "a black dividing line, just a thin one"): the
   system's thin line, --hair, solid, in the outline colour (ink on white), across the content column with no width of
   its own, and --head-gap under it: the room the intro or head before it keeps above it (rule 6), so the room above it
   and below it is equal, 48px on a phone and 64px from 900px. Lint 16 holds these; the check measures them. */
.divider { height: 0; margin: 0 0 var(--head-gap); border: 0; border-top: var(--hair) solid var(--outline); }

/* ---- src/components/steps.css ---- */
/* Numbered steps (components/steps.js; topic 625 msg 5575). Phone first: a disc beside its heading, centred on its
   first line, the words under the heading, and an ink line (--edge) from each disc down to the next. From 900px three
   across, the disc over the heading, no line. */
.steps { display: grid; gap: var(--s6); }
.steps__item { position: relative; display: grid; grid-template-columns: var(--step-disc) minmax(0, 1fr); gap: var(--s3) var(--s4); }
.steps__num {
  grid-row: 1; display: grid; place-items: center; width: var(--step-disc); height: var(--step-disc);
  border-radius: var(--radius-round); background: var(--step-hue); color: var(--ink);
  font: var(--type-title);
}
.steps__num--green { --step-hue: var(--green); }
.steps__num--red { --step-hue: var(--red); }
.steps__num--sky { --step-hue: var(--sky); }
.steps__num--ochre { --step-hue: var(--ochre); }
.steps__title { grid-row: 1; grid-column: 2; align-self: center; font: var(--type-title); letter-spacing: var(--ls-display); text-transform: uppercase; text-wrap: balance; }
.steps__text { grid-column: 2; max-width: var(--measure-text); color: var(--fg-2); }
/* the line joining the discs: from the bottom of one to the top of the next, through the gap between the steps */
.steps__item:not(:last-child)::before {
  content: ""; position: absolute; top: var(--step-disc); bottom: calc(-1 * var(--s6)); left: calc((var(--step-disc) - var(--edge)) / 2);
  width: var(--edge); background: var(--outline);
}
@media (min-width: 900px) {
  .steps { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s8) var(--gap-cards); }
  .steps__item { grid-template-columns: minmax(0, 1fr); align-content: start; }
  .steps__num { margin-bottom: var(--s3); }
  .steps__title, .steps__text { grid-row: auto; grid-column: 1; }
  .steps__title { align-self: start; }
  .steps__item:not(:last-child)::before { display: none; }
}

/* ---- src/components/ways.css ---- */
/* The two answers (components/ways.js; topic 625 msg 6639): laid out as the numbered steps are (components/steps.css).
   Phone first: the disc beside its heading, the words under the heading, one answer under the other. From 900px two
   halves, --gap-cards apart, the disc over its heading. On white only. */
.ways { display: grid; gap: var(--s6); }
.way { display: grid; grid-template-columns: var(--step-disc) minmax(0, 1fr); gap: var(--s3) var(--s4); }
.way--green { --way-hue: var(--green); }
.way--red { --way-hue: var(--red); }
.way--sky { --way-hue: var(--sky); }
.way--ochre { --way-hue: var(--ochre); }
.way .ico { grid-row: 1; width: var(--step-disc); height: var(--step-disc); background: var(--way-hue); }
.way .ico__svg { width: calc(var(--step-disc) / 2); height: calc(var(--step-disc) / 2); }
.way__title { grid-row: 1; grid-column: 2; align-self: center; font: var(--type-title); letter-spacing: var(--ls-display); text-transform: uppercase; text-wrap: balance; }
.way__text { grid-column: 2; max-width: var(--measure-text); color: var(--fg-2); }
@media (min-width: 900px) {
  .ways { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--gap-cards); }
  .way { grid-template-columns: minmax(0, 1fr); align-content: start; }
  .way .ico { margin-bottom: var(--s3); }
  .way__title, .way__text { grid-row: auto; grid-column: 1; }
  .way__title { align-self: start; }
}

/* ---- src/components/cases.css ---- */
.cases { display: grid; gap: var(--s8); }
/* the picture at --case-img (topic 625 msg 5573: "all the animations need to be a bit ... smaller"): 240px on a phone,
   over the words, so a piece's title and the start of its words show on the first screen; from 900px beside the words.
   Until 29 Oct it took the column's width on a phone and a quarter of the piece from 900px. The picture is the first
   thing in a case: its drawing, or on Products the product's tile (components/product.js, 3 Nov, msgs 6128, 6137 and
   6139), the same size */
.case { display: grid; gap: var(--s5); }
.case > :first-child { width: min(100%, var(--case-img)); }
/* the link (Free tools: topic 625 msg 6466) under the picture, --s6 from it at every width (the case's gap and --s2;
   msg 6643: "more space between the animation and the GitHub button"). On a phone it sits between the picture and the
   words, --s7 over the words, which start a new group (msg 6643 again); from 900px in the picture's column, with the
   words beside both, so it takes no row of its own. Until msg 6466 it sat --s7 under the parts (button.css) */
.case > .acts { margin-top: var(--s2); }
.case > .acts + .case__main { margin-top: var(--s5); }
/* wide (Products, msg 6643): the picture across the column, the words under it, the link under the last part */
.case--wide { gap: var(--s6); }
.case--wide > :first-child { width: 100%; }
.case__part > .acts { margin-top: var(--s5); }
.case__main { min-width: 0; }
.case__title { margin-top: var(--s3); font: var(--type-tile); letter-spacing: var(--ls-display); text-transform: uppercase; text-wrap: balance; }
/* with no type over it (a free tool's or a product's headline, msg 6468) the title starts the words, level with the
   picture's top from 900px, as the parts did before it */
.case__title:first-child { margin-top: 0; }
/* the parts: side by side when the words' columns are at least --case-row wide, else one under the other */
.case__parts { display: flex; flex-wrap: wrap; gap: var(--s5) var(--s6); margin-top: var(--s5); }
/* with no type or title (Free tools and Products until msg 6468) the parts start the words, level with the picture's top */
.case__parts:first-child { margin-top: 0; }
.case__part { flex: 1 1 calc((var(--case-row) - 100%) * 999); min-width: 0; }
/* the part labels in the band's text colour (--fg-3: ink-3 on white, the light text on black, ink on a colour), never a
   colour: the type's kicker is the one coloured label in a piece (msg 5234; on black they were the lead green until
   27 Oct, through --kicker) */
.case__label { font: var(--type-label); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--fg-3); }
.case__text { max-width: var(--measure-text); margin-top: var(--s2); color: var(--fg-2); }
@media (min-width: 900px) {
  .cases { gap: var(--s9); }
  /* the picture's row as tall as the picture, the link's row taking the rest of the words' height (1fr), so the link
     sits --s5 under the picture however long the words run; the words --s7 beside them, as before.
     The picture's column is --case-img, or as wide as its link's button needs where that is wider, so the button is
     never wider than the picture over it (the brief of msg 6466): the column's most is its min-content, and the link's
     row asks for its button and --s2 more, room for the button's block (--stamp, 6px). Only View on GitHub needs it,
     from 900px until --case-img reaches it (about 1213px); a drawing or a tile alone asks for less than --case-img, so
     Work's pieces and Sleekit's tile stay --case-img */
  .case { grid-template-columns: minmax(var(--case-img), min-content) minmax(0, 1fr); grid-auto-rows: auto 1fr; gap: var(--s5) var(--s7); align-items: start; }
  .case > :first-child { width: auto; }
  .case > .acts { grid-column: 1; grid-row: 2; padding-right: var(--s2); }
  .case > .acts + .case__main { grid-column: 2; grid-row: 1 / span 2; margin-top: 0; }
  .case--wide { grid-template-columns: minmax(0, 1fr); grid-auto-rows: auto; gap: var(--s7); }
  .case--wide > :first-child { width: 100%; }
}

/* ---- src/components/product.css ---- */
/* A product entry (components/product.js; topic 625 msgs 5294, 5303 and 5574, and since 4 Nov 2026 msg 6643): a wide case
   study (cases.css), its film first, its words under it. */
/* the film, the first thing in the entry (msg 6643): the column's full width, in rule 7's edge, the 3px outline and 12px
   corners, keeping its own proportions */
.product__film { display: block; width: 100%; height: auto; border: var(--edge) solid var(--outline); border-radius: var(--radius); background: var(--white); }

/* ---- src/components/built.css ---- */
.built { display: grid; gap: var(--s7); }
.made { display: grid; grid-template-rows: auto auto auto 1fr auto; height: 100%; padding-top: var(--s5); border-top: var(--rule-bold) solid var(--outline); text-decoration: none; }
.made > .ico { margin-bottom: var(--s5); }
.made__title { font: var(--type-title); letter-spacing: var(--ls-display); text-transform: uppercase; text-wrap: balance; }
.made > .kicker { margin: var(--s2) 0 var(--s3); }
.made__text { color: var(--fg-2); }
.made__more { padding-top: var(--s4); font: var(--type-body-strong); overflow-wrap: anywhere; text-decoration: underline; text-decoration-thickness: var(--underline); text-underline-offset: var(--underline-offset); }
a.made:hover .made__title, .made.is-hover .made__title { text-decoration: underline; text-decoration-thickness: var(--underline-l); text-underline-offset: var(--underline-offset); }
a.made:hover .made__more, .made.is-hover .made__more { text-decoration-thickness: var(--underline-l); }
@media (min-width: 900px) {
  .built { grid-template-columns: repeat(4, 1fr); gap: var(--s8) var(--gap-cards); }
  .built--3 { grid-template-columns: repeat(3, 1fr); }
}

/* ---- src/components/quote.css ---- */
.quote__text { max-width: var(--measure-quote); font: var(--type-quote); letter-spacing: var(--ls-display); text-transform: uppercase; text-wrap: balance; }
.quote__by { display: flex; align-items: center; gap: var(--s4); margin-top: var(--s7); font: var(--type-body-strong); }

/* about: the person. A heading, running text, and a picture: his drawing, bare on the band (and the square
   black-and-white photo beside it, where it is kept). On a phone they stack in that order, so the picture sits low on
   the band, clear of the asterisk turning in its top right corner. From 900px the picture goes under the heading. */
.quote--about { display: grid; gap: var(--head-gap); }
.quote__pics { display: flex; align-items: flex-end; gap: var(--s5); }
.quote__photo { flex: 0 1 var(--about-photo-w); min-width: 0; }
.quote__draw { flex: 0 1 var(--about-draw-w); min-width: 0; }
/* About me's heading is a section's heading (rule 6, topic 625 msg 5573): trimmed to its letters, the band's --sec-pad
   above it and --head-gap, the same, below it before the next thing in its column */
.quote__title { font: var(--type-heading); letter-spacing: var(--ls-display); text-transform: uppercase; text-wrap: balance; text-box: trim-both cap alphabetic; }
.quote__body { min-width: 0; }
.quote__para { max-width: var(--measure-lead); font: var(--type-lead); color: var(--fg-2); }
.quote__para + .quote__para { margin-top: var(--s4); }
@media (min-width: 900px) {
  .quote--about {
    grid-template-columns: var(--cols-head); grid-template-rows: auto 1fr; grid-template-areas: "title body" "pics body";
    gap: var(--head-gap) var(--gap-split); align-items: start;
  }
  .quote__title { grid-area: title; }
  .quote__body { grid-area: body; }
  .quote__pics { grid-area: pics; }
  .quote__para + .quote__para { margin-top: var(--s5); }   /* a step looser (msg 5609): 24px between paragraphs, 16px until 29 Oct */
}

/* ---- src/components/tools.css ---- */
.tool {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: var(--s1) var(--s5); align-items: center;
  height: 100%; padding: var(--s5) 0; border-bottom: var(--hair) solid var(--line); text-decoration: none;
}
.tools > li:first-child > .tool { border-top: var(--hair) solid var(--line); }
.tool > .ico { grid-row: 1 / 3; }
.tool__title { font: var(--type-title); letter-spacing: var(--ls-display); text-transform: uppercase; text-wrap: balance; }
.tool__text { grid-column: 2 / 4; font: var(--type-small); color: var(--fg-2); }
.tool:hover .tool__title, .tool.is-hover .tool__title { text-decoration: underline; text-decoration-thickness: var(--underline-l); text-underline-offset: var(--underline-offset); }
@media (min-width: 900px) {
  .tools { display: grid; grid-template-columns: 1fr 1fr; column-gap: var(--gap-split); }
  .tools > li:nth-child(2) > .tool { border-top: var(--hair) solid var(--line); }
}

/* ---- src/components/posts.css ---- */
.posts { display: grid; gap: var(--s6); }
.post { display: grid; grid-template-rows: auto auto 1fr auto; gap: var(--s3); height: 100%; padding-top: var(--s5); border-top: var(--rule-bold) solid var(--outline); text-decoration: none; }
.post__title { font: var(--type-title); letter-spacing: var(--ls-display); text-transform: uppercase; text-wrap: balance; }
.post__text { color: var(--fg-2); }
.post__more { padding-top: var(--s3); font: var(--type-body-strong); text-decoration: underline; text-decoration-thickness: var(--underline); text-underline-offset: var(--underline-offset); }
.post:hover .post__more, .post.is-hover .post__more { text-decoration-thickness: var(--underline-l); }
@media (min-width: 900px) {
  .posts { grid-template-columns: repeat(3, 1fr); gap: var(--gap-cards); }
}

/* ---- src/components/contact.css ---- */
.contact__title { max-width: var(--measure-statement); font: var(--type-statement); letter-spacing: var(--ls-display); text-transform: uppercase; text-wrap: balance; }
.contact__text { max-width: var(--measure-lead); font: var(--type-lead); color: var(--fg-2); }
/* One gap between heading, line and buttons (Antony, topic 625 msg 4911). text-box trims each text box to its capitals
   and baseline, so the gap set here is the gap seen, not the gap plus the type's leading. */
.contact__title, .contact__text { text-box: trim-both cap alphabetic; }
.contact__text, .contact__title ~ .acts { margin-top: var(--s6); }
@media (min-width: 900px) {
  .contact__text, .contact__title ~ .acts { margin-top: var(--s7); }
}

/* ---- src/components/todo.css ---- */
/* Placeholder: the words that will be there, in a dashed outline the button's height, with what is missing beside them
   in brackets. The dash says "not yet": no stamp, no press, nothing to click. */
.todo {
  display: inline-flex; align-items: center; gap: var(--s3);
  min-height: var(--tap); padding: var(--s3) var(--s5);
  color: var(--fg); border: var(--edge) dashed var(--outline); border-radius: var(--radius);
}
.todo__label { font: var(--type-button); letter-spacing: var(--ls-button); text-transform: uppercase; white-space: nowrap; }
.todo__gap { font: var(--type-small); color: var(--fg-2); white-space: nowrap; }

/* ---- src/components/form.css ---- */
/* The Get in touch form (topic 625 msgs 5180 to 5202; components/form.js). The CSS does all the showing, so the form
   works the same without its script: the script only names a state in data-state (empty, invalid, sending, sent,
   failed). */
.form { color: var(--fg); }

/* the question: a real fieldset and legend, reset to plain blocks. The legend floats so it lays out like any heading. */
.form__q { min-width: 0; border: 0; }
.form__legend { float: left; width: 100%; }
.form__title, .form__thanks {
  max-width: var(--measure-statement); font: var(--type-statement); letter-spacing: var(--ls-display); text-transform: uppercase; text-wrap: balance;
  text-box: trim-both cap alphabetic;   /* the gap set below is the gap seen, as in the contact block */
}

/* the choices: big tap buttons, stacked on a phone; from 900px three across, then two wider ones (five choices). They
   start --head-gap under the question's letters, as much as the band's --sec-pad above them (rule 6, topic 625 msg
   5573) */
.form__choices { clear: both; display: grid; gap: var(--s4); padding-top: var(--head-gap); }
.form__choice {
  position: relative; display: flex; align-items: center; gap: var(--s4);
  min-height: var(--tap); padding: var(--s4) var(--s5);
  font: var(--type-choice); color: var(--ink);
  background: var(--choice-face, var(--white)); border: var(--edge) solid var(--outline); border-radius: var(--radius);
  box-shadow: var(--stamp) var(--stamp) 0 var(--outline);   /* the stamp, as a button and the credit stamp have it */
  cursor: pointer; transition: transform var(--t-press), box-shadow var(--t-press);
}
/* the colour it lights in when picked (form.js sets data-hue: the rotation without the band's own colour) */
.form__choice[data-hue="green"] { --choice-hue: var(--green); }
.form__choice[data-hue="red"] { --choice-hue: var(--red); }
.form__choice[data-hue="sky"] { --choice-hue: var(--sky); }
.form__choice[data-hue="ochre"] { --choice-hue: var(--ochre); }
.form__choice:has(.form__radio:checked) { --choice-face: var(--choice-hue); }
/* hovered, it presses onto its block like a button; picked, it stays pressed in */
.form__choice:hover, .form__choice.is-hover, .form__choice:has(.form__radio:checked) { transform: translate(var(--stamp), var(--stamp)); box-shadow: 0 0 0 var(--outline); }
/* the radio covers the whole button, its border included: the button is its tap target, and its focus ring goes round
   the button, clear of the block */
.form__radio { appearance: none; position: absolute; inset: calc(-1 * var(--edge)); border-radius: var(--radius); cursor: pointer; }
.form__radio:focus-visible, .form__radio.is-focus { outline: var(--focus-w) solid var(--ring); outline-offset: var(--focus-offset-stamp); }
/* the ring that fills when picked: the pick shows by more than its colour */
.form__dot { flex: none; display: grid; place-items: center; width: var(--radio); height: var(--radio); background: var(--white); border: var(--edge) solid var(--ink); border-radius: var(--radius-round); }
.form__pip { width: var(--radio-pip); height: var(--radio-pip); border-radius: var(--radius-round); }
.form__choice:has(.form__radio:checked) .form__pip { background: var(--ink); }
.form__text { min-width: 0; }   /* the choice's words: they wrap beside the ring */
@media (prefers-reduced-motion: reduce) { .form__choice { transition: none; } }

/* what opens under a pick: the box, the email and Send (and Book a call, once there is a booking page). With the script
   on, they wait for a pick; with it off, they show from the start, so the form works as a plain one */
.form__more { display: grid; gap: var(--s5); max-width: var(--measure-text); padding-top: var(--s7); }
@media (scripting: enabled) { .form:not(:has(.form__radio:checked)) .form__more { display: none; } }
.form__field { display: grid; gap: var(--s2); }
.form__name { font: var(--type-choice); }
.form__opt { font: var(--type-body); color: var(--fg-2); }
.form__input {
  display: block; width: 100%; padding: var(--s3) var(--s4);
  font: var(--type-body); color: var(--ink); background: var(--white);
  border: var(--edge) solid var(--outline); border-radius: var(--radius);
}
.form__input--box { resize: vertical; }
.form__acts { margin-top: var(--s2); }
.form__send { border: 0; cursor: pointer; }

/* the error lines, each shown by its state, in a live region so it is read out */
.form__say { display: block; }   /* the live region: always there, so a line shown in it is read out */
.form__error { display: none; font: var(--type-body-strong); color: var(--fg); }
.form__end .form__error { padding-top: var(--s4); }
.form[data-state="empty"] .form__error[data-error="empty"],
.form[data-state="invalid"] .form__error[data-error="invalid"],
.form[data-state="failed"] .form__error[data-error="failed"] { display: block; }
/* without the script there is no bot check (Cloudflare Turnstile needs JavaScript), so the endpoint refuses the
   message and sends the browser back to #unsent: the failed line shows, with the address to write to instead */
.form__error:target { display: block; }
/* the bot check (Cloudflare Turnstile, on the test link and the live site only: components/form.js), above Send. With
   interaction-only it shows nothing unless Cloudflare needs the visitor to tick a box */
.form__bot { max-width: var(--measure-text); }

/* sending: Send held down on its block until the endpoint answers */
.form[data-state="sending"] .form__send { transform: translate(var(--stamp), var(--stamp)); box-shadow: 0 0 0 var(--btn-block); cursor: progress; }

/* sent: the thank-you line in the form's place. Without the script the endpoint sends the browser back to #thanks */
.form__thanks { display: none; }
.form__thanks:focus { outline: none; }   /* focused by the script so it is read out; not a control */
.form[data-state="sent"] .form__ask, .form:has(.form__thanks:target) .form__ask { display: none; }
.form[data-state="sent"] .form__thanks, .form__thanks:target { display: block; }

@media (min-width: 900px) {
  .form__choices { grid-template-columns: repeat(6, minmax(0, 1fr)); gap: var(--s5); }
  .form__choice { grid-column: span 2; }
  .form__choice:nth-child(n + 4) { grid-column: span 3; }
  /* on the homepage a step looser down the page (topic 625 msg 5609: "a tiny bit more spaced out"; styles/tokens.css,
     page--home): the choices' rows 32px apart, and what opens under a pick 64px below them, its parts 32px apart and
     Send 12px further */
  .page--home .form__choices { row-gap: var(--s6); }
  .page--home .form__more { gap: var(--s6); padding-top: var(--s8); }
  .page--home .form__acts { margin-top: var(--s3); }
}

/* ---- src/components/footer.css ---- */
.foot { padding: var(--s8) 0 var(--s7); font: var(--type-small); color: var(--fg-2); }
.foot__top { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s5) var(--s6); padding-bottom: var(--s5); border-bottom: var(--hair) solid var(--line); }
/* the logo is the link home (logo.css: a 44px target, its tile in the rotation's colour on hover and focus) */
/* the four links on one line at every width (topic 625 msg 5573: "I don't want there to be 2 lines of footers"): on a
   phone the line spans the column under the logo, its links spread evenly, and it sits out by each link's --s1 of
   padding (left, not a negative margin), so WORK's letters start on the logo's edge and PRODUCTS' end on the column's;
   from 900px it sits beside the logo */
.foot__links { position: relative; left: calc(-1 * var(--s1)); display: flex; flex-wrap: nowrap; justify-content: space-between; flex: 1 0 calc(100% + 2 * var(--s1)); gap: 0 var(--s2); }
/* the links are menu links (mlink.css: no underline, the rotation's colour, no grow, msgs 5168, 5169 and 5232), in the
   menu's capitals (msg 5098) */
.foot__link {
  display: inline-flex; align-items: center; justify-content: center; min-width: var(--tap); min-height: var(--tap); padding: 0 var(--s1);
  font: var(--type-menu); letter-spacing: var(--ls-button); text-transform: uppercase; color: var(--fg);
}
.foot__bottom { display: grid; gap: var(--s4); padding-top: var(--s5); }
/* the legal line */
.foot__fine { display: flex; flex-wrap: wrap; align-items: center; }
/* the icon links, email and LinkedIn, where the address stood until 27 Oct (msg 5201): menu links with an icon for a
   face, each centred in its 44px tap target. The row sits half a target's spare width to the left (left, not a
   negative margin), so the first icon's edge lines up with the logo's and the legal line's */
.foot__icons { display: flex; gap: 0 var(--s1); position: relative; left: calc((var(--icon) - var(--tap)) / 2); }
.foot__icon { display: inline-flex; align-items: center; justify-content: center; width: var(--tap); height: var(--tap); color: var(--fg); }
@media (min-width: 900px) {
  .foot__links { left: 0; flex: none; justify-content: flex-start; gap: 0 var(--s4); }
  .foot__bottom { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s4) var(--s6); }
}

/* ---- src/components/credit.css ---- */
/* Fixed: the same values on every site (mickey9s.com, sleekit.app). Change nothing here per site.
   A button's face and block, white with a thin ink line inside its edge (topic 149 msg 6488), a button's size (51px
   high, 24px each side, msg 6271), and one line, 8px apart: the label, Tanker at 19px with the buttons' tracking, its
   capitals on a button's lines (msg 6489); then Square9's real logo (msgs 6510 and 6513), bigger than the label
   (--credit-logo, msg 6517), its capitals' middle on the label's (--credit-logo-lift).
   Its space (topic 625 msg 6546): 32px clear above the face and below the block (--credit-space), at every width. The
   block hangs --stamp out of the face, so the bottom padding adds it. */
.credit { display: flex; justify-content: center; padding: var(--credit-space) 0 calc(var(--credit-space) + var(--stamp)); }
.credit__link { display: inline-flex; border-radius: var(--radius); text-decoration: none; }
.credit__card {
  display: inline-flex; align-items: center; gap: var(--s2); min-height: var(--credit-face-h); padding: 0 var(--s5);
  background: var(--white); border-radius: var(--radius);
  outline: var(--credit-line) solid var(--ink); outline-offset: calc(-1 * var(--credit-line));   /* the line inside the face's edge: it takes no room, so the sides stay 24px */
  box-shadow: var(--stamp) var(--stamp) 0 var(--ink); transition: transform var(--t-press), box-shadow var(--t-press);
}
.credit__label, .credit__logo { position: relative; top: calc(-1 * var(--credit-lift)); display: block; height: var(--credit-text); width: auto; }
.credit__logo { top: calc(-1 * var(--credit-logo-lift)); height: var(--credit-logo); }
.credit__link:hover .credit__card, .credit__link.is-hover .credit__card, .credit__link:active .credit__card { transform: translate(var(--stamp), var(--stamp)); box-shadow: 0 0 0 var(--ink); }
.credit__link:focus-visible, .credit__link.is-focus { outline: var(--focus-w) solid var(--ring); outline-offset: var(--focus-offset-stamp); }
@media (prefers-reduced-motion: reduce) { .credit__card { transition: none; } }

/* ---- src/components/skip.css ---- */
/* The skip link (rule 12): the first thing Tab reaches, to the page's main content. Kept above the top of the page,
   out of sight, until it has focus; then it sits on the banner's top left, a white face with ink words, underlined as
   a text link is, a full tap target, the focus ring drawn inside its edge so it shows on the face rather than on the
   banner's black. It never moves anything on the page: it sits over it. */
.skip {
  position: absolute; top: var(--s2); left: var(--s2); z-index: 20;
  display: inline-flex; align-items: center; min-height: var(--tap); padding: 0 var(--s4);
  background: var(--white); color: var(--ink); border-radius: var(--radius-focus);
  font: var(--type-body-strong); text-decoration: underline; text-decoration-thickness: var(--underline); text-underline-offset: var(--underline-offset);
  transform: translateY(calc(-100% - var(--s4)));
}
.skip:focus, .skip.is-focus { transform: none; outline: var(--focus-w) solid var(--ink); outline-offset: calc(-2 * var(--focus-w)); }
.skip.is-focus { position: static; }   /* the reference page's specimen: in place */

/* ---- src/components/drawings/ai-brain.css ---- */
/* Drawing: ai-brain (green, the free tool "ai-brain"). Made with its SVG by a script (tools/drawings/ai-brain.mjs).
   A session starts with the console empty. The journal (red) lifts, and a note comes out of it to the console, which
   writes its first line in green: what it remembered. Two more lines, then a note goes back into the journal, and the
   journal settles. The console clears: the session ends, and the files keep it. Still, with reduced motion: three
   lines on the console, the journal lifted. */
.draw__ai-brain-row, .draw__ai-brain-journal, .draw__ai-brain-note { animation: var(--t-draw) infinite; }
.draw__ai-brain-row { transform-origin: 27% 0; }
.draw__ai-brain-row1 { animation-name: draw-ai-brain-row1; }
.draw__ai-brain-row2 { animation-name: draw-ai-brain-row2; }
.draw__ai-brain-row3 { animation-name: draw-ai-brain-row3; }
.draw__ai-brain-journal { transform: translate(0.73%, -6.96%); animation-name: draw-ai-brain-journal; }
.draw__ai-brain-note { transform: translate(57.52%, 59.29%) scale(0); animation-name: draw-ai-brain-note; }
@keyframes draw-ai-brain-row1 {
  0% { transform: scaleX(0); }
  20.83% { transform: scaleX(0); animation-timing-function: linear; }
  27.5% { transform: none; }
  81.67% { transform: none; animation-timing-function: ease-in; }
  85.83% { transform: scaleX(0); }
  100% { transform: scaleX(0); }
}
@keyframes draw-ai-brain-row2 {
  0% { transform: scaleX(0); }
  30.83% { transform: scaleX(0); animation-timing-function: linear; }
  37.5% { transform: none; }
  81.67% { transform: none; animation-timing-function: ease-in; }
  85.83% { transform: scaleX(0); }
  100% { transform: scaleX(0); }
}
@keyframes draw-ai-brain-row3 {
  0% { transform: scaleX(0); }
  39.17% { transform: scaleX(0); animation-timing-function: linear; }
  44.17% { transform: none; }
  81.67% { transform: none; animation-timing-function: ease-in; }
  85.83% { transform: scaleX(0); }
  100% { transform: scaleX(0); }
}
@keyframes draw-ai-brain-journal {
  0% { transform: none; }
  5% { transform: none; animation-timing-function: ease-out; }
  8.33% { transform: translate(0.73%, -6.96%); }
  21.67% { transform: translate(0.73%, -6.96%); animation-timing-function: ease-in-out; }
  25% { transform: none; }
  49.17% { transform: none; animation-timing-function: ease-out; }
  52.5% { transform: translate(0.73%, -6.96%); }
  65.83% { transform: translate(0.73%, -6.96%); animation-timing-function: ease-in-out; }
  69.17% { transform: none; }
  100% { transform: none; }
}
@keyframes draw-ai-brain-note {
  0% { transform: translate(57.52%, 59.29%) scale(0); }
  8.33% { transform: translate(57.52%, 59.29%) scale(0); animation-timing-function: ease-out; }
  10.42% { transform: translate(57.52%, 59.29%) scale(1); animation-timing-function: ease-in-out; }
  14.17% { transform: translate(44.21%, 35.14%) scale(1); animation-timing-function: ease-in-out; }
  17.92% { transform: translate(30.9%, 25%) scale(1); animation-timing-function: ease-in; }
  20% { transform: translate(30.9%, 25%) scale(0); }
  52.5% { transform: translate(59.09%, 37.54%) scale(0); animation-timing-function: ease-out; }
  54.58% { transform: translate(59.09%, 37.54%) scale(1); animation-timing-function: ease-in-out; }
  58.33% { transform: translate(58.31%, 41.41%) scale(1); animation-timing-function: ease-in-out; }
  62.08% { transform: translate(57.52%, 59.29%) scale(1); animation-timing-function: ease-in; }
  64.17% { transform: translate(57.52%, 59.29%) scale(0); }
  100% { transform: translate(57.52%, 59.29%) scale(0); }
}

/* ---- src/components/drawings/assistants.css ---- */
/* Drawing: assistants (sky, "AI assistants for the leadership team, sharing one set of company facts"). Made with its
   SVG by a script (tools/drawings/assistants.mjs). Each assistant types in turn, a third of the cycle apart, and a fact card
   with its owner's coloured tab flies from its bubble into the back of the one shared register; then the register
   moves up a card. Still, with reduced motion: the register full, and the middle assistant's card on its way in. */
.draw__assistants-stack { animation: draw-assistants-stack var(--t-draw) infinite; }
.draw__assistants-fly { transform: scale(0); animation: var(--t-draw) infinite; }
.draw__assistants-fly1 { transform-origin: 50% 58%; animation-name: draw-assistants-fly1; }
.draw__assistants-fly2 { transform-origin: 50% 53%; animation-name: draw-assistants-fly2; }
.draw__assistants-fly3 { transform-origin: 50% 48%; animation-name: draw-assistants-fly3; }
.draw__assistants-fly2 { transform: translate(0%, -20%) scale(0.65); }
.draw__assistants-dot { animation: draw-assistants-dot var(--t-draw) infinite; }
.draw__assistants-dot12 { animation-delay: calc(var(--t-draw) * -0.9875); }
.draw__assistants-dot13 { animation-delay: calc(var(--t-draw) * -0.975); }
.draw__assistants-dot21 { animation-delay: calc(var(--t-draw) * -0.6667); }
.draw__assistants-dot22 { animation-delay: calc(var(--t-draw) * -0.6542); }
.draw__assistants-dot23 { animation-delay: calc(var(--t-draw) * -0.6417); }
.draw__assistants-dot31 { animation-delay: calc(var(--t-draw) * -0.3333); }
.draw__assistants-dot32 { animation-delay: calc(var(--t-draw) * -0.3208); }
.draw__assistants-dot33 { animation-delay: calc(var(--t-draw) * -0.3083); }
@keyframes draw-assistants-stack {
  0% { transform: none; }
  25% { transform: translate(0%, 0%); animation-timing-function: ease-in-out; }
  30% { transform: translate(0%, 5%); }
  58.33% { transform: translate(0%, 5%); animation-timing-function: ease-in-out; }
  63.33% { transform: translate(0%, 10%); }
  91.67% { transform: translate(0%, 10%); animation-timing-function: ease-in-out; }
  96.67% { transform: translate(0%, 15%); }
  100% { transform: translate(0%, 15%); }
}
@keyframes draw-assistants-fly1 {
  0% { transform: translate(-23%, -27%) scale(0); }
  12.92% { transform: translate(-23%, -27%) scale(0); animation-timing-function: ease-out; }
  15% { transform: translate(-23%, -27%) scale(0.3); animation-timing-function: cubic-bezier(0.4, 0, 0.3, 1); }
  23.33% { transform: translate(0%, -15%); }
  25% { transform: translate(0%, -15%); animation-timing-function: ease-in-out; }
  30% { transform: translate(0%, -10%); }
  58.33% { transform: translate(0%, -10%); animation-timing-function: ease-in-out; }
  63.33% { transform: translate(0%, -5%); }
  91.67% { transform: translate(0%, -5%); animation-timing-function: ease-in-out; }
  96.67% { transform: translate(0%, 0%); }
  100% { transform: none; }
}
@keyframes draw-assistants-fly2 {
  0% { transform: translate(0%, -30%) scale(0); }
  46.25% { transform: translate(0%, -30%) scale(0); animation-timing-function: ease-out; }
  48.33% { transform: translate(0%, -30%) scale(0.3); animation-timing-function: cubic-bezier(0.4, 0, 0.3, 1); }
  56.67% { transform: translate(0%, -10%); }
  58.33% { transform: translate(0%, -10%); animation-timing-function: ease-in-out; }
  63.33% { transform: translate(0%, -5%); }
  91.67% { transform: translate(0%, -5%); animation-timing-function: ease-in-out; }
  96.67% { transform: translate(0%, 0%); }
  100% { transform: none; }
}
@keyframes draw-assistants-fly3 {
  0% { transform: translate(23%, -17%) scale(0); }
  79.58% { transform: translate(23%, -17%) scale(0); animation-timing-function: ease-out; }
  81.67% { transform: translate(23%, -17%) scale(0.3); animation-timing-function: cubic-bezier(0.4, 0, 0.3, 1); }
  90% { transform: translate(0%, -5%); }
  91.67% { transform: translate(0%, -5%); animation-timing-function: ease-in-out; }
  96.67% { transform: translate(0%, 0%); }
  100% { transform: none; }
}
@keyframes draw-assistants-dot {
  0% { transform: none; }
  10% { transform: none; animation-timing-function: ease-in-out; }
  12.08% { transform: translateY(-2%); animation-timing-function: ease-in-out; }
  14.17% { transform: none; animation-timing-function: ease-in-out; }
  16.25% { transform: translateY(-2%); animation-timing-function: ease-in-out; }
  18.33% { transform: none; animation-timing-function: ease-in-out; }
  20.42% { transform: translateY(-2%); animation-timing-function: ease-in-out; }
  22.5% { transform: none; }
  100% { transform: none; }
}

/* ---- src/components/drawings/compliance.css ---- */
/* Drawing: compliance (sky, "Automated compliance checks for marketing"). Made with its SVG by a script
   (tools/drawings/compliance.mjs). The glass reads the email, then the advert, then the post, a quarter of the cycle each,
   and marks each one as it finishes: green, ochre, red. The red one drops out of the row, to go to a person, and the
   glass goes back to the start; then the marks come off for the next three. Still, with reduced motion: all three marked, the red one out of the row,
   the glass on the advert. */
.draw__compliance-lens { animation: draw-compliance-lens var(--t-draw) infinite; }
.draw__compliance-mark { animation: var(--t-draw) infinite; }
.draw__compliance-mark1 { transform-origin: 43% 26.5%; animation-name: draw-compliance-mark1; }
.draw__compliance-mark2 { transform-origin: 66.5% 18%; animation-name: draw-compliance-mark2; }
.draw__compliance-mark3 { transform-origin: 87% 27.5%; animation-name: draw-compliance-mark3; }
.draw__compliance-out { transform: translate(0%, 17%) rotate(5deg); transform-origin: 72% 48%; animation: draw-compliance-out var(--t-draw) infinite; }
@keyframes draw-compliance-lens {
  0% { transform: translate(-22%, 6%); }
  2.5% { transform: translate(-22%, 6%); animation-timing-function: ease-in-out; }
  9.17% { transform: translate(-22%, 6%) translateX(-2%); animation-timing-function: ease-in-out; }
  15.83% { transform: translate(-22%, 6%) translateX(2%); animation-timing-function: ease-in-out; }
  22.5% { transform: translate(-22%, 6%); animation-timing-function: ease-in-out; }
  27.5% { transform: translate(0%, 0%); animation-timing-function: ease-in-out; }
  34.17% { transform: translate(0%, 0%) translateX(-2%); animation-timing-function: ease-in-out; }
  40.83% { transform: translate(0%, 0%) translateX(2%); animation-timing-function: ease-in-out; }
  47.5% { transform: translate(0%, 0%); animation-timing-function: ease-in-out; }
  52.5% { transform: translate(22%, 7%); animation-timing-function: ease-in-out; }
  59.17% { transform: translate(22%, 7%) translateX(-2%); animation-timing-function: ease-in-out; }
  65.83% { transform: translate(22%, 7%) translateX(2%); animation-timing-function: ease-in-out; }
  72.5% { transform: translate(22%, 7%); animation-timing-function: ease-in-out; }
  75.83% { transform: translate(22%, 7%); animation-timing-function: ease-in-out; }
  87.5% { transform: translate(-22%, 6%); }
  100% { transform: translate(-22%, 6%); }
}
@keyframes draw-compliance-mark1 {
  0% { transform: scale(0); }
  17.5% { transform: scale(0); animation-timing-function: ease-out; }
  19.17% { transform: scale(1.2); animation-timing-function: ease-in-out; }
  20.42% { transform: none; }
  90% { transform: none; animation-timing-function: ease-in; }
  92.5% { transform: scale(0); }
  100% { transform: scale(0); }
}
@keyframes draw-compliance-mark2 {
  0% { transform: scale(0); }
  42.5% { transform: scale(0); animation-timing-function: ease-out; }
  44.17% { transform: scale(1.2); animation-timing-function: ease-in-out; }
  45.42% { transform: none; }
  90% { transform: none; animation-timing-function: ease-in; }
  92.5% { transform: scale(0); }
  100% { transform: scale(0); }
}
@keyframes draw-compliance-mark3 {
  0% { transform: scale(0); }
  67.5% { transform: scale(0); animation-timing-function: ease-out; }
  69.17% { transform: scale(1.2); animation-timing-function: ease-in-out; }
  70.42% { transform: none; }
  90% { transform: none; animation-timing-function: ease-in; }
  92.5% { transform: scale(0); }
  100% { transform: scale(0); }
}
@keyframes draw-compliance-out {
  0% { transform: none; }
  71.67% { transform: none; animation-timing-function: cubic-bezier(0.5, 0, 0.7, 1.3); }
  76.67% { transform: translate(0%, 17%) rotate(5deg); }
  90% { transform: translate(0%, 17%) rotate(5deg); animation-timing-function: ease-in-out; }
  96.67% { transform: none; }
  100% { transform: none; }
}

/* ---- src/components/drawings/experiment.css ---- */
/* Drawing: experiment (sky, the free tool "experiment"). Made with its SVG by a script (tools/drawings/experiment.mjs).
   Two versions start level. Their scores come in: both bars rise, the red one stops lower, the ochre one (the change)
   higher, and it is ticked green, kept. The red one sinks into the floor, undone. Then the tick goes, the ochre bar
   goes back down and the red version rises again, for the next test. Still, with reduced motion: both scores in, the
   ochre version higher and ticked. */
.draw__experiment-v, .draw__experiment-kept { animation: var(--t-draw) infinite; }
.draw__experiment-v1 { animation-name: draw-experiment-v1; }
.draw__experiment-v2 { animation-name: draw-experiment-v2; }
.draw__experiment-kept { transform-origin: 91.5% 15.5%; animation-name: draw-experiment-kept; }
@keyframes draw-experiment-v1 {
  0% { transform: translateY(13%); }
  5% { transform: translateY(13%); animation-timing-function: ease-in-out; }
  18.33% { transform: none; }
  45% { transform: none; animation-timing-function: ease-in; }
  56.67% { transform: translateY(61%); }
  81.67% { transform: translateY(61%); animation-timing-function: ease-out; }
  95% { transform: translateY(13%); }
  100% { transform: translateY(13%); }
}
@keyframes draw-experiment-v2 {
  0% { transform: translateY(36%); }
  5% { transform: translateY(36%); animation-timing-function: ease-in-out; }
  31.67% { transform: none; }
  80% { transform: none; animation-timing-function: ease-in-out; }
  91.67% { transform: translateY(36%); }
  100% { transform: translateY(36%); }
}
@keyframes draw-experiment-kept {
  0% { transform: scale(0); }
  33.33% { transform: scale(0); animation-timing-function: ease-out; }
  35.42% { transform: scale(1.12); animation-timing-function: ease-in-out; }
  37.08% { transform: none; }
  77.5% { transform: none; animation-timing-function: ease-in; }
  80% { transform: scale(0); }
  100% { transform: scale(0); }
}

/* ---- src/components/drawings/hr.css ---- */
/* Drawing: hr (ochre, "In-house HR platform instead of a paid subscription"). Made with its SVG by a script
   (tools/drawings/hr.mjs). Three times a cycle, leave is booked across a few days (a sky bar fills along them) and
   the stamp comes over and approves it, leaving a green tick; then the month turns over to a fresh page.
   Still, with reduced motion: the month with its three bookings approved, the stamp at rest. */
.draw__hr-stamp { transform-origin: 82.5% 51%; animation: draw-hr-stamp var(--t-draw) infinite; }
.draw__hr-bar { animation: var(--t-draw) ease-in-out infinite; }
.draw__hr-bar1 { transform-origin: -16.5% -10%; animation-name: draw-hr-book1; }
.draw__hr-bar2 { transform-origin: 6.5% 12%; animation-name: draw-hr-book2; }
.draw__hr-bar3 { transform-origin: -28% 23%; animation-name: draw-hr-book3; }
.draw__hr-mark { animation: var(--t-draw) step-end infinite; }
.draw__hr-mark1 { animation-name: draw-hr-mark1; }
.draw__hr-mark2 { animation-name: draw-hr-mark2; }
.draw__hr-mark3 { animation-name: draw-hr-mark3; }
.draw__hr-page { transform-origin: 0 -31%; animation: draw-hr-turn var(--t-draw) infinite; }
@keyframes draw-hr-stamp {
  0% { transform: translate(0%, 0%) rotate(0deg) scale(1, 1); }
  4.17% { transform: translate(0%, 0%) rotate(0deg) scale(1, 1); animation-timing-function: ease-in-out; }
  10% { transform: translate(-38.89%, -3.48%) rotate(-13deg) scale(1, 1); animation-timing-function: ease-in; }
  12.08% { transform: translate(-38.89%, 4.52%) rotate(-13deg) scale(1, 1); }
  12.92% { transform: translate(-38.89%, 4.52%) rotate(-13deg) scale(1, 0.92); }
  13.75% { transform: translate(-38.89%, 4.52%) rotate(-13deg) scale(1, 1); animation-timing-function: ease-out; }
  16.25% { transform: translate(-38.89%, -3.48%) rotate(-13deg) scale(1, 1); animation-timing-function: ease-in-out; }
  21.67% { transform: translate(0%, 0%) rotate(0deg) scale(1, 1); }
  29.17% { transform: translate(0%, 0%) rotate(0deg) scale(1, 1); animation-timing-function: ease-in-out; }
  35% { transform: translate(-19.79%, 16.93%) rotate(-13deg) scale(1, 1); animation-timing-function: ease-in; }
  37.08% { transform: translate(-19.79%, 24.93%) rotate(-13deg) scale(1, 1); }
  37.92% { transform: translate(-19.79%, 24.93%) rotate(-13deg) scale(1, 0.92); }
  38.75% { transform: translate(-19.79%, 24.93%) rotate(-13deg) scale(1, 1); animation-timing-function: ease-out; }
  41.25% { transform: translate(-19.79%, 16.93%) rotate(-13deg) scale(1, 1); animation-timing-function: ease-in-out; }
  46.67% { transform: translate(0%, 0%) rotate(0deg) scale(1, 1); }
  54.17% { transform: translate(0%, 0%) rotate(0deg) scale(1, 1); animation-timing-function: ease-in-out; }
  60% { transform: translate(-47.47%, 30.39%) rotate(-13deg) scale(1, 1); animation-timing-function: ease-in; }
  62.08% { transform: translate(-47.47%, 38.39%) rotate(-13deg) scale(1, 1); }
  62.92% { transform: translate(-47.47%, 38.39%) rotate(-13deg) scale(1, 0.92); }
  63.75% { transform: translate(-47.47%, 38.39%) rotate(-13deg) scale(1, 1); animation-timing-function: ease-out; }
  66.25% { transform: translate(-47.47%, 30.39%) rotate(-13deg) scale(1, 1); animation-timing-function: ease-in-out; }
  71.67% { transform: translate(0%, 0%) rotate(0deg) scale(1, 1); }
  100% { transform: translate(0%, 0%) rotate(0deg) scale(1, 1); }
}
@keyframes draw-hr-book1 { 0% { transform: scaleX(0); } 7.5%, 100% { transform: none; } }
@keyframes draw-hr-book2 { 0%, 25% { transform: scaleX(0); } 32.5%, 100% { transform: none; } }
@keyframes draw-hr-book3 { 0%, 50% { transform: scaleX(0); } 57.5%, 100% { transform: none; } }
@keyframes draw-hr-mark1 { 0% { transform: scale(0); } 12.92%, 100% { transform: none; } }
@keyframes draw-hr-mark2 { 0% { transform: scale(0); } 37.92%, 100% { transform: none; } }
@keyframes draw-hr-mark3 { 0% { transform: scale(0); } 62.92%, 100% { transform: none; } }
@keyframes draw-hr-turn {
  0%, 83.33% { transform: none; animation-timing-function: ease-in; }
  90%, 100% { transform: scaleY(0); }
}

/* ---- src/components/drawings/knowledge.css ---- */
/* Drawing: knowledge (green, "Company knowledge base that staff ask in Slack"). Made with its SVG by a script
   (tools/drawings/knowledge.mjs). A question comes in; the red binder lifts off the shelf; the answer comes back with a red
   tab, its source, and the binder goes back. Then a second question, answered from the ochre binder. Then the chat
   clears. Still, with reduced motion: both questions answered, the ochre binder lifted. */
.draw__knowledge-msg, .draw__knowledge-bin { animation: var(--t-draw) infinite; }
.draw__knowledge-msg1 { transform-origin: 8.5% 30.5%; animation-name: draw-knowledge-msg1; }
.draw__knowledge-msg2 { transform-origin: 56.5% 50.5%; animation-name: draw-knowledge-msg2; }
.draw__knowledge-msg3 { transform-origin: 8.5% 64.5%; animation-name: draw-knowledge-msg3; }
.draw__knowledge-msg4 { transform-origin: 56.5% 84.5%; animation-name: draw-knowledge-msg4; }
.draw__knowledge-bin1 { animation-name: draw-knowledge-bin1; }
.draw__knowledge-bin2 { transform: translateY(-9%); animation-name: draw-knowledge-bin2; }
@keyframes draw-knowledge-msg1 {
  0% { transform: scale(0); }
  3.33% { transform: scale(0); animation-timing-function: ease-out; }
  5.42% { transform: scale(1.08); animation-timing-function: ease-in-out; }
  6.67% { transform: none; }
  85% { transform: none; animation-timing-function: ease-in; }
  87.5% { transform: scale(0); }
  100% { transform: scale(0); }
}
@keyframes draw-knowledge-msg2 {
  0% { transform: scale(0); }
  16.67% { transform: scale(0); animation-timing-function: ease-out; }
  18.75% { transform: scale(1.08); animation-timing-function: ease-in-out; }
  20% { transform: none; }
  86% { transform: none; animation-timing-function: ease-in; }
  88.5% { transform: scale(0); }
  100% { transform: scale(0); }
}
@keyframes draw-knowledge-msg3 {
  0% { transform: scale(0); }
  41.67% { transform: scale(0); animation-timing-function: ease-out; }
  43.75% { transform: scale(1.08); animation-timing-function: ease-in-out; }
  45% { transform: none; }
  87% { transform: none; animation-timing-function: ease-in; }
  89.5% { transform: scale(0); }
  100% { transform: scale(0); }
}
@keyframes draw-knowledge-msg4 {
  0% { transform: scale(0); }
  55% { transform: scale(0); animation-timing-function: ease-out; }
  57.08% { transform: scale(1.08); animation-timing-function: ease-in-out; }
  58.33% { transform: none; }
  88% { transform: none; animation-timing-function: ease-in; }
  90.5% { transform: scale(0); }
  100% { transform: scale(0); }
}
@keyframes draw-knowledge-bin1 {
  0% { transform: none; }
  10% { transform: none; animation-timing-function: ease-out; }
  13.33% { transform: translateY(-9%); }
  28.33% { transform: translateY(-9%); animation-timing-function: ease-in-out; }
  31.67% { transform: none; }
  100% { transform: none; }
}
@keyframes draw-knowledge-bin2 {
  0% { transform: none; }
  48.33% { transform: none; animation-timing-function: ease-out; }
  51.67% { transform: translateY(-9%); }
  66.67% { transform: translateY(-9%); animation-timing-function: ease-in-out; }
  70% { transform: none; }
  100% { transform: none; }
}

/* ---- src/components/drawings/lab.css ---- */
/* Drawing: lab (green, the About me band). Made with its SVG by a script (tools/drawings/lab.mjs). Antony as
   a mad scientist at his bench. Twice a cycle one messy sheet drops into his machine's hopper, he tips his flask
   over it and pours his potion in, and the sheet sinks in with the potion. In the machine's window the potion rises,
   the sheet drops into it and swirls while the gear turns and the machine chugs, then turns over into a green card
   with a tick. The card rises out of the window and slides out of the machine's side onto the pile; the potion drains
   away. His eyes follow the pour, the window, then the card, and his brows go up as it lands.
   Still, with reduced motion: mid-pour, the sheet going into the hopper with the potion, the window full, a finished
   card coming out. */
.draw__lab-tip { transform-origin: 65% 28%; animation: draw-lab-tip calc(var(--t-draw) / 2) ease-in-out infinite; }
.draw__lab-pour { transform-origin: 72.6% 35.6%; animation: draw-lab-pour calc(var(--t-draw) / 2) ease-out infinite; }
.draw__lab-tail { transform-origin: 74.1% 50%; animation: draw-lab-tail calc(var(--t-draw) / 2) ease-in infinite; }
.draw__lab-in { transform-origin: 78.5% 41%; animation: draw-lab-in calc(var(--t-draw) / 2) infinite; }
.draw__lab-level { animation: draw-lab-level calc(var(--t-draw) / 2) ease-in-out infinite; }
.draw__lab-gear { transform-origin: 73.5% 74.5%; animation: draw-lab-turn calc(var(--t-draw) / 2) ease-in-out infinite; }
.draw__lab-machine { animation: draw-lab-chug calc(var(--t-draw) / 2) ease-in-out infinite; }
.draw__lab-mix { transform: translate(73.5%, 56%); animation: draw-lab-mix calc(var(--t-draw) / 2) infinite; }
.draw__lab-flip { animation: draw-lab-flip calc(var(--t-draw) / 2) ease-in-out infinite; }
.draw__lab-paper { animation: draw-lab-paper calc(var(--t-draw) / 2) infinite; }
.draw__lab-done { opacity: 0; animation: draw-lab-done calc(var(--t-draw) / 2) infinite; }
.draw__lab-out { transform: translate(89.5%, 63.5%) rotate(18deg); animation: draw-lab-out calc(var(--t-draw) / 2) infinite; }
.draw__lab-eyes { transform: translate(1%, -0.75%); animation: draw-lab-look calc(var(--t-draw) / 2) ease-in-out infinite; }
.draw__lab-brow { animation: draw-lab-brow calc(var(--t-draw) / 2) ease-in-out infinite; }
@keyframes draw-lab-tip {
  0%, 3%, 56%, 100% { transform: rotate(-105deg); }
  14%, 42% { transform: none; }
}
@keyframes draw-lab-pour { 0%, 12% { transform: scaleY(0); } 17%, 38% { transform: none; } 38.1%, 100% { transform: scaleY(0); } }
@keyframes draw-lab-tail { 0%, 32% { transform: none; animation-timing-function: ease-in; } 38% { transform: scaleY(0); } 38.1%, 100% { transform: none; } }
@keyframes draw-lab-in {
  0%, 17% { transform: none; opacity: 1; animation-timing-function: ease-in; }
  34% { transform: translate(-9%, 19%) rotate(-6deg); opacity: 1; }
  34.01%, 88% { transform: translate(0%, -7%); opacity: 0; animation-timing-function: linear; }
  90% { transform: translate(0%, -5.95%); opacity: 1; animation-timing-function: ease-out; }
  100% { transform: none; opacity: 1; }
}
@keyframes draw-lab-level {
  0%, 18% { transform: translateY(16%); animation-timing-function: ease-out; }
  32%, 72% { transform: none; animation-timing-function: ease-in; }
  90%, 100% { transform: translateY(16%); }
}
@keyframes draw-lab-turn { 0%, 32% { transform: none; } 70%, 100% { transform: rotate(180deg); } }
@keyframes draw-lab-chug {
  0%, 40%, 46%, 52%, 58%, 100% { transform: none; }
  43%, 49%, 55% { transform: translateY(-0.75%); }
}
@keyframes draw-lab-mix {
  0%, 32% { transform: translate(73.5%, 56%); animation-timing-function: ease-out; }
  38% { transform: translate(73.5%, 64.5%) rotate(-14deg); animation-timing-function: ease-in-out; }
  43% { transform: translate(74.25%, 64%) rotate(12deg); animation-timing-function: ease-in-out; }
  48% { transform: translate(73%, 64.5%) rotate(-8deg); animation-timing-function: ease-in-out; }
  52%, 64% { transform: translate(73.5%, 64.5%); animation-timing-function: ease-in; }
  69%, 100% { transform: translate(73.5%, 56%); }
}
@keyframes draw-lab-flip { 0%, 52%, 58%, 100% { transform: none; } 55% { transform: scaleX(0); } }
@keyframes draw-lab-paper { 0%, 55% { opacity: 1; } 55.01%, 69% { opacity: 0; } 69.01%, 100% { opacity: 1; } }
@keyframes draw-lab-done { 0%, 55% { opacity: 0; } 55.01%, 69% { opacity: 1; } 69.01%, 100% { opacity: 0; } }
@keyframes draw-lab-out {
  0%, 68% { transform: translate(79%, 59%); animation-timing-function: ease-out; }
  75% { transform: translate(88.5%, 59%); animation-timing-function: ease-in-out; }
  80% { transform: translate(90.75%, 66.25%) rotate(12deg); animation-timing-function: ease-in; }
  85%, 100% { transform: translate(91.75%, 71.25%); }
}
@keyframes draw-lab-look {
  0%, 100% { transform: translate(1%, 0%); }
  10%, 32% { transform: translate(1.25%, -1%); }
  40%, 64% { transform: translate(1.25%, 0.5%); }
  74%, 88% { transform: translate(1.5%, 1%); }
}
@keyframes draw-lab-brow { 0%, 80%, 94%, 100% { transform: none; } 84%, 90% { transform: translateY(-1.25%); } }

/* ---- src/components/drawings/orders.css ---- */
/* Drawing: orders (red, "Order processing that chases every signature and document"). Made with its SVG by a
   script (tools/drawings/orders.mjs). The clock's hand goes round every third of the cycle; the first two times it comes
   round the clock rings and a chaser flies off. Then the signature is written in, the contract is sealed green and
   slides off to be sent on, and the next contract slides in, unsigned.
   Still, with reduced motion: the contract signed and sealed, the clock, and a chaser on its way. */
.draw__orders-hand { transform-origin: 75% 56%; animation: draw-orders-hand calc(var(--t-draw) / 3) linear infinite; }
.draw__orders-clock { transform-origin: 75% 74.5%; animation: draw-orders-rock var(--t-draw) infinite; }
.draw__orders-ring { transform-origin: 75% 40.5%; animation: draw-orders-ring var(--t-draw) infinite; }
.draw__orders-env { animation: draw-orders-env var(--t-draw) infinite; }
.draw__orders-doc { animation: draw-orders-doc var(--t-draw) infinite; }
.draw__orders-cover { transform-origin: 49% 0; transform: scaleX(0); animation: draw-orders-cover var(--t-draw) infinite; }
.draw__orders-seal { transform-origin: 49% 79%; animation: draw-orders-seal var(--t-draw) infinite; }
@keyframes draw-orders-hand { to { transform: rotate(360deg); } }
@keyframes draw-orders-rock {
  0% { transform: none; animation-timing-function: ease-in-out; }
  1% { transform: rotate(-6deg); animation-timing-function: ease-in-out; }
  2.67% { transform: rotate(5deg); animation-timing-function: ease-in-out; }
  4.33% { transform: rotate(-3deg); animation-timing-function: ease-in-out; }
  6% { transform: rotate(1.5deg); animation-timing-function: ease-in-out; }
  7.5% { transform: none; }
  33.33% { transform: none; animation-timing-function: ease-in-out; }
  34.33% { transform: rotate(-6deg); animation-timing-function: ease-in-out; }
  36% { transform: rotate(5deg); animation-timing-function: ease-in-out; }
  37.67% { transform: rotate(-3deg); animation-timing-function: ease-in-out; }
  39.33% { transform: rotate(1.5deg); animation-timing-function: ease-in-out; }
  40.83% { transform: none; }
  100% { transform: none; }
}
@keyframes draw-orders-ring {
  0% { transform: scale(0); animation-timing-function: ease-out; }
  1.25% { transform: none; }
  6.25% { transform: none; animation-timing-function: ease-in; }
  7.5% { transform: scale(0); }
  33.33% { transform: scale(0); animation-timing-function: ease-out; }
  34.58% { transform: none; }
  39.58% { transform: none; animation-timing-function: ease-in; }
  40.83% { transform: scale(0); }
  100% { transform: scale(0); }
}
@keyframes draw-orders-env {
  0% { transform: translate(-11%, 35%); animation-timing-function: cubic-bezier(0.3, 0, 0.6, 1); }
  12.5% { transform: translate(27%, -38%); }
  12.58% { transform: translate(-11%, 35%); }
  33.33% { transform: translate(-11%, 35%); animation-timing-function: cubic-bezier(0.3, 0, 0.6, 1); }
  45.83% { transform: translate(27%, -38%); }
  45.92% { transform: translate(-11%, 35%); }
  100% { transform: translate(-11%, 35%); }
}
@keyframes draw-orders-cover {
  0% { transform: none; }
  51.67% { transform: none; animation-timing-function: ease-in-out; }
  60% { transform: scaleX(0); }
  81.58% { transform: scaleX(0); }
  81.67% { transform: none; }
  100% { transform: none; }
}
@keyframes draw-orders-seal {
  0% { transform: scale(0); }
  61.67% { transform: scale(0); animation-timing-function: ease-out; }
  63.75% { transform: scale(1.15); animation-timing-function: ease-in-out; }
  65% { transform: none; }
  81.58% { transform: none; }
  81.67% { transform: scale(0); }
  100% { transform: scale(0); }
}
@keyframes draw-orders-doc {
  0% { transform: none; }
  75% { transform: none; animation-timing-function: ease-in; }
  81.58% { transform: translate(95%, -8%); }
  81.67% { transform: translate(-90%, 8%); }
  86.67% { transform: translate(-90%, 8%); animation-timing-function: ease-out; }
  95% { transform: none; }
  100% { transform: none; }
}

/* ---- src/components/drawings/pipeline.css ---- */
/* Drawing: pipeline (red, "Sales pipeline that tracks every lead"). Made with its SVG by a script
   (tools/drawings/pipeline.mjs). A see-through pipeline: lead cards ride through it, seen all the way, and take each
   stage's colour as they pass its flange (sky, ochre, then green with a tick: won). Each card takes the whole cycle
   to cross, 4 of them a quarter apart; the gauge's needle kicks as each one is won.
   Still, with reduced motion: the cards spread along the pipe, one in each stage. */
.draw__pipeline-card { animation: draw-pipeline-run var(--t-draw) linear infinite; }
.draw__pipeline-sky, .draw__pipeline-ochre, .draw__pipeline-green { animation: var(--t-draw) step-end infinite; }
.draw__pipeline-sky { animation-name: draw-pipeline-sky; }
.draw__pipeline-ochre { animation-name: draw-pipeline-ochre; }
.draw__pipeline-green { animation-name: draw-pipeline-green; }
.draw__pipeline-card1, .draw__pipeline-card1 * { animation-delay: calc(var(--t-draw) * -0.13); }
.draw__pipeline-card1 { transform: translate(8.39%, 44%) rotate(0deg) translate(0%, -13%); }
.draw__pipeline-card1 .draw__pipeline-sky { transform: scale(0); }
.draw__pipeline-card1 .draw__pipeline-ochre { transform: scale(0); }
.draw__pipeline-card1 .draw__pipeline-green { transform: scale(0); }
.draw__pipeline-card2, .draw__pipeline-card2 * { animation-delay: calc(var(--t-draw) * -0.38); }
.draw__pipeline-card2 { transform: translate(38%, 44%) rotate(42.33deg) translate(0%, -13%); }
.draw__pipeline-card2 .draw__pipeline-sky { transform: none; }
.draw__pipeline-card2 .draw__pipeline-ochre { transform: scale(0); }
.draw__pipeline-card2 .draw__pipeline-green { transform: scale(0); }
.draw__pipeline-card3, .draw__pipeline-card3 * { animation-delay: calc(var(--t-draw) * -0.63); }
.draw__pipeline-card3 { transform: translate(64%, 62%) rotate(44.19deg) translate(0%, 13%); }
.draw__pipeline-card3 .draw__pipeline-sky { transform: none; }
.draw__pipeline-card3 .draw__pipeline-ochre { transform: none; }
.draw__pipeline-card3 .draw__pipeline-green { transform: scale(0); }
.draw__pipeline-card4, .draw__pipeline-card4 * { animation-delay: calc(var(--t-draw) * -0.88); }
.draw__pipeline-card4 { transform: translate(93.18%, 62%) rotate(0deg) translate(0%, 13%); }
.draw__pipeline-card4 .draw__pipeline-sky { transform: none; }
.draw__pipeline-card4 .draw__pipeline-ochre { transform: none; }
.draw__pipeline-card4 .draw__pipeline-green { transform: none; }
.draw__pipeline-needle { transform-origin: 85% 49%; animation: draw-pipeline-kick calc(var(--t-draw) / 4) ease-out infinite; animation-delay: calc(var(--t-draw) * -0.125); }
@keyframes draw-pipeline-run {
  0% { transform: translate(-12%, 44%) rotate(0deg) translate(0%, -13%); }
  31.88% { transform: translate(38%, 44%) rotate(0deg) translate(0%, -13%); }
  44.9% { transform: translate(38%, 44%) rotate(90deg) translate(0%, -13%); }
  56.38% { transform: translate(64%, 62%) rotate(90deg) translate(0%, 13%); }
  69.4% { transform: translate(64%, 62%) rotate(0deg) translate(0%, 13%); }
  100% { transform: translate(112%, 62%) rotate(0deg) translate(0%, 13%); }
}
@keyframes draw-pipeline-kick { 0% { transform: none; } 12% { transform: rotate(70deg); animation-timing-function: ease-in-out; } 70%, 100% { transform: none; } }
@keyframes draw-pipeline-sky { 0% { transform: scale(0); } 25.5%, 100% { transform: none; } }
@keyframes draw-pipeline-ochre { 0% { transform: scale(0); } 50.5%, 100% { transform: none; } }
@keyframes draw-pipeline-green { 0% { transform: scale(0); } 75.5%, 100% { transform: none; } }

/* ---- src/components/drawings/plan.css ---- */
/* Drawing: plan (sky, "Delivery plan that manages itself"). Made with its SVG by a script
   (tools/drawings/plan.mjs). The bars slide left one repeat (84 units) every half cycle,
   behind the today line; left of the line they are drawn in their colour. The voice line in the bubble moves, each
   stroke a step behind the last. Still, with reduced motion: the plan as drawn, the voice line at rest. */
.draw__plan-slide { transform-box: fill-box; animation: draw-plan-slide calc(var(--t-draw) / 2) linear infinite; }
.draw__plan-voice { transform-origin: 0 16%; animation: draw-plan-voice calc(var(--t-draw) / 12) ease-in-out infinite alternate; }
.draw__plan-voice1 { animation-delay: calc(var(--t-draw) / -48); }
.draw__plan-voice2 { animation-delay: calc(var(--t-draw) / -24); }
.draw__plan-voice3 { animation-delay: calc(var(--t-draw) / -16); }
.draw__plan-voice4 { animation-delay: calc(var(--t-draw) / -12); }
.draw__plan-voice5 { animation-delay: calc(var(--t-draw) * -5 / 48); }
@keyframes draw-plan-slide { to { transform: translateX(-33.87%); } }
@keyframes draw-plan-voice { from { transform: scaleY(1); } to { transform: scaleY(0.45); } }

/* ---- src/components/drawings/pricing.css ---- */
/* Drawing: pricing (green, "Weekly pricing report that matches the company's own quotes"). Made with its SVG by
   a script (tools/drawings/pricing.mjs). A pulse runs along each feed into the report, a third of the cycle apart. Row by
   row, the worked-out price appears, then the company's own quote beside it, the same length, and the row ticks green.
   Once every row matches, the report clears for the next week. Still, with reduced motion: the report full, every row
   ticked. */
.draw__pricing-cell, .draw__pricing-ok { animation: var(--t-draw) infinite; }
.draw__pricing-c11 { transform-origin: 39% 0; animation-name: draw-pricing-c11; }
.draw__pricing-c12 { transform-origin: 63% 0; animation-name: draw-pricing-c12; }
.draw__pricing-ok1 { transform-origin: 86.5% 32%; animation-name: draw-pricing-ok1; }
.draw__pricing-c21 { transform-origin: 39% 0; animation-name: draw-pricing-c21; }
.draw__pricing-c22 { transform-origin: 63% 0; animation-name: draw-pricing-c22; }
.draw__pricing-ok2 { transform-origin: 86.5% 44.5%; animation-name: draw-pricing-ok2; }
.draw__pricing-c31 { transform-origin: 39% 0; animation-name: draw-pricing-c31; }
.draw__pricing-c32 { transform-origin: 63% 0; animation-name: draw-pricing-c32; }
.draw__pricing-ok3 { transform-origin: 86.5% 57%; animation-name: draw-pricing-ok3; }
.draw__pricing-c41 { transform-origin: 39% 0; animation-name: draw-pricing-c41; }
.draw__pricing-c42 { transform-origin: 63% 0; animation-name: draw-pricing-c42; }
.draw__pricing-ok4 { transform-origin: 86.5% 69.5%; animation-name: draw-pricing-ok4; }
.draw__pricing-c51 { transform-origin: 39% 0; animation-name: draw-pricing-c51; }
.draw__pricing-c52 { transform-origin: 63% 0; animation-name: draw-pricing-c52; }
.draw__pricing-ok5 { transform-origin: 86.5% 82%; animation-name: draw-pricing-ok5; }
.draw__pricing-pulse { animation: draw-pricing-pulse calc(var(--t-draw) / 3) infinite; }
.draw__pricing-pulse2 { animation-delay: calc(var(--t-draw) * -0.8889); }
.draw__pricing-pulse3 { animation-delay: calc(var(--t-draw) * -0.7778); }
@keyframes draw-pricing-c11 {
  0% { transform: scaleX(0); }
  8.33% { transform: scaleX(0); animation-timing-function: ease-out; }
  11.67% { transform: none; }
  85% { transform: none; animation-timing-function: ease-in; }
  88.33% { transform: scaleX(0); }
  100% { transform: scaleX(0); }
}
@keyframes draw-pricing-c12 {
  0% { transform: scaleX(0); }
  11.67% { transform: scaleX(0); animation-timing-function: ease-out; }
  15% { transform: none; }
  85% { transform: none; animation-timing-function: ease-in; }
  88.33% { transform: scaleX(0); }
  100% { transform: scaleX(0); }
}
@keyframes draw-pricing-ok1 {
  0% { transform: scale(0); }
  15.83% { transform: scale(0); animation-timing-function: ease-out; }
  17.5% { transform: scale(1.2); animation-timing-function: ease-in-out; }
  18.75% { transform: none; }
  85% { transform: none; animation-timing-function: ease-in; }
  87.5% { transform: scale(0); }
  100% { transform: scale(0); }
}
@keyframes draw-pricing-c21 {
  0% { transform: scaleX(0); }
  20.83% { transform: scaleX(0); animation-timing-function: ease-out; }
  24.17% { transform: none; }
  85% { transform: none; animation-timing-function: ease-in; }
  88.33% { transform: scaleX(0); }
  100% { transform: scaleX(0); }
}
@keyframes draw-pricing-c22 {
  0% { transform: scaleX(0); }
  24.17% { transform: scaleX(0); animation-timing-function: ease-out; }
  27.5% { transform: none; }
  85% { transform: none; animation-timing-function: ease-in; }
  88.33% { transform: scaleX(0); }
  100% { transform: scaleX(0); }
}
@keyframes draw-pricing-ok2 {
  0% { transform: scale(0); }
  28.33% { transform: scale(0); animation-timing-function: ease-out; }
  30% { transform: scale(1.2); animation-timing-function: ease-in-out; }
  31.25% { transform: none; }
  85% { transform: none; animation-timing-function: ease-in; }
  87.5% { transform: scale(0); }
  100% { transform: scale(0); }
}
@keyframes draw-pricing-c31 {
  0% { transform: scaleX(0); }
  33.33% { transform: scaleX(0); animation-timing-function: ease-out; }
  36.67% { transform: none; }
  85% { transform: none; animation-timing-function: ease-in; }
  88.33% { transform: scaleX(0); }
  100% { transform: scaleX(0); }
}
@keyframes draw-pricing-c32 {
  0% { transform: scaleX(0); }
  36.67% { transform: scaleX(0); animation-timing-function: ease-out; }
  40% { transform: none; }
  85% { transform: none; animation-timing-function: ease-in; }
  88.33% { transform: scaleX(0); }
  100% { transform: scaleX(0); }
}
@keyframes draw-pricing-ok3 {
  0% { transform: scale(0); }
  40.83% { transform: scale(0); animation-timing-function: ease-out; }
  42.5% { transform: scale(1.2); animation-timing-function: ease-in-out; }
  43.75% { transform: none; }
  85% { transform: none; animation-timing-function: ease-in; }
  87.5% { transform: scale(0); }
  100% { transform: scale(0); }
}
@keyframes draw-pricing-c41 {
  0% { transform: scaleX(0); }
  45.83% { transform: scaleX(0); animation-timing-function: ease-out; }
  49.17% { transform: none; }
  85% { transform: none; animation-timing-function: ease-in; }
  88.33% { transform: scaleX(0); }
  100% { transform: scaleX(0); }
}
@keyframes draw-pricing-c42 {
  0% { transform: scaleX(0); }
  49.17% { transform: scaleX(0); animation-timing-function: ease-out; }
  52.5% { transform: none; }
  85% { transform: none; animation-timing-function: ease-in; }
  88.33% { transform: scaleX(0); }
  100% { transform: scaleX(0); }
}
@keyframes draw-pricing-ok4 {
  0% { transform: scale(0); }
  53.33% { transform: scale(0); animation-timing-function: ease-out; }
  55% { transform: scale(1.2); animation-timing-function: ease-in-out; }
  56.25% { transform: none; }
  85% { transform: none; animation-timing-function: ease-in; }
  87.5% { transform: scale(0); }
  100% { transform: scale(0); }
}
@keyframes draw-pricing-c51 {
  0% { transform: scaleX(0); }
  58.33% { transform: scaleX(0); animation-timing-function: ease-out; }
  61.67% { transform: none; }
  85% { transform: none; animation-timing-function: ease-in; }
  88.33% { transform: scaleX(0); }
  100% { transform: scaleX(0); }
}
@keyframes draw-pricing-c52 {
  0% { transform: scaleX(0); }
  61.67% { transform: scaleX(0); animation-timing-function: ease-out; }
  65% { transform: none; }
  85% { transform: none; animation-timing-function: ease-in; }
  88.33% { transform: scaleX(0); }
  100% { transform: scaleX(0); }
}
@keyframes draw-pricing-ok5 {
  0% { transform: scale(0); }
  65.83% { transform: scale(0); animation-timing-function: ease-out; }
  67.5% { transform: scale(1.2); animation-timing-function: ease-in-out; }
  68.75% { transform: none; }
  85% { transform: none; animation-timing-function: ease-in; }
  87.5% { transform: scale(0); }
  100% { transform: scale(0); }
}
@keyframes draw-pricing-pulse {
  0% { transform: translateX(-15%); animation-timing-function: linear; }
  37.5% { transform: translateX(25%); }
  100% { transform: translateX(25%); }
}

/* ---- src/components/drawings/renewlist.css ---- */
/* Drawing: renewlist (red, "Renewals list that finds every customer due to renew"). Made with its SVG by a script
   (tools/drawings/renewlist.mjs). The calendar's day page turns over; three customers due to renew drop onto the list. One
   row at a time, a reminder envelope flies off until a reply comes back; then the row ticks green and its date tab
   moves on along its timeline. Then the rows drop out and the next day begins.
   Still, with reduced motion: two rows renewed, ticked and moved on, and the third's reminder on its way. */
.draw__renewlist-row, .draw__renewlist-tick, .draw__renewlist-tab, .draw__renewlist-env, .draw__renewlist-bub, .draw__renewlist-page { animation: var(--t-draw) infinite; }
.draw__renewlist-row, .draw__renewlist-tick, .draw__renewlist-tab { transform-box: fill-box; }
.draw__renewlist-page { transform-origin: 18% 5.5%; animation-name: draw-renewlist-page; }
.draw__renewlist-row1 { animation-name: draw-renewlist-row1; }
.draw__renewlist-tick1 { transform-origin: 50% 50%; animation-name: draw-renewlist-tick1; }
.draw__renewlist-tab1 { animation-name: draw-renewlist-tab1; }
.draw__renewlist-env1 { transform-origin: 87.6% 43.6%; animation-name: draw-renewlist-env1; }
.draw__renewlist-bub1 { transform-origin: 75% 50.5%; animation-name: draw-renewlist-bub1; }
.draw__renewlist-row2 { animation-name: draw-renewlist-row2; }
.draw__renewlist-tick2 { transform-origin: 50% 50%; animation-name: draw-renewlist-tick2; }
.draw__renewlist-tab2 { animation-name: draw-renewlist-tab2; }
.draw__renewlist-env2 { transform-origin: 87.6% 58.6%; animation-name: draw-renewlist-env2; }
.draw__renewlist-bub2 { transform-origin: 75% 65.5%; animation-name: draw-renewlist-bub2; }
.draw__renewlist-row3 { animation-name: draw-renewlist-row3; }
.draw__renewlist-tick3 { transform-origin: 50% 50%; animation-name: draw-renewlist-tick3; }
.draw__renewlist-tab3 { animation-name: draw-renewlist-tab3; }
.draw__renewlist-env3 { transform-origin: 87.6% 73.6%; animation-name: draw-renewlist-env3; }
.draw__renewlist-bub3 { transform-origin: 75% 80.5%; animation-name: draw-renewlist-bub3; }
.draw__renewlist-tick3, .draw__renewlist-bub1, .draw__renewlist-bub2, .draw__renewlist-bub3, .draw__renewlist-env1, .draw__renewlist-env2 { transform: scale(0); }
.draw__renewlist-tab3 { transform: translateX(-228.6%); }
@keyframes draw-renewlist-page {
  0% { transform: none; animation-timing-function: ease-in; }
  5.83% { transform: scaleY(0); }
  99.58% { transform: scaleY(0); }
  100% { transform: none; }
}
@keyframes draw-renewlist-row1 {
  0% { transform: translateY(-625%); }
  6.67% { transform: translateY(-625%); animation-timing-function: ease-in; }
  10.42% { transform: none; animation-timing-function: ease-out; }
  11.25% { transform: translateY(-12.5%); animation-timing-function: ease-in; }
  12.08% { transform: none; }
  91.67% { transform: none; animation-timing-function: ease-in; }
  95.83% { transform: translateY(625%); }
  100% { transform: translateY(625%); }
}
@keyframes draw-renewlist-tick1 {
  0% { transform: scale(0); }
  35.83% { transform: scale(0); animation-timing-function: ease-out; }
  37.5% { transform: scale(1.15); animation-timing-function: ease-in-out; }
  38.5% { transform: none; }
  99.17% { transform: none; }
  99.58% { transform: scale(0); }
  100% { transform: scale(0); }
}
@keyframes draw-renewlist-tab1 {
  0% { transform: translateX(-228.6%); }
  35.83% { transform: translateX(-228.6%); animation-timing-function: ease-in-out; }
  40.83% { transform: none; }
  99.17% { transform: none; }
  99.58% { transform: translateX(-228.6%); }
  100% { transform: translateX(-228.6%); }
}
@keyframes draw-renewlist-env1 {
  0% { transform: translate(-9.6%, 5.4%) scale(0); }
  18.33% { transform: translate(-9.6%, 5.4%) scale(0); animation-timing-function: ease-out; }
  20% { transform: translate(-9.6%, 5.4%); animation-timing-function: ease-in; }
  28.33% { transform: translate(22.4%, -12.6%); }
  28.42% { transform: translate(-9.6%, 5.4%) scale(0); }
  100% { transform: translate(-9.6%, 5.4%) scale(0); }
}
@keyframes draw-renewlist-bub1 {
  0% { transform: translate(30%, -5%) scale(0); }
  29.17% { transform: translate(30%, -5%) scale(0); }
  29.25% { transform: translate(30%, -5%); animation-timing-function: ease-out; }
  35% { transform: none; }
  35.83% { transform: none; animation-timing-function: ease-in; }
  37.5% { transform: scale(0); }
  100% { transform: scale(0); }
}
@keyframes draw-renewlist-row2 {
  0% { transform: translateY(-625%); }
  8.75% { transform: translateY(-625%); animation-timing-function: ease-in; }
  12.5% { transform: none; animation-timing-function: ease-out; }
  13.33% { transform: translateY(-12.5%); animation-timing-function: ease-in; }
  14.17% { transform: none; }
  91.67% { transform: none; animation-timing-function: ease-in; }
  95.83% { transform: translateY(625%); }
  100% { transform: translateY(625%); }
}
@keyframes draw-renewlist-tick2 {
  0% { transform: scale(0); }
  60% { transform: scale(0); animation-timing-function: ease-out; }
  61.67% { transform: scale(1.15); animation-timing-function: ease-in-out; }
  62.67% { transform: none; }
  99.17% { transform: none; }
  99.58% { transform: scale(0); }
  100% { transform: scale(0); }
}
@keyframes draw-renewlist-tab2 {
  0% { transform: translateX(-228.6%); }
  60% { transform: translateX(-228.6%); animation-timing-function: ease-in-out; }
  65% { transform: none; }
  99.17% { transform: none; }
  99.58% { transform: translateX(-228.6%); }
  100% { transform: translateX(-228.6%); }
}
@keyframes draw-renewlist-env2 {
  0% { transform: translate(-9.6%, 5.4%) scale(0); }
  42.5% { transform: translate(-9.6%, 5.4%) scale(0); animation-timing-function: ease-out; }
  44.17% { transform: translate(-9.6%, 5.4%); animation-timing-function: ease-in; }
  52.5% { transform: translate(22.4%, -12.6%); }
  52.58% { transform: translate(-9.6%, 5.4%) scale(0); }
  100% { transform: translate(-9.6%, 5.4%) scale(0); }
}
@keyframes draw-renewlist-bub2 {
  0% { transform: translate(30%, -5%) scale(0); }
  53.33% { transform: translate(30%, -5%) scale(0); }
  53.42% { transform: translate(30%, -5%); animation-timing-function: ease-out; }
  59.17% { transform: none; }
  60% { transform: none; animation-timing-function: ease-in; }
  61.67% { transform: scale(0); }
  100% { transform: scale(0); }
}
@keyframes draw-renewlist-row3 {
  0% { transform: translateY(-625%); }
  10.83% { transform: translateY(-625%); animation-timing-function: ease-in; }
  14.58% { transform: none; animation-timing-function: ease-out; }
  15.42% { transform: translateY(-12.5%); animation-timing-function: ease-in; }
  16.25% { transform: none; }
  91.67% { transform: none; animation-timing-function: ease-in; }
  95.83% { transform: translateY(625%); }
  100% { transform: translateY(625%); }
}
@keyframes draw-renewlist-tick3 {
  0% { transform: scale(0); }
  84.17% { transform: scale(0); animation-timing-function: ease-out; }
  85.83% { transform: scale(1.15); animation-timing-function: ease-in-out; }
  86.83% { transform: none; }
  99.17% { transform: none; }
  99.58% { transform: scale(0); }
  100% { transform: scale(0); }
}
@keyframes draw-renewlist-tab3 {
  0% { transform: translateX(-228.6%); }
  84.17% { transform: translateX(-228.6%); animation-timing-function: ease-in-out; }
  89.17% { transform: none; }
  99.17% { transform: none; }
  99.58% { transform: translateX(-228.6%); }
  100% { transform: translateX(-228.6%); }
}
@keyframes draw-renewlist-env3 {
  0% { transform: translate(-9.6%, 5.4%) scale(0); }
  66.67% { transform: translate(-9.6%, 5.4%) scale(0); animation-timing-function: ease-out; }
  68.33% { transform: translate(-9.6%, 5.4%); animation-timing-function: ease-in; }
  76.67% { transform: translate(22.4%, -12.6%); }
  76.75% { transform: translate(-9.6%, 5.4%) scale(0); }
  100% { transform: translate(-9.6%, 5.4%) scale(0); }
}
@keyframes draw-renewlist-bub3 {
  0% { transform: translate(30%, -5%) scale(0); }
  77.5% { transform: translate(30%, -5%) scale(0); }
  77.58% { transform: translate(30%, -5%); animation-timing-function: ease-out; }
  83.33% { transform: none; }
  84.17% { transform: none; animation-timing-function: ease-in; }
  85.83% { transform: scale(0); }
  100% { transform: scale(0); }
}

/* ---- src/components/drawings/screening.css ---- */
/* Drawing: screening (ochre, "Sanctions screening built in-house in three days"). Made with its SVG by a script
   (tools/drawings/screening.mjs). A card steps into the arch every third of the cycle; the beam reads it and the lamp
   answers. The first two are clear: green, and they walk on to buy. The third is close to a listed name: ochre, and it
   goes into the tray in front, for a person, then slides off to them. Still, with reduced motion: a card in the arch
   with the lamp green, the next one waiting, the held one in the tray. */
.draw__screening-card { animation: var(--t-draw) infinite; }
.draw__screening-card1 { animation-name: draw-screening-card1; }
.draw__screening-card2 { animation-delay: calc(var(--t-draw) * -0.6667); animation-name: draw-screening-card2; }
.draw__screening-card3 { animation-delay: calc(var(--t-draw) * -0.3333); animation-name: draw-screening-card3; }
.draw__screening-badge { transform-origin: 9.5% -27.5%; animation: var(--t-draw) infinite; }
.draw__screening-badge1 { animation-name: draw-screening-badge; }
.draw__screening-badge2 { transform: scale(0); animation-delay: calc(var(--t-draw) * -0.6667); animation-name: draw-screening-badge; }
.draw__screening-badge3 { animation-delay: calc(var(--t-draw) * -0.3333); animation-name: draw-screening-badge3; }
.draw__screening-beam { transform: scale(1, 0); animation: draw-screening-beam calc(var(--t-draw) / 3) infinite; }
.draw__screening-go, .draw__screening-hold { transform-origin: 51% 25%; animation: var(--t-draw) infinite; }
.draw__screening-go { animation-name: draw-screening-go; }
.draw__screening-hold { transform: scale(0); animation-name: draw-screening-hold; }
@keyframes draw-screening-card1 {
  0% { transform: translate(-38%, 0%); animation-timing-function: ease-in; }
  3.33% { transform: translate(-19%, -3%); animation-timing-function: ease-out; }
  6.67% { transform: translate(0%, 0%); }
  21.67% { transform: translate(0%, 0%); animation-timing-function: ease-in; }
  31.67% { transform: translate(74%, 0%); }
  31.75% { transform: translate(-66%, 0%); }
  75% { transform: translate(-66%, 0%); animation-timing-function: ease-in; }
  79.17% { transform: translate(-52%, -3%); animation-timing-function: ease-out; }
  83.33% { transform: translate(-38%, 0%); }
  100% { transform: translate(-38%, 0%); }
}
@keyframes draw-screening-card2 {
  0% { transform: translate(0%, 0%); animation-timing-function: ease-in; }
  3.33% { transform: translate(19%, -3%); animation-timing-function: ease-out; }
  6.67% { transform: translate(38%, 0%); }
  21.67% { transform: translate(38%, 0%); animation-timing-function: ease-in; }
  31.67% { transform: translate(112%, 0%); }
  31.75% { transform: translate(-28%, 0%); }
  75% { transform: translate(-28%, 0%); animation-timing-function: ease-in; }
  79.17% { transform: translate(-14%, -3%); animation-timing-function: ease-out; }
  83.33% { transform: translate(0%, 0%); }
  100% { transform: translate(0%, 0%); }
}
@keyframes draw-screening-card3 {
  0% { transform: translate(-69%, -9%); animation-timing-function: ease-in; }
  3.33% { transform: translate(-50%, -12%); animation-timing-function: ease-out; }
  6.67% { transform: translate(-31%, -9%); }
  21.67% { transform: translate(-31%, -9%); animation-timing-function: ease-in; }
  25.83% { transform: translate(-15.5%, -7.5%); animation-timing-function: ease-out; }
  30% { transform: translate(0%, 0%); }
  66.67% { transform: translate(0%, 0%); animation-timing-function: ease-in; }
  73.33% { transform: translate(43%, 0%); }
  73.42% { transform: translate(-97%, -9%); }
  75% { transform: translate(-97%, -9%); animation-timing-function: ease-in; }
  79.17% { transform: translate(-83%, -12%); animation-timing-function: ease-out; }
  83.33% { transform: translate(-69%, -9%); }
  100% { transform: translate(-69%, -9%); }
}
@keyframes draw-screening-badge {
  0% { transform: scale(0); }
  20% { transform: scale(0); animation-timing-function: ease-out; }
  21.67% { transform: scale(1.2); animation-timing-function: ease-in-out; }
  22.92% { transform: none; }
  31.67% { transform: none; }
  31.75% { transform: scale(0); }
  100% { transform: scale(0); }
}
@keyframes draw-screening-badge3 {
  0% { transform: scale(0); }
  20% { transform: scale(0); animation-timing-function: ease-out; }
  21.67% { transform: scale(1.2); animation-timing-function: ease-in-out; }
  22.92% { transform: none; }
  73.33% { transform: none; }
  73.42% { transform: scale(0); }
  100% { transform: scale(0); }
}
@keyframes draw-screening-beam {
  0% { transform: scale(1, 0); }
  20% { transform: scale(1, 0); }
  20.25% { transform: none; animation-timing-function: ease-in-out; }
  57.5% { transform: translateY(23%); }
  57.75% { transform: scale(1, 0); }
  100% { transform: scale(1, 0); }
}
@keyframes draw-screening-go {
  0% { transform: scale(0); }
  20% { transform: scale(0); animation-timing-function: ease-out; }
  21.67% { transform: none; }
  30% { transform: none; animation-timing-function: ease-in; }
  31.67% { transform: scale(0); }
  53.33% { transform: scale(0); animation-timing-function: ease-out; }
  55% { transform: none; }
  63.33% { transform: none; animation-timing-function: ease-in; }
  65% { transform: scale(0); }
  100% { transform: scale(0); }
}
@keyframes draw-screening-hold {
  0% { transform: scale(0); }
  86.67% { transform: scale(0); animation-timing-function: ease-out; }
  88.33% { transform: none; }
  96.67% { transform: none; animation-timing-function: ease-in; }
  98.33% { transform: scale(0); }
  100% { transform: scale(0); }
}

/* ---- src/components/drawings/staff.css ---- */
/* Drawing: staff (ochre, "Staff platform for a contact centre"). Made with its SVG by a script
   (tools/drawings/staff.mjs). A change is typed into the customer's account. The red key turns, and its lamp goes green;
   two seconds later the white key turns, from the other side, and its lamp goes green. Only then does the field go
   green and tick. Then the keys turn back and the field clears for the next change.
   Still, with reduced motion: both keys turned, both lamps green, the change in and ticked. */
.draw__staff-key { animation: var(--t-draw) infinite; }
.draw__staff-key1 { transform: rotate(90deg); transform-origin: 29% 77%; animation-name: draw-staff-key1; }
.draw__staff-key2 { transform: rotate(-90deg); transform-origin: 71% 77%; animation-name: draw-staff-key2; }
.draw__staff-lamp { animation: var(--t-draw) infinite; }
.draw__staff-lamp1 { transform-origin: 29% 68%; animation-name: draw-staff-lamp1; }
.draw__staff-lamp2 { transform-origin: 71% 68%; animation-name: draw-staff-lamp2; }
.draw__staff-typed { transform-origin: 22% 0; animation: draw-staff-typed var(--t-draw) infinite; }
.draw__staff-ok { transform-origin: 17% 0; animation: draw-staff-ok var(--t-draw) infinite; }
.draw__staff-tick { transform-origin: 74% 39%; animation: draw-staff-tick var(--t-draw) infinite; }
@keyframes draw-staff-key1 {
  0% { transform: none; }
  18.33% { transform: none; animation-timing-function: ease-in-out; }
  23.33% { transform: rotate(90deg); }
  78.33% { transform: rotate(90deg); animation-timing-function: ease-in-out; }
  83.33% { transform: none; }
  100% { transform: none; }
}
@keyframes draw-staff-key2 {
  0% { transform: none; }
  35% { transform: none; animation-timing-function: ease-in-out; }
  40% { transform: rotate(-90deg); }
  78.33% { transform: rotate(-90deg); animation-timing-function: ease-in-out; }
  83.33% { transform: none; }
  100% { transform: none; }
}
@keyframes draw-staff-lamp1 {
  0% { transform: scale(0); }
  23.33% { transform: scale(0); animation-timing-function: ease-out; }
  25% { transform: scale(1.2); animation-timing-function: ease-in-out; }
  26.25% { transform: none; }
  81.67% { transform: none; animation-timing-function: ease-in; }
  83.33% { transform: scale(0); }
  100% { transform: scale(0); }
}
@keyframes draw-staff-lamp2 {
  0% { transform: scale(0); }
  40% { transform: scale(0); animation-timing-function: ease-out; }
  41.67% { transform: scale(1.2); animation-timing-function: ease-in-out; }
  42.92% { transform: none; }
  81.67% { transform: none; animation-timing-function: ease-in; }
  83.33% { transform: scale(0); }
  100% { transform: scale(0); }
}
@keyframes draw-staff-typed {
  0% { transform: scaleX(0); }
  3.33% { transform: scaleX(0); animation-timing-function: ease-out; }
  13.33% { transform: none; }
  78.33% { transform: none; animation-timing-function: ease-in; }
  81.67% { transform: scaleX(0); }
  100% { transform: scaleX(0); }
}
@keyframes draw-staff-ok {
  0% { transform: scaleX(0); }
  43.33% { transform: scaleX(0); animation-timing-function: ease-in-out; }
  47.5% { transform: none; }
  78.33% { transform: none; animation-timing-function: ease-in; }
  81.67% { transform: scaleX(0); }
  100% { transform: scaleX(0); }
}
@keyframes draw-staff-tick {
  0% { transform: scale(0); }
  48.33% { transform: scale(0); animation-timing-function: ease-out; }
  50% { transform: scale(1.2); animation-timing-function: ease-in-out; }
  51.25% { transform: none; }
  76.67% { transform: none; animation-timing-function: ease-in; }
  78.33% { transform: scale(0); }
  100% { transform: scale(0); }
}

/* ---- src/components/drawings/store.css ---- */
/* Drawing: store (green, "Online store with automatic ordering"). Made with its SVG by a script
   (tools/drawings/store.mjs). Twice a cycle: the pay button on the shop's page is pressed, the order flies from the
   page straight into the printer, the printer prints the T-shirt out of its front, and the T-shirt drops away,
   posted. Nobody touches it. Still, with reduced motion: the page with its pay button, and a T-shirt just printed. */
.draw__store-pay { transform-origin: 26% 64%; animation: draw-store-pay calc(var(--t-draw) / 2) ease-in-out infinite; }
.draw__store-order { transform: translate(28.26%, 65.09%) scale(0); animation: draw-store-order calc(var(--t-draw) / 2) linear infinite; }
.draw__store-print { animation: draw-store-print calc(var(--t-draw) / 2) infinite; }
@keyframes draw-store-pay { 0%, 2%, 9%, 100% { transform: none; } 5% { transform: scale(0.84); } }
@keyframes draw-store-order {
  0%, 6% { transform: translate(28.26%, 65.09%) scale(0); animation-timing-function: ease-out; }
  10% { transform: translate(28.26%, 65.09%) scale(1); }
  11.7% { transform: translate(29.52%, 65.09%) scale(1); }
  13.4% { transform: translate(33.19%, 64.29%) scale(1); }
  15.1% { transform: translate(38.25%, 60.63%) scale(1); }
  16.8% { transform: translate(43.35%, 53.46%) scale(1); }
  18.5% { transform: translate(49.08%, 43.69%) scale(1); }
  20.2% { transform: translate(56.28%, 35%) scale(1); }
  21.9% { transform: translate(63.99%, 30.88%) scale(1); }
  23.6% { transform: translate(70.22%, 30.18%) scale(1); }
  25.3% { transform: translate(73.98%, 30.54%) scale(1); }
  27% { transform: translate(75.22%, 30.77%) scale(1); animation-timing-function: ease-in; }
  33% { transform: translate(75.22%, 49.77%) scale(1); }
  33.01%, 100% { transform: translate(75.22%, 49.77%) scale(0); }
}
@keyframes draw-store-print {
  0%, 34% { transform: translateY(-24%); animation-timing-function: linear; }
  58%, 77% { transform: none; animation-timing-function: cubic-bezier(0.5, 0, 0.9, 0.4); }
  93%, 100% { transform: translateY(39%); }
}

/* ---- src/components/spots/ai-tools.css ---- */
/* Spot: ai-tools (Services, "AI tools & pipelines"). Made with its SVG by a script (tools/spots/ai-tools.mjs). The voice
   line rises and falls the whole cycle: each stroke grows and shrinks about its height, two thirds of a second behind
   the one before, so a wave runs across it three times a cycle, as it speaks. Still, with reduced motion: the bubble and its voice line. */
.spot__ai-tools-voice { transform-origin: 0 45.54%; animation: spot-ai-tools-speak var(--t-draw) infinite; }
.spot__ai-tools-voice1 { animation-delay: calc(var(--t-draw) / -18 * 4); }
.spot__ai-tools-voice2 { animation-delay: calc(var(--t-draw) / -18 * 3); }
.spot__ai-tools-voice3 { animation-delay: calc(var(--t-draw) / -18 * 2); }
.spot__ai-tools-voice4 { animation-delay: calc(var(--t-draw) / -18 * 1); }
.spot__ai-tools-voice5 { animation-delay: calc(var(--t-draw) / -18 * 0); }
@keyframes spot-ai-tools-speak {
  0%, 33.33%, 66.67%, 100% { transform: scaleY(0.69); animation-timing-function: cubic-bezier(0.37, 0, 0.63, 1); }
  16.67%, 50%, 83.33% { transform: scaleY(1.31); animation-timing-function: cubic-bezier(0.37, 0, 0.63, 1); }
}

/* ---- src/components/spots/automation.css ---- */
/* Spot: automation (Services, "Automation"). Made with its SVG by a script (tools/spots/automation.mjs). The cog turns
   steadily, two teeth a cycle, so it looks the same at the end as at the start and never stops; the tick in the hub
   stays put. Still, with reduced motion: the cog and its tick. */
.spot__automation-cog { transform-origin: 50% 50%; animation: spot-automation-turn var(--t-draw) linear infinite; }
@keyframes spot-automation-turn {
  from { transform: none; }
  to { transform: rotate(90deg); }
}

/* ---- src/components/spots/integration.css ---- */
/* Spot: integration (Services, "Integrations"). Made with its SVG by a script (tools/spots/integration.mjs). Three times a
   cycle the plug and the socket ease apart, the prongs clear, and ease back together, without a stop.
   Still, with reduced motion: the plug in its socket. */
.spot__integration-plug { animation: spot-integration-out var(--t-draw) infinite; }
.spot__integration-socket { animation: spot-integration-back var(--t-draw) infinite; }
@keyframes spot-integration-out {
  0%, 33.33%, 66.67%, 100% { transform: none; animation-timing-function: cubic-bezier(0.37, 0, 0.63, 1); }
  16.67%, 50%, 83.33% { transform: translateX(-4.02%); animation-timing-function: cubic-bezier(0.37, 0, 0.63, 1); }
}
@keyframes spot-integration-back {
  0%, 33.33%, 66.67%, 100% { transform: none; animation-timing-function: cubic-bezier(0.37, 0, 0.63, 1); }
  16.67%, 50%, 83.33% { transform: translateX(4.02%); animation-timing-function: cubic-bezier(0.37, 0, 0.63, 1); }
}

/* ---- src/components/spots/mistakes.css ---- */
/* Spot: mistakes (AI-native business, "Fewer mistakes": quality going up). Made with its SVG by a script
   (tools/spots/mistakes.mjs). Twice a cycle the tick is drawn slowly, its short stroke and then its long one at one even
   pace, holds, shrinks away into its middle, and is drawn again. Still, with reduced motion: the tick. */
.spot__mistakes-short, .spot__mistakes-long { transform-origin: 0 0; }
.spot__mistakes-short { animation: spot-mistakes-short var(--t-draw) linear infinite; }
.spot__mistakes-long { animation: spot-mistakes-long var(--t-draw) linear infinite; }
.spot__mistakes-tick { transform-origin: 50% 63.39%; animation: spot-mistakes-tick var(--t-draw) linear infinite; }
@keyframes spot-mistakes-short {
  0% { transform: scaleX(0); animation-timing-function: linear; }
  7.08% { transform: none; animation-timing-function: step-end; }
  45.83% { transform: scaleX(0); animation-timing-function: step-end; }
  50% { transform: scaleX(0); animation-timing-function: linear; }
  57.08% { transform: none; animation-timing-function: step-end; }
  95.83% { transform: scaleX(0); animation-timing-function: step-end; }
  100% { transform: scaleX(0); }
}
@keyframes spot-mistakes-long {
  0% { transform: scaleX(0); animation-timing-function: step-end; }
  7.08% { transform: scaleX(0); animation-timing-function: linear; }
  20.83% { transform: none; animation-timing-function: step-end; }
  45.83% { transform: scaleX(0); animation-timing-function: step-end; }
  50% { transform: scaleX(0); animation-timing-function: step-end; }
  57.08% { transform: scaleX(0); animation-timing-function: linear; }
  70.83% { transform: none; animation-timing-function: step-end; }
  95.83% { transform: scaleX(0); animation-timing-function: step-end; }
  100% { transform: scaleX(0); }
}
@keyframes spot-mistakes-tick {
  0% { transform: none; animation-timing-function: step-end; }
  33.33% { transform: none; animation-timing-function: linear; }
  43.75% { transform: scale(0); animation-timing-function: step-end; }
  48.75% { transform: none; animation-timing-function: step-end; }
  50% { transform: none; animation-timing-function: step-end; }
  83.33% { transform: none; animation-timing-function: linear; }
  93.75% { transform: scale(0); animation-timing-function: step-end; }
  98.75% { transform: none; animation-timing-function: step-end; }
  100% { transform: none; }
}

/* ---- src/components/spots/own.css ---- */
/* Spot: own (AI-native business, "Systems you own": independence going up). Made with its SVG by a script
   (tools/spots/own.mjs). The padlock stands open. Twice a cycle its shackle rises and falls back, slowly, about
   where it stands, coming close to shut; it never shuts. Still, with reduced motion: the lock open. */
.spot__own-shackle { animation: spot-own-rise var(--t-draw) infinite; }
@keyframes spot-own-rise {
  0%, 50%, 100% { transform: translateY(3.01%); animation-timing-function: cubic-bezier(0.37, 0, 0.63, 1); }
  25%, 75% { transform: translateY(-3.01%); animation-timing-function: cubic-bezier(0.37, 0, 0.63, 1); }
}

/* ---- src/components/spots/processes.css ---- */
/* Spot: processes (AI-native business, "Automated processes": speed going up). Made with its SVG by a script
   (tools/spots/processes.mjs). Twice a cycle the progress bar fills at an even pace, holds full, empties, and fills again.
   Still, with reduced motion: the bar full. */
.spot__processes-bar { transform-origin: 38.21% 0; animation: spot-processes-run var(--t-draw) linear infinite; }
@keyframes spot-processes-run {
  0% { transform: scaleX(0); animation-timing-function: linear; }
  20.83% { transform: none; animation-timing-function: step-end; }
  25% { transform: none; animation-timing-function: linear; }
  45.83% { transform: scaleX(0); animation-timing-function: step-end; }
  50% { transform: scaleX(0); animation-timing-function: linear; }
  70.83% { transform: none; animation-timing-function: step-end; }
  75% { transform: none; animation-timing-function: linear; }
  95.83% { transform: scaleX(0); animation-timing-function: step-end; }
  100% { transform: scaleX(0); }
}

/* ---- src/components/spots/software.css ---- */
/* Spot: software (Services, "Software delivery"). Made with its SVG by a script (tools/spots/software.mjs). Three times a
   cycle the window rises out of the box and sinks back, about where it stands. Still, with reduced motion:
   the window standing in the open box. */
.spot__software-app { animation: spot-software-lift var(--t-draw) infinite; }
@keyframes spot-software-lift {
  0%, 33.33%, 66.67%, 100% { transform: translateY(3.01%); animation-timing-function: cubic-bezier(0.37, 0, 0.63, 1); }
  16.67%, 50%, 83.33% { transform: translateY(-3.01%); animation-timing-function: cubic-bezier(0.37, 0, 0.63, 1); }
}

/* ---- src/components/spots/subscriptions.css ---- */
/* Spot: subscriptions (AI-native business, "Fewer subscriptions": cost going down). Made with its SVG by a script
   (tools/spots/subscriptions.mjs). Twice a cycle a pen crosses the bill out, one stroke and then the other, at an even pace;
   the cross holds, shrinks away into its middle, and is drawn again. Still, with reduced motion: the bill crossed out. */
.spot__subscriptions-stroke1, .spot__subscriptions-stroke2 { transform-origin: 0 0; }
.spot__subscriptions-stroke1 { animation: spot-subscriptions-stroke1 var(--t-draw) linear infinite; }
.spot__subscriptions-stroke2 { animation: spot-subscriptions-stroke2 var(--t-draw) linear infinite; }
.spot__subscriptions-cross { transform-origin: 50% 44.64%; animation: spot-subscriptions-cross var(--t-draw) linear infinite; }
@keyframes spot-subscriptions-stroke1 {
  0% { transform: scaleX(0); animation-timing-function: linear; }
  14.58% { transform: none; animation-timing-function: step-end; }
  47.92% { transform: scaleX(0); animation-timing-function: step-end; }
  50% { transform: scaleX(0); animation-timing-function: linear; }
  64.58% { transform: none; animation-timing-function: step-end; }
  97.92% { transform: scaleX(0); animation-timing-function: step-end; }
  100% { transform: scaleX(0); }
}
@keyframes spot-subscriptions-stroke2 {
  0% { transform: scaleX(0); animation-timing-function: step-end; }
  14.58% { transform: scaleX(0); animation-timing-function: linear; }
  29.17% { transform: none; animation-timing-function: step-end; }
  47.92% { transform: scaleX(0); animation-timing-function: step-end; }
  50% { transform: scaleX(0); animation-timing-function: step-end; }
  64.58% { transform: scaleX(0); animation-timing-function: linear; }
  79.17% { transform: none; animation-timing-function: step-end; }
  97.92% { transform: scaleX(0); animation-timing-function: step-end; }
  100% { transform: scaleX(0); }
}
@keyframes spot-subscriptions-cross {
  0% { transform: none; animation-timing-function: step-end; }
  37.5% { transform: none; animation-timing-function: linear; }
  45.83% { transform: scale(0); animation-timing-function: step-end; }
  49.17% { transform: none; animation-timing-function: step-end; }
  50% { transform: none; animation-timing-function: step-end; }
  87.5% { transform: none; animation-timing-function: linear; }
  95.83% { transform: scale(0); animation-timing-function: step-end; }
  99.17% { transform: none; animation-timing-function: step-end; }
  100% { transform: none; }
}
