/*
  TigerStripes' own stylesheet, served at /css/site.css.

  A theme is files, and this is the one that makes the site look like the site: the same address the
  built-in stylesheet is served at, with different contents, which is what overriding an asset by name
  means. Nothing here is generated and nothing here is per-page - there is one stylesheet for the whole
  site, so a change to the type scale is a change to every page at once.

  Four ideas do most of the work:

  1. **Paper, ink and one accent.** A warm off-white page, near-black type, and a single orange used
     for the things a reader should act on. Colour is not decoration here; it says "this is the thing
     to do" and "this is the part of the sentence that matters", and that only works if it is rare.
  2. **Rules and space instead of boxes.** Sections are separated by hairlines and a lot of air rather
     than by cards and shadows, so the page reads as one document instead of a dashboard.
  3. **The prose the editor wrote is the page.** Headings become section headings, lists become rows
     and grids, blockquotes become bands, and a list of questions becomes an accordion - which is why
     this file carries the whole design rather than the templates carrying it. See the comments on
     .prose for the vocabulary, which is the contract between this stylesheet and the starter content.
  4. **One design, two schemes, and the reader's answer.** Every colour in the palette is a pair - the
     ground it is drawn on in the light scheme and the one in the dark - so no rule below this block is
     written twice and nothing below it knows which scheme it is drawing in. Which pair is in use is
     `color-scheme`: the reader's machine until the reader answers, and their answer after that, written
     on <html> by the switch in the header and kept in their browser. See the note under the palette for
     the mechanism, the measurements every pair is held to, and what the choice costs.

  The two custom properties below are the site's own signature, and the one thing a check reads back to
  prove whose bytes a browser was given: see scripts/verify-sites.sh.
*/

:root {
    /* The site's accent, named after the site so that two profiles serving two stylesheets can be told
       apart by reading them, and the one colour here that is not a pair: the orange is the identity, so
       it is the same orange in both schemes. */
    --tigerstripes-accent: #e4551f;

    /* Paper and ink, and the steps between them. Warm rather than neutral, every value here having more
       red in it than blue: the page is meant to read as paper and printer's ink, and a grey page is the
       one thing this design is not. Light ground first, dark ground second - see the note below for what
       decides which of them is drawn, and why the two are one design rather than two. */
    --paper: light-dark(#f7f4ef, #17130f);
    --band: light-dark(#efece5, #1f1a15);
    --ink: light-dark(#14110f, #f7f4ef);
    /* The colour prose is set in, which is the ink softened by a step: a page of long paragraphs at the
       full contrast of the headings is a page nobody reads to the bottom. */
    --reading-ink: light-dark(#332e2a, #ded5cb);
    --muted: light-dark(#6b6560, #90867c);
    --rule: light-dark(#ddd6c9, #322b25);
    /* The hairline inside a band that is the other colour, and the softer of the two inks such a band
       sets. Named for the band rather than for their value, because which colour they are depends on
       the scheme - as does whether that band is the dark one. */
    --rule-dark: light-dark(#2c2723, #ddd6c9);
    /* The softer of the two inks a band sets, and each half is its own band's ink at a step rather than
       the other half at a different alpha: in the dark scheme the band is the light one, so what is soft
       there is a dark ink, and equal alpha would not be equal legibility on a ground that dark. */
    --band-soft: light-dark(rgba(255, 255, 255, 0.78), rgba(20, 17, 15, 0.85));
    /* A panel raised off the page: a card in a grid, a form field, a download button. */
    --surface: light-dark(#fff, #211c17);
    /* The accent used as a background hint rather than as ink - the wash under a link being pointed at,
       which has to be visible without turning a word of a sentence into a highlighted one. Stronger over
       the dark page, where a tint of orange loses most of what it has: the gesture has to be the same
       size, not the same alpha. */
    --accent-wash: light-dark(rgba(228, 85, 31, 0.09), rgba(228, 85, 31, 0.16));
    /* The plate the drawn stand-in is made of: a warm off-white, a step away from the paper, with the
       stripes and the ink stroke drawn on it. */
    --plate-from: light-dark(#f1ece1, #1f1a15);
    --plate-to: light-dark(#e2dacb, #2e261f);

    /* Which of those grounds the page is drawn in, and the whole of the mechanism: `light dark` is the
       reader's machine, and the browser picks - see the note below for the reader's own answer, which
       outranks it. This property is also the browser's instruction rather than this design's: it is how
       a select's popup, a scrollbar and the canvas behind the page are drawn for the scheme the reader
       is in rather than for the one the page was authored in. */
    color-scheme: light dark;

    --shell: 78rem;
    --gutter: clamp(1.25rem, 4vw, 3.5rem);
    --section: clamp(3rem, 7vw, 6rem);

    /* Two widths that used to be one number, and are not the same thing.

       The column is how wide a body of prose is: every block in one shares it - a row list and its two
       columns, a grid of cards, the hairline this design puts above a section - and it is the widest
       anything in a body may be.

       The measure is how long a line of running text is, and it is narrower than the column on purpose.
       A paragraph filling the column comes out at 91 characters a line, a third longer than a reader can
       hold: past about seventy the eye loses which line it came from, and the paragraph below is the one
       that gets read twice. Measured on this theme, 34rem at the body's 17px is 67 characters, the middle
       of the range books are set in. Keeping it its own number is what lets a paragraph be a readable
       length while the row list beside it still has the room its two columns need - the same 46rem that
       is too long to read is right to lay out. */
    --column: 46rem;
    --measure: 34rem;

    --font: "Helvetica Neue", Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Arial,
    sans-serif;

    --display-size: clamp(2.5rem, 6.5vw, 5.25rem);
    --title-size: clamp(2rem, 4.5vw, 3.5rem);
}

/*
  The dark page, and everything about it: where the pairs above come from, and what decides which of
  them is drawn.

  It is an exchange rather than a second palette. The page takes the ink's colour, the type takes the
  paper's, and the marks still read as paper and ink because both materials are the same two colours
  the other way round. Every relationship the design rests on survives it, and the second value of each
  pair is chosen for those relationships rather than by eye - each measured as a contrast ratio against
  its own ground, against the same one in the light scheme:

    ink on the page          16.8 to 1   (the light scheme: 17.1)
    prose's own ink          12.8        (12.2)
    muted text                5.2        (5.2)
    a hairline on the page    1.33       (1.32)
    a hairline in a band      1.32       (1.27)
    the quiet band's step     1.07       (1.08)
    a panel raised off it     1.09       (1.10)

  That is what makes it one design rather than two: the hierarchy of the page is the same in both, so
  a date is as quiet and a heading as loud whichever scheme a reader is in. Two of the values cannot be
  a mirror of the light one and are not: there is only so much room below a near-black page, so the
  quiet band steps *up* there where it steps down here, and the softer ink of a band is at a higher
  alpha, because equal alpha is not equal legibility on a ground that dark.

  A band that goes dark on a light page goes light on a dark one - a blockquote or the closing call is
  the other colour, and it is the difference from the page that the design is after rather than the
  darkness - and `--rule-dark` and `--band-soft` travel with it, being the rule and the softer ink *of
  that band* rather than of the page.

  The accent is the one value that does not move. The orange is the site's signature, and it is the
  colour a reader would notice being replaced; it also happens to be more legible on this ground than
  on the light one - 4.9 to 1 against the dark paper, where the light scheme manages 3.4 to 1 - so the
  exchange leaves it exactly where it was, the same orange in both schemes.

  Three things are drawn on a colour of their own rather than on the page, and are left alone for that
  reason: the white type on the accent band, the front door's note over the tiger, and the lightbox,
  which is a near-black scrim with a photograph in the middle of it. Inverting any of them would be
  changing the surface underneath rather than following the reader.

  Which pair is drawn is settled in two steps, and they are the two rules below this note. The value on
  :root above is the reader's machine, which is how this site behaved before it had a switch and is all
  a reader whose JavaScript is off ever gets - deliberately, because a preference is not worth a page
  that cannot be read. The rules below are the reader's own answer, and they outrank the machine: that
  is what the property is for and what `light-dark()` reads, so dark asked for on a light machine is
  dark rather than dark-if-the-machine-agrees. `data-scheme` carries only the two answers that are not
  System, whose whole meaning is the attribute's absence - there is nothing to override, so the machine
  takes the question back.

  What this costs, said plainly. The answer is kept in the browser rather than in an account or on the
  site, so it does not travel: a reader who chooses dark on a laptop and opens the site on a phone gets
  the phone's own scheme until they answer again. And a reader who never runs the script is never shown
  the switch, because the stylesheet draws it only for a header the script has marked - a control that
  could not remember an answer would be worse than no control at all. Both are the price of a setting
  that needs no server, no cookie and no consent, and the second is the same bargain the accordion and
  the menu make: the page is the page with or without the script, and the script is what adds the
  choices on top of it.
*/
:root[data-scheme="light"] {
    color-scheme: light;
}

:root[data-scheme="dark"] {
    color-scheme: dark;
}

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

html {
    /* A page that jumps to an anchor under a sticky header loses the heading; this is the room the
       header takes. */
    scroll-padding-top: 6rem;
}

body.theme-tigerstripes {
    margin: 0;
    background: var(--paper);
    color: var(--ink);
    font-family: var(--font);
    font-size: 1.0625rem;
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
}

img {
    max-width: 100%;
    height: auto;
}

a {
    color: inherit;
    text-decoration: none;
}

:focus-visible {
    outline: 2px solid var(--tigerstripes-accent);
    outline-offset: 3px;
}

/* ------------------------------------------------------------------ structure */

.shell {
    width: 100%;
    max-width: var(--shell);
    margin: 0 auto;
    padding-inline: var(--gutter);
    /* The page's column, and the container every rule below the header that asks about a width reads
       from - see the two-axis note at the foot of this file. Its content box is the column and nothing
       else: measured against the served page, this element's content box and `.prose`'s own box are the
       same number at every window up to 800px, and both stop where `.prose > *` caps its children at the
       `--column` 46rem - so a query here is the column's width, and above 800px it is the column plus the
       room the column has not taken yet.

       It is here rather than on `.prose` for one measured reason: a body of prose numbers its own
       sections and rows, and `container-type` carries style containment, which scopes counters to the
       element it is declared on. Declared on `.prose` - which is where those counters are reset - the
       numbers all come out 01; declared on anything inside `.prose` the reset is on the far side of the
       scope and they also all come out 01. Above it, with the resets and the increments both inside,
       they count. Read off the rendered page, since that is the only place a counter resolves. */
    container-type: inline-size;
}

/* ---------------------------------------------------------------------- type */

.display {
    margin: 0;
    font-size: var(--display-size);
    font-weight: 800;
    line-height: 0.98;
    letter-spacing: -0.035em;
    text-wrap: balance;
}

.display__accent {
    color: var(--tigerstripes-accent);
}

.lede {
    max-width: var(--measure);
    margin: 1.75rem 0 0;
    font-size: 1.125rem;
    color: var(--muted);
}

/* A kicker is the small uppercase label above a heading. The mark before it is the studio's own - three
   tapered stripes at the size of a separator - because a label is a good place to say whose page this
   is, and it costs one drawing that the header, the marquee and the diagrams are drawing too. */
.kicker {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin: 0 0 1.5rem;
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--muted);
}

.kicker::before {
    content: "";
    width: 0.75rem;
    height: 0.9rem;
    flex: none;
    background-color: var(--tigerstripes-accent);
    mask: var(--draw-stripes) center / contain no-repeat;
}

.kicker--light {
    color: rgba(255, 255, 255, 0.72);
}

.kicker--light::before {
    background-color: currentColor;
}

/*
  A date, drawn the one way this theme draws one.

  Three places put a date on a page and they were drawing it two ways: the news list and the front door set
  a note's date as a small letterspaced uppercase label, and an article set the same date as a line of
  sentence-case body text under its hero. That is one fact in two vocabularies, which is the thing the rest
  of this file's comments keep arguing against, so the treatment is named once here and everything that
  draws a date draws from it - the two news rows and the article's own date line.

  It is the same family as `.kicker`: a small uppercase label about the thing rather than part of it, which
  is what a date is. A date says when something was said, which is a fact a reader decides with before
  reading any of it, and it is not a sentence the studio wrote - so it is set in the muted ink at a label's
  size rather than at the size of the prose it sits above. The two treatments differ in exactly two
  numbers, and both are deliberate: a date is set a step larger than a kicker (12px against 11px) and
  tracked a little tighter (0.12em against 0.18em), because a kicker is two or three words and can afford
  the air, while a date runs to fourteen characters and the kicker's tracking starts to read as a fence.
*/
.date {
    margin: 0;
    font-size: 0.75rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--muted);
}

/*
  The trail: where the reader is, and the way back up it.

  It is drawn in the kicker above, in the same row as the label it replaced, because that slot is where this
  design says which part of the site a page belongs to - and a trail says it properly, naming the section
  and offering every step above the reader. So a page's label and a page's way out are one row rather than
  two, and on the two pages that have no trail (the front door, the error page) the plain kicker is what
  fills the slot.

  A link is the page's own ink and the step the reader is on is the muted ink the row is already set in,
  and the two are told apart by a rule as well as by that colour: a step that can be walked carries a 1px
  hairline under it and the step the reader is on carries none. The rule is here because colour may not be
  the only thing that says which of these labels is a link (WCAG 1.4.1): a reader who cannot tell the two
  inks apart - low colour vision, a monochrome screen, a photograph of the page - would otherwise have a
  row of five labels and no way to know that four of them are the way back up. A faint rule under 11px
  labels is a smaller cost than that, and it is the one place in this theme where a link is underlined,
  since `a { color: inherit; text-decoration: none }` is right inside a sentence and wrong in a row whose
  whole purpose is to be walked up.

  Hover is where a link answers: its ink goes to the accent and so does its rule - the one colour this
  design uses for the thing it wants a reader to do next. Keyboard focus keeps the site's own 2px ring, so
  a reader who never points at anything still has the cue, and the ring is drawn outside the step's box so
  the truncation below cannot clip it.
*/
.trail ol {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin: 0;
    padding: 0;
    list-style: none;
    /* The list is a flex item of the kicker, and a flex item floors at its own content's width: without
       this the whole row refuses to be narrower than its longest step and runs off the side of the page. */
    min-width: 0;
}

.trail a {
    color: var(--ink);
    text-decoration: underline;
    text-decoration-color: var(--rule);
    text-decoration-thickness: 1px;
    text-underline-offset: 0.3em;
}

.trail a:hover {
    color: var(--tigerstripes-accent);
    text-decoration-color: currentColor;
}

.trail li + li::before {
    content: "/";
    margin-right: 0.5rem;
    color: var(--rule);
}

/*
  A step is one line, and the step the reader is on gives up its length first.

  A page's name is usually a name - "Web design", "Kafka Connect" - but an article's is a sentence, and the
  last step of the trail is the page's own title: "We rebuilt our own site first. It was harder than a
  client's." is 63 characters of 11px tracked uppercase, wider than the column the row sits in, and it put
  the row on two lines with the stripes mark left centred on the pair - a label that had stopped being a
  label. So a step that does not fit ends in an ellipsis instead, and the row says where the reader is
  without reservation while the heading below the row says the page's name in full.

  The cap is what keeps the row on one line rather than the ellipsis on its own, because a flex line breaks
  before it shrinks: without a maximum, the long step is simply moved to a line of its own, where it has
  all the room it needs and nothing is shortened. Three fifths leaves the two fifths the way up needs and
  still draws forty-odd characters of a title, and a step longer than a whole line still ends in an
  ellipsis rather than running off the page.

  It is the step the reader is on that is capped, not the ones above it, and the reason is which of the two
  is a name: an ancestor is a section or a service and is short (the longest here, "Software development",
  is 188px), while the reader's own step is a page title, which may be a sentence. Shortening the way up to
  make room for a sentence would be the row losing the thing it is for; `:has()` reads that step off the
  attribute the fragment already sets rather than off its position.

  Nothing is cut in the markup: the name an assistive technology, a search engine or a translator reads is
  the page's title entire, and only the drawing of it is shortened.

  The shortening is a maximum width rather than a share of the row, which is worth the words because the
  obvious way round does not work: a flex item that is allowed to shrink shrinks by whatever fraction of a
  pixel is left over after the row's arithmetic, and the fractions here are not in the row's favour - a
  flex container measures a tracked text run a couple of pixels short of where it paints it, so "Contact"
  came out "Conta…" on a page with three hundred pixels to spare. A step is therefore `flex: none` and a
  `max-width`: it is as wide as its name until its name is wider than the room it has, and then it is as
  wide as the room. `min-width: 0` is what lets the maximum win - a flex item's automatic minimum is its
  own content, which for a step that is one unbroken line is the whole name.

  Shortening the step rather than the list item is what keeps the ring: a box's own `overflow: hidden` does
  not clip its own outline, but it does clip a descendant's, so a link shortened by an ancestor would focus
  invisibly - and the ring is the one thing on this row a keyboard has.
*/
.trail {
    /* The mark rides with the first step rather than sitting in the middle of the block: a trail of four
       steps is three rows tall on a phone, and a mark centred on that lands beside the second step, where
       it reads as that step's bullet. */
    align-items: flex-start;
}

.trail li {
    display: flex;
    flex: none;
    align-items: center;
    max-width: 100%;
}

.trail li:has([aria-current="page"]) {
    max-width: 60%;
}

.trail a,
.trail [aria-current="page"] {
    display: block;
    flex: none;
    min-width: 0;
    max-width: 100%;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* --------------------------------------------------------------------- marks */

.stripes {
    display: inline-flex;
    gap: 2px;
    align-items: flex-end;
    height: 1.35em;
}

.stripes__stroke {
    display: block;
    width: 0.28em;
    height: 100%;
    background: var(--tigerstripes-accent);
    /* The taper is what makes three rectangles read as a tiger's stripes rather than as a bar chart. */
    clip-path: polygon(70% 0, 100% 0, 30% 100%, 0 100%);
}

/* --------------------------------------------------------------------- icons */

/*
  The drawings this site uses for things, after the one drawing it uses for itself.

  An icon here is a mask rather than a file or a sprite. The shape is a data URI in this stylesheet and
  an element carrying it is painted in whatever colour it already has, so one drawing serves the accent,
  the ink and white without three copies of it, and nothing about a page is fetched, cached, versioned
  or licensed to be drawn - which is the promise the stripes have always kept and this is how it keeps
  being kept as the vocabulary grows. A sprite would have meant a second file in the theme and an
  <svg><use> in every template that wants one; a mask is one line of CSS per drawing and one class per
  place it is used.

  The set is drawn on one grid - 24 units square, a 1.7 unit stroke, rounded ends and joins - so a row
  of them reads as a set rather than as a collection of pictures, and each one is the *shape of* a
  subject rather than an illustration of it: brackets for code, a rack for hosting, a stack for a
  platform, a cylinder for a datastore.

  `--draw-*` holds the drawings; `.icon--*` names one, and `.icon` takes the spark when nothing names
  it. That default is not decoration: a mask that is not set paints the whole box rather than nothing,
  and an unnamed thing here should be drawn as this studio's own drawing rather than as an empty square.
  The spark rather than the three stripes, because the stripes are what is usually behind it - the plate,
  the band, the kicker - and a mark drawn on top of itself is a shape with no edge. It is also what makes
  `.icon--<something nobody drew>` harmless, which is what lets a template hand this file the last
  segment of a page's path and get a drawing back without a switch: see `.rows__tile`, and the hero's
  plate, which draws every page under /services and every technology page with something of its own.
*/
:root {
    /* The studio's own mark, which everything else falls back to. */
    --draw-mark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3.5v17M4.6 7.8l14.8 8.4M19.4 7.8L4.6 16.2'/%3E%3C/svg%3E");

    /* The three tapered stripes, at the size of a separator rather than the size of the header's mark.
       Filled rather than stroked, because a taper is two edges and a stroke would draw them both. */
    --draw-stripes: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 15 20' fill='%23000'%3E%3Cpath d='M2.52 0H4.2L2.1 20H.42zM7.72 0H9.4L7.3 20H5.62zM12.92 0H14.6L12.5 20h-1.68z'/%3E%3C/svg%3E");

    /* What this studio does: a page, code, a platform, an identity, a server, a gauge, a shield. */
    --draw-browser: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4.5' width='18' height='15' rx='2'/%3E%3Cpath d='M3 9h18M6.5 12.5h6M6.5 16h9'/%3E%3C/svg%3E");
    --draw-code: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 8l-4 4 4 4M15 8l4 4-4 4M13.5 6.5l-3 11'/%3E%3C/svg%3E");
    --draw-stack: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3.5l8 4.5-8 4.5L4 8zM4 12.5l8 4.5 8-4.5M4 16.6l8 4.5 8-4.5'/%3E%3C/svg%3E");
    --draw-rack: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.5' y='4' width='17' height='16' rx='2'/%3E%3Cpath d='M3.5 9.33h17M3.5 14.66h17M6.9 6.7h1.3M6.9 12h1.3M6.9 17.3h1.3'/%3E%3C/svg%3E");
    --draw-gauge: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 17.4a8.5 8.5 0 1 1 17 0'/%3E%3Cpath d='M12 17.4l5-5.6'/%3E%3Ccircle cx='12' cy='17.4' r='1.1'/%3E%3C/svg%3E");
    --draw-support: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3.2l7.6 2.9v6c0 4.4-3.2 7.8-7.6 9-4.4-1.2-7.6-4.6-7.6-9v-6z'/%3E%3Cpath d='M7.9 12h2.4l1.5-3 2 5 1.4-2h2.4'/%3E%3C/svg%3E");

    /* The parts of a thing: structure, type, colour, words, movement - and the same vocabulary again for
       a system's domain, its interface, its data, its tests and its delivery. */
    --draw-layout: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.5' y='4.5' width='17' height='15' rx='2'/%3E%3Cpath d='M10 4.5v15M10 12h10.5'/%3E%3C/svg%3E");
    --draw-type: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5.5 19.5l6.5-15 6.5 15M8.4 13.5h7.2'/%3E%3C/svg%3E");
    --draw-colour: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='9.2' cy='9.6' r='4.6'/%3E%3Ccircle cx='14.8' cy='9.6' r='4.6'/%3E%3Ccircle cx='12' cy='15' r='4.6'/%3E%3C/svg%3E");
    --draw-document: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='4.5' y='3.5' width='15' height='17' rx='2'/%3E%3Cpath d='M8 8h8M8 11.8h8M8 15.6h5'/%3E%3C/svg%3E");
    --draw-motion: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.2 7.4h11.4a3.4 3.4 0 1 0-3.4-3.4M3.2 16.6h7.6a3.4 3.4 0 1 1-3.4 3.4'/%3E%3C/svg%3E");
    --draw-domain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.5' y='3.5' width='17' height='17' rx='2.5'/%3E%3Crect x='8.5' y='8.5' width='7' height='7' rx='1.5'/%3E%3C/svg%3E");
    --draw-api: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='5.6' cy='12' r='2.6'/%3E%3Ccircle cx='18.4' cy='12' r='2.6'/%3E%3Cpath d='M8.2 12h7.6'/%3E%3C/svg%3E");
    --draw-data: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cellipse cx='12' cy='6' rx='7' ry='3'/%3E%3Cpath d='M5 6v12c0 1.66 3.13 3 7 3s7-1.34 7-3V6M5 12c0 1.66 3.13 3 7 3s7-1.34 7-3'/%3E%3C/svg%3E");
    --draw-tests: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.5' y='3.5' width='17' height='17' rx='4'/%3E%3Cpath d='M8 12.4l2.7 2.7 5.4-5.5'/%3E%3C/svg%3E");
    /* Two stages and an arrow between them, which is what a delivery pipeline is: a box with an arrow
       leaving it is the same drawing as the one for a link somebody clicks, and this is not that. */
    --draw-delivery: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.2' y='8.4' width='6.6' height='7.2' rx='1.4'/%3E%3Crect x='14.2' y='8.4' width='6.6' height='7.2' rx='1.4'/%3E%3Cpath d='M9.8 12h1.8M11.4 10.1l2 1.9-2 1.9'/%3E%3C/svg%3E");
    --draw-voice: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6.6A2.6 2.6 0 0 1 6.6 4h10.8A2.6 2.6 0 0 1 20 6.6v7.3a2.6 2.6 0 0 1-2.6 2.6h-7l-4.8 3.9v-3.9A2.6 2.6 0 0 1 4 13.9z'/%3E%3Cpath d='M8 8.6h8M8 12h5'/%3E%3C/svg%3E");
    --draw-checklist: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='4.5' y='3.5' width='15' height='17' rx='2'/%3E%3Cpath d='M8 8.4l1.5 1.5 3.2-3.2M8 15l1.5 1.5 3.2-3.2'/%3E%3C/svg%3E");

    /* One more for the language this site has a page about, whose subject is types and functions rather
       than anything in a rack: the letter it is named after. */
    --draw-lambda: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 19.5l6.6-15 3 15'/%3E%3C/svg%3E");

    /* The stack, from the people using it to the machines it runs on. */
    --draw-clients: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='9' cy='8.6' r='3.1'/%3E%3Cpath d='M3.4 20c0-3.1 2.5-5.6 5.6-5.6s5.6 2.5 5.6 5.6M15.8 5.9a3.1 3.1 0 0 1 0 5.4M16.6 14.6c2.4.5 4 2.7 4 5.4'/%3E%3C/svg%3E");
    --draw-log: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 6.6h7M3.5 11.4h11.5M3.5 16.2h7'/%3E%3C/svg%3E");
    --draw-cloud: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7.2 18.6h9.3a3.9 3.9 0 0 0 .2-7.8 5.3 5.3 0 0 0-10.2-.8 3.9 3.9 0 0 0 .7 8.6z'/%3E%3C/svg%3E");

    /* The datastores, one drawing per family. */
    --draw-table: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.5' y='4.5' width='17' height='15' rx='2'/%3E%3Cpath d='M3.5 9.5h17M3.5 14.5h17M10 4.5v15'/%3E%3C/svg%3E");
    /* Drawn across rather than at an angle, and with two teeth: an angled key with one tooth is a
       magnifier at this size, and Search is a magnifier. */
    --draw-key: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='6.8' cy='12' r='3.6'/%3E%3Cpath d='M10.4 12h9.4M17 12v2.8M20 12v3.6'/%3E%3C/svg%3E");
    --draw-columns: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.5' y='4.5' width='17' height='15' rx='2'/%3E%3Cpath d='M9.2 4.5v15M14.8 4.5v15'/%3E%3C/svg%3E");
    --draw-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='10.6' cy='10.6' r='6'/%3E%3Cpath d='M15.1 15.1l4.9 4.9'/%3E%3C/svg%3E");

    /* The steps of the two chains that had a number and nothing beside it: where a name points, what a
       certificate is, and a copy of the thing itself for the day the original is gone; a page that has
       been kept, a ranking, a line for something still alive, and a bell - the page's own word, because
       what a threshold does is ring. Each is the shape of its subject on the same grid as the rest, and
       each is here rather than made out of the drawings above it: a name pointing somewhere is not the
       link between two things (`--draw-api`), an index is not a page of words (`--draw-document`), and
       a backup is not an archive of lines (`--draw-log`). */
    --draw-route: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='5.4' cy='12' r='2.4'/%3E%3Cpath d='M7.8 12h3.2M11 12l5.6-5.4M11 12l5.6 5.4'/%3E%3Ccircle cx='19' cy='6.6' r='2.4'/%3E%3Ccircle cx='19' cy='17.4' r='2.4'/%3E%3C/svg%3E");
    --draw-lock: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='5.4' y='10.4' width='13.2' height='9.6' rx='2.2'/%3E%3Cpath d='M8.8 10.4V8.2a3.2 3.2 0 0 1 6.4 0v2.2'/%3E%3C/svg%3E");
    --draw-copy: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='8.6' y='8.6' width='11.9' height='11.9' rx='2.2'/%3E%3Cpath d='M15.4 5.5H5.7a2.2 2.2 0 0 0-2.2 2.2v9.7'/%3E%3C/svg%3E");
    --draw-bookmark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='5' y='3.5' width='14' height='17' rx='2'/%3E%3Cpath d='M9.6 3.5v6.8l2.4-2 2.4 2V3.5'/%3E%3C/svg%3E");
    --draw-rank: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.6 20.4h16.8M7.4 20.4v-4.8M12 20.4V10.6M16.6 20.4V5.6'/%3E%3C/svg%3E");
    --draw-pulse: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.2 12.4h3.4l1.9-4.7 3.2 9.5 2.1-4.8h6.9'/%3E%3C/svg%3E");
    --draw-bell: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6.2 16.6h11.6c-1.4-1.4-2-3.2-2-5.4 0-2.7-1.7-4.6-3.8-4.6s-3.8 1.9-3.8 4.6c0 2.2-.6 4-2 5.4z'/%3E%3Cpath d='M10.5 19a2 2 0 0 0 3 0'/%3E%3C/svg%3E");

    /*
      Twenty more, and the reason they are twenty is that they are not drawings of subjects at all: this
      set is the twenty a *news row* cycles through - see `.news__mark` for the cycle and the row it is
      on.

      That is a different kind of drawing from everything above it, and it is worth being explicit about
      which kind, because the rest of the file has one answer and this block has another. Every drawing
      above is the shape of a *subject*: brackets are code, a rack is hosting, a cylinder is a datastore,
      and the set works because a page knows what it is about and can name its own drawing. A note from
      the studio does not: "We rebuilt our own site first" is about a website, a week, a decision and a
      lesson at the same time, and the drawing of its subject would be the drawing of a website for the
      third time on one section - so a news row takes the drawing of the *kind of thing a note is* rather
      than of what it is about, and the kind of thing a note is is a small and closed list. Twenty is
      that list: a dated note, a draft, a place, an idea, a launch, a milestone, a direction, something
      fast, time spent, something sent out, something measured, something wired up, something read,
      something that went up, a break, a plan, a tune-up, something growing, something announced, and
      where to go next.

      Still the same grid as the rest - 24 units, a 1.7 unit stroke, rounded ends and joins - because a
      set that changed its grid for one caller would not be one set. Every one is one to three elements,
      which is a harder rule than it looks and is what the size is for: these are drawn at 24px in a row,
      and a shape with four parts at 24px is a smudge. Two of the twenty are worth their own line:

        * the ruler keeps its ticks on one edge rather than two, because ticks on both edges at 24px read
          as a grid and this file already has a grid (`--draw-table`), a bar chart (`--draw-rank`) and a
          column chart (`--draw-columns`).
        * the compass is a circle with a diamond inside it, and the diamond rather than a needle line is
          what keeps it apart from `--draw-search`, which is also a circle with something on it - a
          needle line would be the handle, at 24px, pointing the wrong way.

      There is one more rule here that nothing else in this file has, and the plate is where it comes from.
      These twenty are the only drawings in the theme that sit on the stripes - see `.news__mark` - and the
      stripes run at 45 degrees, so a stroke drawn along them has nothing to be read against: at 24px the
      halo between a white stroke and a black stripe four pixels away is a pixel wide, and a drawing that
      leans on it is one a reader has to work out. Which directions those are was measured rather than
      assumed, because the answer is not the one line of the rule: one 1.7-unit stroke was drawn on the
      plate at every 15 degrees from the horizontal and looked at at true size, and only three of the
      fourteen read as smudges - 30, 45 and 60 degrees, with 45 (the stripes' own direction) dissolving
      into them almost completely. Everything else read as a line, including the shallow ones at 15 and
      its mirror at 165. So the rule these twenty are drawn to is: verticals, horizontals and circles,
      long diagonals only where they cross the stripes rather than lean on them, and a shallow diagonal
      only where it is short. Five of the twenty could not be drawn that way as they stood and were
      redrawn rather than kept. Each was drawn both ways and looked at on the plate at true size before
      the second version was kept:

        * the pencil was a diagonal pencil, which is the worst case there is on a 45-degree ground, and at
          true size it was a smudge. It lies down now, pointing right: five of its six edges are vertical
          or horizontal and only the point is a diagonal, and the point is drawn long and shallow so that
          its edges sit at about 30 degrees to the horizontal - 15 degrees off the stripes rather than
          along them. A pencil standing up
          was tried first and reads as a thermometer at this size - what makes it a pencil is the ferrule
          and the wood line, and two lines across a vertical bar are the same drawing as two lines across
          a horizontal one.
        * the bolt was the classic wide zigzag, which is four long strokes at 45 degrees in a row: on the
          plate it dissolved into them. The one kept is narrower and taller, with its long strokes at 55
          to 80 degrees and only its two short shelves horizontal - which is what a bolt drawn for a
          striped ground looks like rather than what a bolt usually looks like.
        * the sliders were two horizontal tracks with round knobs: three thin horizontals and two small
          circles inside 24 units, the busiest shape in the set and the one the plate flattened most. They
          are two vertical tracks with solid rounded bars as knobs now - three elements, two of them
          masses, which is the rule the set's readability rests on at this size. Three tracks were tried
          and are worse: three verticals with three bars is the same clutter turned the other way.
        * the plane was a dart with its nose up and right, a wingtip to the left and another below, and
          every one of its five edges was a diagonal: two long ones at 22 and 26 degrees to the horizontal,
          the fold across it at 43 - which is along the stripes rather than across them - and two steep
          ones at 64 and 70. On the plate it was the softest drawing of the twenty: a grey line where
          every drawing printed beside it was white. It lies flat now and points right, with the fold drawn
          across it and the near wing stepped under. Four of its six edges are a horizontal and three
          verticals, and the two diagonals left are at 14 and 26 degrees to the horizontal - the top of the
          body and the near wing's leading edge - which is 31 and 19 degrees across the stripes, where the
          dart had one long edge at 43: two degrees off the stripes, and so the one direction on this plate
          with nothing to be read against. What is given up is the dart's three-dimensional read - the old
          one was the better drawing on a plain ground, and that is not the ground it is read on. What is
          kept is the fold: a wedge with a fold across it is a paper plane, and the same wedge without one
          is an arrow this set already has (`--draw-trend`, and the arrow in `--draw-delivery`).
        * the map was a folded sheet, and its zigzag was four 6-unit diagonals at 20 degrees to the
          stripes - a quarter of its outline, all of it soft. A concertina cannot be drawn on this plate at
          this size: half the fold depth and the folds stop reading as folds, and a fold that is a step
          instead is a card. It is a map rather than a folded sheet now - the page, and a route across it
          from one stop to the next - which is horizontals, verticals and two circles, the three things
          that read on the plate, and which is what a map is *for*: a route across it is what somebody
          looks at a map to find. The frame keeps it apart from `--draw-route`, which is also a way from
          one place to another and has no page under it.
    */
    --draw-calendar: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.5' y='5' width='17' height='15.4' rx='2'/%3E%3Cpath d='M8 3.4v3.2M16 3.4v3.2M3.5 10.4h17'/%3E%3Ccircle cx='8.4' cy='14.8' r='1.15'/%3E%3C/svg%3E");
    --draw-pencil: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.6 8.6h12.4l5.6 3.4-5.6 3.4H3.6z'/%3E%3Cpath d='M16 8.6v6.8M6.8 8.6v6.8'/%3E%3C/svg%3E");
    --draw-pin: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 20.6s6.2-5.7 6.2-10.2a6.2 6.2 0 1 0-12.4 0C5.8 14.9 12 20.6 12 20.6z'/%3E%3Ccircle cx='12' cy='10.3' r='2.3'/%3E%3C/svg%3E");
    --draw-bulb: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='9.4' r='6'/%3E%3Cpath d='M9.8 15.2v2.2h4.4v-2.2M10.5 19.2h3'/%3E%3C/svg%3E");
    --draw-rocket: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3.6c2.4 2.5 3.6 5.7 3.6 9v3.6H8.4v-3.6c0-3.3 1.2-6.5 3.6-9z'/%3E%3Cpath d='M8.4 12.5 5.8 15.9v2.6h2.6M15.6 12.5l2.6 3.4v2.6h-2.6'/%3E%3Ccircle cx='12' cy='10.2' r='1.4'/%3E%3C/svg%3E");
    --draw-flag: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6.4 3.6v17.2'/%3E%3Cpath d='M6.4 4.6h11.2l-2.4 3.6 2.4 3.6H6.4z'/%3E%3C/svg%3E");
    --draw-compass: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='8.4'/%3E%3Cpath d='M15.5 8.5 13.4 13.4 8.5 15.5 10.6 10.6z'/%3E%3C/svg%3E");
    --draw-bolt: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14.2 2.6 6.4 13.6h4.6l-1.6 7.8 7.4-10.8h-4.6z'/%3E%3C/svg%3E");
    --draw-hourglass: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7.4 4.2h9.2v2.2L12 12 7.4 6.4zM7.4 19.8h9.2v-2.2L12 12l-4.6 5.6z'/%3E%3C/svg%3E");
    /* The paper plane, flat and pointing right, its near wing stepped under: see the note on the plate
       below for the dart this replaced and why a dart could not stay. */
    --draw-plane: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20.6 8.4 4.2 4.4v7.6h6.8v5.4l9.6-4.6z'/%3E%3Cpath d='M20.6 12H11'/%3E%3C/svg%3E");
    --draw-ruler: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.4' y='7.6' width='17.2' height='8.8' rx='1.4'/%3E%3Cpath d='M7.4 7.6v2.8M11.2 7.6v4M15 7.6v2.8'/%3E%3C/svg%3E");
    --draw-plug: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9.4 3.6v4.4M14.6 3.6v4.4'/%3E%3Cpath d='M6.8 8h10.4v3.6a5.2 5.2 0 0 1-10.4 0z'/%3E%3Cpath d='M12 16.8v3.6'/%3E%3C/svg%3E");
    --draw-book: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 7.4C10 5.9 7.6 5.2 4.6 5.2v12.2c3 0 5.4.7 7.4 2.2 2-1.5 4.4-2.2 7.4-2.2V5.2c-3 0-5.4.7-7.4 2.2z'/%3E%3Cpath d='M12 7.4v12.2'/%3E%3C/svg%3E");
    --draw-trend: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.6 16.6 9.4 10.8l3.6 3.6 7.4-7.4'/%3E%3Cpath d='M15.4 7h5v5'/%3E%3C/svg%3E");
    --draw-mug: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4.6 6.4h11.2v10.2a3.4 3.4 0 0 1-3.4 3.4H8a3.4 3.4 0 0 1-3.4-3.4z'/%3E%3Cpath d='M15.8 9h2.2a2.6 2.6 0 0 1 0 5.2h-2.2'/%3E%3C/svg%3E");
    --draw-clipboard: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9.4 4.4H6.8a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2h10.4a2 2 0 0 0 2-2v-13a2 2 0 0 0-2-2h-2.6'/%3E%3Crect x='9.4' y='2.6' width='5.2' height='3.6' rx='1.2'/%3E%3Cpath d='M8.8 11.4h6.4M8.8 15.2h4.2'/%3E%3C/svg%3E");
    --draw-sliders: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8.6 4.6v14.8M15.4 4.6v14.8'/%3E%3Crect x='5.4' y='8.4' width='6.4' height='3.2' rx='1.6'/%3E%3Crect x='12.2' y='13.2' width='6.4' height='3.2' rx='1.6'/%3E%3C/svg%3E");
    --draw-sprout: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 20.6v-6'/%3E%3Cpath d='M12 14.6c-3.6 0-6.4-2.6-6.4-6.2 3.6 0 6.4 2.6 6.4 6.2z'/%3E%3Cpath d='M12 14.6c3.6 0 6.4-2.6 6.4-6.2-3.6 0-6.4 2.6-6.4 6.2z'/%3E%3C/svg%3E");
    --draw-megaphone: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4.4 9.4v5.2h3.4l8.6 4.6V4.8L7.8 9.4z'/%3E%3Cpath d='M19.6 9a4.6 4.6 0 0 1 0 6'/%3E%3C/svg%3E");
    /* A map rather than a folded sheet, which is the one thing that could not be drawn on the plate at
       this size: the page, and a route across it from one stop to the next. See the same note below. */
    --draw-map: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.6' y='5.6' width='16.8' height='12.8' rx='1.6'/%3E%3Cpath d='M9.7 15.6h2.1v-4.4h3.1'/%3E%3Cpath d='M8.2 14.1a1.5 1.5 0 1 0 0 3 1.5 1.5 0 1 0 0-3M16.4 9.7a1.5 1.5 0 1 0 0 3 1.5 1.5 0 1 0 0-3'/%3E%3C/svg%3E");
}

/*
  An icon, at whatever colour it is placed in. The box is the size of a line of text so that one in a
  sentence sits in it, and any use that wants it larger says so - a drawn thing that inherited a size
  from its context would be a shape whose weight depends on where it was put.
*/
.icon {
    --draw: var(--draw-mark);
    display: inline-block;
    inline-size: 1.25rem;
    block-size: 1.25rem;
    flex: none;
    background-color: currentColor;
    mask: var(--draw) center / contain no-repeat;
}

.icon--mark { --draw: var(--draw-mark); }
.icon--stripes { --draw: var(--draw-stripes); }
.icon--browser { --draw: var(--draw-browser); }
.icon--code { --draw: var(--draw-code); }
.icon--stack { --draw: var(--draw-stack); }
.icon--rack { --draw: var(--draw-rack); }
.icon--gauge { --draw: var(--draw-gauge); }
.icon--support { --draw: var(--draw-support); }
.icon--layout { --draw: var(--draw-layout); }
.icon--type { --draw: var(--draw-type); }
.icon--colour { --draw: var(--draw-colour); }
.icon--document { --draw: var(--draw-document); }
.icon--motion { --draw: var(--draw-motion); }
.icon--domain { --draw: var(--draw-domain); }
.icon--api { --draw: var(--draw-api); }
.icon--data { --draw: var(--draw-data); }
.icon--tests { --draw: var(--draw-tests); }
.icon--delivery { --draw: var(--draw-delivery); }
.icon--voice { --draw: var(--draw-voice); }
.icon--checklist { --draw: var(--draw-checklist); }
.icon--clients { --draw: var(--draw-clients); }
.icon--log { --draw: var(--draw-log); }
.icon--cloud { --draw: var(--draw-cloud); }
.icon--table { --draw: var(--draw-table); }
.icon--key { --draw: var(--draw-key); }
.icon--columns { --draw: var(--draw-columns); }
.icon--lambda { --draw: var(--draw-lambda); }
.icon--search { --draw: var(--draw-search); }

/* The twenty a news row cycles through. They are named like everything else even though no template and
   no page slug ever asks for one by name, because `.icon--*` is how this file says "this drawing" and a
   second way of naming one would be a second vocabulary: see `.news__mark` for the one caller. */
.icon--calendar { --draw: var(--draw-calendar); }
.icon--pencil { --draw: var(--draw-pencil); }
.icon--pin { --draw: var(--draw-pin); }
.icon--bulb { --draw: var(--draw-bulb); }
.icon--rocket { --draw: var(--draw-rocket); }
.icon--flag { --draw: var(--draw-flag); }
.icon--compass { --draw: var(--draw-compass); }
.icon--bolt { --draw: var(--draw-bolt); }
.icon--hourglass { --draw: var(--draw-hourglass); }
.icon--plane { --draw: var(--draw-plane); }
.icon--ruler { --draw: var(--draw-ruler); }
.icon--plug { --draw: var(--draw-plug); }
.icon--book { --draw: var(--draw-book); }
.icon--trend { --draw: var(--draw-trend); }
.icon--mug { --draw: var(--draw-mug); }
.icon--clipboard { --draw: var(--draw-clipboard); }
.icon--sliders { --draw: var(--draw-sliders); }
.icon--sprout { --draw: var(--draw-sprout); }
.icon--megaphone { --draw: var(--draw-megaphone); }
.icon--map { --draw: var(--draw-map); }
.icon--route { --draw: var(--draw-route); }
.icon--lock { --draw: var(--draw-lock); }
.icon--copy { --draw: var(--draw-copy); }
.icon--bookmark { --draw: var(--draw-bookmark); }
.icon--rank { --draw: var(--draw-rank); }
.icon--pulse { --draw: var(--draw-pulse); }
.icon--bell { --draw: var(--draw-bell); }

/*
  And the same drawings again, named the way a page is: the last segment of a page's path. This is what
  lets a template draw the icon of whatever page it is listing - and of the page it is - without a switch
  tables of its own, which is the one piece of knowledge about the site's own pages that belongs in the
  stylesheet rather than in a fragment: a name here costs a line, and a page this file has never heard of
  gets the studio's mark, which is a drawing rather than a gap.

  `services` and the three section fronts are here too, so that the front of a section is drawn with
  something of its own rather than with the mark every unnamed page gets.
*/
.icon--web-design { --draw: var(--draw-browser); }
.icon--software-engineering { --draw: var(--draw-code); }
.icon--software-development { --draw: var(--draw-stack); }
.icon--brand-strategy { --draw: var(--draw-mark); }
.icon--hosting-domains { --draw: var(--draw-rack); }
.icon--seo-performance { --draw: var(--draw-gauge); }
.icon--care-support { --draw: var(--draw-support); }
.icon--services { --draw: var(--draw-layout); }
.icon--portfolio { --draw: var(--draw-table); }
.icon--news { --draw: var(--draw-document); }
.icon--contact { --draw: var(--draw-voice); }
/* The page about the studio, drawn with the studio's own drawing - the spark rather than the stripes,
   because the band this page's mark sits on is already the stripes, and a mark drawn on top of itself is
   a shape with no edge. */
.icon--about { --draw: var(--draw-mark); }

/* And the seven technology pages, each drawn with the drawing the stack diagram gives the same subject:
   the log for Kafka, the cylinder for the datastores, the pipeline for delivery. A subject that appears
   in two places should look like the same subject in both, or the vocabulary is not one. */
.icon--java { --draw: var(--draw-stack); }
.icon--scala { --draw: var(--draw-lambda); }
.icon--spring-framework { --draw: var(--draw-domain); }
.icon--kafka { --draw: var(--draw-log); }
.icon--kafka-connect { --draw: var(--draw-api); }
.icon--devops { --draw: var(--draw-delivery); }
.icon--databases { --draw: var(--draw-data); }

/*
  A thing's icon on the plate the design draws it on: a rounded square of the stripes with the drawing in
  white over them. It is the one place in this theme where a drawing sits on a patterned ground rather
  than on a flat plate, and it is the design's own motif rather than an invention - the stripes are the
  identity, and a glyph over them is how this studio labels a thing it does.

  The plate is the accent with ink stripes through it, so it is the same plate in both schemes, like the
  mark plates and the diagrams' plates and for the same reason: the accent is one colour either way, and
  a glyph that has to be legible on it can be one colour too. The glyph is white - 3.74:1 against the
  accent, over the 3:1 a graphic that is not text is owed, and 19:1 against the ink between the stripes -
  and the ink outline is what stops the plate dissolving into an ink page in the dark scheme.
*/
.tile {
    display: grid;
    place-items: center;
    inline-size: 2.75rem;
    block-size: 2.75rem;
    flex: none;
    border: 1px solid #14110f;
    border-radius: 3px;
    background-color: var(--tigerstripes-accent);
    background-image: repeating-linear-gradient(45deg, transparent 0 4px, #14110f 4px 8px);
    color: #fff;
}

/* The drawing sits *on* the stripes rather than on a clear patch of the plate, which is what the design
   does, and it is the one drawing in this file whose legibility is decided by what is behind it: white
   over the accent is 3.74:1 and white over the ink between the stripes is 19:1, but a white line that
   crosses from one to the other has no clean edge to be read against. The drop shadow is what gives it
   one - an ink halo a pixel wide, which is the same colour as the stripes, so the glyph reads as a hole
   cut in them rather than as a drawing that happens to be lying across them. */
.tile > .icon {
    inline-size: 1.5rem;
    block-size: 1.5rem;
    filter: drop-shadow(0 0 1px #14110f) drop-shadow(0 0 1px #14110f);
}

/* The marking of a document: the same stripe, at the size of a separator, where the site draws one.
   Three of them are the studio's mark, so a list that separates its items with the mark is separating
   them with the identity rather than with a bullet. */
.mark {
    inline-size: 0.75rem;
    block-size: 0.9rem;
    color: var(--tigerstripes-accent);
}

/* -------------------------------------------------------------------- header */

.site-header {
    position: sticky;
    top: 0;
    z-index: 20;
    background: color-mix(in srgb, var(--paper) 92%, transparent);
    backdrop-filter: blur(8px);
    border-bottom: 1px solid var(--rule);
}

.site-header__inner {
    display: flex;
    align-items: center;
    gap: clamp(1rem, 3vw, 3rem);
    padding-block: 1.1rem;
}

.brand {
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    font-size: 1.3rem;
    font-weight: 700;
    letter-spacing: -0.02em;
}

/*
  The panel: the sections and the colour-scheme switch, in one box.

  One box rather than two because the two arrangements of this header want the same two things in the
  same order: above the breakpoint they are the second half of the row - the links, then the switch - and
  below it they are what the menu button folds away and unfolds. It is also why the button's
  `aria-controls` names this box rather than the nav inside it: what the button opens is the panel.

  `margin-left: auto` is the nav's own declaration, moved up to the box that now holds it: a row that
  pushed two things to its end would have one of them pushing the other.
*/
.site-header__panel {
    display: flex;
    align-items: center;
    gap: clamp(0.75rem, 1.5vw, 1.5rem);
    margin-left: auto;
}

.site-nav {
    display: flex;
    flex-wrap: wrap;
    gap: clamp(1rem, 2.5vw, 2.25rem);
    font-size: 0.9375rem;
}

.site-nav__link {
    padding-block: 0.25rem;
    border-bottom: 1px solid transparent;
}

.site-nav__link:hover,
.site-nav__link[aria-current="page"] {
    border-bottom-color: var(--ink);
}

/*
  The colour-scheme switch, and the reader's answer to a question the site would otherwise ask their
  machine.

  Three marks and, where there is room for them, three words: the same element in both arrangements,
  because a phone draws it as a row of the panel and a desk as the end of the header's row, and a second
  copy of a control is a second copy to keep in step. The words are each option's name in both - shown in
  the panel and clipped to nothing in the row - and clipped rather than removed, because a label that is
  `display: none` stops being the name of the radio it labels.

  Real radios in a named group, which is what this design already uses for a question with a few answers
  (.choices, and the contact form's chips): the browser gives the group one keyboard stop and the arrow
  keys between its options, so none of the control itself is script. What the script does is the two
  things a radio cannot: it remembers the answer, and it draws the control at all - the switch appears
  only for a header the script has marked, because a radio that could not remember anything would be
  worse than no control. See the narrow block at the foot of this file for where it sits on a phone, and
  js/theme.js for the rest.

  Nothing below sets a colour for the mark: the mark is `currentColor`, so one shape is the muted ink of
  an option that is not in use and the paper of the ink pill the design already draws for the one that
  is. The pill is the same one the form's chips wear - .choices__input:checked + .choices__label - and
  the label sets its own quiet colour below, which is why the chosen one states its colour again.
*/
.site-header .scheme {
    display: none;
    flex-wrap: nowrap;
    margin: 0;
    gap: 0.3rem;
}

.site-header--scheme .scheme {
    display: inline-flex;
}

.site-header .scheme .choices__label {
    gap: 0.45rem;
    padding: 0.25rem 0.3rem;
    color: var(--muted);
}

.site-header .scheme .choices__input:checked + .choices__label {
    color: var(--paper);
}

.scheme__mark {
    display: block;
    width: 0.75rem;
    height: 0.75rem;
    border-radius: 50%;
}

/* The sun: the page on its light ground. */
.scheme__mark--light {
    background: currentColor;
}

/* The moon: that disc with a bite out of it, cut with the gradient rather than painted over with a
   colour, so the crescent is the same shape on the page's ground and on the chosen pill's ink. */
.scheme__mark--dark {
    background: radial-gradient(circle at 32% 32%, transparent 0 0.44rem, currentColor 0.49rem);
}

/* The half: the machine's own answer, a disc with one side filled and a rim, which is neither of the two
   above at a glance. */
.scheme__mark--system {
    border: 1px solid currentColor;
    background: linear-gradient(90deg, currentColor 0 50%, transparent 50%);
}

/* The word, which the panel shows and the header's row cannot hold. Off the page rather than out of it,
   with the recipe the clipped wordmark uses below. */
.scheme__word {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/*
  The menu button, which is a phone's control and nothing at any other width: not drawn above the
  breakpoint, and below it drawn only for a header the theme's script has marked as having a menu
  (`site-header--menu`). Two conditions rather than one because the arrangement the menu replaces is an
  arrangement that works - without the script the five links are a row of their own under the brand,
  which is what this header did before it had a menu - and a button that opened nothing would be worse
  than no button at all. See the narrow block at the foot of this file for the mark itself, and
  js/theme.js for what puts it there.
*/
.site-header__menu {
    display: none;
}

/* ------------------------------------------------------------------- buttons */

.button {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.85rem 1.5rem;
    border: 1px solid transparent;
    border-radius: 999px;
    background: transparent;
    color: var(--ink);
    font: inherit;
    font-size: 0.9375rem;
    font-weight: 600;
    cursor: pointer;
    transition: transform 120ms ease, background-color 120ms ease, color 120ms ease;
}

.button:hover {
    transform: translateY(-1px);
}

.button--accent {
    background: var(--tigerstripes-accent);
    color: #fff;
}

.button--dark {
    background: var(--ink);
    color: var(--paper);
}

.button--ghost {
    border-color: var(--ink);
}

.button__arrow {
    font-size: 0.9em;
}

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

.hero {
    padding-block: var(--section) calc(var(--section) * 0.6);
}

.hero__inner {
    display: grid;
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
    gap: clamp(2rem, 5vw, 4rem);
    align-items: center;
}

.hero__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    margin-top: 2.25rem;
}

.hero__art {
    /* The design's cover art: an orange field with the stripes across it. Drawn rather than shipped,
       so a page with no picture of its own is finished rather than a gap - and it is the layer under
       the picture, so a page that has one covers the drawing with it. */
    position: relative;
    overflow: hidden;
    /* The sentence written on the frame is set to the size the frame can hold, so the frame is what that
       rule asks - see the query on `.hero__art-note` below. It matters here rather than being a tidy-up:
       this class is also the panel a section draws for itself (`.plate`), and a panel's frame is a band
       the width of the page where a hero's is a square beside the words - two frames of very different
       widths drawing the same sentence, which is exactly the question the window could not answer. */
    container-type: inline-size;
    aspect-ratio: 1 / 1;
    background: var(--tigerstripes-accent);
    border-radius: 2px;
}

.hero__art::after {
    content: "";
    position: absolute;
    inset: 0;
    background: radial-gradient(120% 90% at 80% 10%, rgba(0, 0, 0, 0.14), transparent 60%);
}

/* A picture in the art box, whichever the page has: the page's own, or the front door's tiger. Over
   the drawing rather than beside it - absolute, so both cases fill the box and the note above them
   stays where it is - and above the stripes, so the front door is the picture and the plate under it
   is what is left if the picture never arrives. */
.hero__picture {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    z-index: 1;
}

/* The marquee: the words this studio is made of, moving slowly enough to read. */
.marquee {
    overflow: hidden;
    padding-block: 1.1rem;
    border-block: 1px solid var(--rule);
    background: var(--band);
}

.marquee__track {
    display: flex;
    gap: 3rem;
    width: max-content;
    animation: marquee 42s linear infinite;
}

.marquee__item {
    display: inline-flex;
    align-items: center;
    gap: 3rem;
    font-size: 0.8125rem;
    font-weight: 600;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    white-space: nowrap;
}

/* The separator between the words: the same three stripes the kicker and the header are marked with,
   drawn rather than typed, so the row of words is punctuated by the identity rather than by a character
   from whatever font happens to be loaded. */
.marquee__star {
    inline-size: 0.7rem;
    block-size: 1rem;
    color: var(--tigerstripes-accent);
}

@keyframes marquee {
    from { transform: translateX(0); }
    to { transform: translateX(-50%); }
}

@media (prefers-reduced-motion: reduce) {
    .marquee__track {
        animation: none;
    }

    /* The menu's bars still become an X and still say which way the button will go; they just do not
       travel there. The state is the point and the movement is the flourish. */
    .site-header__bars > span {
        transition: none;
    }
}

/* ---------------------------------------------------------------------- bands */

.band {
    padding-block: var(--section);
}

/* The band the site ends every page with, and the one band that carries the stripes at a size rather
   than at a mark's: the accent field and five tapered strokes running off its right-hand edge, which is
   the drawing the design puts behind its closing call. Behind the words rather than beside them - the
   words stay in the shell's column and their own weight is what makes them readable over it - and the
   inner box is lifted above it so that a link inside the band is still a link rather than a stripe.

   Hidden below the breakpoint, deliberately: the band folds to one column there, so the stripes would
   be behind the sentence rather than beside it, and the sentence is the one thing on the band that has
   to be read. A drawing that costs a reader a sentence is not worth the nine hundred pixels it was
   drawn for.

   The one drawing in this file whose colour is written rather than paired, and the reason is the band
   itself: this band's words are white in both schemes, so its stripes have to be the ink in both - the
   hero's plate follows the scheme because nothing is written on it, and a drawing that went off-white in
   the dark scheme would be white type on white stripes on the one band a reader is being asked to act
   on. Measured on the served page, the longest word on the band is 18.8:1 over those stripes. */
.band--accent {
    position: relative;
    overflow: hidden;
    /* The band's own width is the question its drawing's rule asks - see the query below - and declaring
       the box here is what lets the rule be asked of the band rather than of the window. `inline-size`
       and not `size`: the band's height is its words' own, and a container that had to be told its height
       would be a band nobody could write a sentence in. */
    container-type: inline-size;
    background: var(--tigerstripes-accent);
    color: #fff;
}

.band__inner {
    position: relative;
    z-index: 1;
}

.band__stripes {
    position: absolute;
    inset: -14% -5% -14% auto;
    z-index: 0;
    display: flex;
    align-items: flex-end;
    gap: 6%;
    width: min(44%, 28rem);
    pointer-events: none;
}

.band__stripes > span {
    flex: 1 1 0;
    height: 100%;
    background: #14110f;
    clip-path: polygon(70% 0, 100% 0, 30% 100%, 0 100%);
    opacity: 0.9;
}

.band__stripes > span:nth-child(2) {
    height: 72%;
}

.band__stripes > span:nth-child(3) {
    height: 88%;
    opacity: 0.7;
}

.band__stripes > span:nth-child(4) {
    height: 56%;
    opacity: 0.48;
}

.band__stripes > span:nth-child(5) {
    height: 80%;
    opacity: 0.6;
}

/* 46rem: the band's own width, which is the width at which a sentence and five tapered strokes stop
   fitting in it. Same number as the viewport width this used to be written at, because on every page
   that draws a band the band is the page's own width - what changes is whose width it is. A band in a
   narrow column hides its drawing now, where before a narrow window hid every band's. */
@container (max-width: 46rem) {
    .band__stripes {
        display: none;
    }
}

.band--dark {
    background: var(--ink);
    color: var(--paper);
}

.band--quiet {
    background: var(--band);
}

.band__row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 2rem;
}

.band__title {
    margin: 0;
    font-size: var(--title-size);
    font-weight: 800;
    line-height: 1.02;
    letter-spacing: -0.03em;
}

/* --------------------------------------------------------------------- prose */

/*
  The vocabulary, and the contract between this stylesheet and the content.

  A page body is Markdown, and Markdown renders to elements: this file gives each of them the meaning
  the design needs, so that an editor writing a page is choosing layouts by writing a document. Which
  is the only honest way to do this here - raw HTML in a body is escaped rather than executed, so a
  template cannot be told to open a <section> around a third paragraph.

    ## Heading        a section heading, with the hairline this design puts above every section
    ### Heading       the heading of one item in a list of them
    > quote           a band: full-bleed, dark, with anything inside it laid out in columns
    - item            a row list (services, index rows)
    1. item           a grid of cards (concepts)
    **Q?** text       a list of these becomes an accordion - see js/theme.js
*/

.prose {
    padding-block: calc(var(--section) * 0.5);
    /* Sections are numbered by position - what an editor gets for free by writing headings in order,
       and one less thing to keep in step than the design's hand-written "01 / WHAT WE DO". Both
       counters are reset here, once, and nowhere else: a second `.prose` rule that reset one of them
       would replace this declaration rather than add to it, and the sections would all be 01.

       That "here" is also what keeps this element from being a container, and it was measured rather than
       reasoned: a `container-type` here, or on anything inside it, puts the resets and the increments on
       opposite sides of a containment scope, and every marker on the page comes out 01. The containment
       that the rows' query needs is above both, on `.shell` - see the declaration there. */
    counter-reset: sections rows;
}

.prose > * {
    max-width: var(--column);
}

/*
  A paragraph is the one block in a body set to the measure rather than to the column - see the note where
  the two are declared.

  A heading is not: a heading is short by nature and never reaches either width, and the hairline above it
  is this design's mark across a section rather than across a sentence, so it keeps the whole column. A
  row list and a grid of cards are not, for the reason the two numbers exist.

  The nested selector is an accordion's answer, which is prose and is as long as any other paragraph. The
  one inside a blockquote is not selected: that is a band, laid out by the band's own columns, and it
  already leaves the column entirely.
*/
.prose > p,
.prose details p {
    max-width: var(--measure);
}

.prose h2 {
    margin: calc(var(--section) * 0.8) 0 1.25rem;
    padding-top: 1.5rem;
    border-top: 1px solid var(--rule);
    font-size: clamp(1.75rem, 3.4vw, 2.75rem);
    font-weight: 800;
    line-height: 1.05;
    letter-spacing: -0.03em;
}

.prose h2::before {
    counter-increment: sections;
    content: counter(sections, decimal-leading-zero);
    display: block;
    margin-bottom: 0.75rem;
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.18em;
    color: var(--muted);
}

.prose > h2:first-child {
    margin-top: 0;
}

.prose h3 {
    margin: 2.25rem 0 0.5rem;
    font-size: 1.25rem;
    font-weight: 700;
    letter-spacing: -0.01em;
}

.prose p {
    margin: 0 0 1.25rem;
    color: var(--reading-ink);
}

.prose strong {
    color: var(--ink);
}

.prose a {
    border-bottom: 1px solid var(--tigerstripes-accent);
}

.prose a:hover {
    background: var(--accent-wash);
}

.prose hr {
    height: 1px;
    margin: 3rem 0;
    border: 0;
    background: var(--rule);
}

.prose img {
    border-radius: 2px;
}

/* A row list: the number, the thing, what it is for, and a way in. Dashes in Markdown, columns here. */
.prose ul {
    margin: 0 0 1.5rem;
    padding: 0;
    list-style: none;
}

/*
  A row of this studio's list: a number, the thing, and the line about it - and the number is drawn in the
  row's own gutter rather than given a column of its own.

  It has to be: these rows are an editor's Markdown, and the body of a page is escaped rather than
  executed, so the line about the thing is an anonymous grid item - a bare text run, which no selector can
  reach and no rule can place. Auto-placement puts such a run in the first free cell of the row, so a
  column reserved for a number is a column that a nested list, a second paragraph or any other extra piece
  of an editor's content will land in: measured on this site, one nested list under a row put its whole
  sentence in the 40px number column at a laptop width and in the 32px one on a phone.

  So the number is not a track. It shares the title's cell and is shifted out of it by its own width and a
  matching negative margin, which puts its box exactly in the gutter the padding leaves and leaves the row
  with nothing but content tracks for auto-placement to fill. `justify-self: start` is what holds its own
  box still while its margin does the work, and sharing the cell is also what keeps the number on the
  title's baseline - the row is still the same row, drawn one measure to the left.

  The arrow keeps a track of its own, because it is the last thing in the row and there is nothing left to
  push it into a gutter with - but the track is declared only on a row that is a link, so a row of things
  you get has no narrow column in it at all. Earlier in the cascade than the phone block below, whose
  selector list repeats the `:has()` for that reason: `:has()` would otherwise win a narrower width back.
*/
.prose ul > li {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
    gap: 1rem;
    align-items: baseline;
    padding: 1.35rem 3rem 1.35rem 3.5rem;
    border-top: 1px solid var(--rule);
}

/*
  A row that is a link gets the arrow's track, and gives back the padding that stood in for it: the 3rem
  of trailing padding is the empty arrow column a row of things you get has always had, so a row that has
  an arrow has its track and its gap there instead. Two of those would be the arrow's space counted twice,
  which is 48px the content columns would lose.
*/
.prose ul > li:has(a) {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr) 2rem;
    padding-right: 0;
}

.prose ul > li::before {
    grid-column: 1;
    grid-row: 1;
    justify-self: start;
    inline-size: 3.5rem;
    margin-inline-start: -3.5rem;
    counter-increment: rows;
    content: counter(rows, decimal-leading-zero);
    font-size: 0.75rem;
    letter-spacing: 0.14em;
    color: var(--muted);
}

.prose ul > li > strong {
    grid-column: 1;
    grid-row: 1;
    font-size: 1.1875rem;
    font-weight: 700;
}

/*
  A row whose title is a way to one of this studio's services is drawn with that service's icon before the
  title, which is the design's own row: a number, the thing's mark, the thing, and the line about it.

  It cannot be markup, and that is the honest reason it is done this way. A page body is Markdown and its
  HTML is escaped rather than executed, so nothing in the theme can put a <span> inside an editor's list;
  what the stylesheet can see is where the row leads, so the drawing is attached to the link. That is the
  right way round anyway: a row that is not a link to a service keeps its number and nothing else, and a
  service renamed in the structure takes its drawing with it, because the drawing is chosen by the address.

  `:has()` and `href$=`, both deliberately: there is no class to select on because the markup is the
  editor's, and matching the end of the address means a deployment serving this site under a context path
  draws the same rows rather than silently losing every icon to a leading segment.

  Seven services, seven rules - a drawing on the strong's own pseudo-element, so that the number, the
  title and the arrow keep the columns the row already had. */
.prose ul > li:has(> strong > a[href$="/services/web-design"]) { --draw-offer: var(--draw-browser); }
.prose ul > li:has(> strong > a[href$="/services/software-engineering"]) { --draw-offer: var(--draw-code); }
.prose ul > li:has(> strong > a[href$="/services/software-development"]) { --draw-offer: var(--draw-stack); }
.prose ul > li:has(> strong > a[href$="/services/brand-strategy"]) { --draw-offer: var(--draw-mark); }
.prose ul > li:has(> strong > a[href$="/services/hosting-domains"]) { --draw-offer: var(--draw-rack); }
.prose ul > li:has(> strong > a[href$="/services/seo-performance"]) { --draw-offer: var(--draw-gauge); }
.prose ul > li:has(> strong > a[href$="/services/care-support"]) { --draw-offer: var(--draw-support); }

.prose ul > li > strong:has(> a[href$="/services/web-design"])::before,
.prose ul > li > strong:has(> a[href$="/services/software-engineering"])::before,
.prose ul > li > strong:has(> a[href$="/services/software-development"])::before,
.prose ul > li > strong:has(> a[href$="/services/brand-strategy"])::before,
.prose ul > li > strong:has(> a[href$="/services/hosting-domains"])::before,
.prose ul > li > strong:has(> a[href$="/services/seo-performance"])::before,
.prose ul > li > strong:has(> a[href$="/services/care-support"])::before {
    content: "";
    display: inline-block;
    inline-size: 1.15em;
    block-size: 1.15em;
    margin-inline-end: 0.55em;
    vertical-align: -0.12em;
    background-color: var(--tigerstripes-accent);
    mask: var(--draw-offer, var(--draw-mark)) center / contain no-repeat;
}

/* The arrow is drawn only on a row that is a way somewhere. A row of things you get is a list, not a
   set of links, and an arrow on it promises a click that does nothing - which is the kind of detail
   that makes a page feel like a template rather than like a site. */
.prose ul > li:has(a)::after {
    grid-column: 3;
    grid-row: 1;
    content: "\2197";
    justify-self: end;
    color: var(--muted);
}

/* A grid of cards: concepts, features, anything that is a set rather than a sequence. */
.prose ol {
    display: grid;
    /*
      min(17rem, 100%) rather than 17rem, which is what every auto-fit grid in this file does: 17rem is
      the width a column wants, but a track that is not allowed to be narrower than the screen it is in
      is a column wider than the page, and a column wider than the page is a horizontal scrollbar for
      the whole site. On a phone narrow enough to ask the question - 320 pixels, or a handset held
      sideways - the answer is one column the width of the page rather than a grid that hangs off it.
    */
    grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr));
    gap: 1.5rem;
    margin: 0 0 2rem;
    padding: 0;
    list-style: none;
    counter-reset: cards;
}

.prose ol > li {
    padding: 1.5rem;
    border: 1px solid var(--rule);
    background: var(--surface);
    border-radius: 2px;
}

.prose ol > li::before {
    counter-increment: cards;
    content: counter(cards, decimal-leading-zero);
    display: block;
    margin-bottom: 0.75rem;
    font-size: 0.75rem;
    letter-spacing: 0.14em;
    color: var(--muted);
}

.prose ol > li > strong {
    display: block;
    font-size: 1.25rem;
    font-weight: 700;
    letter-spacing: -0.02em;
}

/* A blockquote is a band: it leaves the column, takes the full width and goes dark. Blockquotes are
   the one block element whose meaning in prose ("this is a different voice") matches what a band does
   in this design ("this is a different part of the page"). */
.prose blockquote {
    max-width: none;
    margin: calc(var(--section) * 0.6) calc(var(--gutter) * -1);
    padding: var(--section) var(--gutter);
    border: 0;
    background: var(--ink);
    color: var(--paper);
}

.prose blockquote > * {
    max-width: var(--shell);
    margin-inline: auto;
}

.prose blockquote h2 {
    padding-top: 0;
    border-top: 0;
    color: var(--paper);
}

/*
  42.5rem: the page's column, which is the box a row of prose is laid out in, and the same number a row of
  the service list gives way at - which is not a coincidence but the point of it. A row of prose is the
  same row: a number, the thing, the line about it, a way in. It was measured against the window while the
  window was the only axis there was, and the window was the wrong one here in a way that never showed: a
  body of prose caps its children at the column, so its rows are the width of its column, and the column
  is 677px at a 736px window - the width the numbers were quietly measuring all along. The container is
  the shell rather than the body of prose, because the prose is where the counters the rows are numbered
  by are reset - see both declarations, which say why the containment cannot live any lower.

  One content column and the number's gutter, which is the same arrangement as a laptop's with the second
  content column gone: the title and the line about it stack, and nothing else changes. It is also the
  arrangement with no narrow track left in it at all - the arrow is dropped here (below), so this row
  cannot squeeze anything an editor writes into it, whatever shape it takes.

  The trap it avoids is that a prose row is not a template's row. `- **A direction, not a mood board.**
  Type, colour, photography and motion decided together` has an element around the title and *no* element
  around the rest of the line - so the line is an anonymous grid item, and an anonymous item cannot be
  given a column or a name. With two tracks and three things to place, auto-placement filled column 1 with
  the number, column 1's row with the title beside it, and put the line about the row underneath the
  number: a 32px column of single words. Measured on `/services/web-design` at 320px of window, where this
  row is 288px, the summary was drawn 40px wide over 22 lines, at about seven characters a line.

  Telling the number to span both rows is the fix, and it is the whole of it: auto-placement then fills
  the second row of the *second* column rather than starting a new line under the first, so the summary
  gets the width it has on a desk - 230 of those 288px, which is 18 to 22 characters a line where the
  measure allows it. Nothing about the row's own order changes.

  The arrow is dropped rather than placed, because in a column this narrow the row is a block of text with
  a marker at its left, and an arrow at the right of it is the smallest thing on the page under a thumb -
  the same reason a row of the service list drops its arrow at this width.

  The quote is the last thing here and the only block in the body that is not a row. A pull-quote leaves
  the column by its own negative margin, and a column this narrow gives back half a gutter instead, so the
  band it draws has a little air rather than sitting against the page's edge.
*/
@container (max-width: 42.5rem) {
    .prose ul > li,
    .prose ul > li:has(a) {
        grid-template-columns: minmax(0, 1fr);
        padding-inline: 3rem 0;
        row-gap: 0.35rem;
    }

    .prose ul > li::before {
        inline-size: 3rem;
        margin-inline-start: -3rem;
        align-self: start;
    }

    .prose ul > li::after {
        display: none;
    }
}

/*
  46rem: the pull-quote stops leaving the column, and it is the one rule this file has that a container
  could not answer. What the narrow arrangement gives back is the page's gutter - the quote is pulled out
  by `calc(var(--gutter) * -1)` and padded in by the same amount, and a gutter is a page's value rather
  than a quote's. So this is the page's own width, and the same one the header folds at; it is named in
  the scale at the foot of the file.
*/
@media (max-width: 46rem) {
    .prose blockquote {
        margin-inline: calc(var(--gutter) * -0.5);
        padding-inline: calc(var(--gutter) * 0.5);
    }
}

/* The band's own title: the first thing written inside it, set the way a section heading is set rather
   than the way a paragraph is, because that is what it is being used as. */
.prose blockquote > p:first-child {
    margin-bottom: 0;
    color: var(--paper);
    font-size: var(--title-size);
    font-weight: 800;
    line-height: 1.05;
    letter-spacing: -0.03em;
}

.prose blockquote > p:first-child + * {
    margin-top: 1.5rem;
}

.prose blockquote p,
.prose blockquote strong {
    color: var(--band-soft);
}

.prose blockquote ol {
    grid-template-columns: repeat(auto-fit, minmax(min(13rem, 100%), 1fr));
    margin-top: 2.5rem;
}

.prose blockquote ol > li {
    padding: 1.25rem 0 0;
    border: 0;
    border-top: 1px solid var(--rule-dark);
    background: transparent;
}

.prose blockquote ol > li > strong {
    color: var(--paper);
}

/* An offer list, which is what the faq's list becomes in the browser: see js/theme.js. */
.prose details {
    border-top: 1px solid var(--rule);
    border-bottom: 1px solid var(--rule);
}

.prose details + details {
    border-top: 0;
}

.prose summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 1.25rem 0;
    font-weight: 600;
    cursor: pointer;
    list-style: none;
}

.prose summary::-webkit-details-marker {
    display: none;
}

.prose summary::after {
    content: "+";
    font-size: 1.25rem;
    color: var(--muted);
}

.prose details[open] summary::after {
    content: "\2212";
}

.prose details p {
    margin: 0 0 1.25rem;
    color: var(--muted);
}

/* --------------------------------------------------------------- listings */

/* The pages nested under a section, drawn as the section's own grid. Each one is a title, a line
   about it, and a way in - and a picture when the item has one, which is what an attached library
   image becomes here. */
.cards {
    display: grid;
    /* The card's own width, capped at the screen: see the note on .prose ol above. */
    grid-template-columns: repeat(auto-fit, minmax(min(20rem, 100%), 1fr));
    /*
      The gap between two cards is asked of the grid's own column rather than of the window, which is the
      only box a gap between two tracks is about. It was measured in the window and it showed: 3vw kept
      growing while the page's column stopped at the shell, so at 1440px and above the cards stood 40px
      apart for no reason a reader could see, and a narrower window did not narrow the gap.

      `.cards` is a direct child of the shell, so the shell's content box is the grid's own box - measured
      the same number at every window from 375px to 1920px - and `cqw` here is that box, one element up,
      because an element cannot ask itself. The two clamps are the ones the design was measured with: the
      gap is the 1.5rem floor in any column under about 700px, and 2.5rem in a column at a desk's width,
      where it now stops growing because the column has.
    */
    gap: clamp(1.5rem, 3.4cqw, 2.5rem);
    padding-bottom: calc(var(--section) * 0.5);
}

.card {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.card__art {
    position: relative;
    overflow: hidden;
    aspect-ratio: 16 / 10;
    background: var(--band);
    border-radius: 2px;
}

.card__picture {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* The drawn stand-in for a concept whose picture has not been attached yet: the stripes again, at the
   size of a thumbnail. A placeholder that looks deliberate is better than one that looks broken, and
   it is the same mark the header uses. */
.card__art--drawn {
    display: flex;
    align-items: flex-end;
    gap: 0.75rem;
    padding: 1.75rem;
    background: linear-gradient(135deg, var(--plate-from), var(--plate-to));
}

.card__art--drawn::before,
.card__art--drawn::after {
    content: "";
    flex: 1 1 auto;
    height: 58%;
    background: var(--tigerstripes-accent);
    clip-path: polygon(70% 0, 100% 0, 30% 100%, 0 100%);
}

/* The second stroke is the ink one, at the height of the panel: the mark is two colours, and a card
   whose picture has not been attached yet should look like the studio rather than like a missing
   image. */
.card__art--drawn::after {
    height: 100%;
    background: var(--ink);
}

.card__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    padding-top: 0.75rem;
    border-top: 1px solid var(--rule);
}

.card__title {
    margin: 0;
    font-size: 1.5rem;
    font-weight: 700;
    letter-spacing: -0.02em;
}

.card__arrow {
    color: var(--muted);
}

/*
  A concept's arrow, which is the same statement as the title beside it and says so with thirteen pixels
  of glyph - which is fine under a pointer and the smallest thing on the page under a thumb. The head is
  a row of two things with a gap between them, so what grows here is a ring around the arrow rather than
  the arrow itself: nothing moves, and the place a finger has to land on becomes thumb-sized.

  A thumb rather than a width, which is what this was written as and what it should always have been: the
  question is what is pointing at the page and not how wide the window is, and the two only look alike
  because a phone is both narrow and thumbed. `any-pointer` rather than `pointer`, because a thumb is
  whatever else is touching the screen - a touchscreen laptop with a mouse plugged into it has one of
  each, and the reader whose finger is on it is the reader this is for. A device with no coarse pointer
  gets no ring, and loses nothing: the arrow is a hit target that is already the right size for a mouse.
*/
@media (any-pointer: coarse) {
    .card__arrow {
        position: relative;
    }

    .card__arrow::after {
        content: "";
        position: absolute;
        inset: -0.85rem -0.9rem;
    }
}

.card__summary {
    margin: 0;
    color: var(--muted);
}

.card__link {
    display: block;
}

.cards__note {
    max-width: var(--measure);
    margin: 0 0 2rem;
    font-size: 0.9375rem;
    color: var(--muted);
}

/*
  A section's own list: the pages filed under it, in the structure's order. The markup is the same
  shape as the prose row list above - number, name, line about it, way in - because that is the same
  thing said twice, once about content somebody wrote and once about pages somebody filed. This one is
  a component rather than a rule inside `.prose` because a section's list is not part of the prose: it
  is drawn by the template, from the children the page service resolved.
*/
.rows {
    margin: 0;
    padding: 0;
    list-style: none;
    /* How wide this list is decides how many columns a row of it may have, so the list is what its rows
       ask - see the query below. */
    container-type: inline-size;
}

.rows > li {
    display: grid;
    /* The number, the thing's own drawing on its plate, the thing, what it is for, a way in - which is
       the design's own row, and one column more than this list had before the drawings existed. */
    grid-template-columns: 2.5rem 2.75rem minmax(0, 1.1fr) minmax(0, 1.4fr) 2.5rem;
    gap: 1rem;
    align-items: baseline;
    padding: 1.5rem 0;
    border-top: 1px solid var(--rule);
}

/* The row's drawing, which the template names after the page it lists: `.icon--<last segment of the
   page's path>` is what turns a page that is filed under a section into the drawing of what it is, and
   a page with no drawing of its own arrives with the studio's mark rather than with a blank plate. */
.rows__tile {
    align-self: center;
}

.rows__number {
    font-size: 0.75rem;
    letter-spacing: 0.14em;
    color: var(--muted);
}

.rows__title {
    margin: 0;
    font-size: 1.375rem;
    font-weight: 700;
    letter-spacing: -0.02em;
}

.rows__title:hover {
    color: var(--tigerstripes-accent);
}

.rows__summary {
    margin: 0;
    color: var(--muted);
}

.rows__arrow {
    justify-self: end;
    color: var(--muted);
}

.rows__arrow:hover {
    color: var(--ink);
}

/* 42.5rem: the row's own width, and it is the row's own numbers from here on. The four columns gave way
   at 677px of row when this was a 46rem window - 677px being what the row is at a 736px window, which is
   how the number was found and not what it means - and 42.5rem is that width rounded up to the half-rem
   this file works in, because a row that stacks a pixel late has lost nothing and one that stacks a pixel
   early has lost a column. */
@container (max-width: 42.5rem) {
    /* Named areas rather than a second declaration of the same four columns: on a phone the row is two
       lines - the title and the line about it, side by side with the number and the plate - and the
       number and the plate are told to span both rather than being placed where auto-placement drops
       the summary. */
    .rows > li {
        grid-template-columns: 2.5rem 2.25rem minmax(0, 1fr);
        grid-template-areas:
            "number tile title"
            "number tile summary";
        row-gap: 0.35rem;
    }

    /* Both are told to sit at the top rather than being centred against the two rows they span: a row
       whose line about it runs to four lines would otherwise float its number and its plate down to the
       middle of the paragraph, and a list's marks belong beside the thing they mark. */
    .rows__number {
        grid-area: number;
        align-self: start;
    }

    .rows__tile {
        grid-area: tile;
        align-self: start;
        inline-size: 2.25rem;
        block-size: 2.25rem;
    }

    .rows__title { grid-area: title; }

    .rows__summary { grid-area: summary; }

    .rows__arrow {
        display: none;
    }
}

/*
  The studio's news: a date, a title, the line about it and a way in. One component drawn in two places -
  the news page, which draws all of it, and the front door, which draws the first few - for the reason
  the section list above is one component: the same thing said twice should not become two vocabularies
  that drift apart.

  The date column is the whole difference between this and `.rows`, and it is wider than that list's
  number because "15 October 2026" is a phrase rather than a digit: set in a three-character column it
  would be a heap. How the date is drawn is not this component's business - that is `.date`, which the
  article's own date line draws too, so one note's date looks the same in the list and on the page it
  belongs to.
*/
.news {
    margin: 0;
    padding: 0 0 calc(var(--section) * 0.4);
    list-style: none;
    /* The list's own width is what its rows are measured against - see the query below - so the list is
       the container rather than the window being the unit. */
    container-type: inline-size;
}

.news > li {
    display: grid;
    /* The note's own plate, the date, the note, what it is about, a way in - one column more than this
       row had before the drawings existed, and the same shape of change the service list went through for
       the same reason: see `.rows > li`, which is this row with a number instead of a date. The first two
       tracks are the service row's own two tracks, because the plate is the same plate at the same size. */
    grid-template-columns: 2.75rem 9rem minmax(0, 1fr) minmax(0, 1.5fr) 2.5rem;
    gap: 1rem;
    align-items: baseline;
    padding: 1.5rem 0;
    border-top: 1px solid var(--rule);
}

/*
  Each part of a row is told which column it is in rather than left to auto-placement, and the reason is
  the one part of a row that a page may not have: the date is written only when the article it belongs to
  has one (`th:if="${item.publishedAt}"`), so an undated article's row arrives with three parts for four
  columns. Auto-placement fills the tracks in document order from the top left, so three parts in four
  columns is not a row with a gap in it - it is every part one place to the left: measured on /news at
  1024px with the date removed, the title was drawn 144px wide over five lines where it has 284px in two,
  and the line about it followed it one column left with a quarter of its width gone. It is the same trap
  as the numbering on a prose row, in the
  other direction - there a piece of an editor's content took a column reserved for a number, and here a
  part of the row that is not written takes the column reserved for the date.

  So the row is placed part by part, and a row that has no date leaves the date's column empty. The
  selector is the child rather than each class so that the row keeps one place where its columns are
  described; the block below, where the row becomes a single column, is where this is dropped again.
*/
.news > li > * {
    grid-row: 1;
}

/* The type is not set here: a date is `.date`'s to draw, and this rule is only where the row puts it. */
.news__date {
    grid-column: 2;
}

.news__title {
    grid-column: 3;
    margin: 0;
    font-size: 1.375rem;
    font-weight: 700;
    letter-spacing: -0.02em;
}

.news__title a:hover {
    color: var(--tigerstripes-accent);
}

.news__summary {
    grid-column: 4;
    margin: 0;
    color: var(--muted);
}

.news__arrow {
    grid-column: 5;
    justify-self: end;
    color: var(--muted);
}

.news__arrow:hover {
    color: var(--ink);
}

/*
  The drawing on a news row, and the twenty a list of them cycles through.

  A news row had no drawing because a note has no subject a drawing can name - see the note on the twenty
  at the top of this file. What it takes instead is the drawing of the kind of thing a note is, and which
  of the twenty a particular row gets is decided by *where it is in the list* rather than by the note.

  That is a decision worth its own paragraph, because the two alternatives are both available and both are
  wrong here. Letting the editor pick one would be a field on a page, a control in the editor and a step
  in the writing of every note, for something no reader is told and no writer asked for. Letting the
  drawing follow the note's subject would put the drawing of a website on three notes about websites, in
  a row, which is the opposite of what a set of twenty is for.

  So the cycle is positional, and it is written with `:nth-child` rather than with a number the template
  works out, for three reasons. It needs no markup: the two templates that draw this row each hand it
  their own list and neither has to know how many drawings there are or count anything. It cannot come
  out of step: the twentieth row wears the twentieth drawing whatever the list does, and a note added,
  dragged or unpublished upstream moves the whole cycle with it by itself. And it is a rule about a list,
  which is what a cycle between rows is - the same argument the twenty drawings themselves rest on, that
  a shape a page can work out for itself is not a shape a template has to choose.

  `20n + k` rather than `nth-child(k)`, so the twenty repeat: the twenty-first row wears the first
  drawing, which is what "cycled through" means and what a section with more than twenty notes in it
  needs.

  The drawing is not bare ink. A news row wears the plate a service row wears - `.tile`, a rounded square
  of the accent with the ink stripes through it and the drawing in white over them - because a news row
  and a service row are the same kind of row: a list of things this studio does, each one labelled with
  the studio's own mark. What differs between them is only who chooses the drawing, and a news row is the
  one place the list chooses rather than the thing.

  That reverses an earlier decision in this file, and the reversal is worth recording because the
  objection to it was not wrong, only outweighed. The objection: this design's first rule is that the
  accent stays rare enough to mean something, and `/news` puts one plate on every note in one list, so a
  studio with twenty notes has twenty orange plates on one page - more than anywhere else on the site.
  What outweighs it is what the plate *says*. Bare ink in the row's own colour said "furniture", which is
  right for a date and wrong for a drawing: it made the mark read as a bullet beside a title. On the plate
  it is the studio's mark, which is what a service row's drawing is, and that the two rows are the same
  family is the point rather than a side effect - `/services` lists what the studio does and `/news` lists
  what it has done, and both are labelled the way this identity labels a thing of its own. A studio that
  disagrees has one class to remove: drop `tile` from the span in the two templates and every row falls
  back to ink, which is how it was drawn before.

  What is not given up is the accent's rarity in the places that rule was written for. Nothing here is a
  call to action - the plate is not a link, the arrow beside the title still is, and pointing at a title
  still turns the accent on for the one thing the reader is pointing at. A plate is a label, and a label
  repeated down a list is a list of labelled things rather than twenty competing buttons.

  The plate is the service row's own size, 2.75rem, rather than a size invented for this caller, so the
  theme has one plate and not two. That is 44px where the bare drawing was 24px, so the row's first track
  went from 1.5rem to 2.75rem with it and the words are what pay for it - which is why the row's stacking
  point moved again below. The plate is centred on the row rather than sharing its baseline, which is what
  `.rows__tile` does and for the same reason: a plate has no baseline to share.

  `.news__mark` carries no rule of its own, deliberately. It is the glyph inside the plate - the thing the
  cycle below assigns a drawing to - and everything about how it looks is `.tile > .icon`'s: 1.5rem, white,
  and the ink halo that gives a white line crossing from the accent to the stripes an edge to be read
  against. It stays a class rather than becoming a bare `.icon` because the twenty selectors below need
  something to hold, and because a name is how the two templates that draw this row say which drawing the
  list is choosing for.

  The order is the order a reader meets them, so that two drawings that could be mistaken for each other
  never land next to each other: the twenty are also arranged so that no two neighbours in this list share
  a silhouette - a circle is followed by a bolt, the bolt by an hourglass, the two upright marks (the flag
  and the pencil) by a compass and a pin. Reordering these lines changes which drawings sit beside each
  other, which is the only thing about the order that is a decision rather than a preference.
*/
.news__tile {
    grid-column: 1;
    align-self: center;
}

.news > li:nth-child(20n + 1) .news__mark { --draw: var(--draw-calendar); }
.news > li:nth-child(20n + 2) .news__mark { --draw: var(--draw-pencil); }
.news > li:nth-child(20n + 3) .news__mark { --draw: var(--draw-pin); }
.news > li:nth-child(20n + 4) .news__mark { --draw: var(--draw-bulb); }
.news > li:nth-child(20n + 5) .news__mark { --draw: var(--draw-rocket); }
.news > li:nth-child(20n + 6) .news__mark { --draw: var(--draw-flag); }
.news > li:nth-child(20n + 7) .news__mark { --draw: var(--draw-compass); }
.news > li:nth-child(20n + 8) .news__mark { --draw: var(--draw-bolt); }
.news > li:nth-child(20n + 9) .news__mark { --draw: var(--draw-hourglass); }
.news > li:nth-child(20n + 10) .news__mark { --draw: var(--draw-plane); }
.news > li:nth-child(20n + 11) .news__mark { --draw: var(--draw-ruler); }
.news > li:nth-child(20n + 12) .news__mark { --draw: var(--draw-plug); }
.news > li:nth-child(20n + 13) .news__mark { --draw: var(--draw-book); }
.news > li:nth-child(20n + 14) .news__mark { --draw: var(--draw-trend); }
.news > li:nth-child(20n + 15) .news__mark { --draw: var(--draw-mug); }
.news > li:nth-child(20n + 16) .news__mark { --draw: var(--draw-clipboard); }
.news > li:nth-child(20n + 17) .news__mark { --draw: var(--draw-sliders); }
.news > li:nth-child(20n + 18) .news__mark { --draw: var(--draw-sprout); }
.news > li:nth-child(20n + 19) .news__mark { --draw: var(--draw-megaphone); }
.news > li:nth-child(20n + 20) .news__mark { --draw: var(--draw-map); }

/*
  The row stacks from 69rem rather than from 46rem, and the date's column is why: it is a fixed 9rem
  because "15 October 2026" is a phrase rather than a digit, so a row that draws a date has 232px of itself
  spoken for - the date, the arrow's track and three gaps - before the title and the line about it are
  given anything at all.

  It has moved twice, and both moves are the same arithmetic. It stacked from 62rem until the drawings
  arrived and added a fifth column, which took 40px off the two columns that carry words (24px of track
  and one more 16px gap) and put it at 68rem. The drawings then went onto the service row's plate, which
  is 44px rather than 24px, and took another 20px off the same two columns - so the row gives way at 69rem
  now.

  What decides the number is the threshold the title needs, which is a title two lines wide. A row of five
  columns gives the title (row width - 292px) / 2.5, its share of what is left after the plate, the date,
  the arrow and four gaps, and the boundary was measured rather than derived: with the plates in place and
  the five-column grid forced over the served page, a 283px title is drawn over three lines and a 284px one
  over two, which puts the boundary at a row 999px wide (62.44rem) - a 1086px window when the row was the
  page's own width, which is how the number was found and not what it means. This query gives way at
  1016px of row (63.5rem), so the five-column row only ever runs where a title is at least 290px - 17px of
  row clear of the boundary. That margin is deliberate and it is the reason this is 63.5rem and not the
  62.5rem the boundary nearly reaches: the measurement is one machine's font metrics, and a row that gave
  way at the boundary itself would draw a three-line title on the next machine over. Below it a note's
  name is drawn on one line of the whole row, which is what the rule below does; above it, in two lines of
  its own column.

  The drawings' cost above this breakpoint is a constant and not a squeeze - the title is 8px narrower and
  the line about the note 12px narrower than they were with a bare 24px drawing, at every width, and the
  row is the same height - so the spacing, the columns and the parts are otherwise left exactly as they
  were. The plate is wider than the drawing it replaced and the words pay for it, which is the same trade
  the fifth column made and the same one `.rows > li` made when the service rows got their plates.

  Measured with every part of every row present, that is what the row is when it has four columns, and it
  is the measurement the threshold rests on: at a row 706.6px wide the title was drawn 190px wide over
  four lines and at 736px 202px over four, having been 677px in one line while the row was one column, and
  its own measure of 346px came back at a row 942px wide, where the row drew it in two lines of 284px. A
  row of four columns needs about the width a laptop gives it, and a row of five needs 64px more of row
  for the same two lines, which is where the 63.5rem below comes from before the plate's own 20px is added
  to it. The two arrangements the list has are the two the design wants, and the boundary between them is
  the row's own: the list is the container, and see `.rows > li` for the same question asked of the
  service list.

  One column, so the row's parts stack in the order they are written and the placement above is dropped
  with the columns it was written for. It is also the arrangement in which nothing can be squeezed: the
  only track is the width of the list, and a row with no date is simply a row with one line fewer.
*/
@container (max-width: 63.5rem) {
    .news > li {
        grid-template-columns: minmax(0, 1fr);
        row-gap: 0.4rem;
    }

    .news > li > * {
        grid-column: auto;
        grid-row: auto;
    }

    .news__arrow {
        display: none;
    }
}

/*
  The news band on the front door: a hairline, a heading row that names the section and links to all of
  it, then the same list. A rule and a lot of air rather than a box, because the front door is one
  document with sections in it - the same reason nothing else on this site is a card.
*/
.news-band {
    margin-top: calc(var(--section) * 0.4);
    padding-top: calc(var(--section) * 0.35);
    border-top: 1px solid var(--rule);
}

.news-band__head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: 1rem 2rem;
    margin-bottom: 1rem;
}

/* The kicker that labels a band sits closer to its heading than the one that opens a page: this is a
   label on a section of a page rather than a label above a whole page's title. */
.news-band .kicker {
    margin-bottom: 0.75rem;
}

.news-band__title {
    margin: 0;
    font-size: var(--title-size);
    font-weight: 800;
    letter-spacing: -0.03em;
}

.news-band__all {
    font-size: 0.875rem;
    font-weight: 700;
    color: var(--muted);
}

.news-band__all:hover {
    color: var(--tigerstripes-accent);
}

/* ---------------------------------------------------------------- contact */

.contact {
    display: grid;
    /*
      The words get the measure; the form gets what is left.

      It was a 0.8fr/1.2fr split, and that is how this page came to disagree with every other page about
      how long a line is: a share of a grid is not a line length, and at a desk the column came out at
      433px, which is 54 characters against the 91 the same paragraph was set to everywhere else. Neither
      number was chosen - one was inherited from the column and the other from the fraction.

      The form is the half that can take what remains, because a field is as wide as its column and its
      label sits above it; a paragraph cannot, which is the whole reason the measure exists.
    */
    grid-template-columns: minmax(0, var(--measure)) minmax(0, 1fr);
    gap: clamp(2rem, 5vw, 4.5rem);
    padding-block: calc(var(--section) * 0.5) var(--section);
}

.contact .prose {
    padding-block: 0;
}

.contact .prose h2 {
    padding-top: 1.25rem;
    font-size: 1.75rem;
}

/*
  A blockquote in this column is a note rather than a band - and that is the whole reason the contact
  page gets its own rule: the same element is a full-bleed dark band in the middle of a page of prose,
  where it changes the subject, and a remark beside a form, where changing the subject would be the
  loudest thing on the screen. Which one it is depends on the column, not on the markdown.
*/
.contact .prose blockquote {
    margin: 2.5rem 0 0;
    padding: 1.25rem 1.5rem;
    border-left: 2px solid var(--tigerstripes-accent);
    background: transparent;
    color: var(--muted);
    font-size: 0.9375rem;
}

.contact .prose blockquote > p:first-child {
    margin-bottom: 0;
    color: var(--muted);
    font-size: 0.9375rem;
    font-weight: 400;
    letter-spacing: normal;
    line-height: 1.6;
}

/* The mark at the foot of the left column: the studio's own signature, drawn large, because a contact
   page is the one place it is allowed to take up room. */
.contact__mark {
    margin: 3rem 0 0;
}

.contact__mark .stripes {
    height: 3.5rem;
    gap: 4px;
}

.contact__mark .stripes__stroke {
    width: 0.9rem;
}

.form {
    padding-top: 1.5rem;
    border-top: 1px solid var(--rule);
}

.form__legend {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 2rem;
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--muted);
}

.form__row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
    gap: 1.5rem;
}

.field {
    margin-bottom: 1.75rem;
}

.field__label {
    display: block;
    margin-bottom: 0.5rem;
    font-size: 0.9375rem;
    font-weight: 600;
}

.field__optional {
    font-weight: 400;
    color: var(--muted);
}

.field__input,
.field__select,
.field__textarea {
    width: 100%;
    padding: 0.85rem 1rem;
    border: 1px solid var(--rule);
    border-radius: 2px;
    background: var(--surface);
    color: var(--ink);
    font: inherit;
    font-size: 1rem;
}

.field__textarea {
    min-height: 11rem;
    resize: vertical;
}

.field__hint {
    display: block;
    margin-top: 0.6rem;
    font-size: 0.875rem;
    color: var(--muted);
}

/* The chips that answer "what can we help with?": a radio each, drawn as a pill, so the whole control
   is one keyboard stop per option rather than a script. */
/* Bottom margin kept here rather than inherited from .field: this selector sets the margin for the
   fieldset it is also a field, and a rule that reset it to zero would take the spacing with it. */
.choices {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
    margin: 0 0 1.75rem;
    padding: 0;
    border: 0;
}

.choices__option {
    position: relative;
}

.choices__input {
    position: absolute;
    inset: 0;
    opacity: 0;
    cursor: pointer;
}

.choices__label {
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.55rem 1.1rem;
    border: 1px solid var(--rule);
    border-radius: 999px;
    font-size: 0.875rem;
    cursor: pointer;
}

/* The pill's drawing, at the size of the label rather than of a line of body text: the pills are one row
   of five and a glyph inheriting `.icon`'s 1.25rem sits heavy in a 0.875rem control. It is painted in the
   pill's own ink, which is the ink of the page until the pill is chosen and the paper colour after it, so
   the chosen pill's drawing is white without a rule for it. */
.choices__label .icon {
    inline-size: 1.05rem;
    block-size: 1.05rem;
}

.choices__input:checked + .choices__label {
    border-color: var(--ink);
    background: var(--ink);
    color: var(--paper);
}

.choices__input:focus-visible + .choices__label {
    outline: 2px solid var(--tigerstripes-accent);
    outline-offset: 2px;
}

/*
  The note and the one action, on a row while there is room for both.

  They get a line each when there is not, and that is the whole of this rule's work: the row was laid out
  with the note and the button side by side at every width, so on a narrow phone the button was given
  about a third of the row and its label broke into three lines - "Send" / "your" / "brief" - inside a pill
  built to hold one. Wrapping is what the hero's own pair of actions already does, and the button is the
  thing on the form a visitor has to press, so it is the last thing that should be squeezed to fit beside
  a note about asterisks.
*/
.form__actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 1.5rem;
    margin-top: 1rem;
}

.form__note {
    margin: 0;
    font-size: 0.8125rem;
    color: var(--muted);
}

/* What the server said was wrong with a field, under the field it is about. The accent is the site's
   one colour for "this is the thing to act on", which is exactly what a refused field is; the field
   itself is outlined in the same colour, so a reader who scrolled past the message can still see which
   one it was. Both are drawn from the markup the server sent rather than from a script, so a
   submission refused with JavaScript off looks the same as one refused with it on. */
.field__error {
    margin: 0.6rem 0 0;
    font-size: 0.875rem;
    color: var(--tigerstripes-accent);
}

.field__input[aria-invalid="true"],
.field__select[aria-invalid="true"],
.field__textarea[aria-invalid="true"] {
    border-color: var(--tigerstripes-accent);
}

/* The honeypot: off the page rather than `display: none`, because a script that fills in every field it
   can find is not put off by a field it cannot see - and one that checked whether the field was visible
   would be a script that had already read this stylesheet. Moving it out of the flow keeps it in the
   document, where the tab order, the assistive-technology tree and the spam script can all still reach
   the attributes that tell them to leave it alone. */
.form__trap {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

/* What a visitor who has just sent something sees in place of the form. It keeps the form's own top
   rule and column width, so the confirmation lands where the form was rather than beside a gap. */
.form__sent {
    padding-top: 1.5rem;
    border-top: 1px solid var(--rule);
}

.form__sent h2 {
    margin: 0 0 0.75rem;
    font-size: 1.5rem;
}

.form__sent p {
    margin: 0 0 1.5rem;
    color: var(--muted);
}

.form__sent-actions {
    margin: 0;
}

/* --------------------------------------------------------------------- media */

/* What a page's own media becomes: the markup is written once in cms-core and this is how this site
   draws it. A gallery is a grid of thumbnails that open the full picture with or without the site's
   script - see the built-in js/site.js, which this theme inherits. */
.media {
    margin: 2rem 0;
}

.media figcaption {
    margin-top: 0.75rem;
    font-size: 0.875rem;
    color: var(--muted);
}

/* A row of technology marks: the pictures a technology page carries, drawn as one band rather than one
   plate each. A mark is neither a photograph nor this site's own art, so it gets the least treatment
   that still looks deliberate - a plate of its own, the mark centred on it, and one height for all of
   them.

   The height is the thing with a size and the width follows the artwork, which is not how a grid of
   equal cells reads but is the only arrangement in which the marks do read as equal. A mark wall is
   compared by eye across the row, and what an eye compares is height: the fifteen marks of this site
   run from Scala's 0.62:1 to Oracle's 7.65:1, so a plate of one fixed width draws the narrow one the
   full 40px and the wide one 13px - a third of the others, and near invisible. Measured on
   /services/software-development before this rule, that is exactly what four of them did (Oracle 13.2px,
   MongoDB 26.5, Spring 27.3, SQL Server 28.2). Sizing by height instead, every mark is drawn 40px tall
   and a wide lockup simply gets a wide plate, which is what a row of marks is normally allowed to look
   like. A plate is as wide as the mark it holds plus its own padding, and the widest of them is 337px -
   the price of the rule, paid once, on a plate that is still under a third of the shell.

   The plate behind each mark is deliberately not a pair. A mark is drawn in the colours its owner
   publishes it in and several of them are nearly black, so a plate that went dark with the scheme would
   swallow half the row; like the accent, it is one colour in both schemes. */
.marks {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin: 2rem 0 0;
    padding: 0;
    list-style: none;
}

/* Stretched rather than centred, so a row of plates is one height even where a mark is bounded by its
   width: the plate is what the row is built of, and a plate that fitted its mark exactly would make the
   row's own edge ragged for no gain. */
.marks > li {
    display: grid;
    place-items: center;
    padding: 0.9rem;
    border: 1px solid var(--rule);
    border-radius: 2px;
    background: #f7f4ef;
}

/* The figure a mark arrives in is the platform's own markup, so nothing here is about the markup: it is
   taken back to nothing - no margin, no caption, no width of its own. */
.marks .media {
    display: grid;
    place-items: center;
    margin: 0;
}

/* One height, whatever the artwork's shape: a named height with an automatic width, which is the whole
   of the rule. The cap is a guard rather than a size - a mark too wide to sit on a line at this height
   is scaled down with its own ratio kept, and the one mark that comes near it is Oracle at 307px of the
   320px allowed. */
.marks .media img {
    height: 2.5rem;
    width: auto;
    max-width: min(20rem, 100%);
}

/* The owners' line under the row: whose marks those are, at the size the footer's own legal line is set
   at, and under the row rather than in the footer because it is about the marks on this page and about
   nothing else on it. Small on purpose in both senses - it is a credit line rather than an argument, and
   it is set at 13px so a reader who is not looking for it is not stopped by it, which is the way round
   this is normally asked for. The measured column keeps it to a paragraph rather than a line across the
   page. See the note in templates/technology.html for why the wording is not this stylesheet's. */
.marks__note {
    max-width: var(--measure);
    margin: 1.25rem 0 0;
    font-size: 0.8125rem;
    line-height: 1.7;
    color: var(--muted);
}

/* ---------------------------------------------------------------- diagrams */

/* A page under /services draws its own shape in this band: a row of plates, the accent for what orders
   them and for the small shapes that stand for their parts, and type for the words. Two materials, the
   same two the rest of the identity is made of, and drawn rather than shipped - which is the whole
   point of it. A photograph of a rack room says "a rack room"; a diagram of where a certificate sits
   between a domain and a server says what the page is about.

   Drawn by the template rather than written into a page's prose, so an editor can rewrite the words
   under it without the drawing going with them, and drawn from a fixed vocabulary rather than one
   bespoke thing per page: see fragments/diagrams.html, which is the only file that has to know a page
   by name. Two shapes, one per kind of subject - `--chain`, the steps of something that happens in
   order, numbered the way a row in a list is; and `--set`, the parts of something that
   simply is, each with a drawn glyph instead of a number, because a set has no first member.

   Both carry the drawing of their own subject, and on a chain's plate that drawing sits in the corner
   beside the number rather than on a row of its own: the number is what says where the step falls in the
   order and it has to read first, and the glyph is a mark on the plate rather than a heading. A step that
   had a number and nothing beside it was the one plate in this site that carried nothing of what it was
   about - the reader got the order and had to take the subject on trust. */
.diagram {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin: 2rem 0 0;
    padding: 0;
    list-style: none;
}

.diagram__step {
    display: grid;
    align-content: start;
    gap: 0.4rem;
    flex: 1 1 10rem;
    min-width: 0;
    padding: 0.9rem 1rem;
    border: 1px solid var(--rule);
    border-radius: 2px;
    /* The same plate a mark sits on, and like it the same in both schemes: the shapes drawn on it are
       the accent, and the accent is one colour either way. That is also why the type on the plate is
       fixed rather than paired - an off-white plate keeps the light scheme's inks whatever the page
       around it does, and a note set in the dark scheme's grey would be grey on off-white. */
    background: #f7f4ef;
    color: #14110f;
}

/* The number and the step's own drawing, on the one row: the number at the start where a chain's reader
   looks for it, the drawing at the end, and the drawings of the five plates lining up down the diagram's
   right-hand side the way a set's line up down its left. Nothing here is sized to its content: the row is
   two things that are both fixed, so it cannot overflow a plate narrower than either of them. */
.diagram__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem;
}

/* The number is a word as far as a reader is concerned, and it is set at 12px, so it is the muted ink
   rather than the accent: measured on the plate, the accent as small type comes out at 3.39:1, under the
   4.5:1 small text is owed, which is the same reason a row's number in this site is muted. The accent is
   left to the shapes, which carry no text and no contrast rule. */
.diagram__number {
    font-size: 0.75rem;
    letter-spacing: 0.14em;
    color: #6b6560;
}

.diagram__name {
    font-weight: 700;
    letter-spacing: -0.01em;
}

.diagram__note {
    margin: 0;
    font-size: 0.8125rem;
    line-height: 1.6;
    color: #6b6560;
}

/* A member's own drawing rather than a shape standing in for one: the drawings are the icon set above,
   each member of a set is given the one that is its subject rather than the one that comes next in a
   rotation of four, and fragments/diagrams.html is where a member is named.

   The accent, because a drawing on this plate is the colour this theme draws its shapes in, and because
   a drawing is not small text: measured on the plate, the accent is 3.41:1 where the 4.5:1 that small
   text is owed applies to the number beside it and the 3:1 a graphic is owed does not. */
.diagram__icon {
    inline-size: 1.5rem;
    block-size: 1.5rem;
    color: var(--tigerstripes-accent);
}

/* What a page gets when the theme has no diagram for it: the studio's own drawing, the accent field with
   the stripes across it, because a page under /services with nothing drawn on it would be the one page
   of the section that looks unfinished. Nothing on it is written or said - it is the identity rather
   than a diagram, which is why it is the fallback rather than the house style. */
.diagram__band {
    position: relative;
    /*
      A band is as tall as it is wide, so the band's height is its own width's and not the window's. 16vw
      measured the window rather than the band: the ratio the design asks for - a band 5.75 times as wide
      as it is tall, which is what the clamp it replaced worked out to wherever it was not saturated -
      held only in a slice of widths in the middle, and outside it the band was 96px on a 1920px window
      and 96px on a 375px one.

      23 / 4 is that ratio and the two clamps are the design's own floor and ceiling, which is why the
      geometry it was measured at is unchanged: against the served page, the band is 96px at a 375px and
      a 600px window, 123px at 768px, and 144px from 900px up - the six readings 16vw gave at 375, 600,
      768, 900, 1024 and 1280px, to the pixel. The inline size is stated rather than left to the ratio:
      with both axes auto, `min-height` is a constraint the ratio is applied to, and a band in a column
      narrower than 552px came out 552px wide - a band that overflowed the page it was drawn on. What the
      change is for shows on a band that is not the page's width: this one is in a section, and an editor
      who puts it beside something gets a band that is short there rather than one sized for a screen it
      is not on.
    */
    inline-size: 100%;
    aspect-ratio: 23 / 4;
    min-height: 6rem;
    max-height: 9rem;
    margin: 2rem 0 0;
    border-radius: 2px;
    overflow: hidden;
    background: var(--tigerstripes-accent);
}

.diagram__band > span {
    position: absolute;
    inset: 16% 10% 20% 42%;
    display: flex;
    align-items: flex-end;
    gap: 5%;
}

.diagram__band > span > span {
    flex: 1 1 0;
    height: 100%;
    background: var(--ink);
    clip-path: polygon(70% 0, 100% 0, 30% 100%, 0 100%);
    opacity: 0.92;
}

.media__player,
.media__map {
    aspect-ratio: 16 / 9;
}

.media iframe,
.media video {
    width: 100%;
    height: 100%;
}

.gallery__items {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(10rem, 100%), 1fr));
    gap: 0.75rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.gallery__item img {
    display: block;
    aspect-ratio: 1 / 1;
    object-fit: cover;
}

.gallery__caption {
    display: block;
    padding-top: 0.4rem;
    font-size: 0.8125rem;
    color: var(--muted);
}

.media__download {
    display: inline-flex;
    gap: 0.5rem;
    padding: 0.75rem 1.1rem;
    border: 1px solid var(--rule);
    border-radius: 2px;
    background: var(--surface);
}

.lightbox {
    position: fixed;
    inset: 0;
    z-index: 60;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(20, 17, 15, 0.94);
}

.lightbox__image {
    max-width: min(88vw, 90rem);
    max-height: 78vh;
    object-fit: contain;
}

.lightbox__caption,
.lightbox__counter {
    position: absolute;
    bottom: 2rem;
    margin: 0;
    color: rgba(255, 255, 255, 0.75);
    font-size: 0.875rem;
}

.lightbox__counter {
    right: var(--gutter);
}

.lightbox__caption {
    left: var(--gutter);
}

.lightbox__nav,
.lightbox__close {
    position: absolute;
    padding: 0.6rem 1rem;
    border: 1px solid rgba(255, 255, 255, 0.35);
    border-radius: 999px;
    background: transparent;
    color: #fff;
    font: inherit;
    font-size: 0.875rem;
    cursor: pointer;
}

.lightbox__close {
    top: 1.5rem;
    right: var(--gutter);
}

.lightbox__nav--previous {
    left: var(--gutter);
    top: 50%;
}

.lightbox__nav--next {
    right: var(--gutter);
    top: 50%;
}

body.is-lightbox-open {
    overflow: hidden;
}

/* ------------------------------------------------------------------ footer */

.site-footer {
    border-top: 1px solid var(--rule);
    padding-top: calc(var(--section) * 0.6);
}

.site-footer__inner {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr) auto;
    gap: 2rem;
    align-items: start;
    padding-bottom: 2.5rem;
}

.site-footer__note p {
    margin: 0 0 0.35rem;
    color: var(--muted);
}

.site-footer__nav {
    display: flex;
    flex-wrap: wrap;
    gap: 1.25rem;
    font-size: 0.9375rem;
}

.site-footer__legal {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: 1rem;
    padding-block: 1.25rem;
    border-top: 1px solid var(--rule);
    font-size: 0.8125rem;
    color: var(--muted);
}

.site-footer__legal p {
    margin: 0;
}

/* --------------------------------------------------------- the front door */

/* The studio's own plate, on the pages where the art is a band of its own rather than the hero's
   companion - the same drawing at the size a section gives it. */
.plate {
    aspect-ratio: 21 / 9;
    /*
      No bottom margin, and that is now the whole of what the space above a body of prose is.

      This plate is the one thing on the site drawn as a band of its own immediately above a page's prose -
      `about.html` draws one on /about and `work.html` on each concept, four addresses of the twenty-seven,
      and nothing else above a body of prose draws a band at all. A body of prose carries its own air:
      `.prose` is padded by half a section top and bottom. The 0.4 of a section this used to add on top of
      that was the only other contributor in the site: the first line of prose sat 80.6px below this
      plate's own bottom edge at a 1280px window where the same line sat 44.8px below whatever preceded it
      on every other page (86.4px against 48px at 1440 and above, 56.7px against 31.5px at 900). That is a
      margin of 0px on twenty-three addresses and 35.8px on these four, which is 38.4px at a desk's width
      and 19.2px on a phone, and it is the whole of what the reader saw as the difference.

      One space, one value: the prose's own padding, measured from the band above to the first line of the
      body at 44.8px, 48px and 31.5px at those three windows, on all twenty-seven addresses - from a plate,
      from a hero, from a list, from the front door's cover, and including /contact, where the wrapper
      around the prose supplies the same half section and the prose itself is padded by nothing. Nothing
      drawn above a body of prose adds to it.
    */
    margin: 0;
}

/* The front door's own art: the stripes at the size of a cover, and the sentence the studio is. */
.hero__stripes {
    position: absolute;
    inset: 12% 8% 18% 30%;
    display: flex;
    align-items: flex-end;
    gap: 4%;
    z-index: 1;
}

.hero__stripes > span {
    flex: 1 1 0;
    height: 100%;
    background: var(--ink);
    clip-path: polygon(70% 0, 100% 0, 30% 100%, 0 100%);
    opacity: 0.92;
}

.hero__stripes > span:nth-child(2) {
    height: 78%;
}

.hero__stripes > span:nth-child(3) {
    height: 88%;
    opacity: 0.7;
}

.hero__stripes > span:nth-child(4) {
    height: 56%;
    opacity: 0.45;
}

.hero__art-note {
    position: absolute;
    left: 1.5rem;
    bottom: 1.5rem;
    z-index: 2;
    margin: 0;
    color: #fff;
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.18em;
    line-height: 1.8;
    text-transform: uppercase;
}

.hero__art-note strong {
    font-weight: 600;
    color: rgba(255, 255, 255, 0.72);
}

/*
  The drawing on the plate a hero draws when the page it belongs to has no picture of its own: the
  subject of the page, at the size of a signature, in white at the foot of the plate.

  Which drawing is not decided here: the template hands this element the last segment of the page's path
  (`icon--<name>`), the icon set above resolves it, and a page it has never heard of arrives with the
  studio's mark - which is the point of the whole arrangement rather than a shortcoming of it. A section front
  gets something of its own, a service gets what it is, and a page nobody has drawn yet gets the tiger's
  stripes rather than an empty orange box.

  White, and 3.5rem: the plate under it is the accent field with the ink stripes across its right-hand
  side, so the foot of the plate's left corner is the one part of it that is a flat colour - and white on
  the accent is 3.74:1, over the 3:1 a drawing is owed, in both schemes, because the plate is the same
  plate in both.
*/
.hero__art-mark {
    position: absolute;
    left: 1.5rem;
    bottom: 1.5rem;
    inline-size: 3.5rem;
    block-size: 3.5rem;
    color: #fff;
    z-index: 2;
}

/*
  The one plate whose corner is already taken: the band the about page draws for itself has its own words
  in the same place a hero's drawing goes, so the drawing is stacked above them rather than placed beside
  them, and the two cannot land on top of each other however narrow the band gets.

  The drawing is the studio's own - this is the page that is about the studio - and it is sized to the
  plate's own width rather than fixed, because this plate is a band: at the width of a phone the band is
  about 120px tall, and a 3.5rem drawing above two lines of uppercase note is taller than the plate it is
  drawn on. The frame it is measured against is the plate, which is already the container the note on it
  asks - see `.hero__art` - so the drawing is a fixed share of its plate at any width. 4vw measured the
  window instead, which agreed with the plate only while both were growing: at 1440px and above the
  plate's width is capped by the shell and the window's is not, so the drawing sat at its 3.5rem ceiling
  on a plate that had stopped getting bigger. It is 4.5cqw now: measured against the served page, 32, 37,
  42 and 52px at 768, 900, 1024 and 1280px of window, where the window rule drew 31, 36, 41 and 51, and
  51px at 1440 and above, where it drew 56 on a plate that had not grown since 1280. It is white on the accent for the same reason the other plates' marks are: this plate is the
  same colour in both schemes, so one ink is legible on it in both.
*/
.hero__art-sign {
    position: absolute;
    left: 1.5rem;
    bottom: 1.5rem;
    z-index: 2;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.7rem;
}

.hero__art-sign .hero__art-note,
.hero__art-sign .hero__art-mark {
    position: static;
}

.hero__art-sign .hero__art-mark {
    inline-size: clamp(1.75rem, 4.5cqw, 3.5rem);
    block-size: clamp(1.75rem, 4.5cqw, 3.5rem);
}

/* The front door's note is written on a picture rather than on one colour, so the bottom of the art is
   darkened under it: white type over a photograph is legible where it happens to be, and the corner
   this note sits in is white fur. The rest of the drawing does not need the help - a flat orange field
   under one colour of type is the contrast the note was written for - so only the front door gets it,
   and only the band the note is in. */
.hero--front .hero__art::after {
    z-index: 2;
    background:
        radial-gradient(120% 90% at 80% 10%, rgba(0, 0, 0, 0.14), transparent 60%),
        linear-gradient(to top, rgba(0, 0, 0, 0.72), transparent 26%);
}

.hero--front .hero__art-note {
    z-index: 3;
}

/* ------------------------------------------------- what this theme inherits */

/*
  The templates this theme does not override still render - the built-in landing page, the 404, and
  anything a page names that this site does not offer. They arrive with the built-in classes, so this is
  the smallest amount of styling that keeps them looking like this site rather than like an unstyled
  document: a theme inherits markup and has to dress it, not only its own.
*/
.content {
    max-width: var(--shell);
    margin: 0 auto;
    padding: calc(var(--section) * 0.6) var(--gutter);
}

.content h1 {
    font-size: var(--title-size);
    font-weight: 800;
    letter-spacing: -0.03em;
}

.empty-state {
    max-width: 40rem;
}

.empty-state a {
    border-bottom: 1px solid var(--tigerstripes-accent);
}

.page-list {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
    gap: 1.5rem;
    padding: 0;
    list-style: none;
}

.page-path {
    font-size: 0.8125rem;
    color: var(--muted);
}

.breadcrumbs ol {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: 0.875rem;
    color: var(--muted);
}

.byline {
    display: flex;
    gap: 1rem;
    align-items: center;
    margin-bottom: 1.5rem;
}

.byline__avatar {
    border-radius: 50%;
}

.byline__name {
    font-weight: 700;
}

.byline__bio {
    margin: 0;
    font-size: 0.875rem;
    color: var(--muted);
}

/* --------------------------------------------------------------- responsive */

/*
  Two axes, and which one a block belongs to is the whole of what this section is about.

  **A component asks about its own box.** A row list, a news list, a body of prose, the accent band and the
  sentence on a plate are arrangements rather than parts of the page, and what each of them is really
  asking is "has this got room for four columns" rather than "is the window 46rem wide". `@container` is
  the property that asks the first question, so each of those components declares itself a container where
  it is defined (`container-type: inline-size`) and carries its own query beside it - which is also what
  puts a component's breakpoint next to the component rather than in a block at the foot of this file. A
  body of prose is the one exception, and it is forced: the counters its sections and rows are numbered by
  are reset on it, and containment cannot go on the element that resets a counter or on anything inside
  it without the numbers all coming out 01 - so the containment is on `.shell`, which is the same column
  one box higher, and its query sits beside the rows it decides on. No fallback is wanted or needed:
  container queries shipped before every other feature this file already requires, so a browser that draws
  this theme's colours at all can read these queries.

  **The page's furniture asks about the page.** The header is the page's edge, and its row is decided by how
  much room the window has for a brand, a menu and a way to get in touch. The hero's columns and the
  contact section's are the page's own grids at the page's own width. Those stay on the viewport, and the
  seven widths they use are the seven below - the last of the one-off numbers, each of them now named in
  the block that uses it.

  Nothing here could be a token, and that is a fact about CSS rather than a decision: a `var()` is not read
  in a media condition, and not reliably in a container condition either - Firefox reads one there,
  Chromium and WebKit do not. `@custom-media`, the at-rule that exists for exactly this job, has shipped
  in no browser. So the numbers are written out, and this is the one place that says what each of them
  means:

    62rem    a page's furniture gives way: a hero with nothing to put beside its words takes the whole row
             rather than holding an empty half of it, the contact section's two columns become one, and the
             footer's three parts stack
    56rem    the header's row tightens before it folds: its own gaps, the links' gaps and the links' size
    46rem    the header folds behind the menu button
    34rem    a hero that has a plate puts it above the words, at the size of a signature
    26rem    the header's row tightens again: the brand's name steps down and the action loses padding
    22.5rem  the header's row gives up the page's gutter and the action its arrow
    19rem    the header's row gives up the wordmark itself, leaving the mark that is the way home

  Every viewport block below opens with the one of those it is written at, so a width and its reason are
  one reading rather than two.
*/

@media (max-width: 62rem) {
    /*
      62rem: a page's furniture gives way.

      A hero with something to put beside its words keeps it beside them at every width down to the block
      below; a hero with nothing to put there takes the whole row rather than holding an empty half of it.

      Which one a page is, is `:has()`'s question rather than a class's: the art exists when the page
      carries a picture or when its template allows the plate, and a page with neither has none - so the
      rule is about the art rather than about the front door, and every hero that draws one behaves the
      same way at a tablet width. Measured across the site, 23 of the 27 addresses draw a plate in their
      hero; the 4 that do not are the concept pages and About, whose own panel stands below the hero
      instead and which would otherwise hold an empty 0.85fr column beside their words.

      This is the arrangement the front door had to itself until it belonged to all of them, and the
      justification was never about the photograph on it: a square beside a column of type needs less room
      than two full-width blocks do, and at 768px the two columns come to a 384px column of type and a
      284px square - one hero rather than a band with a sticker above it. The band above the words is what
      the panels below the hero still are, and `.plate` is now the only rule that shapes one: it is their
      own 21:9 at every width, where a rule written for the hero's art used to reach them and draw them at
      16:10 below this breakpoint.
    */
    .hero__inner:not(:has(.hero__art)),
    .contact {
        grid-template-columns: minmax(0, 1fr);
    }

    .site-footer__inner {
        grid-template-columns: minmax(0, 1fr);
    }
}

/*
  34rem: a hero that has a plate puts it above the words.

  The size of a signature rather than the width of the page. The plate is the identity and the words are
  what the visitor came for, so it stands above
  them at the size a mark is drawn at - the same square the design gives it, and no longer the loudest
  thing on the page. Every hero that has one does this, which is what the front door has done since its
  picture went in.

  This is 34rem rather than the 62rem the page's other grids give way at, because a square beside a column
  of type needs less room than two full-width blocks do: at 46rem the words fit beside the plate with room
  to spare, and a 15rem square alone on a 46rem row is a sticker in the corner of an empty band rather than
  a hero. Below 34rem the column of type is down to about 300px and the square goes over it.
*/
@media (max-width: 34rem) {
    .hero__inner:has(.hero__art) {
        grid-template-columns: minmax(0, 1fr);
    }

    .hero__inner > .hero__art {
        order: -1;
        max-inline-size: 15rem;
    }
}

/*
  The sentence written on a hero's plate, at the size the frame can hold. The note is set in fixed type on
  a frame that is not fixed, and a small frame is what it has to be written for: between 34rem and about
  39rem of window the square beside the words is barely wider than the sentence on it, and at the full size
  the second line ran to within a pixel of the frame's right-hand edge, where an overflow hidden frame
  would have clipped it.

  Which frame is small is the frame's own answer, and that is the whole of what this rule changed to. The
  frame measured 272.1px at the 46rem window this used to be written for, and 17.1rem is 273.6px: the
  boundary the design wants is the width at which the full-size note stops having room, and the number is
  the nearest round step above the measurement rather than the step below it, because a note a step small
  is still legible and a note that runs to the edge of its frame is clipped. So the reading at a 736px
  window is the design's exactly, and the note stays small over the next four pixels of window - the width
  at which the full-size version was already touching the frame's edge.

  The panel a section draws for itself carries the same sentence and is a different frame, and asking the
  frame is what makes the rule right for both: the panel is a band the width of the page, so it keeps the
  full-size note at every width the page is read at - 280px of frame at a 320px window - and below that it
  shrinks the note with the rest, where the version of this rule that named the hero's frame left the
  panel's note at full size in a 240px band, which is where a sentence is longest over the fewest lines.

  So below 17.1rem of frame the note is set for the smaller frame - a little smaller, closer to the corner,
  and tracked a little tighter - and its two lines are set closer together, which is the part that keeps
  the sentence inside the darkened band the front door's picture is given under it: the band is a fixed
  share of the picture's height, the picture is not a fixed height, and the version of this note that was
  40px tall between them put its first line above the part of the gradient that has any black in it at all.

  Every hero's note rather than the front door's only, because every hero's frame is the same size at the
  same width:  the 404's plate carries a note too, and a rule that shrank one and not the other would clip
  the other's sentence. The panels below a hero are not in this: their own note is set at full size at
  every width, since the band it sits on is the width of the page.
*/
@container (max-width: 17.1rem) {
    .hero__art-note {
        left: 1rem;
        bottom: 1rem;
        font-size: 0.625rem;
        letter-spacing: 0.12em;
        line-height: 1.5;
    }
}

/*
  56rem: the header's row tightens before it folds.

  The row between this width and a desk with room to spare, which is where the switch has to fit
  beside five sections and the action in a header that was measured to the pixel before it had one.

  Measured rather than guessed, and this block is the whole difference: at the breakpoint the row's three
  things want about twelve pixels more than there are, and a nav that does not fit wraps - the sections
  as two rows of a sticky header, which is the arrangement the menu exists to prevent. So three gaps and
  one type size give, and they are the ones the narrow blocks below give when the row runs out too: the
  header's own gaps, the gaps between the links, the gap before the switch, and the size the links are
  set in. Nothing is dropped and nothing wraps, and above this width every one of them is back at full
  size, which is where the switch sits in the row at the size it is drawn.
*/
@media (max-width: 56rem) {
    .site-header__inner {
        gap: 0.75rem;
    }

    .site-header__panel {
        gap: 0.75rem;
    }

    .site-nav {
        gap: 0.75rem;
        font-size: 0.875rem;
    }
}

@media (max-width: 46rem) {
    /*
      46rem: the header folds behind the menu button.

      What a phone gets without the script: the links on a row of their own under the brand and the one
      action, which is the arrangement this header had before it had a menu at all. Two rows rather than
      one, and the header is sticky, so it is two rows of every screen - which is what the menu below
      buys back.
    */
    .site-header__inner {
        flex-wrap: wrap;
        row-gap: 0.75rem;
    }

    /* The panel becomes the row's second line, which is the arrangement this header had before it had a
       menu and the one a browser that never runs the script still keeps. */
    .site-header__panel {
        order: 3;
        display: block;
        width: 100%;
        margin-left: 0;
    }

    .site-nav {
        width: 100%;
        gap: 1.25rem;
        font-size: 0.875rem;
    }

    .site-header__action {
        margin-left: auto;
    }

    /*
      And what a phone gets with it: one row - the brand, the menu, the action - and the links folded
      into a panel. The row is the whole reason for the panel: 133 pixels of sticky header became 79,
      on every screen, and the thing that took the space was a row of five words most visitors never
      press.
    */
    .site-header--menu .site-header__menu {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 2.75rem;
        height: 2.75rem;
        margin-left: auto;
        padding: 0;
        border: 1px solid var(--rule);
        border-radius: 999px;
        background: transparent;
        color: inherit;
        cursor: pointer;
    }

    .site-header--menu .site-header__menu:hover {
        border-color: var(--ink);
    }

    .site-header--menu .site-header__action {
        margin-left: 0;
        padding: 0.7rem 1.1rem;
        font-size: 0.9375rem;
    }

    /*
      Three bars, and an X while the panel is out. The offset each outer bar travels is its own height
      plus the gap between bars, which is what puts it exactly on top of the middle one; the transition
      is the only motion in this header, and it is on the bars rather than on the panel so that a reader
      who has asked for less motion (see the top of this file) loses a flourish rather than a state.
    */
    .site-header__bars {
        display: grid;
        gap: 0.3rem;
        width: 1.05rem;
    }

    .site-header__bars > span {
        height: 2px;
        border-radius: 2px;
        background: currentColor;
        transition: transform 0.18s ease, opacity 0.18s ease;
    }

    .site-header--menu .site-header__menu[aria-expanded="true"] .site-header__bars > span:nth-child(1) {
        transform: translateY(calc(2px + 0.3rem)) rotate(45deg);
    }

    .site-header--menu .site-header__menu[aria-expanded="true"] .site-header__bars > span:nth-child(2) {
        opacity: 0;
    }

    .site-header--menu .site-header__menu[aria-expanded="true"] .site-header__bars > span:nth-child(3) {
        transform: translateY(calc(-2px - 0.3rem)) rotate(-45deg);
    }

    /* Folded: the links are not on the screen, so nothing is covered by a list nobody asked for. */
    .site-header--menu .site-header__panel {
        display: none;
    }

    /*
      Out: the panel, hung from the header's own bottom edge rather than from the button, because what
      it holds is the page's navigation rather than a bubble about one control - so it spans the screen
      and its links line up with everything else the page lines up. Full-bleed rather than inset for the
      same reason: a panel that stopped short of the edges would read as a card floating over the page.

      It scrolls inside itself if it has to, and says so rather than passing the scroll on to the page
      behind it, which is what `overscroll-behavior` is for. That matters on one screen only - a phone
      held sideways, where five rows are taller than the window - and it is the difference between the
      last section being reachable and being below the bottom of the screen with nothing to scroll.
    */
    .site-header--menu .site-header__panel.is-open {
        position: absolute;
        top: 100%;
        left: 0;
        right: 0;
        display: flex;
        flex-direction: column;
        align-items: stretch;
        gap: 0;
        max-height: calc(100vh - 6rem);
        overflow-y: auto;
        overscroll-behavior: contain;
        margin: 0;
        padding: 0.35rem var(--gutter) 0.9rem;
        background: var(--paper);
        border-bottom: 1px solid var(--rule);
        font-size: 1rem;
    }

    /* The links are the panel's first rows, and only then: the nav stops being a row of its own and
       becomes a column inside the panel. */
    .site-header--menu .site-header__panel.is-open .site-nav {
        display: flex;
        flex-direction: column;
        gap: 0;
    }

    .site-header--menu .site-header__panel.is-open .site-nav__link {
        padding: 0.85rem 0;
        border-bottom: 1px solid var(--rule);
    }

    .site-header--menu .site-header__panel.is-open .site-nav__link:last-child {
        border-bottom: 0;
    }

    /*
      The switch on a phone: the last row of the panel, under one hairline. It closes the list, because
      the last link has no bottom border of its own and this is what takes its place.

      It is in the panel rather than in the header's row because that row has no room for it: at 320
      pixels the brand, the button and the action already want every pixel of it, and the fourth thing
      would take the space the site's own name is using.
    */
    .site-header--menu .site-header__panel.is-open .scheme {
        margin-top: 0.9rem;
        padding-top: 0.9rem;
        border-top: 1px solid var(--rule);
    }

    /* The words, which the row cannot hold and this panel can. */
    .scheme__word {
        position: static;
        width: auto;
        height: auto;
        overflow: visible;
        clip-path: none;
        white-space: normal;
    }

    .site-header .scheme .choices__label {
        gap: 0.5rem;
        padding: 0.5rem 0.9rem;
        font-size: 0.875rem;
    }

    /*
      The section the reader is in, in colour rather than with the underline the row uses: in a column
      of rows the bottom edge is a separator, so an underline there would say nothing, and a hairline
      that reads as "here" in one arrangement and "next link" in the other is worse than either.
    */
    .site-header--menu .site-header__panel.is-open .site-nav__link:hover,
    .site-header--menu .site-header__panel.is-open .site-nav__link[aria-current="page"] {
        color: var(--tigerstripes-accent);
        border-bottom-color: var(--rule);
    }

}

/*
  26rem: the header's row tightens again.

  The small handset, where the header's single row is the row with least to spare: it is tightened here
  rather than rearranged. Three things share it and only one of them is text, so the
  brand is what gives way: the name steps down, and the action loses a little of its padding. Nothing is
  dropped and nothing wraps - the row says the same three things in less room, which is the whole of what
  this block does. The screens narrower than this one, where a further step down is needed, are the block
  below.
*/
@media (max-width: 26rem) {
    .brand {
        font-size: 1.0625rem;
    }

    .site-header--menu .site-header__action {
        padding: 0.65rem 0.95rem;
    }
}

/*
  22.5rem: the header's row gives up the page's gutter and the action its arrow.

  The narrowest screen this site is built for, which is a 320-pixel handset - the smallest viewport a
  browser is asked to render a page at, and the width at which the single row finally runs out: the
  brand, the button and the action want about 315 pixels between them, and a 320-pixel viewport has 280
  once the gutters are paid for. Without this block the action wrapped to a second line of a sticky
  header, which is two rows on every screen of the page - the thing the menu above exists to stop.

  Four things give way rather than the arrangement, and each is the smallest of its kind: the page's own
  gutter, the gap between the header's three things, the brand's step down from the size the block above
  already set, and the action's own padding and type size. The one thing dropped rather than shrunk is
  the arrow in the action, because an arrow beside a two-word button is decoration at a width where every
  pixel of the row is spoken for - and the button still says what it does without it.

  Nothing but the arrow is dropped and nothing wraps: the row says the same three things in less room.
  The gutter here is the whole page's rather than the header's, and a 320-pixel screen has no use for
  20-pixel margins.
*/
@media (max-width: 22.5rem) {
    :root {
        --gutter: 1rem;
    }

    .site-header__inner {
        gap: 0.5rem;
    }

    .brand {
        font-size: 1rem;
    }

    .site-header--menu .site-header__action {
        padding: 0.6rem 0.85rem;
        font-size: 0.875rem;
    }

    .site-header--menu .site-header__action .button__arrow {
        display: none;
    }
}

/*
  19rem: the header's row gives up the wordmark, leaving the mark that is the way home.

  A folding phone's cover screen, which is narrower than the narrowest handset the block above was
  written for: 280 pixels of viewport, and a row with less room in it than the brand, the button and the
  action need between them. The name is what gives way, and it is the last thing that can: the block
  above has already spent the gutter, the gap, the type scale and the arrow, and what is left on the row
  is two controls and a mark.

  Gone from sight rather than from the page. The mark is still the way home, and the name is still the
  link's name - read out by a screen reader, and still the words a crawler reads to know whose site this
  is - which is why it is clipped rather than removed: `display: none` would take the wordmark away from
  both. The recipe is the admin interface's own, for the same job on the same kind of header, and
  `position: relative` on the brand is what keeps the hidden copy inside it rather than in whichever
  ancestor happens to be positioned.
*/
@media (max-width: 19rem) {
    .brand {
        position: relative;
    }

    .brand__name {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
    }
}

/* --------------------------------------------------------------------- print */

/*
  Paper, which is not a narrow screen.

  Everything above this block is a page on a screen, and every decision in it is still the right one when
  that page is printed - with five exceptions, and they are what follows. A sheet has no colour scheme of
  its own, nothing on it can be pointed at, it drops the backgrounds it was not asked to keep, it arrives
  in pages rather than in a scroll, and a link on it cannot be followed.

  **The scheme.** A reader whose answer is Dark gets a near-black page and a page of toner to print it,
  and worse than the toner is the direction: inks chosen against a dark ground are being asked to print on
  white. So print settles the question rather than putting it to the reader - `color-scheme: light` on the
  root, which is the light half of every pair in the palette above. The attribute is named beside the root
  because the reader's answer outranks the machine there, and here it must not: this is the same design
  printed on the paper it was drawn for, not a second design.

  **The chrome.** The menu button, the switch and the lightbox are controls, and a sheet has nothing to
  control: the switch would print a row of marks about a choice the ink has already made, and the lightbox
  is a near-black scrim with a photograph in it, i.e. a black page. The menu is the one that proves the
  rule, because what it folds away is the navigation rather than a preference - the button goes, and the
  panel is drawn as the row of links it is on a desk, since there is no menu on paper to open and the
  links are content.

  **The ink.** A browser asked to print drops every background it was not told to keep, and in this theme
  that takes more than colour with it. Every drawing in the icon set is a mask painted with a
  `background-color`, so the plates, the news rows' twenty and the diagrams' marks would print as holes;
  the closing band's words are white on the accent, so the words would print as nothing; the front door's
  note is white type over a picture that a gradient darkens under it; and this theme draws its section
  rule, its pull-quotes and a checked chip in a form out of backgrounds too. All of those are ink rather
  than ground, so all of them are marked `print-color-adjust: exact`, which says only "this colour is the
  drawing, keep it" and is inherited - so a surface named here brings what is drawn on it with it. The
  opposite list is deliberately not written: the paper, the quiet bands and the raised panels are grounds,
  and a printer whose reader has asked it to save ink may leave them out.

  **The breaks.** Paper's one own behaviour is that it ends a line of text mid-sentence to start a new
  sheet, and a row, a card or a plate it lands inside is cut in half. So the units this design is built
  from are kept whole and a heading is kept with what follows it. Where the page ends is still the
  printer's arithmetic; this block is only about what that arithmetic must not split.

  **The links.** A link printed keeps its words and its rule and loses the one part of it that made it a
  link - the destination, which is the part a reader working from the sheet needs most. So the destination
  is printed beside the words, in the muted ink and a step smaller, because it is a fact *about* the link
  rather than part of the sentence it sits in: the same register the date and the trail's steps are set
  in. Every link the page sets prints one - the prose's, the trail's, the closing band's and the footer's -
  and between them those are every address the page has: the header's own links are the footer's five and
  the front door, and the header's row is the one row a destination would put onto a second line. It is
  the print block's own, so a screen is untouched - there the destination is the status bar.

  Nothing above is wrapped in `@supports`, and nothing below needs to be: every property here is either
  one every browser that prints has had for years, or `print-color-adjust`, whose absence costs a drawing
  and never a word.
*/

/*
  The page box, and the margins that come with it. A sheet's margin is the printer's business rather than
  the design's, so the design stops paying for one: the gutter is spent below and the printer's own margin
  is the whole of it, which is also what puts everything that used the gutter as a bleed - a pull-quote,
  the closing band - inside the printable area rather than off the edge of it. The millimetres are a
  default and not a rule: a reader who wants another margin says so in the print dialogue, and that
  outranks this.
*/
@page {
    margin: 16mm;
}

@media print {
    :root,
    :root[data-scheme="dark"] {
        color-scheme: light;
    }

    :root {
        --gutter: 0;
    }

    /* Each control is named twice, and both times for the same reason: what draws it is a rule about the
       arrangement it is drawn in - the switch is the desktop header's (`.site-header .scheme`, drawn only
       for a header the script has marked), and the button is the phone header's
       (`.site-header--menu .site-header__menu`) - so naming them the one way here would be naming rules
       that never applied to them, and the control would survive onto the sheet with the arrangement. */
    .scheme,
    .site-header .scheme,
    .site-header__menu,
    .site-header--menu .site-header__menu,
    .lightbox {
        display: none;
    }

    /* A header that follows a page as it is scrolled is a header printed once, at the top of the first
       sheet: sticky is a screen's answer to a page taller than the window, and a sheet is not scrolled.
       The translucent paper behind it was what the blur was for; the hairline under it is a rule like any
       other and stays. */
    .site-header {
        position: static;
        background: transparent;
        backdrop-filter: none;
    }

    /*
      The panel, unfolded and back in the header's row: the button above is the only thing on a screen
      that unfolds it, and without that button the folded state is a navigation nobody can reach. Named
      twice because the narrow block writes it twice - once folded, once open - and the open one is the
      more specific of the two on a screen it was written for.
    */
    .site-header--menu .site-header__panel,
    .site-header--menu .site-header__panel.is-open {
        position: static;
        display: flex;
        flex-direction: row;
        align-items: center;
        gap: clamp(0.75rem, 1.5vw, 1.5rem);
        max-height: none;
        overflow: visible;
        margin-left: auto;
        padding: 0;
        background: transparent;
        border: 0;
        font-size: inherit;
    }

    .site-header--menu .site-header__panel.is-open .site-nav {
        flex-direction: row;
        gap: clamp(1rem, 2.5vw, 2.25rem);
    }

    /* The links are a row again, so the hairline a folded row put under each of them - which was the
       panel's own separator - is not one, and the mark of the page the reader is on still is. */
    .site-header--menu .site-header__panel.is-open .site-nav__link {
        padding: 0.25rem 0;
        border-bottom-color: transparent;
    }

    .site-header--menu .site-header__panel.is-open .site-nav__link[aria-current="page"] {
        border-bottom-color: var(--ink);
    }

    /* The page opened the lightbox, and its own scroll is what would be clipped: a fixed, full-height
       scrim on paper is a black page, and the lock that came with it is a document that stops at one. */
    body.is-lightbox-open {
        overflow: visible;
    }

    /* Ink rather than ground, and every entry is one of the things above: a drawing, a surface with
       something written on it, or a rule this theme paints instead of drawing. */
    .icon,
    .mark,
    .stripes__stroke,
    .kicker::before,
    .tile,
    .hero__art,
    .band--accent,
    .band--dark,
    .diagram__band,
    .card__art--drawn,
    .button--accent,
    .button--dark,
    .prose hr,
    .prose blockquote,
    .prose ul > li > strong::before,
    .choices__input:checked + .choices__label {
        -webkit-print-color-adjust: exact;
        print-color-adjust: exact;
    }

    /* The marquee is a loop, and a sheet has no loop: the four words are printed once, wrapping rather
       than running off the edge, and the second copy - which the markup writes so that the animation can
       carry the track half its own width - is dropped along with the animation it exists for. */
    .marquee__track {
        width: auto;
        flex-wrap: wrap;
        animation: none;
    }

    .marquee__item:nth-child(n + 5) {
        display: none;
    }

    /*
      Where a link went, printed after the words that carried it.

      The destination is the part of a link that survives to paper least: the href is a browser's job,
      and a sheet keeps the text and the rule. Which makes a printed link a thing a reader can recognise
      but not revisit, so it is printed, at a step smaller and in the muted ink, and the parentheses are
      the whole of the ornament: a destination is set apart from the sentence it is in rather than joined
      to it.

      Every region of this theme that carries a link is named here, and the header's row is the one that
      is not. The prose's, where the destination may be another site's and nothing else on the sheet could
      say so. The trail's, which is the way back up and so the address a reader on paper has least chance
      of guessing. A section's row list and the news list, whose rows each name a page. The concept cards,
      whose headings do. The front door's two hero buttons and the 404's, which are where a reader is asked
      to go before anything has been read. The news band, in both its parts - the link its head offers into
      the section and the four notes under it, which is why the band and its own list are two selectors
      rather than one: the head's link is beside the list, not in it. The closing band, which is the one
      thing the site asks a reader to do next. And the footer, where the sections are listed again - the
      footer's own wordmark included, whose destination is the front door.

      The header's links are deliberately not among them, and it costs the sheet nothing: they are the
      footer's same five and the front door, its one action button repeats the footer's Contact, so every
      address this page has is printed somewhere on the page without the header's row - a single line of
      five links - growing a path wide five times over.

      What it does not print, and each thing for its own reason. A link into this page (`#...`), because
      the thing it names is on the same sheet and the fragment would say only "a little further down". A
      link marked `aria-hidden`, because that attribute is a claim that the link says nothing a reader is
      not already told: the arrow every row, card and note wears is a second link to the address its title
      has just printed, the card's picture is a third, and the row list is twelve links over six
      addresses. So the destinations are printed where the words that name them are, and the decorations
      beside them are left as decorations - which is what a screen reader is told of them too, and a link
      retired from the spoken page is not one a sheet has to name. Anything on a screen: the rule is
      inside this block, so nothing about it is in the DOM a reader meets, and a screen already has the
      destination in the status bar. And nothing is invented about a site's own address: the href is
      printed as the stylesheet knows it, so a link inside the site prints its path rather than an absolute
      URL, which is what it is - the stylesheet is one file for the whole site and is served at an address
      it did not choose, so it has no origin to add. An editor who wants a destination printed in full
      writes it in full, and the site's own pages are filed with the site.

      One case is printed twice, and it cannot be caught here: an editor who writes `<https://example.com>`
      gets the address as the link's text *and* as its href, and CSS has no way to compare them, since an
      anchor's text and its href are two values no selector can ask about. The starter prose writes no
      autolinks and no bare addresses, so nothing on this site does it today.

      `overflow-wrap: anywhere` is what keeps a long destination on the sheet: a URL is one unbroken word,
      and a word that cannot break is a line that runs past the measure and off the printable edge. It is
      scoped to the destination alone, so a sentence still breaks where a reader would break it.
    */
    .prose a:not([href^="#"]):not([aria-hidden="true"])::after,
    .trail a:not([href^="#"]):not([aria-hidden="true"])::after,
    .rows a:not([href^="#"]):not([aria-hidden="true"])::after,
    .cards a:not([href^="#"]):not([aria-hidden="true"])::after,
    .news a:not([href^="#"]):not([aria-hidden="true"])::after,
    .news-band a:not([href^="#"]):not([aria-hidden="true"])::after,
    .hero__actions a:not([href^="#"]):not([aria-hidden="true"])::after,
    .band a:not([href^="#"]):not([aria-hidden="true"])::after,
    .site-footer a:not([href^="#"]):not([aria-hidden="true"])::after {
        content: " (" attr(href) ")";
        color: var(--muted);
        font-size: 0.9em;
        overflow-wrap: anywhere;
    }

    /*
      The trail is the one row here that has to give something up to carry a destination.

      On a screen a step is a single line and a step that will not fit ends in an ellipsis (see .trail
      above). A box that clips its own text clips the destination with it, so a printed step would keep
      its words and lose where they lead - the one thing the rule above exists to prevent. So on paper a
      step wraps instead: the same box, the same maximum width, with the ellipsis gone because the line
      it was guarding no longer has to be one line. The step the reader is on is deliberately not named
      here: it is the label the row is set in rather than a link, so it has no destination to lose, and
      it keeps its screen treatment on paper - a page's own title still ends in an ellipsis above it.
    */
    .trail a {
        white-space: normal;
    }

    h1,
    h2,
    h3,
    h4,
    .kicker,
    .date {
        break-after: avoid;
    }

    .rows > li,
    .news > li,
    .card,
    .marks > li,
    .diagram__step,
    .diagram__band,
    .prose blockquote,
    .prose ol > li,
    .prose ul > li,
    .form__row,
    .site-footer__legal,
    img,
    .hero__art {
        break-inside: avoid;
    }
}
