/* =========================================================================
   Societal Socks — the public website.
   Staged at /work-in-progress-site/. Nothing here is shared with the CRM:
   app.css is an admin design system (dense, grey, information-first) and this
   is a shop window. They are deliberately separate stylesheets.

   THE BRAND, TAKEN FROM societalsocks.com RATHER THAN INVENTED (task P155-#25).
   The live site was loaded and measured on 10 Sep 2026. Everything in the
   :root block below is a value read off it, not a preference:

     * Type is BLACK. rgb(0,0,0) on white, rgb(255,255,255) on black. There is
       no slate blue anywhere on the live site; the slate-blue palette the
       first pass invented is gone, variables and all.
     * The one accent is #ab96ff, the periwinkle the live site puts behind
       "Discover More", "Our Work", "Get Started" and "Send". It is measured
       from rgb(171,150,255). It is a BACKGROUND ONLY — black on it is 8.5:1,
       but it is 2.0:1 on white, so it never carries text of its own.
     * Sora 700 with negative tracking is the display face: the live site sets
       its 8,242 figure in sora/700/135px/-2.7px and its section headings in
       sora/700/56-59px/-1.18px. Those ratios are reproduced here.
     * Space Grotesk carries the live site's buttons, steps and labels
       (space grotesk/700/24-26px).
     * Montserrat is the third face in the live site's font set and is the
       workhorse here: every paragraph, every nav item, every caption.

   FLATNESS IS A DECISION, NOT AN OVERSIGHT — WITH ONE MEASURED EXCEPTION.
   There is no texture overlay, no border-radius and no drop shadow in this
   file, and every section change is made with flat black or flat white and
   with space, the way the live site does it.

   THE EXCEPTION IS THE OPENING, and it is the live site's own (task P155-#62,
   Toby: "add in original colour gradient background for landing page"). The
   top of societalsocks.com is a six-colour mesh gradient, not white; this file
   said otherwise for eleven days because the first pass measured the site's
   BANDS and not its TOP. The six colours and their six positions are
   transcribed from the live page's computed style in the "#opening" block
   below. It is the only gradient here that is decoration rather than a mask,
   and it exists because it is on the real site.
   ========================================================================= */

/* THE TOKENS MOVED. The :root block that used to sit here — the measured
   black, the periwinkle, the three faces, --wrap — plus the box-sizing reset
   are now in ../brand.css, which this page links BEFORE this file. Task
   P155-#31 gave the customer's reorder page the same design language, and a
   second copy of these values would have been a second thing to keep in step.
   Nothing about them changed; they are the same lines in a different file.
   Every rule below still reads them as variables exactly as it did. */

body {
    margin: 0;
    /* Belt and braces against a wide child: nothing on this page may make the
       document scroll sideways on a phone. Every marquee is inside its own
       overflow:hidden track, but a single mis-measured element should not be
       able to shift the whole page. */
    overflow-x: hidden;
    background: var(--paper);
    color: var(--ink);
    font-family: var(--body);
    font-size: 17px;
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }
a { color: var(--ink); }

.wrap { width: 100%; max-width: var(--wrap); margin: 0 auto; padding: 0 28px; }

h1, h2, h3 {
    font-family: var(--display);
    font-weight: 700;
    line-height: 1.02;
    letter-spacing: -0.02em;   /* the live site's -2.7px on 135px = -0.02em */
    margin: 0;
}

/* The one label style on the site. Space Grotesk, because that is what the
   live site sets its short bold statements in. */
.eyebrow {
    font: 700 12px/1 var(--alt);
    letter-spacing: .18em; text-transform: uppercase;
    color: var(--ink); margin: 0 0 18px;
}

/* ---- BUTTONS ------------------------------------------------------------
   Rectangles. The live site's buttons are rectangles with black type on the
   periwinkle; the pill shape that was here before is a template default and
   is not on societalsocks.com anywhere. */
.btn {
    display: inline-flex; align-items: center;
    padding: 14px 28px; border-radius: 0;
    font: 700 14px/1 var(--alt); letter-spacing: .02em;
    text-decoration: none;
    background: var(--accent); color: #000;
    border: 2px solid var(--accent);
    transition: background .15s, border-color .15s, color .15s;
}
.btn:hover { background: #000; border-color: #000; color: var(--on-dark); }
.btn.ghost { background: transparent; color: var(--ink); border-color: var(--ink); }
.btn.ghost:hover { background: var(--ink); color: var(--on-dark); }

/* =========================================================================
   1. THE STICKY TOP BAR — THE MASTHEAD, AND NOTHING ELSE NOW.

   IT USED TO BE TICKER + NAV IN ONE STICKY CONTAINER. Task P155-#74 moved the
   ticker to the foot of the page (Toby, 22 Sep 2026: "move the ticker tape to
   the bottom of the website (not fixed) but just as a ribbon above the
   footer"), so #topbar wraps one child and the bar is the masthead's height.
   The wrapper is kept rather than collapsed because it is what carries the
   stickiness, and nav-site.js's outside-click handler asks it whether a click
   landed in the bar.

   It does not COVER anything. A sticky element keeps its space in normal flow,
   so the page begins below the bar rather than under it. The only thing that
   could land beneath it is an in-page anchor jump, which is what the
   scroll-margin-top rule further down is for.

   THE RIBBON'S OWN STYLES ARE AT THE END OF SECTION 9, beside the footer it
   now sits on top of. Only the keyframes stayed here, because #opening's wall
   and the logo wall both live in this half of the file and a reader looking
   for "what animates on this page" should find them together.
   ========================================================================= */
#topbar { position: sticky; top: 0; z-index: 60; }

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

/* ---- 2. THE NAV --------------------------------------------------------
   Flat black, like the live site's header. No blur, no translucency.

   SIX ITEMS AND A BUTTON. Task P155-#56 cut the bar from nine: four of the
   nine were anchors into sections of the home page, two of those present only
   when the database had something to put in the section they pointed at. The
   anchors moved to an in-page bar, which task P155-#65 has since removed from
   the home page altogether at Toby's request; the category pages are one menu
   rather than siblings, and that menu gained Businesses in #65, which is what
   makes it six. The markup is written by tools/apply-header.js into every
   page; this styles it.

   THE BREAKPOINT MOVED FROM 860px TO 900px. Five items, a logo half as large
   again and a button no longer fit on an 834px tablet, and the old rule below
   860px did not fold the bar into a menu — it deleted every link from it and
   left a phone with no route into the site at all.
   ------------------------------------------------------------------------ */
#masthead { background: var(--ink); position: relative; }
/* MORE BLACK AROUND THE LOGO. Task P155-#68. Toby, 22 Sep 2026: "Make the
   black boarder around the societal socks logo at the top thicker."

   WHICH "BLACK BORDER" THIS IS. The wordmark is an SVG with no stroke in it and
   no frame drawn around it anywhere; the only black around it is THIS BAR,
   which is `background: var(--ink)` on #masthead and is exactly what a reader
   sees as a black border around the logo. So the bar is what got thicker. The
   alternative reading - a stroke inside the artwork - would have meant editing
   ../logo.svg, which is the shared company mark used by the CRM, the favicon
   and the invoice templates, and a heavier outline there would have changed the
   logo everywhere for a request about one bar on one site.

   IT IS PADDING, NOT A SMALLER LOGO. 12px -> 26px of vertical padding, so
   +28px of black in total and the mark is untouched at 52px. Measured on the
   rendered page: the masthead goes 76px -> 104px on a desktop and the whole
   sticky bar (ticker + masthead) 107px -> 135px.

   THE NAV DOES NOT WRAP BECAUSE OF IT. This changes the row's HEIGHT only; its
   width is the same logo, the same links and the same gaps it was before. What
   DID change the width is the seventh nav item this task adds ("Gallery"), and
   that is paid for at the fold breakpoint further down rather than here.

   AND THE OPENING STILL FILLS THE SCREEN. #opening's min-height subtracts
   --topbar-h, which nav-site.js MEASURES off the real bar on load, on resize
   and from a ResizeObserver on the bar itself - so a taller bar is subtracted
   the moment it is painted, with no number in this file to keep in step. The
   two --topbar-fallback constants below, which cover only the frame before
   that script runs, have been re-measured to match. */
.masthead-in {
    display: flex; align-items: center; justify-content: space-between; gap: 18px;
    padding: 26px 28px; max-width: var(--wrap); margin: 0 auto;
}

/* THE WORDMARK. 20px before this task, which is what "the logo appears too
   small across our website" was about: at 20px the mark was shorter than the
   nav text beside it was tall, so the bar read as a row of links that happened
   to have a picture at one end. 30px is half as large again and now leads the
   bar. The vertical padding came down from 16px to 12px to pay for most of it:
   measured on the deployed page the masthead goes 55px -> 59px on a desktop
   and 64px -> 51.5px below the breakpoint. The 4px it does gain on a desktop
   is not the logo, it is the call to action finally getting its own padding
   (see .nav-cta below); the phone, where 4px would matter, gets 12.5px back.

   The file carries no fill attribute, so it paints black; inverting it gives
   the white mark the live site puts on its black header.

   ---------------------------------------------------------------------------
   AND 30px WAS STILL NOT ENOUGH. Task P155-#62. Toby, 21 Sep 2026: "Make the
   main societal socks logo larger." It is now 52px on a desktop, which is
   1.73x the 30px above and 2.6x the 20px this started at. For scale: the live
   societalsocks.com sets its wordmark at about 90px on a header with nothing
   else in it but a burger. This bar carries six nav items and a button, so it
   cannot go there — but it can stop being the smallest thing in its own row.

   THE STEPS DOWN ARE ABOUT THE NAV WRAPPING, NOT ABOUT TASTE. The row is
   logo + gap + nav, inside a 1180px measure, and the nav does not collapse to
   the burger until 900px. A 52px mark is ~340px wide, so between 900px and
   ~1060px the three would not fit on one line and the nav would break onto a
   second — which is the one thing the brief rules out. The mark therefore
   steps 52 -> 48 -> 42 on the way down to the burger breakpoint, where it
   stops mattering because the nav is no longer beside it and it can go back
   up to 36px. Every step was measured against the real rendered nav width,
   not estimated; see the note on .masthead-nav's gap immediately below.

   THE BAR GREW WITH IT and that is intended, not a side effect: 59px -> 76px
   on a desktop. A wordmark cannot lead a bar it is crammed into. */
.brandmark { display: flex; flex: 0 0 auto; }
.brandmark img { height: 52px; width: auto; filter: invert(1); display: block; }
@media (max-width: 1180px) { .brandmark img { height: 48px; } }
@media (max-width: 1060px) { .brandmark img { height: 40px; } }
/* THE GAPS CAME IN FROM 26px TO 20px to buy the wordmark its extra width
   (task P155-#62). Six nav items and a button at 26px spent 156px on air
   alone; at 20px they spend 120px, which is most of one step of logo height.
   SEVEN items since task P155-#68, so 20px now buys 140px of air; the single
   step down to 12px below 1080px is immediately BELOW these two declarations,
   and the 15px step that used to sit there has gone rather than fighting it -
   two overlapping gap rules in opposite directions is how a bar ends up wider
   at 1050px than it is at 1070px. */
.masthead-nav { display: flex; align-items: center; gap: 20px; }
.nav-top { display: flex; align-items: center; gap: 20px; list-style: none; margin: 0; padding: 0; }
/* THE LAST 40px BEFORE THE BURGER. The bar carries SEVEN items now, not six -
   task P155-#68 added Gallery - and the fold has moved up to 1040px to pay for
   it (see the note on the fold breakpoint below). So the narrowest the desktop
   bar is ever asked to be is 1041px, and this is the step that makes it fit:
   40px of mark rather than 48, and 12px of gap rather than 20. It has to come
   AFTER the two declarations above, not before them, or the unconditional
   20px simply wins on source order and the step does nothing. */
@media (max-width: 1080px) {
    .masthead-nav { gap: 12px; }
    .nav-top { gap: 12px; }
}
.nav-item { position: relative; }

/* One rule for a top-level link and for the disclosure button, so the two are
   indistinguishable to a reader: same face, same size, same underline. */
.nav-link {
    display: inline-flex; align-items: center; gap: 6px;
    font: 600 13px/1 var(--body); letter-spacing: .03em;
    color: var(--on-dark); text-decoration: none;
    /* THE LINE IS THICKER. Task P155-#82. Toby, 23 Sep 2026: "please make the
       line 1.5X the size it currently is". It is a 3px rule now.

       THE RESTING BORDER IS 3px OF TRANSPARENT, NOT NONE, and that is the
       whole of why nothing moves when you hover. The border is part of this
       element's box, so a border that appeared only on hover would make the
       item 3px taller at the moment the pointer arrived and shunt the row.
       Reserving the space at the FINAL weight means hover changes a colour
       and not a layout. Measured on the deployed bar: the topbar is 104px
       and every nav item's top is identical with and without a hover. */
    padding: 4px 0; border: 0; border-bottom: 3px solid transparent;
    background: none; cursor: pointer; white-space: nowrap;
}
/* THE UNDERLINE BELONGS TO THE LINK, NOT TO THE PAGE. Task P155-#82, which
   corrects task P155-#81. Toby, 23 Sep 2026: "I want the underline colour to
   be for each page. So no matter what page i'm on if i hover over how it works
   it should be that shade of green."

   WHAT #81 GOT WRONG. It read --hue-on-dark, which resolves off the
   `body.hue-*` class, so every underline in the bar was the SAME colour and
   that colour CHANGED as the visitor moved around the site: all seven items
   were ochre on the FAQ page and all seven were olive on How it works. Toby's
   sentence asks for the opposite — a bar of seven FIXED colours, where
   "How it works" is the olive green from everywhere, including from the Blog.

   THE MAPPING IS KEYED ON THE DESTINATION, WHICH THE MARKUP ALREADY CARRIES.
   Every item's `href` is its destination and every page writes the same flat
   relative form of it (verified across all 17 pages), so an attribute selector
   is a per-link mapping that needs no new class, no data- attribute and no
   JavaScript. Each link declares its own --nav-hue; the two state rules at the
   bottom read that one token and nothing else, so they no longer know what
   page they are on.

   THE VALUES ARE #81'S OWN TABLE, NOT A NEW ONE. Each destination takes the
   "on dark" value of the hue that destination's <body> actually carries — the
   hue at full strength where it clears 3:1 on this black bar, and the hue's
   lighter --hue-tint where it does not (olive 2.99:1 and navy 1.32:1 are the
   two that do not). Measured on #000000 with the WCAG formula, as in section
   12, and re-measured on the deployed page for task P155-#82:

       nav item       destination page       body hue    value used      on bar
       How it works   how-it-works.html      hue-olive   --c-olive-tint  11.41:1
       FAQs           faq.html               hue-ochre   --c-ochre        9.98:1
       Our work       our-work.html          hue-corn    --c-corn        11.49:1
       Gallery        gallery.html           hue-navy    --c-navy-tint    8.96:1
       Blog           blog.html              hue-peri    --c-peri         8.54:1
       Private shop   shop.html              hue-peach   --c-peach       10.99:1
       Socks for      (no single page)       —           --on-dark       21.00:1

   "SOCKS FOR" IS THE ONE ITEM WITH NO COLOUR, because it is a disclosure
   button with no href and therefore no destination to take a colour from.
   It falls through to the base --nav-hue below, which is the white the bar's
   type is already set in — a neutral underline rather than a borrowed hue.
   Its five CHILDREN do each have a destination, and they are coloured the
   same way further down, on the submenu's own ground.

   HOVER AND CURRENT ARE THE SAME COLOUR, as they were under #81, and now that
   is not merely deliberate but automatic: on the page a link points at, "the
   link's colour" and "the page's colour" are the same value, so the
   current-page underline is that page's own colour with no second rule to
   write. #81's separate --hue-driven current-page rule is gone rather than
   left behind.

   THE TEXT IS UNTOUCHED. Every .nav-link is --on-dark at 21:1 exactly as it
   was; these rules move the border colour and nothing else. */
.nav-link { --nav-hue: var(--on-dark); }
.nav-link:is([href="how-it-works.html"], [href="/how-it-works"]) { --nav-hue: var(--c-olive-tint); }
.nav-link:is([href="faq.html"], [href="/faq"])          { --nav-hue: var(--c-ochre); }
.nav-link:is([href="our-work.html"], [href="/our-work"])     { --nav-hue: var(--c-corn); }
.nav-link:is([href="gallery.html"], [href="/gallery"])      { --nav-hue: var(--c-navy-tint); }
.nav-link:is([href="blog.html"], [href="/blog"])         { --nav-hue: var(--c-peri); }
.nav-link:is([href="shop.html"], [href="/shop"])         { --nav-hue: var(--c-peach); }

.nav-link:hover { border-bottom-color: var(--nav-hue); }
.nav-link[aria-current="page"], .nav-link.is-current { border-bottom-color: var(--nav-hue); }

/* THE PRIVATE SHOP IS NO LONGER DIMMED. Task P155-#68. Toby, 22 Sep 2026:
   "Make the 'private shop' button the same colour as the other ones (socks for,
   how it works, FAQs, Our work, Blog)."

   `.nav-quiet` set that one link in --on-dark-mute (#b3b3b3) rather than
   --on-dark (#ffffff), on the reasoning that it sells nothing to a visitor who
   arrived from a search. Toby has looked at the result and overruled it, so the
   RULE is gone and not merely neutralised, and `quiet: true` is gone from
   tools/apply-header.js with it. There is now no selector anywhere that can
   make one item in this bar render dimmer than its siblings: every .nav-link
   takes --on-dark from the rule above, on every page, generated by one
   template. */

.nav-caret {
    width: 7px; height: 7px; flex: 0 0 auto; margin-left: 1px;
    border-right: 1.6px solid currentColor; border-bottom: 1.6px solid currentColor;
    transform: translateY(-2px) rotate(45deg);
    transition: transform .15s ease;
}
.nav-has-menu.is-open .nav-caret { transform: translateY(1px) rotate(-135deg); }

/* ---- the "Socks for" menu ----------------------------------------------
   CLOSED IS display:none, NOT the `hidden` attribute and not opacity. A menu
   left in the layer tree with opacity:0 is still tabbable, which is how a
   keyboard ends up focused on something nobody can see. And it is opened by a
   class the script sets, never by :hover — see nav-site.js.

   Before nav-site.js has run, .nav-js is absent and the block below does not
   apply: the submenu is then a plain visible list, so a visitor with
   JavaScript off is never shown a button that does nothing. */
.nav-sub {
    list-style: none; margin: 0; padding: 0;
    display: flex; gap: 18px;
}
.nav-js .nav-sub {
    display: none;
    position: absolute; top: calc(100% + 11px); left: -18px; z-index: 5;
    min-width: 288px; padding: 8px;
    background: var(--ink); border: 1px solid var(--dark-line);
    flex-direction: column; gap: 0;
}
.nav-js .nav-has-menu.is-open .nav-sub { display: flex; }
.nav-sub a {
    display: block; padding: 11px 12px; text-decoration: none;
    color: var(--on-dark); border-bottom: 0;
}
/* THE FIVE SUBMENU ROWS CARRY THEIR OWN DESTINATIONS' COLOURS TOO, on the
   same per-link principle as the bar above and for the same reason: "Socks
   for" has no colour, but Schools, Clubs, Universities, Fundraising and
   Businesses each lead to a page that does. Task P155-#82.

   THE INDICATOR HERE IS A LEFT EDGE, NOT AN UNDERLINE, because a submenu row
   is a full-width block with a two-line label and has no underline to take.
   Under #81 only the CURRENT row carried it and hover was a bare background
   change, so hovering a submenu row showed no colour at all. Hover now takes
   the same coloured edge as current, which is what makes these five items
   answer Toby's sentence the way the seven above it do.

   THE GROUND HERE IS #1a1a1a, NOT THE BAR'S BLACK, so the ratios are measured
   against that and not reused from the table above. All five clear the 3:1
   floor for a non-text indicator with room to spare:

       submenu row    destination page                      value        on #1a1a1a
       Schools        custom-socks-for-schools.html         olive-tint      9.46:1
       Clubs          custom-socks-for-clubs.html           --c-corn        9.52:1
       Universities   custom-socks-for-universities.html    --c-peri        7.08:1
       Fundraising    custom-socks-for-fundraising.html     --c-peach       9.11:1
       Businesses     custom-socks-for-businesses.html      --c-sage        7.73:1

   (Below the fold the submenu drops its own background and sits on the
   panel's var(--ink) #000000 instead, where the same five run 11.41, 11.49,
   8.54, 10.99 and 9.33:1. #1a1a1a is the LIGHTER of the two grounds and so
   the harder one, which is why it is the ground the floor is cleared
   against and the one tabulated above.)

   THE EDGE IS 3px, UP FROM 2px — exactly the 1.5x Toby asked for, applied
   here as well so the bar's line and the submenu's marker stay the same
   weight as each other. A box-shadow is drawn inside the border box and
   takes no space, so unlike the underline above this one cannot shift
   anything whatever its width. */
.nav-sub a { --nav-hue: var(--on-dark); }
.nav-sub a:is([href="custom-socks-for-schools.html"], [href="/custom-socks-for-schools"])      { --nav-hue: var(--c-olive-tint); }
.nav-sub a:is([href="custom-socks-for-clubs.html"], [href="/custom-socks-for-clubs"])        { --nav-hue: var(--c-corn); }
.nav-sub a:is([href="custom-socks-for-universities.html"], [href="/custom-socks-for-universities"]) { --nav-hue: var(--c-peri); }
.nav-sub a:is([href="custom-socks-for-fundraising.html"], [href="/custom-socks-for-fundraising"])  { --nav-hue: var(--c-peach); }
.nav-sub a:is([href="custom-socks-for-businesses.html"], [href="/custom-socks-for-businesses"])   { --nav-hue: var(--c-sage); }

.nav-sub a:hover, .nav-sub a:focus-visible { background: #1a1a1a; box-shadow: inset 3px 0 0 var(--nav-hue); }
.nav-sub a[aria-current="page"] { background: #1a1a1a; box-shadow: inset 3px 0 0 var(--nav-hue); }
.nav-sub-label { display: block; font: 700 13px/1.2 var(--body); letter-spacing: .02em; }
.nav-sub-note { display: block; margin-top: 3px; font: 400 11.5px/1.35 var(--body); color: var(--on-dark-mute); }

/* THE ONE CALL TO ACTION, and the only thing in the bar that is not a plain
   link. It was a cramped chip before — `.masthead-nav a`'s 3px padding beat
   `.btn`'s on specificity — so it is given its own padding here rather than
   losing the argument by accident. */
.masthead-nav .btn.nav-cta {
    flex: 0 0 auto;
    font: 700 13px/1 var(--alt); letter-spacing: .02em; color: #000;
    padding: 9px 18px; border: 2px solid var(--accent);
}
.masthead-nav .btn.nav-cta:hover { background: var(--on-dark); border-color: var(--on-dark); color: #000; }

/* Focus must be visible on black. */
#masthead a:focus-visible, #masthead button:focus-visible {
    outline: 2px solid var(--accent); outline-offset: 3px;
}

/* ---- the phone button --------------------------------------------------- */
.nav-burger {
    display: none; align-items: center; gap: 9px;
    background: none; border: 1px solid var(--dark-line); cursor: pointer;
    color: var(--on-dark); padding: 8px 12px;
    font: 700 11.5px/1 var(--alt); letter-spacing: .14em; text-transform: uppercase;
}
.burger-bars, .burger-bars::before, .burger-bars::after {
    display: block; width: 16px; height: 2px; background: currentColor;
    content: ""; position: relative;
}
.burger-bars::before { top: -5px; }
.burger-bars::after  { top: 3px; }
.nav-open .nav-burger { border-color: var(--on-dark); }
.nav-open .burger-bars { background: transparent; }
.nav-open .burger-bars::before { top: 0; transform: rotate(45deg); }
.nav-open .burger-bars::after  { top: -2px; transform: rotate(-45deg); }

/* ---- below the breakpoint -----------------------------------------------
   THE FOLD MOVED FROM 900px TO 1040px. Task P155-#68. The bar gained a SEVENTH
   top-level item - Gallery - and seven items plus a 40px wordmark plus the call
   to action do not fit on one line between 900px and about 1030px: the nav
   broke onto a second row, which is the one outcome the brief rules out. The
   answer is to fold into the Menu button sooner rather than to shrink the mark
   further or to drop an item, because below the fold the panel carries all
   seven at full size and nothing is lost. A tablet at 900-1040px now gets the
   phone's menu, which is the same menu it already got at 899px.

   THE PADDING HERE IS 22px, NOT THE DESKTOP'S 26px. Toby asked for more black
   around the logo; below the fold the bar is the mark and a button with no nav
   competing for the row, so it can afford the black without the bar eating a
   third of a phone screen. 11px -> 22px is still double what it was. */
@media (max-width: 1040px) {
    .masthead-in { padding: 22px 20px; gap: 12px; }
    /* 26px -> 36px, task P155-#62. Below this width the nav is a burger and is
       no longer competing with the mark for the row, so the constraint that
       held it to 42px just above has gone and the only limit is the width of
       the phone: 36px of height is ~235px of width, which still leaves room
       for the Menu button on a 360px screen. */
    .brandmark img { height: 36px; }
    .nav-burger { display: inline-flex; }

    /* THE PANEL. Absolutely positioned under the bar rather than in flow, so
       opening it does not make the sticky container a screen tall. */
    .masthead-nav {
        display: none;
        position: absolute; top: 100%; left: 0; right: 0; z-index: 55;
        flex-direction: column; align-items: stretch; gap: 0;
        padding: 6px 20px 22px;
        background: var(--ink); border-top: 1px solid var(--dark-line);
        max-height: calc(100vh - 100%); overflow-y: auto;
    }
    #masthead.nav-open .masthead-nav { display: flex; }

    .nav-top { flex-direction: column; align-items: stretch; gap: 0; }
    /* THE BORDER IN THIS PANEL IS A ROW SEPARATOR, NOT THE SELECTION
       INDICATOR, so it is NOT taking the 1.5x of task P155-#82 and it is NOT
       taking the per-link colours either. Every row in the stacked panel
       carries this rule whether or not it is hovered or current — that is what
       makes it a divider — and the hover rule immediately below deliberately
       holds it at --dark-line so hovering does not light a divider up. Taking
       it to 3px would thicken fourteen hairlines between menu rows, which is
       not the line Toby was looking at. It stays 1px of --dark-line.

       These two declarations come AFTER the desktop rules in source order and
       inside a media query of equal specificity, so they are what actually
       applies below 1040px: the 3px transparent border and the --nav-hue
       colour above are both overridden here, by design. The panel's own
       current-page mark is the colour rule further down. */
    .nav-link { padding: 14px 0; border-bottom: 1px solid var(--dark-line); font-size: 15px; }
    .nav-link:hover { border-bottom-color: var(--dark-line); }
    /* THE PHONE PANEL MARKS THE CURRENT PAGE ON THE TEXT, not on a border:
       every row in this panel already carries a --dark-line rule under it as a
       divider, so an underline here would be a divider that changed colour
       rather than a mark. It followed --accent, which is the one periwinkle
       task P155-#81 is removing from the bar's state colours — leaving it
       would have put a periwinkle label and an ochre divider on the same row
       of the FAQ page's menu. It reads --hue-on-dark with the rest of them.
       The panel's ground is var(--ink), the same #000000 as the bar, so the
       numbers are the bar's numbers: 8.54:1 at worst, against the 4.5:1 this
       one needs as text. Every OTHER row in the panel stays --on-dark.

       THIS ONE KEEPS --hue-on-dark, AND TASK P155-#82 LEFT IT ALONE ON
       PURPOSE. It is the one rule in the nav that is genuinely ABOUT THE PAGE
       rather than about the link: it answers "which page am I on" in a
       collapsed panel, so the page's own hue is the correct source for it.
       Three things follow, and all three are why it was not converted to
       --nav-hue:
         * It colours TEXT, not a border. It is not an underline, so the
           per-link mapping above does not contradict it, and the brief for
           #82 rules the nav's text colours out of scope explicitly.
         * On every top-level destination the two tokens RESOLVE TO THE SAME
           VALUE anyway — on faq.html the page hue is ochre and the FAQs
           link's --nav-hue is ochre — so converting it would change no
           rendered colour on any page in the bar.
         * On the five segment pages the current item is the "Socks for"
           DISCLOSURE, which has no destination and so has no --nav-hue of its
           own; driving this from --nav-hue would render it white, identical
           to every other row, and the phone panel would silently lose its
           current-page mark on those five pages. */
    /* THE SEPARATOR STAYS A SEPARATOR ON THE CURRENT ROW TOO. Task P155-#82.
       The desktop current-page rule sets border-bottom-color: var(--nav-hue)
       and it is not otherwise overridden in here, so without this declaration
       the current row's hairline took a SELECTION colour in a panel whose
       border is explicitly not the selection indicator. On the five segment
       pages that was visibly wrong: the current item there is the "Socks for"
       disclosure, which has no destination and so falls back to --nav-hue's
       white, painting one white hairline among six --dark-line ones. Holding
       it at --dark-line makes all seven rows separate identically and leaves
       the current-page mark to the text colour on the same line, which is
       what the comment above says this panel does. */
    .nav-link[aria-current="page"], .nav-link.is-current { color: var(--hue-on-dark); border-bottom-color: var(--dark-line); }
    .nav-disclosure { width: 100%; justify-content: space-between; }

    /* The submenu opens in flow inside the panel — a second floating layer on
       a phone would be a menu on top of a menu. */
    .nav-js .nav-sub {
        position: static; min-width: 0; border: 0; padding: 4px 0 10px 14px;
        background: none; box-shadow: none;
    }
    .nav-sub a { padding: 10px 10px; }

    .masthead-nav .btn.nav-cta {
        margin-top: 18px; justify-content: center; padding: 14px 18px; font-size: 13px;
    }
}

/* AN ANCHOR JUMP MUST NOT LAND UNDER THE STICKY BAR.

   --topbar-h is written by nav-site.js from the bar's measured height on load,
   on resize and whenever the bar changes shape, because that height is not a
   constant: the ticker is on the home page only, it appears only once the
   database has something for it to say, and the bar folds at 900px. The two
   values below are the fallback for the moment before the script runs, and
   they are the heights actually measured on the built page: 90px with the
   ticker up on a desktop, 82px folded. The pair they replace — 108px and 92px
   — were a guess: 18px too generous on a laptop and, on a phone, 2.5px SHORT
   of the 94.5px bar, which put the target heading under the bar it was
   supposed to clear.

   RE-MEASURED FOR THE BIGGER WORDMARK, task P155-#62. The mark went 30px ->
   52px and took the bar with it: 90px -> 107px with the ticker up on a
   desktop, 82px -> 89px folded. Both numbers below are read off the rendered
   page at 1440px and at 390px, not adjusted by arithmetic — the bar is
   padding plus the taller of the mark and the nav, and which of those two
   wins changes at the breakpoint. */
/* RE-MEASURED WITH THE TICKER GONE FROM THE TOP, task P155-#74. The bar is
   now the masthead alone: the ticker's 30.5px has left it, and these two
   fallbacks come down by exactly that. Read off the rendered page, not
   adjusted by arithmetic:

       1440 x 900   #masthead 104px   (was #topbar 134.5px, incl. 30.5px ticker)
        390 x 844   #masthead  80px   (was #topbar 110.5px)

   nav-site.js measures #masthead and publishes those same numbers as
   --topbar-h. These matter for exactly one frame - the one before that script
   runs - but a fallback that is 31px too GENEROUS is a 31px white gap under
   the opening on first paint, and one that is short is 31px of the next
   section showing, which is the bug task P155-#66 existed to fix.

   (Before P155-#74 these were 135px and 111px, re-measured by P155-#68 when
   the masthead's vertical padding went 12px -> 26px.) */
:root { --topbar-fallback: 104px; }
section[id] { scroll-margin-top: calc(var(--topbar-h, var(--topbar-fallback)) + 14px); }
@media (max-width: 1040px) { :root { --topbar-fallback: 80px; } }

/* =========================================================================
   3. THE OPENING — the sock counter, as the live site opens.

   societalsocks.com opens on the words "Socks Knitted:" and the figure 8,242
   set in Sora 700 at 135px. Not a hero headline with the number tucked into a
   card beside it: the NUMBER IS THE FIRST THING. That composition is the one
   reproduced here, at the same ratio of label to figure.

   ---------------------------------------------------------------------------
   THE GRADIENT IS BACK, AND IT IS THE REAL ONE. Task P155-#62.

   Toby, 21 Sep 2026: "Add in original colour gradient background for landing
   page." It had been flattened to white by task P155-#25, whose note further
   up this file still says "there is no gradient, no radial wash" — that was a
   correct reading of the live site's SECTIONS and a wrong one of its TOP,
   because the thing it measured was the band behind the counter and the band
   under it, both of which are mesh gradients and neither of which is flat.

   MEASURED, NOT CHOSEN. societalsocks.com was opened on 21 Sep 2026 and the
   computed `background-image` of the element behind the opening was read
   straight out of the browser. It is six overlapping radial gradients, and
   these are its six colours and its six positions, transcribed:

       rgb( 77,  96,   1)  dark olive      at   4%   7%   (top left)
       rgb(214, 222,  72)  acid yellow     at  47%  12%   (top centre)
       rgb(250, 170,  99)  peach           at  93%  10%   (top right)
       rgb(151, 180, 157)  sage            at   4%  93%   (bottom left)
       rgb(147, 195, 255)  cornflower      at  51%  90%   (bottom centre)
       rgb(201, 198, 255)  periwinkle      at  95%  90%   (bottom right)

   A seventh stop in the original is a black radial at dead centre whose two
   stops are both at 100%; it contributes nothing visible and is not copied.
   The periwinkle in the bottom right corner is #c9c6ff, one step off the
   --accent the buttons already use — which is why the live page looks like one
   thing rather than a gradient with a purple button sitting on it.

   WHY IT IS ON ::before AND NOT ON THE SECTION. The gradient needs to be
   BEHIND the text at reduced strength, and `opacity` on the section itself
   would fade the counter with it. A pseudo-element takes the opacity on its
   own; `z-index: 0` on it and `position: relative` on the children put every
   word in front of it at full black.

   THE TINT IS GONE AND THE GRADIENT IS AT FULL STRENGTH. Task P155-#65.
   Toby, 21 Sep 2026: "Remove the tint/shadow over the gradient background on
   the landing page."

   WHAT THE TINT ACTUALLY WAS. Not a scrim, not a shadow, not a second layer
   over the top: `opacity: .55` on this pseudo-element, which painted the
   gradient itself at 55% over white and is what made it look washed out. It is
   now `opacity: 1`. There is no overlay on this band of any kind - no
   ::after (its computed `content` is `none`), no rgba wash, no `filter`, no
   `background-blend-mode`, and the section's own background is transparent.
   The six radial stops below are the whole of what is painted here.

   AND THE CONTRAST WAS FIXED IN THE TYPE, NOT BY PUTTING THE TINT BACK.
   Removing it genuinely did cost contrast, so both halves were measured off
   the rendered page at a 1919px viewport rather than reasoned about: the copy
   was hidden, the bare gradient sampled at every second pixel inside each text
   box, and the worst pixel under any glyph taken.

   The BLACK type was never the problem. The counter label, the 150px figure
   and the h1 measured 6.71:1, 6.74:1 and 7.57:1 - all past AA, and past AAA
   for large text. The old note over this block feared black numerals at "3:1"
   on the dark olive corner; that corner is at 3.7% / 6.9%, which is off the
   left edge of where the copy column actually sits, so the type never crosses
   it.

   THE GREY WAS. #opening-sub and .opening-note were --ink-mute, rgb(92,92,92),
   and measured 2.62:1 and 3.11:1 against the untinted gradient - both under
   the 4.5:1 that body text needs. Both are now --ink, which takes the same two
   worst pixels to 8.23:1 and 9.77:1. That is the whole fix, it is in the type,
   and --ink-mute is unchanged everywhere else on the site.

   The worst pixel under any glyph in the opening is now 6.71:1.

   ONE BAND, NOT TWO. The live Wix page puts a second gradient strip below the
   hero as a separator. That is a Wix section divider; here the opening simply
   ends and "Socks we have actually made" begins on white, which is the same
   effect with one fewer element.
   ========================================================================= */
/* ---------------------------------------------------------------------------
   THE OPENING FILLS THE SCREEN, AND THE NEXT SECTION STARTS BELOW THE FOLD.
   Task P155-#66. Toby, 22 Sep 2026: "Make sure the landing page gradient
   background fills screen, so there you cant see the top of the next section
   below."

   It did not. Measured on the deployed page before this change: at 1440x900
   the opening's bottom edge was at y=746, so 154px of the strip below it was
   on screen at first paint; at 390x844 it ended at y=574 and showed 270px of
   it. The band was a fixed 92px+84px of padding around whatever the copy
   happened to be, which is a height that has nothing to do with the screen.

   WHAT IT SUBTRACTS, AND WHY IT IS NOT A GUESS. #topbar is `position: sticky`,
   and a sticky element KEEPS ITS SPACE IN NORMAL FLOW — so the opening does
   not start at the top of the viewport, it starts below the ticker and the
   masthead. Filling the rest of the screen therefore means the viewport MINUS
   that bar, and the bar's height is not a constant: the ticker only appears
   once the database has something for it to say, and the whole thing folds at
   900px. --topbar-h is the bar's REAL measured height, written onto the root
   by nav-site.js on load, on resize and from a ResizeObserver on the bar
   itself. --topbar-fallback is the value for the frame before that script has
   run and is itself a measurement (107px desktop / 89px folded), not an
   estimate. This reads the measurement rather than assuming a number.

   svh, WITH vh UNDERNEATH IT. The first declaration is the fallback for a
   browser that does not know the new units; the second wins wherever it is
   understood. `svh` is the SMALL viewport height — the screen as it is when
   the mobile browser's toolbars are SHOWN, which is exactly the state at first
   load, and it is the one unit of the three that cannot leave a strip of the
   next section peeking out from under a retracted toolbar. `dvh` would resize
   the band mid-scroll as the toolbar slides away, which is visible as a jolt;
   `lvh` would overflow the screen on first paint, which is the bug this is
   fixing.

   THE +1px IS A ROUNDING GUARD, not padding. nav-site.js rounds the measured
   height to a whole pixel, so on a bar that is genuinely 106.5px tall it
   publishes 107px and this calculation subtracts half a pixel too much —
   enough to leave a hairline of the section below showing along the bottom
   edge. 1px covers the worst case in the safe direction; the cost is that the
   opening can be up to 1.5px taller than the space available, which nobody can
   see and which pushes the next section further out of view rather than into
   it.

   min-height, NEVER height, AND THE CENTRING IS OVERFLOW-SAFE. If the copy is
   ever taller than the screen — a short laptop, a phone on its side, a visitor
   who has scaled the text up — a fixed `height` would crop the buttons or the
   MOQ line off the bottom with no way to reach them. `min-height` lets the
   band grow past the viewport and the page simply scrolls. The centring is
   done with `margin-block: auto` on the child rather than `align-items:
   center`, because an auto margin resolves to ZERO when free space runs out,
   whereas centre alignment keeps centring and pushes the top of the content
   above the top of the band, where it cannot be scrolled back to. Same look
   when it fits, no unreachable copy when it does not.
   --------------------------------------------------------------------------- */
/* =========================================================================
   THE SOCK STRIPE ARTWORK, REBUILT IN CSS. Task P155-#71.

   Toby, 22 Sep 2026, attaching a picture of eight knitted sock stripe
   patterns: "Please put this as the background instead of the current
   gradient one."

   THE ARTWORK IS IN THE REPO AND IS NOT WHAT IS SERVED. His file is committed
   at images/sock-stripes-reference.png and is the source of truth for what
   these patterns ARE - the colours below and the stripe weights below were
   both measured off it, pixel row by pixel row, not eyeballed. It is not used
   as an image anywhere, and the reason is its size: 672x447. The opening fills
   the viewport, which on a desktop is 1920 wide and routinely 2560, so serving
   that file means upscaling it about 3x and every stripe edge arrives soft.
   The first thing a visitor sees cannot be a blurred bitmap.

   THESE ARE FLAT HORIZONTAL STRIPES, WHICH IS THE ONE THING CSS REPRODUCES
   EXACTLY. A repeating-linear-gradient with hard stops is resolution
   independent: it is rasterised at whatever the device's pixel density is, so
   it is as crisp on a 3x phone as on a 1x monitor, and it costs no bytes.

   HOW THE WEIGHTS WERE TAKEN. A script walked the centre column of each of the
   eight swatches in the reference and recorded every run of constant colour.
   That produced, for each pattern, its repeat PERIOD and the width of each band
   inside it - which is the part a two-colour approximation throws away. The
   eight are not eight versions of the same rhythm:

     navy/orange     period 37   two 4-wide pinstripes 5 apart on a 24 ground
     sky/white       period 47   a 9 white band on a 38 sky ground
     navy/gold       period 61   a 14 gold band on a 47 indigo ground
     red/white       period 58   29 and 29, even broad bands
     yellow/black    period 98   49 and 49, the broadest pair in the set
     navy/red/white  period 45   red 4, white 4, red 4, on a 33 midnight ground
     purple/white    period 28   white 3, purple 7, white 3, purple 15
     green/white     period 45   a 16 white band on a 29 green ground

   ONE UNIT, --u, SCALES ALL EIGHT TOGETHER. Every number above is multiplied
   by --u, so the whole set keeps its relative rhythm at any size and a single
   declaration makes the wall finer or bolder. It is a LENGTH, not a factor, and
   it is deliberately a whole number of pixels at every breakpoint: a hard
   gradient stop that lands on a fractional pixel is the one way this technique
   can go soft, and integers avoid it.

   THEY ARE CLASSES, NOT A ONE-OFF BACKGROUND. The brief asks for them to be
   reusable, and they are: `.sock-stripe` plus one `.ss-*` modifier paints any
   element - a band, a rule, a card edge, a divider - with any of the eight.
   Nothing outside the opening uses one yet, and that is on purpose: task
   P155-#70 has just finished careful work on how colour is applied across the
   site and this task is not allowed to disturb it.
   ------------------------------------------------------------------------- */
:root {
    /* Sampled from images/sock-stripes-reference.png. Where Toby's brief named
       a hex and the sample differed by a shade, his hex is the one used - the
       artwork was supplied with those values and they are the ones he means. */
    --ss-navy:     #001e57;   /* 1 ground */
    --ss-orange:   #d95000;   /* 1 pinstripe */
    --ss-sky:      #00a3d7;   /* 2 ground */
    --ss-white:    #ffffff;   /* 2, 4, 6, 7, 8 */
    --ss-indigo:   #33418c;   /* 3 ground */
    --ss-gold:     #e2aa00;   /* 3 band */
    --ss-red:      #c81c23;   /* 4 band, 6 pinstripe */
    --ss-yellow:   #fec700;   /* 5 band */
    --ss-black:    #000000;   /* 5 ground */
    --ss-midnight: #120c3e;   /* 6 ground */
    --ss-purple:   #5b0e77;   /* 7 ground */
    --ss-green:    #4f7a28;   /* 8 ground */
}

.sock-stripe { --u: 2px; }

/* 1. NAVY WITH FINE ORANGE PINSTRIPES. The one pattern in the set whose
      character is entirely in the GAP: two hairlines close together, then a
      wide run of navy. Rendered as two even stripes it stops being this
      pattern at all. */
.ss-navy-orange {
    background-color: var(--ss-navy);
    background-image: repeating-linear-gradient(to bottom,
        var(--ss-orange) 0                   calc(var(--u) *  4),
        var(--ss-navy)   calc(var(--u) *  4) calc(var(--u) *  9),
        var(--ss-orange) calc(var(--u) *  9) calc(var(--u) * 13),
        var(--ss-navy)   calc(var(--u) * 13) calc(var(--u) * 37));
}
/* 2. SKY BLUE WITH A NARROWER WHITE BAND. Not a 50/50 stripe: the white is
      about a quarter of the repeat, so the swatch reads as blue with white
      through it rather than as blue and white. */
.ss-sky-white {
    background-color: var(--ss-sky);
    background-image: repeating-linear-gradient(to bottom,
        var(--ss-white) 0                   calc(var(--u) *  9),
        var(--ss-sky)   calc(var(--u) *  9) calc(var(--u) * 47));
}
/* 3. INDIGO WITH A GOLD BAND. Same shape as 2 at a larger scale - a 14 band on
      a 47 ground - which is why the two must not be collapsed into one rule
      with different colours. */
.ss-navy-gold {
    background-color: var(--ss-indigo);
    background-image: repeating-linear-gradient(to bottom,
        var(--ss-gold)   0                    calc(var(--u) * 14),
        var(--ss-indigo) calc(var(--u) * 14)  calc(var(--u) * 61));
}
/* 4. RED AND WHITE, EVEN. The only true 50/50 in the set apart from 5. */
.ss-red-white {
    background-color: var(--ss-red);
    background-image: repeating-linear-gradient(to bottom,
        var(--ss-white) 0                    calc(var(--u) * 29),
        var(--ss-red)   calc(var(--u) * 29)  calc(var(--u) * 58));
}
/* 5. YELLOW AND BLACK, EVEN AND BROAD. Period 98 against the navy/orange's
      37: nearly three times the band. This is the loudest swatch in the set
      and it is loud because of its SIZE, not its colour. */
.ss-yellow-black {
    background-color: var(--ss-yellow);
    background-image: repeating-linear-gradient(to bottom,
        var(--ss-black)  0                    calc(var(--u) * 49),
        var(--ss-yellow) calc(var(--u) * 49)  calc(var(--u) * 98));
}
/* 6. MIDNIGHT WITH A RED-WHITE-RED TRIPLET. Three 4-wide lines touching, then
      33 of ground. The triplet reading as one thick stripe is the failure
      mode; each of the three stays 4. */
.ss-navy-red-white {
    background-color: var(--ss-midnight);
    background-image: repeating-linear-gradient(to bottom,
        var(--ss-red)      0                    calc(var(--u) *  4),
        var(--ss-white)    calc(var(--u) *  4)  calc(var(--u) *  8),
        var(--ss-red)      calc(var(--u) *  8)  calc(var(--u) * 12),
        var(--ss-midnight) calc(var(--u) * 12)  calc(var(--u) * 45));
}
/* 7. PURPLE WITH WHITE LINES AT TWO SPACINGS. The subtlest of the eight: the
      purple bands ALTERNATE 7 and 15, so the white lines are unevenly spaced
      and the swatch has a beat to it. A single even repeat loses that. */
.ss-purple-white {
    background-color: var(--ss-purple);
    background-image: repeating-linear-gradient(to bottom,
        var(--ss-white)  0                    calc(var(--u) *  3),
        var(--ss-purple) calc(var(--u) *  3)  calc(var(--u) * 10),
        var(--ss-white)  calc(var(--u) * 10)  calc(var(--u) * 13),
        var(--ss-purple) calc(var(--u) * 13)  calc(var(--u) * 28));
}
/* 8. GREEN WITH A WIDE WHITE BAND. 16 of white to 29 of green - much more
      white than 2 or 3 carry, which is what makes it the lightest swatch. */
.ss-green-white {
    background-color: var(--ss-green);
    background-image: repeating-linear-gradient(to bottom,
        var(--ss-white) 0                    calc(var(--u) * 16),
        var(--ss-green) calc(var(--u) * 16)  calc(var(--u) * 45));
}

/* ---------------------------------------------------------------------------
   THE WALL: EIGHT SWATCHES IN A GRID, BEHIND THE OPENING.

   THE COMPOSITION IS THE ARTWORK'S OWN. Toby's picture is a GRID of eight
   swatches, four across and two down, and that is what this paints. The
   alternative - picking two or three and running them full width - would have
   been a different piece of design from the one he sent, and the grid is also
   the only arrangement that shows all eight patterns at a size where each
   one's rhythm is legible. A viewport is wider and shorter than his 672x447
   file, so the cells come out squarer than his; the stripes inside them do not
   change shape with the cell, because they are sized in --u and not in
   percentages.

   IT REFLOWS RATHER THAN SQUASHING. Four columns down to 560px, two columns
   below it: at 390px a four-column grid gives 97px cells, which is narrower
   than one of Toby's swatches and reads as a barcode. Two columns at that
   width give 195px cells - almost exactly his 168 - so the phone sees the same
   artwork rearranged, not a compressed one. The reading order is unchanged
   either way: navy/orange, sky, navy/gold, red, then yellow/black, navy/red,
   purple, green.

   --u IS SET AGAINST THE CELL, NOT AGAINST THE SCREEN WIDTH, and the control
   that matters is HEIGHT, because the stripes run horizontally. On a 1080 or
   1200 screen a cell is ~475-550 tall and --u: 2px puts 2.4 repeats of the
   broadest pattern in it, which is what the reference has. On a 650px laptop
   the cell is ~270 and the same 2px would show ONE band of yellow/black - a
   plain yellow rectangle, not a stripe - so --u drops to 1px there and the
   count comes back to 2.8. The phone gets 1px for the same reason. Both values
   are whole pixels, so every gradient stop lands on a pixel boundary and no
   edge is antialiased.

   IT IS PRESENTATIONAL AND CARRIES NO CONTENT. aria-hidden, no text inside it,
   pointer-events none, and no data-copy block anywhere in it, so copy-edit.js
   and copy.js are untouched by it.
   --------------------------------------------------------------------------- */
.stripe-wall {
    position: absolute; inset: 0; z-index: 0;
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    grid-template-rows: repeat(2, 1fr);
    pointer-events: none;
}
.stripe-wall > * { --u: 2px; }

@media (max-height: 760px) {
    .stripe-wall > * { --u: 1px; }
}
@media (max-width: 560px) {
    .stripe-wall {
        grid-template-columns: repeat(2, 1fr);
        grid-template-rows: repeat(4, 1fr);
    }
    .stripe-wall > * { --u: 1px; }
}

#opening {
    min-height: calc(100vh  - var(--topbar-h, var(--topbar-fallback)) + 1px);
    min-height: calc(100svh - var(--topbar-h, var(--topbar-fallback)) + 1px);
    display: flex;
    flex-direction: column;
    padding: 48px 0;
    position: relative;
    isolation: isolate;          /* the wash can never paint over the sticky bar */
    overflow: hidden;
}
/* THE SIX-COLOUR GRADIENT THAT WAS HERE IS GONE. Task P155-#71.

   Toby, 22 Sep 2026, attaching the sock-stripe artwork: "Please put this as
   the background instead of the current gradient one."

   What stood here was `#opening::before`, six radial-gradient stops
   transcribed off the live societalsocks.com in September and painted over
   --paper. It has been deleted rather than overlaid, so there is no gradient
   left underneath the stripes to bleed through at the cell seams. The
   background of this section is now the `.stripe-wall` element in index.html
   and the eight `.ss-*` pattern classes above, and nothing else paints here.

   THE FIVE PALETTE COLOURS ARE UNAFFECTED BY THAT. The palette in section 12
   was DERIVED from the gradient's stops back in P155-#61, but it does not read
   them - each of the five is a literal hex in its own custom property. Removing
   the gradient changes no value anywhere else on the site. */
/* IN FRONT OF THE WASH. Without this the two columns are painted in the same
   stacking context as ::before and the gradient lands on top of the numerals.

   `margin-block: auto` is the overflow-safe centring described in the note
   over #opening — left and right stay `auto` from the .wrap rule, so the
   column is still horizontally centred and still capped at --wrap. */
#opening > .wrap {
    position: relative; z-index: 1;
    margin-top: auto; margin-bottom: auto;
}

/* ---------------------------------------------------------------------------
   THE WHITE STRIPES, BEHIND THE COUNTER AND BEHIND THE MOQ LINE.
   Task P155-#66. Toby, 22 Sep 2026: "Put a white stripe behind the socks
   knitted count and to the MOQ 100 pairs on the initial landing page. Like the
   original website."

   WHAT THE ORIGINAL ACTUALLY DOES, read off societalsocks.com at 1440px on
   22 Sep 2026 rather than remembered: its opening is a stack of FULL-BLEED
   HORIZONTAL BANDS that alternate gradient, white, gradient. A warm gradient
   strip runs edge to edge under the masthead; beneath it a WHITE band, also
   edge to edge, carries "Socks Knitted:" and the 8,242; beneath that a cool
   gradient strip, edge to edge again. The white is not a card, a panel or a
   rounded box sitting on the gradient — there is no corner radius, no border
   and no shadow anywhere on it, and it does not stop at the text column. It is
   a stripe the full width of the page that the type happens to sit on.

   SO THESE RUN THE FULL PAGE WIDTH, NOT JUST BEHIND THE TEXT BLOCK. That is
   the choice, and the original's own construction is the reason for it: a
   white rectangle that ended at the text column would read as a label stuck
   onto the gradient, which is the card treatment Toby has already had removed
   from this opening once. Running it edge to edge reads as the page being
   built in bands, which is what his site does.

   HOW THE FULL BLEED IS DONE FROM INSIDE A CENTRED COLUMN. The counter and the
   MOQ line both live inside `.wrap`, which is capped at --wrap and padded
   28px. The stripe is a pseudo-element pinned to its own element's top and
   bottom and then pushed 100vw past it on BOTH sides. Stretching it outward
   from the element, rather than centring a 100vw box on the viewport, means it
   reaches both edges whatever the column's width or offset and does not depend
   on the copy being centred. #opening is `overflow: hidden` and body is
   `overflow-x: hidden`, so the excess is clipped and nothing can scroll
   sideways.

   z-index: -1 PUTS IT BETWEEN THE GRADIENT AND THE WORDS. `#opening > .wrap`
   is z-index 1 and so is a stacking context of its own; a negative child sits
   behind the text but still inside that context, which is above the gradient
   on #opening::before at z-index 0. Gradient, then stripe, then type.

   THE STRIPE IS PART OF THE COUNTER, NOT PART OF THE BAND. It is drawn BY
   #counter-block, so when site.js leaves that block `hidden` — which is the
   shipped state until site/counter holds a value — the stripe goes with it and
   the opening is gradient all the way down to the headline. A stripe painted
   by the section, or by a spacer element, would have left an empty white band
   across a page whose counter was switched off.

   CONTRAST. --paper is #ffffff and every glyph on both stripes is --ink,
   #000000: the counter label, the figure and the MOQ line all measure 21:1.
   AA wants 4.5:1 for body text and 3:1 for large, so the MOQ line at 11.5px
   clears the stricter bar with 16.5 points to spare. Nothing here is
   translucent — the stripe is flat opaque white, so the gradient underneath
   cannot drag the ratio down the way a tinted panel would.
   --------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------
   ONE STRIPE, NOT THREE. Task P155-#68.
   Toby, 22 Sep 2026: "Make the white stripe one singular stripe not 3 separate
   ones."

   #66 gave the counter a white stripe and the MOQ line a second; #67 gave the
   headline block a third. Each of the three drew its OWN ::before, so what was
   on screen was three white bands with two strips of gradient cutting across
   between them. #67's note argued those gaps were the point - that they kept
   the opening reading as a gradient opening. Toby has looked at it and does not
   agree, and his reading is the one that decides.

   THERE IS NOW EXACTLY ONE ELEMENT THAT PAINTS A STRIPE, and it is the wrapper
   around all four things: `.opening-white`. The counter, the headline, the two
   buttons and the MOQ line are its children and sit on it; none of them paints
   anything itself any more. One band, edge to edge, no seams.

   THE FULL BLEED IS #66's, UNCHANGED - a pseudo-element pinned to its own
   element's top and bottom and pushed 100vw past it on BOTH sides, so it
   reaches both screen edges whatever the column's width or offset, with the
   excess clipped by #opening's `overflow: hidden`. Flat opaque white, no
   radius, no border, no shadow, and it does not stop at the text column: a
   white rectangle that ended at the copy would read as a card on the gradient,
   which is the treatment Toby has had removed from this opening once already.

   z-index: -1 PUTS IT BETWEEN THE GRADIENT AND THE WORDS. `#opening > .wrap`
   is z-index 1 and so is a stacking context of its own; a negative child sits
   behind the text but still inside that context, which is above the gradient on
   #opening::before at z-index 0. Gradient, then stripe, then type.

   IT RESIZES WHEN THE COUNTER IS HIDDEN, AND THAT IS THE WHOLE REASON THE
   STRIPE HANGS OFF A WRAPPER RATHER THAN OFF #opening. `hidden` on
   #counter-block is display:none, so the counter contributes no height and no
   margin; the wrapper is shorter by exactly the counter's box and the band
   closes up around the headline, the buttons and the MOQ line. A stripe drawn
   by the SECTION, or sized by a fixed height, would have left a white band with
   an empty top third - a hole - on the shipped state where site/counter has no
   value yet. The padding below is on the wrapper, not on the counter, for the
   same reason: padding that belonged to a display:none child would vanish with
   it and the headline would sit against the top edge of the white.

   CONTRAST. --paper is #ffffff. The counter label and the figure are --ink
   (#000000) at 21:1; the MOQ line is --ink at 21:1; the h1 is --c-peri-deep
   (#4d4473) at 8.79:1. AA wants 4.5:1 for body text and 3:1 for large, so the
   MOQ line at 11.5px clears the stricter bar with 16.5 points to spare and the
   headline clears it with 4.3. Nothing here is translucent - the stripe is flat
   opaque white, so the gradient underneath cannot drag any of those down the
   way a tinted panel would.
   --------------------------------------------------------------------------- */
.opening-white {
    position: relative;                  /* the stripe anchors to this box */
    padding: 26px 0 22px;
}
/* THE STRIPE GETS TWO RULES. Task P155-#71.

   It used to sit on a soft six-colour gradient, where a bare white band was
   already the strongest thing on screen. It now sits on eight saturated
   knitted stripe patterns, and against those a plain white rectangle stops
   reading as a band that was PUT there and starts reading as a gap where the
   artwork failed to load. A 3px black rule along its top and bottom edge fixes
   that at the cost of nothing: it is the same move the artwork itself makes -
   every one of the eight swatches is bands separated by lines - so the white
   reads as one more stripe in the wall, the widest one, rather than as a hole
   in it. 5px is the weight the rest of the site already uses for a rule: the
   56px mark over a section heading is 5px and so is the left edge of a category
   row, so the band is edged in the site's own furniture and not in a new one.

   THE RULES ARE INSIDE THE BAND, NOT ADDED TO IT. This element is positioned
   with top:0 and bottom:0 and no height, so its used height is fixed by those
   two offsets and the borders resolve INWARDS. The white band is therefore
   exactly as tall as it was, the type sits in exactly the same place, and
   nothing below the opening moved.

   STILL EXACTLY ONE WHITE STRIPE. This is the same single element P155-#68
   reduced three stripes to; the counter, the headline, the buttons and the MOQ
   line are all still on it, and no other element on this page paints white.
   The rules are --ink, not paper, so they cannot be mistaken for a second band.

   CONTRAST IS UNCHANGED BY THIS: the ground is still flat opaque --paper with
   no transparency, so the stripes behind cannot reach any glyph. Measured on
   the rendered page - counter label and figure --ink 21.00:1, MOQ line --ink
   21.00:1, headline --ss-purple #5b0e77 11.85:1. */
.opening-white::before {
    content: "";
    position: absolute;
    top: 0; bottom: 0;
    left: -100vw; right: -100vw;         /* past both edges, clipped by #opening */
    background: var(--paper);
    border-top: 5px solid var(--ink);
    border-bottom: 5px solid var(--ink);
    z-index: -1;
    pointer-events: none;
}
/* Belt and braces for the hidden counter. `hidden` is display:none in every
   UA stylesheet already; stating it here means no later rule can give
   #counter-block a display and quietly put an empty gap back inside the band. */
#counter-block[hidden] { display: none; }

.counter-label {
    font: 700 clamp(20px, 2.4vw, 34px)/1.1 var(--display);
    letter-spacing: -0.02em; color: var(--ink); margin: 0;
}
/* THE SPACING IS NOW INSIDE ONE BAND, NOT BETWEEN THREE. Task P155-#68.

   #66 and #67 had to keep each stripe's padding tight against its own type,
   because padding inside a stripe was visible white hanging off the end of it,
   and the GAPS between the three were separate margins again. With one band
   there is no such thing as a gap: the wrapper's 26/22 padding is the white
   above the counter and below the MOQ line, and the margins below are simply
   the space between one line of type and the next, measured on white.

   28px BETWEEN THE COUNTER AND THE HEADLINE, not #67's 40px. 40 was the width
   of a gradient band that had to be unmistakably a band; there is no band to
   make legible any more, so the number is set against the TYPE - it is a
   little more than the headline's line height, which reads as a paragraph
   break rather than as a seam. */
#counter-block { padding: 0; margin: 0 0 28px; }
.opening-band { padding: 0; }
#counter-value {
    font-family: var(--display); font-weight: 700;
    font-size: clamp(76px, 13.5vw, 150px);
    line-height: .96; letter-spacing: -0.035em;
    margin: 4px 0 0; color: var(--ink);
    font-variant-numeric: tabular-nums;
}
/* THE HEADLINE IS PURPLE. Task P155-#68. Toby, 22 Sep 2026: "Change 'Socks for
   schools, clubs & organisations.' to purple."

   --c-peri-deep, #4d4473, AND NOT --c-peri. Both are the brand periwinkle and
   both are already in the palette, so no sixth colour is introduced either way,
   but only one of them can be read on this stripe:

       --c-peri       #ab96ff   on #ffffff  =  2.46:1
       --c-peri-deep  #4d4473   on #ffffff  =  8.79:1

   WHICH THRESHOLD APPLIES, AND WHY IT DOES NOT ACTUALLY MATTER HERE. The h1 is
   Montserrat 700 at clamp(19px, 2.3vw, 27px) - 27px bold at 1440px, 19px bold
   at the small end. WCAG counts bold text at 14pt (18.66px) or more as LARGE,
   so this heading is large text at every width it is ever set at, and its
   threshold is the 3:1 one rather than 4.5:1. #ab96ff fails BOTH: at 2.46:1 it
   is under even the large-text bar, so there is no size at which the light
   shade would have been usable on white. #4d4473 clears the large-text bar with
   5.8 points to spare and the stricter body bar with 4.3, so it is past AA on
   the reading a strict reviewer would take as well as on the correct one.

   The periwinkle at full strength is not lost from the page: it is still the
   Get Started button's background (black on it, 8.54:1), the ticker's dot and
   the accent under a nav link. It simply cannot be TYPE on white. */
/* AND IT IS NOW THE ARTWORK'S OWN PURPLE. Task P155-#71.

   Toby asked for this line in purple and it stays purple. What changed under
   it is the background: the periwinkle-deep #4d4473 was chosen to sit on a
   gradient built from the same five palette colours, and the opening is not
   built from those any more. Toby's sock-stripe artwork contains a purple of
   its own - #5b0e77, the ground of swatch 7 - and his brief for this task says
   in as many words that it may be used here if it reads better now the
   background has changed.

   It does, for two reasons and neither is taste. It is the deeper colour, so
   the headline gains contrast rather than losing it:

       --c-peri-deep  #4d4473  on #ffffff  =   8.79:1   (what it was)
       --ss-purple    #5b0e77  on #ffffff  =  11.85:1   (what it is)

   And it is a colour a visitor can SEE on the page a few centimetres away,
   in the purple/white swatch, so the one coloured line on the white band is
   picked out of the artwork behind it instead of being a sixth thing.

   WCAG: the h1 is Montserrat 700 at clamp(19px, 2.3vw, 27px), which is bold at
   14pt or more at every width, so it is LARGE text and its bar is 3:1. At
   11.85:1 it clears that by 8.85 and clears the stricter 4.5:1 body bar by
   7.35, so it passes on either reading.

   NOTHING ELSE MOVED TO THIS COLOUR. --ss-purple is used on this one line and
   in the wall behind it. The five palette colours are untouched and no rule
   outside the opening reads it. */
#opening-statement {
    font: 700 clamp(19px, 2.3vw, 27px)/1.32 var(--body);
    letter-spacing: -0.01em; margin: 0; max-width: 20ch; color: var(--ss-purple);
}
.opening-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 28px; }
/* Black on white, 21:1. The 14px of padding it used to carry was there to give
   its own stripe something to be; it has no stripe of its own any more, so the
   space above it is one margin and nothing else. */
.opening-note { margin: 30px 0 0; padding: 0; font: 700 11.5px/1 var(--alt); letter-spacing: .16em; text-transform: uppercase; color: var(--ink); }

@media (max-width: 860px) { #opening { padding: 36px 0; } }

/* SHORT SCREENS: A LAPTOP AT 650px, A PHONE ON ITS SIDE. Task P155-#66.
   The opening has to fill the screen without its own copy outgrowing it, and
   the 150px figure is most of the height. These trim the padding and let the
   figure scale against the VIEWPORT instead of the text column, so the whole
   band — counter, headline, buttons and MOQ line — still fits above the fold
   on a short laptop rather than pushing the MOQ line under it. Nothing here
   changes the layout at a normal height; min-height still means a screenful
   that is genuinely too short just scrolls. */
@media (max-height: 780px) {
    #opening { padding: 28px 0; }
    #counter-value { font-size: clamp(56px, 12vh, 150px); }
    /* The band's own padding is the white above the counter and below the MOQ
       line; on a short screen it is the cheapest height on the page to spend,
       because losing it costs white rather than type. What it buys back is the
       figure staying legible: 12vh rather than 13.5vw on this branch. */
    .opening-white { padding: 16px 0 14px; }
    #counter-block { margin-bottom: 18px; }
    .opening-actions { margin-top: 20px; }
    .opening-note { margin-top: 20px; }
}

/* =========================================================================
   4. SECTION FURNITURE

   Sections are separated by flat colour and by space. `.dark` is pure black
   and is the page's structural weight — the live site uses exactly this for
   "Why Socks?", the invitation and the contact block.
   ========================================================================= */
section.band { padding: 96px 0; }
section.band.alt { background: var(--paper-alt); }
section.band.dark { background: var(--ink); color: var(--on-dark); }
section.band.dark h2, section.band.dark h3 { color: var(--on-dark); }
section.band.dark p { color: var(--on-dark-mute); }
section.band.dark .eyebrow { color: var(--accent); }
section.band.dark a { color: var(--on-dark); }

h2 { font-size: clamp(34px, 5.2vw, 58px); }

/* The section heads are LEFT-ALIGNED and set to a measure, not centred over
   the full width. Centred everything was one of the things that made the old
   page read as a template. */
.band-head { max-width: 640px; margin-bottom: 46px; }
.band-head p { color: var(--ink-mute); margin: 18px 0 0; font-size: 17px; }
section.band.dark .band-head p { color: var(--on-dark-mute); }
@media (max-width: 860px) { section.band { padding: 60px 0; } }

/* =========================================================================
   5. OUR PROCESS — asymmetric on purpose.

   The live site sets its process as a narrow column of three bold lines with
   a picture beside it, not as three equal cards in a row. The three-up grid
   of rounded cards with numbered circles that used to be here is the single
   most generic thing a landing page can do, and it is gone. What replaces it
   is a 1.35 / 1 split: the steps on the left as a rule-separated list, and one
   flat black panel on the right carrying the one commercial fact the live site
   puts in that spot — MOQ 100 Pairs — and the button.
   ========================================================================= */
.process-grid { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); gap: 64px; align-items: start; }

.steps { border-top: 1px solid var(--line); }
.step {
    display: grid; grid-template-columns: 46px 1fr; gap: 12px;
    padding: 26px 0; border-bottom: 1px solid var(--line);
}
/* A number set in the display face, not a badge inside a coloured circle. */
.step .n { font: 700 15px/1.35 var(--alt); letter-spacing: .1em; color: var(--ink-mute); }
.step p { margin: 0; font: 700 clamp(17px, 1.8vw, 21px)/1.4 var(--alt); letter-spacing: -0.01em; color: var(--ink); }

.moq {
    background: var(--ink); color: var(--on-dark);
    padding: 44px 38px 40px;
}
.moq .moq-fig { font: 700 clamp(40px, 5vw, 62px)/1 var(--display); letter-spacing: -0.03em; margin: 0; }
.moq .moq-cap { font: 700 12px/1.4 var(--alt); letter-spacing: .18em; text-transform: uppercase; color: var(--accent); margin: 0 0 14px; }
.moq p.moq-note { color: var(--on-dark-mute); font-size: 15px; margin: 16px 0 28px; }
.moq .btn:hover { background: var(--on-dark); border-color: var(--on-dark); color: #000; }
@media (max-width: 900px) {
    .process-grid { grid-template-columns: 1fr; gap: 40px; }
}

/* =========================================================================
   6. WHY SOCKS? — the black band, in the live site's own words.
   Two columns of running text, not three feature cards.
   ========================================================================= */
/* ---------------------------------------------------------------------------
   "WHY SOCKS?" IS A TWO-UP NOW. Task P155-#71. Toby, 22 Sep 2026: "put a
   picture next to why socks".

   THE COPY KEEPS ITS COLUMN, THE PICTURE TAKES THE OTHER. `.why-two-up` is a
   two-track grid: the text track is flexible with a 420px floor, the picture
   track is capped at 520px so the photograph never grows into the dominant
   half of a section that is fundamentally an argument in prose. `align-items:
   center` hangs the picture against the middle of the copy rather than the top
   of it, because the copy is the taller of the two at every width.

   `.why-cols` DROPS TO ONE COLUMN. It was `columns: 2` across a 900px measure,
   which gave two 420px columns. Inside a track that is now around 520px wide
   those two become 230px each - about 28 characters, which is a newspaper
   classified rather than body copy. One column at 520px is 62 characters,
   which is inside the 45-75 that is comfortable to read. No word was cut to
   achieve this and none needed to be; the section is taller than it was and
   that is the correct trade.

   BELOW 980px IT STACKS, COPY FIRST. The argument is the point of the section
   and the photograph supports it, so on a phone the reader meets the words
   first and the picture underneath. `.why-cols` stays at one column there too -
   it already did below 760px - so the stacked version is one continuous
   measure with a picture under it.

   THE BAND IS UNTOUCHED. `.band.deep` still paints it, the heading, the type
   colours, the 56px mark and the email rule are all exactly as P155-#70 left
   them. This block adds a grid and nothing else. */
.why-two-up {
    display: grid;
    grid-template-columns: minmax(420px, 1fr) minmax(0, 520px);
    gap: 56px;
    align-items: center;
}
/* ---------------------------------------------------------------------------
   PHOTOGRAPH SLOTS, PLURAL. Task P155-#75. Toby, 22 Sep 2026: "Add photo slots
   in next to the text in the 'why socks?' Section."

   P155-#71 put ONE picture in this column. This is the same column holding a
   DELIBERATE GROUP, and the shape of the group was chosen against the shape of
   the text rather than picked off a list:

     the copy beside it     two paragraphs plus the email line, about 440px
                            tall at the 520px desktop measure
     three in one column    3 x 260px + gaps = about 810px. That stops being a
                            picture beside an argument and becomes a gallery
                            with a caption, and it would have made the section
                            nearly twice as tall as the words in it.
     a lead over a pair     260px + 14px + 169px = about 443px. The pictures
                            start where the words start and finish where they
                            finish, which is what makes it read as one block
                            rather than as two things that happen to be level.

   WHAT EACH COUNT DOES, and `data-count` on the container is what selects it -
   set in the HTML for the shipped three and rewritten by site.js when Toby's
   list replaces them:

     1   one photograph across the whole track, 2:1
     2   two side by side, 3:2 each
     3   a 2:1 lead across the track with two 3:2 under it   <- the shipped case
     4   a 2x2 grid, 3:2 each

   Four is the ceiling. A fifth picture in a 520px track is a thumbnail, and
   site.js stops reading the list at four rather than shrinking the grid.

   `object-fit: cover` AND NOT `contain`, which is the opposite of the choice
   the design gallery below makes, and the difference is what is in the frame.
   A design RENDER cropped is a design misrepresented - the crest is at the
   cuff and the cuff is the first thing cover throws away. A PHOTOGRAPH of an
   order is a scene, it survives a crop, and every photograph strip this site
   has ever had has used cover for that reason. The alternative here is a grid
   whose rows are different heights depending on what Toby uploaded, which is
   the ragged stack the arrangement above exists to avoid.

   THE BORDER IS UNCHANGED from #71: a hairline in the page's tint rather than
   a frame, because on black an unbordered photograph floats and a heavy border
   would be the highlighter effect P155-#70 spent a whole task removing.
   --------------------------------------------------------------------------- */
.why-figures {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 14px;
}
.why-figures[data-count="1"] { grid-template-columns: minmax(0, 1fr); }
.why-figure { margin: 0; }
.why-figure img {
    display: block; width: 100%; height: 100%;
    aspect-ratio: 3 / 2;
    object-fit: cover;
    /* A hairline in the page's tint rather than a frame: on black an
       unbordered photograph floats, and a heavy border would be the
       highlighter effect P155-#70 spent a whole task removing. */
    border: 1px solid var(--hue-tint);
}
/* THE LEAD PHOTOGRAPH: the whole track, and 2:1 because that is the shape the
   three photographs in this repository are actually cut to, so it crops none
   of them. It exists at one picture and at three, and not at two or four -
   with two, a full-width lead would leave a single half-width picture stranded
   on the row under it, and with four the 2x2 is the point. */
.why-figures[data-count="1"] .why-figure:first-child,
.why-figures[data-count="3"] .why-figure:first-child { grid-column: 1 / -1; }
.why-figures[data-count="1"] .why-figure:first-child img,
.why-figures[data-count="3"] .why-figure:first-child img { aspect-ratio: 2 / 1; }

@media (max-width: 980px) {
    .why-two-up { grid-template-columns: 1fr; gap: 34px; }
    .why-figures { order: 2; }
}
/* ON A PHONE THE GROUP BECOMES ONE COLUMN OF WIDE PICTURES. Two columns across
   334px of measure gives each photograph about 160px, which is a contact sheet
   rather than a picture of a sock. One column at 2:1 gives each of them 167px
   of height on the same screen and all three are legible; the section gets
   taller, which is what a phone is for. */
@media (max-width: 560px) {
    .why-figures { grid-template-columns: minmax(0, 1fr); }
    .why-figures .why-figure { grid-column: 1 / -1; }
    .why-figures .why-figure img { aspect-ratio: 2 / 1; }
}

.why-cols { columns: 1; column-gap: 56px; max-width: 900px; }
.why-cols p { margin: 0 0 20px; break-inside: avoid; font-size: 16.5px; }
.why-mail { margin-top: 34px; }
.why-mail a { font: 700 15px/1 var(--alt); letter-spacing: .04em; color: var(--on-dark); text-decoration: none; border-bottom: 2px solid var(--accent); padding-bottom: 4px; }
@media (max-width: 760px) { .why-cols { columns: 1; } }

/* =========================================================================
   7. THE CLIENT LOGO WALL — unbounded.

   TASK P155-#25, TOBY'S AMENDMENT: "remove the holders for the logos just have
   them 'loose' (unbounded)". Every trace of the tile is gone — the 196x126 box,
   the white ground, the border, the radius, the padding, and the hover state
   that used to add a border colour, a shadow and a 2px lift. A `.wall-logo` is
   a <button>, so its own user-agent styling has to be reset explicitly or the
   browser draws the holder back.

   WHY THIS SECTION IS ON WHITE AND NOT ON BLACK LIKE THE LIVE SITE. The live
   site puts these logos on a black band — and it can, because it gives each
   one a white tile to sit in. Every client logo we hold is an OPAQUE image with
   a white background (checked: all six are RGB with no alpha). Take the tile
   away and put them on black and each logo paints its own white rectangle,
   which is the holder Toby asked to remove, re-drawn by the image file. On
   white the white ground is invisible and the marks really are loose. That is
   the one place this page departs from the live composition, and it departs in
   order to obey the instruction.

   WHAT REPLACES THE TILE AS A SIZING MECHANISM. A fixed box was flattening
   wildly different aspect ratios — the OKS wordmark is 2.61:1, the Pony Club
   roundel is 1:1, the Tonbridge crest is 0.69:1. Sized naively they would look
   like a jumble. site.js sets `--logo-h` per logo from the image's own
   proportions on an optical curve (see renderWall); the CSS below only supplies
   the fallback height and the ceilings. Spacing is a single fixed gap, so the
   rhythm stays even while the widths do not.
   ========================================================================= */
#wall-viewport { overflow: hidden; position: relative; padding: 8px 0; }
/* The strip fades out at both edges so a moving logo leaves the frame instead
   of being chopped by it. Not applied to the static row, which has real ends. */
#wall-viewport[data-mode="scroll"] {
    mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}

/* ONE ROW OR TWO — TOBY'S SETTING, IN EITHER MODE (task P155-#26).
   #wall-rows holds one .wall-track per row. Each track holds one .wall-run of
   logos when the wall is still, and as many identical runs as it takes to
   cover the viewport when it is moving; site.js measures a run and repeats it,
   which is what lets three logos scroll as convincingly as thirty. The vertical
   gap between two rows is deliberately smaller than the 64px horizontal one:
   equal gaps read as a grid, and this is two rows of loose marks. */
/* P155-#75: 30 -> 44 and 64 -> 96, the same x1.5 the logos themselves took.
   The vertical gap stays deliberately smaller than the horizontal one for the
   reason given above: equal gaps read as a grid. */
#wall-rows { display: grid; gap: 44px; }
.wall-track { display: flex; align-items: center; gap: 96px; }
.wall-run   { display: flex; align-items: center; gap: 96px; flex: 0 0 auto; }

/* STILL. Left-aligned TO THE MEASURE, which means the same 1180px column and
   the same 28px gutter as `.wrap`: a still row starts where "Our Clients"
   starts, because a row with real ends has to line up with the type above it.
   The scrolling row cannot and must not — it runs off both edges under the
   mask, which is why the measure is applied to the static mode only. */
#wall-viewport[data-mode="static"] #wall-rows {
    max-width: var(--wrap); margin: 0 auto; padding: 0 28px;
}
/* A STILL ROW FILLS THE MEASURE. Three big logos at a fixed 64px gap occupy
   about half of an 1180px column and leave the other half blank, which reads as
   a row that failed to load rather than a row of three. `space-between` spends
   the slack on the gaps instead: the first logo still starts where the heading
   starts (this is not centring, which #25 removed) and the last one finishes at
   the far edge of the same column. The 64px gap stays as the MINIMUM. */
#wall-viewport[data-mode="static"] .wall-run {
    flex-wrap: wrap; width: 100%;
    justify-content: space-between;
    /* 60px, not the marquee's 96px, and it is a FLOOR rather than the spacing:
       space-between spends the leftover on the gaps, so six logos on an 1180px
       measure still sit about 63px apart. The floor has to be low enough that
       the row FITS, because at 64px the six we hold came to 1131px against
       1124px of column and the last one wrapped onto a line of its own — one
       logo stranded under five, which is the same fault the two-row split is
       written to avoid. A row genuinely too long for the page still wraps;
       that is what the two-row setting is for. */
    gap: 44px 60px;
}

/* MOVING. The slide is the MEASURED width of one run plus one gap, not -50% of
   the track: a percentage only loops seamlessly when the run count is exactly
   two, and it no longer is. */
#wall-viewport[data-mode="scroll"] .wall-track { width: max-content; }
/* THE ANIMATION WAITS FOR THE MEASUREMENT. --wall-shift and the repeat runs
   are both set by fit() in site.js once the logo images have decoded, and
   `data-fitted` is the flag saying that has happened. Animating before it does
   means animating against the 50% fallback over a single un-repeated run: the
   forward row merely started a fraction early, but the REVERSED row begins at
   translateX(-shift), so it opened half a run to the left of where it belonged
   and snapped back the moment the real shift arrived - measured at one frame of
   400px on a 1400px viewport, which is exactly the "jump on load" this task was
   told to avoid. Gating the animation on the flag removes it: the row's first
   animated frame is its first correct one. */
#wall-viewport[data-mode="scroll"][data-fitted="1"] .wall-track {
    animation: wall-run var(--wall-duration, 20s) linear infinite;
}

/* OPPOSITE DIRECTIONS - Toby, 10 Sep 2026 (task P155-#29).
   Every OTHER row runs the other way, so two rows travel against each other and
   a third, if the setting ever allows one, goes back to travelling with the
   first. With rows set to one there is no :nth-child(even) track and nothing
   below this line applies, which is why the one-row wall is untouched.

   HOW THE REVERSE IS DONE, AND WHY IT IS SEAMLESS. The marquee loops because
   the run is repeated and the track slides by the MEASURED width of exactly one
   run plus one gap (--wall-shift): after that slide the pattern has advanced by
   its own period, so the frame at the end of the animation is pixel-identical to
   the frame at the start. That property is symmetric - it does not care which
   way round the two ends are played - so the reverse row plays the SAME
   keyframe backwards rather than a mirrored one. It therefore starts at
   translateX(-shift) rather than at 0.

   THAT STARTING OFFSET IS THE TRAP. At -shift the track has been pulled one
   whole run to the left, so the run count that was enough to cover the viewport
   going forwards is one run short of covering it going backwards, and the
   shortfall shows as a gap at the right-hand edge on the very first frame.
   The fix is in site.js fit(): the track is built to cover the viewport PLUS
   one full step (see the copies calculation), which is the width the reversed
   row needs at its extreme. Nothing here delays or seeks the animation, so
   there is no jump on load either - the first painted frame is already a full
   row. */
#wall-viewport[data-mode="scroll"] .wall-track:nth-child(even) {
    animation-direction: reverse;
}

/* EACH ROW FREEZES ON ITS OWN. The pause used to hang off the VIEWPORT, so a
   pointer anywhere in the section stopped every row at once. It hangs off the
   TRACK now: hover row one and row one stops while row two keeps going. Keyboard
   focus is paired with hover exactly as before - :focus-within on the same
   element - so tabbing into a logo pauses that logo's row and only that row. */
#wall-viewport[data-mode="scroll"] .wall-track:hover,
#wall-viewport[data-mode="scroll"] .wall-track:focus-within { animation-play-state: paused; }
@keyframes wall-run {
    from { transform: translateX(0); }
    to   { transform: translateX(calc(-1 * var(--wall-shift, 50%))); }
}

/* NO holder. Nothing is drawn around a logo in any state. */
.wall-logo {
    flex: 0 0 auto;
    -webkit-appearance: none; appearance: none;
    background: none; background-color: transparent;
    border: 0; border-radius: 0; box-shadow: none;
    padding: 0; margin: 0;
    display: block; cursor: pointer;
    font: inherit; color: inherit; line-height: 0;
}
.wall-logo img {
    /* The height is the normalised one; the width falls where it falls. The
       ceiling stops a very wide wordmark from running away with the row.
       P155-#26 "displayed bigger": the fallback height went 72px -> 104px and
       the width ceiling 230px -> 330px, in step with LOGO_BASE 80 -> 112 in
       site.js, which is where the real per-logo height comes from.

       P155-#75, Toby 22 Sep 2026: "make the logos on the 'Our Clients'
       Carousel 50% larger". x1.5 applied to every number that sets a logo's
       size, in one pass, so the optical curve P155-#25 arrived at survives it
       exactly:

           .wall-logo img height        104px -> 156px   (this rule)
           .wall-logo img max-width     330px -> 495px   (this rule)
           site.js LOGO_BASE            112   -> 168
           site.js LOGO_MIN              64   ->  96
           site.js LOGO_MAX             152   -> 228

       THE MIN AND THE MAX HAD TO MOVE TOO, and leaving them would have been
       the bug. LOGO_MAX was 152px - already below the new 156px fallback - so
       every tall logo would have been clamped back to roughly its old size and
       only the short ones would have grown. Three numbers make the size; five
       make it correctly.

       THE GAPS MOVED WITH THEM. A 50% bigger mark at the old 64px spacing is a
       denser row, not a bigger one, so the horizontal gap went 64 -> 96 and
       the gap between two rows 30 -> 44 (see .wall-track and #wall-rows
       above). Same rhythm, one scale. */
    height: var(--logo-h, 156px);
    width: auto; max-width: 495px;
    object-fit: contain;
    /* Greyscale AND faded, but NOT reduced in contrast: a client logo is
       usually dark marks on a white ground, and contrast() pulls white towards
       mid-grey, which would paint a visible grey box around every logo — the
       holder again, this time drawn by a filter. Opacity leaves white alone. */
    filter: grayscale(1) opacity(.62);
    transition: filter .3s ease;
}
.wall-logo .wall-initials {
    display: block;
    font: 700 clamp(22px, 2.4vw, 30px)/1 var(--display); letter-spacing: -0.02em;
    color: var(--ink);
    filter: grayscale(1) opacity(.62);
    transition: filter .3s ease;
}
/* HOVER IS THE COLOUR AND THE PAUSE, AND NOTHING ELSE. No border, no shadow,
   no transform. The greyscale-to-colour change carries the whole interaction. */
.wall-logo:hover img, .wall-logo:hover .wall-initials,
.wall-logo:focus-visible img, .wall-logo:focus-visible .wall-initials { filter: none; }
.wall-logo:hover, .wall-logo:focus { background: none; border: 0; box-shadow: none; transform: none; }
/* Keyboard focus still needs to be visible — that is an accessibility floor,
   not a hover decoration, and it never appears for a mouse user. */
.wall-logo:focus-visible { outline: 2px solid var(--ink); outline-offset: 10px; }

.wall-hint { margin: 34px 0 0; font: 700 11.5px/1 var(--alt); letter-spacing: .16em; text-transform: uppercase; color: var(--ink-mute); }
/* ON A PHONE the logos are still bigger than they were, but not desktop-sized:
   ONE UNIFORM SCALE FACTOR, never a second curve, so the optical balance #25
   arrived at is the same balance at both widths. The old 150px width ceiling
   was cropping the OKS wordmark's height before its own cap did, which is the
   other half of why the wall looked small on a phone. Two rows are kept on a
   phone rather than collapsed: each row wraps, so it degrades into short lines
   of large logos instead of one long scroll. */
@media (max-width: 760px) {
    .wall-track, .wall-run { gap: 46px; }
    #wall-rows { gap: 30px; }
    /* P155-#75. THE PHONE GETS A SMALLER SHARE OF THE 50%, AND THAT IS THE
       POINT OF HAVING A SINGLE SCALE FACTOR HERE. --logo-h now arrives 1.5x
       bigger from site.js, so 0.9 would have put a 228px logo on a 390px
       screen - one mark taller than the heading above it, which is a logo
       dominating a phone rather than a logo wall. 0.68 x 1.5 = 1.02, so a
       phone logo is a whisker larger than it was and the desktop takes the
       increase Toby asked for. The width ceiling is capped against the
       VIEWPORT as well as in pixels: 44vw keeps two marks on a line with the
       46px gap between them and can never overflow, whatever shape a logo
       Toby adds turns out to be. */
    .wall-logo img { height: calc(var(--logo-h, 156px) * 0.68); max-width: min(300px, 44vw); }

    /* TWO STILL ROWS COLLAPSE INTO ONE FLOW ON A PHONE. Kept as two separate
       wrapping rows, three logos each, the phone gets two lines of two and two
       stranded singles - the exact fault the ceil(n/2) split exists to avoid,
       reintroduced by the narrow screen. `display: contents` drops the two
       wrappers out of the box tree so every logo becomes an item of one
       wrapping flow, which lands two per line with none left over. It applies
       to the STILL wall only: two marquees on a phone are two marquees, and
       merging those would mean merging their animations. */
    #wall-viewport[data-mode="static"] #wall-rows {
        display: flex; flex-wrap: wrap; justify-content: space-between;
        gap: 26px 40px;
    }
    #wall-viewport[data-mode="static"] .wall-track,
    #wall-viewport[data-mode="static"] .wall-run { display: contents; }
}

/* =========================================================================
   8. TESTIMONIALS
   Rules and space, not boxes. The 62px curly quotation mark at 22% opacity
   that used to head each card is a stock-template flourish and is gone.
   ========================================================================= */
#testimonial-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 0; border-top: 2px solid var(--ink); }
.quote { margin: 0; padding: 34px 34px 30px 0; border-right: 1px solid var(--line); display: flex; flex-direction: column; }
.quote:last-child { border-right: 0; }
.quote + .quote { padding-left: 34px; }
.quote blockquote { margin: 0 0 24px; font: 400 18px/1.5 var(--body); color: var(--ink-soft); flex: 1; }
.quote figcaption { font-size: 13px; line-height: 1.5; }
.quote .who { font: 700 13px/1.4 var(--alt); letter-spacing: .06em; text-transform: uppercase; color: var(--ink); display: block; }
.quote .role { color: var(--ink-mute); }
@media (max-width: 760px) {
    #testimonial-list { grid-template-columns: 1fr; }
    .quote { border-right: 0; border-bottom: 1px solid var(--line); padding: 28px 0; }
    .quote + .quote { padding-left: 0; }
}

/* =========================================================================
   9. THE INVITATION AND THE CONTACT BLOCK
   Both on black, in the live site's order: the invitation, then Contact Us:.
   ========================================================================= */
/* THE INVITATION IS STYLED IN SECTION 14 NOW. Task P155-#69. What stood here
   was `#invite { padding-bottom: 0 }` and a 60px headline - the two rules that
   made this block a full-bleed periwinkle band with its own heading running off
   the bottom edge into the black contact band below. Both are gone; the block
   is a framed card on paper and all three pages that carry it share one rule
   set, which is why it is keyed on `.band.invite` and not on this id. */
#contact { padding-top: 60px; }
#contact h2 { font-size: clamp(26px, 3vw, 34px); }
.contact-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 30px; }
#contact .btn.ghost { color: var(--on-dark); border-color: var(--dark-line); background: transparent; }
#contact .btn.ghost:hover { background: var(--on-dark); color: #000; border-color: var(--on-dark); }
.contact-private { margin: 34px 0 0; color: var(--on-dark-mute); font-size: 15px; }
.contact-private a { color: var(--on-dark); text-decoration: underline; text-underline-offset: 4px; }

/* -------------------------------------------------------------------------
   9b. THE SELLING-POINT RIBBON, AND THE FOOTER IT SITS ON. Task P155-#74.

   Toby, 22 Sep 2026: "move the ticker tape to the bottom of the website (not
   fixed) but just as a ribbon above the footer, also improve the footer".

   THE RIBBON IS IN NORMAL FLOW AND THAT IS THE POINT. It used to be inside
   #topbar, which is `position: sticky`, so it rode down the screen for the
   whole scroll. Nothing here gives it a position or a z-index - `position:
   static` is written out rather than left to the default so that the one
   thing this task was about is stated where somebody would look for it. It
   scrolls up with the page and is read when the reader arrives at the bottom.

   IT TAKES THE PAGE'S COLOUR, WHICH IS THE ONLY WAY IT CAN BE SEEN. The
   footer is black and the band above it on the home page is black too, so a
   black ribbon between them would not read as a ribbon at all - it would read
   as a footer that had grown 40px. It uses --hue/--hue-on, the SAME pair task
   P155-#73 introduced for the solid title blocks, so it is the page's own
   colour and its type pairing is the token rather than a judgement taken six
   times. The measured ratios of that pair, from section 12:

       peri  #ab96ff + #000000   8.54:1      peach #faaa63 + #000000  10.99:1
       corn  #93c3ff + #000000  11.49:1      sage  #97b49d + #000000   9.33:1
       olive #4d6001 + #ffffff   7.03:1      ochre #e2aa00 + #000000   9.98:1

   Every one clears WCAG AA for body text at 4.5:1, and the ribbon's type is
   11.5px bold, which is the size that needs the 4.5 rather than the 3.0.

   THE SEPARATOR DOT FOLLOWS THE TYPE. It was --accent, the periwinkle, which
   is invisible on a periwinkle ground and wrong on the other five. It is
   `currentColor` now: it is decoration, it carries no meaning, and it can
   never be a colour the ground does not support.
   ------------------------------------------------------------------------- */
#ticker {
    position: static;          /* NOT sticky and NOT fixed. See above. */
    background: var(--hue);
    color: var(--hue-on);
    overflow: hidden;          /* the marquee never widens the document */
}
/* The empty state, spelled out: with nothing in site/ticker the element keeps
   its `hidden` attribute and there is no bar at all. A div is display:none
   when hidden by default; this says so explicitly so that no later rule which
   sets a display on #ticker can quietly resurrect an empty ribbon. */
#ticker[hidden] { display: none; }

.ticker-track {
    display: flex; width: max-content;
    animation: ticker-run var(--ticker-duration, 40s) linear infinite;
}
/* Hover pauses it — a selling point you want to read should stop for you.
   :focus-within is the keyboard's half of the same behaviour. Unchanged. */
#ticker:hover .ticker-track,
#ticker:focus-within .ticker-track { animation-play-state: paused; }

.ticker-item {
    display: flex; align-items: center;
    padding: 11px 0; white-space: nowrap;
    font: 700 11.5px/1 var(--alt); letter-spacing: .14em; text-transform: uppercase;
}
.ticker-item::after {
    content: ""; width: 4px; height: 4px;
    background: currentColor; flex: 0 0 auto; margin: 0 22px;
}

/* -------------------------------------------------------------------------
   THE FOOTER. Task P155-#74 rebuilt it from one flat row into four columns
   over a legal line.

   WHAT IT WAS: `.foot-in` was a wrapping flex row holding a logo, a copyright
   sentence and six links, all `align-items: center`, all the same size, in no
   order that meant anything - and the six were not the same six on every
   page, because the footer was hand-copied into seventeen files. It is
   generated by tools/apply-footer.js now, so there is one of it.

   WHAT IT IS: a grid. The stacked mark, then the five category pages, then the
   rest of the site, then the two ways to reach a human - and underneath, on
   its own line and divided by a rule, the copyright and the two things a
   reader goes to a footer to find on purpose: the privacy notice and the
   cookie control. Those two are deliberately NOT in a navigation column; they
   are not pages you browse to, and burying a consent control among "Gallery"
   and "Blog" is how it stops being found.

   CONTRAST, MEASURED, ON --ink #000000:

       column heading   --hue-tint    peri 15.30  corn 16.85  olive 11.41
                                      peach 16.53 sage 15.89  ochre 15.90
       every link       --on-dark      #ffffff  21.00:1
       legal line       --on-dark-mute #b3b3b3  10.02:1
       the two rules    --dark-line    #333333   1.66:1  - decoration, no text

   The lowest text pair on any page of this site is 10.02:1, against AA's 4.5.
   --hue-tint is used for the headings rather than --hue because olive at full
   strength is 2.99:1 on black and could not carry a letter.
   ------------------------------------------------------------------------- */
footer.site-foot {
    background: var(--ink);
    color: var(--on-dark-mute);
    font-size: 13.5px;
    padding: 0;                /* the two rows below own their own padding */
}

.foot-in {
    display: grid;
    grid-template-columns: auto repeat(3, minmax(150px, 1fr));
    gap: 36px 44px;
    align-items: start;
    padding-top: 54px;
    padding-bottom: 44px;
}

/* THE STACKED MARK STAYS, and it is no longer a 26px afterthought at the end
   of a sentence. filter: invert(1) is how it was already put on black. */
.foot-brand img { height: 64px; width: auto; filter: invert(1); }

.foot-col { min-width: 0; }

.foot-head {
    font: 700 12px/1 var(--alt);
    letter-spacing: .18em; text-transform: uppercase;
    color: var(--hue-tint);
    margin: 0 0 18px;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--dark-line);
}

.foot-list { list-style: none; margin: 0; padding: 0; }
.foot-list li + li { margin-top: 10px; }

.foot-list a,
.foot-legal-links a { color: var(--on-dark); text-decoration: none; }
.foot-list a:hover, .foot-list a:focus-visible,
.foot-legal-links a:hover, .foot-legal-links a:focus-visible {
    text-decoration: underline; text-underline-offset: 4px;
}

.foot-legal {
    display: flex; flex-wrap: wrap;
    align-items: center; justify-content: space-between;
    gap: 10px 26px;
    padding-top: 22px;
    padding-bottom: 34px;
    border-top: 1px solid var(--dark-line);
}
.foot-legal-line { margin: 0; color: var(--on-dark-mute); font-size: 12.5px; }
.foot-legal-links {
    list-style: none; margin: 0; padding: 0;
    display: flex; flex-wrap: wrap; gap: 6px 26px;
    font-size: 12.5px;
}

/* ON A PHONE IT STACKS RATHER THAN SQUASHES. The brand takes the full width
   and the link columns go three across, then two. Two columns of short labels
   at 390px is 155px a column, which fits every label in this footer on one
   line; one column would be a 500px-tall wall of links. */
@media (max-width: 980px) {
    .foot-in {
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: 32px 26px;
        padding-top: 42px;
        padding-bottom: 36px;
    }
    .foot-brand { grid-column: 1 / -1; }
    .foot-brand img { height: 54px; }
}
@media (max-width: 560px) {
    .foot-in { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    /* The contact column takes the full width on the last row rather than half
       of it: at 390px a half column is 155px and
       "enquiries@societalsocks.com" is 27 characters, so it would wrap mid
       address. The two link columns are short labels and do not. */
    .foot-col:last-of-type { grid-column: 1 / -1; }
    .foot-legal { align-items: flex-start; }
}

/* =========================================================================
   10. THE CASE-STUDY MODAL — square, flat, no shadow.
   ========================================================================= */
#modal-backdrop {
    position: fixed; inset: 0; z-index: 90;
    background: rgba(0,0,0,.72);
    display: flex; align-items: center; justify-content: center; padding: 24px;
}
#modal-backdrop[hidden] { display: none; }
.modal-card {
    background: var(--paper); border-radius: 0; width: min(760px, 100%);
    max-height: min(86vh, 900px); overflow-y: auto;
    position: relative; border: 2px solid var(--ink);
}
.modal-top { display: flex; align-items: center; gap: 24px; padding: 34px 36px 0; }
.modal-top .modal-logo { height: 54px; width: auto; max-width: 170px; object-fit: contain; }
.modal-top h3 { font-size: 28px; }
.modal-top .modal-summary { color: var(--ink-mute); font-size: 14px; margin: 8px 0 0; }
.modal-body { padding: 26px 36px 34px; }
.modal-body p { margin: 0 0 16px; color: var(--ink-soft); font-size: 16px; }
.modal-body p:last-child { margin-bottom: 0; }
.modal-media { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 14px; margin-top: 26px; }
.modal-media figure { margin: 0; }
.modal-media img { width: 100%; height: 230px; object-fit: cover; border-radius: 0; }
/* A single picture gets more room than one of four. */
.modal-media:has(figure:only-child) img { height: 320px; }
.modal-media figcaption { font-size: 12.5px; color: var(--ink-mute); margin-top: 8px; }
.modal-close {
    position: absolute; top: 14px; right: 14px; width: 36px; height: 36px;
    border-radius: 0; border: 0; background: var(--ink);
    color: var(--on-dark); font: 400 20px/1 var(--body); cursor: pointer;
}
.modal-close:hover { background: var(--accent); color: #000; }
@media (max-width: 620px) {
    .modal-top { flex-direction: column; align-items: flex-start; gap: 16px; padding: 28px 22px 0; }
    .modal-body { padding: 20px 22px 28px; }
}

/* =========================================================================
   11. MOTION
   Two marquees on one page is a lot of movement for someone who has asked
   their operating system for less of it. Honour that: everything still shows,
   nothing moves.
   ========================================================================= */
@media (prefers-reduced-motion: reduce) {
    /* The wall is handled in site.js, which builds the still version outright
       rather than leaving the repeat runs in the DOM with the animation paused —
       paused runs would show every logo several times over. This block is the
       ticker, plus a belt-and-braces stop in case a wall is ever rendered in
       scrolling mode by some other route. */
    .ticker-track, .wall-track { animation: none !important; }
    #wall-viewport[data-mode="scroll"] .wall-track { width: auto; }
    #wall-viewport[data-mode="scroll"] .wall-run { flex-wrap: wrap; row-gap: 30px; }
    #wall-viewport[data-mode="scroll"] .wall-run ~ .wall-run { display: none; }
    #wall-viewport[data-mode="scroll"] { mask-image: none; -webkit-mask-image: none; }
    * { scroll-behavior: auto !important; }
}

/* =========================================================================
   THE CLIENT WALL BEFORE JAVASCRIPT, task P155-#44.

   tools/build-site.js writes the client names into #wall-rows as a plain list
   so that a crawler - and a visitor on a slow connection - reads WHO the
   clients are without waiting for a script and a database round trip. The
   logos cannot go in the HTML: they are base64 images in the database and come
   to about 300KB. site.js clears this list the moment it has them.

   Styled to look deliberate rather than like a stranded <ul>, because on a
   slow phone it is on screen for a real fraction of a second.
   ========================================================================= */
.wall-static {
    list-style: none; margin: 0; padding: 0 28px;
    display: flex; flex-wrap: wrap; justify-content: center;
    gap: 14px 36px; max-width: var(--wrap); margin: 0 auto;
}
.wall-static li {
    font: 700 13px/1.3 var(--alt);
    letter-spacing: .1em; text-transform: uppercase; color: var(--ink-mute);
}

/* =========================================================================
   12. WHO WE MAKE FOR — the five commercial landing pages. Task P155-#52.

   A list, not a card grid. Each row is one link whose whole rectangle is the
   target: a segment name set in the label face, then one line of plain text
   saying what that page answers. Rule-separated, the same way the process
   steps are, because a row of five rounded cards is the single most generic
   thing a landing page can do and this file has said so once already.
   ========================================================================= */
.seg-links { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--line); }
.seg-links li { border-bottom: 1px solid var(--line); }
.seg-links a {
    display: grid; grid-template-columns: minmax(200px, 270px) 1fr; gap: 18px;
    align-items: baseline; padding: 24px 0;
    text-decoration: none; color: var(--ink-soft); font-size: 16.5px;
    /* THE PADDING ANIMATES, THE COLOUR DOES NOT, AND THAT IS A CONTRAST
       DECISION. Task P155-#68. An ACTIVE row fills with its destination page's
       hue, and the two olive rows invert to WHITE type when they do, because
       black on #4d6001 is 2.99:1. If the background crossfaded over 150ms while
       the type jumped, the row would spend those 150ms with near-white text on
       a colour halfway between white and #4d6001 - a real unreadable moment,
       not a theoretical one. Swapping both instantly means there is no
       in-between state to measure. */
    transition: padding .15s;
}
.seg-links a span {
    font: 700 clamp(18px, 2vw, 22px)/1.3 var(--alt);
    letter-spacing: -0.01em; color: var(--ink);
}
/* "For more information see how it works" under the list. Toby, 23 Sep 2026:
   the link is underlined like the enquiries address in Why Socks? - the same
   .why-mail rule, with ink in place of --on-dark because this band is white. */
.seg-more { margin: 26px 0 0; color: var(--ink-soft); font-size: 16.5px; }
.seg-more a { font: 700 15px/1 var(--alt); letter-spacing: .04em; color: var(--ink); text-decoration: none; border-bottom: 2px solid var(--accent); padding-bottom: 4px; }
/* The hover fill is set per row in section 12 - it is the destination page's
   own colour now, not the periwinkle this rule used to paint on all six. */
@media (max-width: 720px) {
    .seg-links a { grid-template-columns: 1fr; gap: 6px; }
}

/* =========================================================================
   PICTURES ON THE HOME PAGE. Task P155-#54.

   The page carried no photograph at all before this: nine logos and otherwise
   type. The block below fixes that, drawing on images that ALREADY EXISTED in
   the database — the client photographs in site/client_media. Nothing was
   generated.

   THERE USED TO BE A SECOND BLOCK HERE. It dropped the cover image of the
   "How a crest goes on a sock" post into the process band. Toby, 15 Sep 2026:
   it was a haphazard link to a blog post, and it went with task P155-#58 —
   the figure, the build marker that filled it, the build code that resolved it
   and its rules, all four. The post, its cover and every link to the blog are
   untouched; only that one homepage block is gone.

   EVERY RULE BELOW IS ABOUT NOT MOVING THE PAGE. Each image is written by the
   build with real width and height attributes, and each box here also carries
   an aspect-ratio, so the space an image will occupy is reserved before a byte
   of it arrives. Both blocks are below the fold and both lazy-load, so on a
   slow connection the page finishes its text first and the pictures arrive
   into holes that are already the right shape.
   ========================================================================= */

/* ---- "Some of what we've made" ------------------------------------------ */
#made .band-head p { max-width: 50ch; color: var(--ink-soft); }

.made-grid {
    display: grid; gap: 26px; margin-top: 30px;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
.made-grid figure { margin: 0; }

/* The whole figure is the link, so the caption is part of the target rather
   than a stranded line of text underneath one. */
.made-grid a { display: block; text-decoration: none; color: inherit; }
.made-grid img {
    width: 100%; height: auto;
    aspect-ratio: 4 / 3;               /* all three sources are 1280x960 */
    object-fit: cover;
    background: var(--line);           /* the reserved box, before it loads */
    transition: opacity .18s ease;
}
.made-grid a:hover img, .made-grid a:focus-visible img { opacity: .88; }
.made-grid figcaption { margin: 12px 0 0; }
.made-grid .who {
    display: block; font-family: var(--alt); font-weight: 700;
    font-size: 15px; letter-spacing: .01em;
}
.made-grid a:hover .who, .made-grid a:focus-visible .who { text-decoration: underline; }
.made-grid .cap { display: block; margin-top: 4px; font-size: 15px; color: var(--ink-mute); }

.made-more { margin: 32px 0 0; font-family: var(--alt); font-weight: 700; font-size: 15px; }

@media (prefers-reduced-motion: reduce) {
    .made-grid img { transition: none; }
}

/* =========================================================================
   THE PHOTOGRAPH CAROUSEL IS GONE. Task P155-#75.

   Toby, 22 Sep 2026: "Remove the photo crousel on the first page below the
   'why socks?' Section." The band whose id was "photos" and every rule that
   dressed it - the grid, the frame, the slides, the arrows, the dots and
   the visually-hidden live region - have been deleted rather than left behind
   commented out, and renderPhotoCarousel()/wireCarousel() have gone from
   site.js with them. Nothing else on this site used any of it: the only page
   that ever carried one was index.html.

   This reverses P155-#67, which brought the carousel back at his request. He
   has now seen it in place and asked for it out; that is his call.

   THE PHOTOGRAPHS ARE NOT GONE. The same three files are the shipped slots
   beside "Why Socks?" - see `.why-figures` in section 6 above - and the list
   that fed the carousel is retired into the one that feeds those slots. The
   note over `#why` in index.html records what happened to `site/photos`.
   ========================================================================= */

/* =========================================================================
   THE SOCK DESIGN GALLERY. Task P155-#62.

   Toby: "just a section for us to add pictures in". Everything in it comes
   from site/gallery and nothing from this repository, so these rules describe
   a SHAPE and never a picture.

   PORTRAIT TILES, because a sock is portrait: every design render on the old
   site is roughly 2:3 and a landscape tile would letterbox all of them. The
   tile ground is #f8fafc rather than white because that is the exact
   background the renders were exported on — on flat white each one paints a
   faintly blue rectangle, which is the "holder" problem the logo wall note
   further up this file already describes once.

   `contain`, NOT `cover`. A design render cropped is a design misrepresented:
   the crest is at the cuff and the cuff is the first thing `cover` throws
   away. Toby uploads whatever he has and the tile letterboxes it.

   FIVE ACROSS AT FULL WIDTH, down to two on a phone, with no media query
   doing the counting — `auto-fill` with a 170px floor gets there on its own
   and keeps working whether he puts three pictures in or thirty.
   ========================================================================= */
.design-grid {
    list-style: none; margin: 0; padding: 0;
    display: grid; gap: 18px;
    grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
}
.design-grid li { margin: 0; }
.design-grid figure { margin: 0; }
.design-grid img {
    width: 100%; height: auto;
    aspect-ratio: 2 / 3;
    object-fit: contain;
    background: #f8fafc;
}
.design-grid figcaption {
    margin: 10px 0 0;
    font: 700 13px/1.4 var(--alt); letter-spacing: .01em; color: var(--ink);
}

/* =========================================================================
   12. THE COLOUR SYSTEM, AND THE SECTION PATTERNS IT PAINTS. Task P155-#64.

   Toby, 21 Sep 2026: "Add some colours! we make colourful branded socks." And,
   in the same breath: "A more colourful vibe. But dont take it too far."

   WHERE THE COLOURS CAME FROM. Not invented, and not a theme picked off a
   palette generator. Task P155-#62 opened societalsocks.com and read the
   computed `background-image` of the element behind the opening straight out
   of the browser: six radial stops, transcribed verbatim into the #opening
   block further up this file. Those six stops ARE the brand's colour, and five
   of them are the five named colours below. The sixth, the acid yellow
   rgb(214,222,72), is deliberately NOT here — see the restraint note.

       --c-olive  #4d6001   rgb( 77, 96,  1)   the gradient's top-left stop
       --c-peach  #faaa63   rgb(250,170, 99)   its top-right stop
       --c-sage   #97b49d   rgb(151,180,157)   its bottom-left stop
       --c-corn   #93c3ff   rgb(147,195,255)   its bottom-centre stop
       --c-peri   #ab96ff   the brand accent. The gradient's bottom-right stop
                            is rgb(201,198,255), one step off it; --accent in
                            brand.css is the same value and this is an alias,
                            not a second opinion.

   RESTRAINT IS A RULE IN THIS FILE, NOT A HOPE. This is a business-to-business
   site: the people reading it are bursars, club secretaries and PTA chairs
   deciding whether to spend four figures. Four things keep it sober:

     1. THE ACID YELLOW IS LEFT OUT. rgb(214,222,72) is the loudest stop in the
        gradient and the only one that cannot be made into a quiet ground. It
        survives where it belongs — inside the hero wash, at .55 opacity, mixed
        with five other colours — and it is never a flat panel.
     2. ONE HUE LEADS A PAGE. Each page sets a signature hue on <body> and the
        page is painted in that one colour's wash, tint and deep. A second hue
        appears only as a rule or a numeral. A page is never a rainbow; the
        SITE is colourful because different pages lead with different hues.
     3. NO HUE IS EVER USED AT FULL STRENGTH AS A LARGE GROUND except the
        periwinkle, which is already the brand's button colour and is the one
        the live site puts behind "Discover More" today.
     4. THE FLATNESS RULE FROM THE TOP OF THIS FILE STILL HOLDS. Nothing below
        adds a border-radius, a drop shadow or a gradient. The colour arrives
        as flat area and flat rule, which is how the live site uses it.

   THREE FORMS PER COLOUR, AND WHY THREE. A named hue on its own can only ever
   be one thing. Each gets a WASH (mixed 86% into white — a ground so quiet it
   reads as tinted paper), a TINT (62% into white — visibly the colour, still
   black text at better than 10:1) and a DEEP (55% toward black — dark enough
   to carry white text at better than 7:1, and therefore also usable as a
   coloured heading or rule on white). Every one of those pairings was computed
   with the WCAG relative-luminance formula, not judged by eye; the measured
   ratios are recorded in the task transcript and in the table below.

       ground            black   #2e2e2e   #5c5c5c        deep on white
       --c-peri-wash     18.7     12.1      5.96          --c-peri-deep   8.79
       --c-corn-wash     19.5     12.6      6.19          --c-corn-deep   7.30
       --c-olive-wash    17.0     11.0      5.43          --c-olive-deep 14.82
       --c-peach-wash    19.2     12.4      6.12          --c-peach-deep 21.00
       --c-sage-wash     19.0     12.3      6.06          --c-sage-deep   8.35

       --c-peri-tint 15.3 / 9.89 / 4.87     --c-peri  on black  8.54
       --c-corn-tint 16.9 / 10.9 / 5.36     --c-corn  on black 11.49
       --c-olive-tint 11.4 / 7.38 / 3.63    --c-olive on white  7.03
       --c-peach-tint 16.5 / 10.7 / 5.26    --c-peach on black 10.99
       --c-sage-tint 15.9 / 10.3 / 5.06     --c-sage  on black  9.33

   The lowest number anywhere in that table is 3.63, and it is --ink-mute on
   the olive TINT — which is why no rule below ever puts --ink-mute on a tint.
   Small print on a coloured ground uses --ink-soft, whose worst case is 7.38.
   ========================================================================= */
:root {
    --c-peri:  #ab96ff;
    --c-corn:  #93c3ff;
    --c-olive: #4d6001;
    --c-peach: #faaa63;
    --c-sage:  #97b49d;
    /* THE SIXTH, AND IT IS THE ONLY ONE THAT IS NOT OUT OF THE OPENING'S OWN
       GRADIENT. Task P155-#73.

       Toby, 22 Sep 2026, allocating a colour to each page: "FAQs -
       Ochre/yellow". There was no yellow in this palette to give it. The five
       above are the five stops of the live site's opening gradient and the
       sixth stop, the acid rgb(214,222,72), was deliberately left out (see the
       restraint note): it is the loudest value in the set and cannot be made
       into a quiet ground.

       SO THE OCHRE COMES OUT OF HIS SOCK ARTWORK INSTEAD, not off a palette
       generator and not from the gradient he has already been shown restraint
       about. images/sock-stripes-reference.png is the eight-swatch picture
       Toby supplied on 22 September; swatch 3, top row third across, is the
       NAVY/GOLD stripe - a 14-unit gold band on a 47-unit indigo ground. That
       gold is already in this stylesheet as --ss-gold #e2aa00 (section 4,
       where every one of the eight patterns is built), and #e2aa00 is the
       value below. The raw file samples at #dba605 in that band; Toby's brief
       named #e2aa00 and the rule at the top of section 4 is that where his hex
       and the sample differ by a shade his hex wins, so the two agree.

       The file also carries a brighter #fec700 (swatch 5, the yellow/black
       pair, bottom row first across). It is a lemon rather than an ochre and
       is 1.5:1 on white - it can be a stripe and it can never be a ground or a
       mark. #e2aa00 is the truer ochre and is the one taken.

       ONE NEW COLOUR, NOT TWO. The palette is six after this and no rule below
       introduces a seventh. */
    --c-ochre: #e2aa00;

    /* THE SEVENTH, AND THE FIRST ONE THAT IS DARK. Task P155-#76.

       Toby, 23 Sep 2026: "make gallery theme colour navy blue rather than
       orange". The Gallery has been hue-peach since P155-#69 and he has now
       looked at it and asked for navy instead, so his instruction is the one
       that stands and the reasoning recorded on gallery.html is superseded
       rather than argued with.

       IT IS NOT AN INVENTED NAVY. --ss-navy is already in this stylesheet, at
       the top of section 4, as the ground of stripe 1 in Toby's own sock
       artwork (images/sock-stripes-reference.png). The line below is an ALIAS
       of that variable and not a second opinion about what navy is, exactly as
       --c-peri aliases the brand accent. Change --ss-navy and the Gallery
       follows.

       THE PALETTE IS SEVEN NOW AND THAT IS DELIBERATE. The six-colour cap was
       written into this file by P155-#73 and it was an agent's restraint, not
       a rule of Toby's. His own six-colour rule is about the SOCK - a design
       carries six colours in total including the base - and has nothing to do
       with how many hues the website leads its pages with. Nothing was retired
       to make room; ochre still leads the FAQs and peach still leads
       Fundraising and the private shop.

       NAVY AND CORNFLOWER ARE SIX DEGREES APART IN HUE AND SIXTY-TWO IN
       LIGHTNESS, which is why they cannot be confused. navy is hsl(219,100%,
       17%) and --c-corn is hsl(213,100%,79%): the same blue family, at
       opposite ends of the tonal scale. Measured against each other they are
       8.68:1 - navy reads as near-black ink and cornflower as pale sky, and no
       visitor sees the two pages side by side in any case. The distinctness
       test P155-#69 set (unlike sage, unlike cornflower) is met on lightness
       rather than on hue angle, and it is met by a wider margin than peach met
       it: peach against navy is 8.31:1. */
    --c-navy: var(--ss-navy);

    --c-peri-wash:  #f3f0ff;  --c-peri-tint:  #dfd7ff;  --c-peri-deep:  #4d4473;
    --c-corn-wash:  #f0f7ff;  --c-corn-tint:  #d6e8ff;  --c-corn-deep:  #425873;
    --c-olive-wash: #e6e9db;  --c-olive-tint: #bbc39e;  --c-olive-deep: #232b00;
    /* PEACH HAS NO DARK SHADE OF ITS OWN, AND THAT IS THE FIX FOR THE BROWN.
       Task P155-#70. --c-peach-deep was #714c2d, and #714c2d is brown: peach
       is hsl(27, 94%, 68%) and there is no way to darken a 27-degree hue far
       enough to carry black-on-white contrast without arriving at brown. Task
       P155-#67 dealt with it one page at a time by moving our-work.html off
       the peach hue entirely; that left the same #714c2d one <body> class away
       and it was still rendering on custom-socks-for-fundraising.html and
       shop.html, which are both hue-peach.

       Toby has ruled brown out, so the variable is retired rather than the
       pages moved: peach's deep role goes to --ink, the brand black that
       brand.css already defines. This introduces NO sixth colour - #000000 is
       the brand's primary and is on every page of the site already.

       WHAT PEACH LOSES AND WHAT IT GAINS. It loses a coloured dark for text
       and gains nothing there (black is 21:1 rather than 7.57:1, so every
       peach-page contrast pair improves). It keeps its full-strength self,
       #faaa63, for every NON-TEXT role - the 56px marks, the section rules,
       the panel edges, the testimonial rules - which after this task is where
       colour mostly lives anyway. A peach page is more peach than it was, not
       less; it is just never brown. */
    --c-peach-wash: #fef3e9;  --c-peach-tint: #fddfc4;  --c-peach-deep: #000000;
    --c-sage-wash:  #f0f5f1;  --c-sage-tint:  #d7e3da;  --c-sage-deep:  #445147;
    /* OCHRE TAKES THE SAME THREE FORMS AS THE OTHER FIVE, AND ITS DEEP IS
       BLACK FOR THE SAME REASON PEACH'S IS. Wash is #e2aa00 mixed 86% into
       white and tint is 62% into white, exactly the arithmetic used above, so
       the ochre sits on the same tonal ladder as the rest and needs no special
       case anywhere in this file.

       The deep does NOT follow the formula, and that is deliberate. #e2aa00 is
       hsl(45, 100%, 44%); taking it 55% toward black gives #664d00, and a dark
       desaturating gold at that value reads as bronze - which is brown, and
       Toby ruled brown out of this site in task P155-#70 when --c-peach-deep
       #714c2d was retired for exactly that. So ochre's deep role goes to the
       brand black, which is the precedent peach already set four lines up: the
       variable exists, every page that asks for --hue-deep gets a real value,
       and no new colour enters the palette to provide it.

       WHAT THAT COSTS AND HOW IT IS PAID. An ochre page loses coloured TYPE -
       #e2aa00 is 2.10:1 on white and may never be a letter. It is paid for in
       the roles where ochre is strongest: the solid title block (black on
       ochre is 9.98:1), the flat tint ground, and every rule, mark, border and
       button fill on the page. See the ochre rules in section 15. */
    --c-ochre-wash: #fbf3db;  --c-ochre-tint: #f4df9e;  --c-ochre-deep: #000000;
    /* NAVY TAKES THE THREE FORMS OFF THE SAME ARITHMETIC AS THE OTHER SIX and
       needs no special case for any of them: wash is #001e57 mixed 86% into
       white, tint is 62% into white, and deep is 55% toward black. It is the
       first hue dark enough that the FORMULA GIVES A REAL COLOURED DEEP rather
       than the brown that forced peach and ochre onto black, so none of the
       section 15 corrections written for those two apply here.

       MEASURED, with the same WCAG relative-luminance formula as the table
       above:

           --c-navy-wash #dbe0e7   black 15.83  #2e2e2e 10.24
           --c-navy-tint #9eaabf   black  8.96  #2e2e2e  5.79
           --c-navy-deep #000d27   as type on white 19.32, white on it 19.32
                                   --c-navy-tint on it 8.24
           --c-navy      #001e57   white 15.87, and see --hue-on below

       WHAT INVERTS, AND IT IS THE ONLY THING THAT DOES. Every other hue in
       this palette is light and carries BLACK type at full strength. Navy
       carries white: black on #001e57 is 1.32:1 and #2e2e2e is 1.17:1, so both
       of the usual block values fail outright rather than marginally. Olive
       set that precedent already and navy follows it exactly - see the
       --hue-on table and `body.hue-navy` below. */
    --c-navy-wash:  #dbe0e7;  --c-navy-tint:  #9eaabf;  --c-navy-deep:  #000d27;

    /* ---- THE OMBRE RULE -------------------------------------------------
       Task P155-#70. Toby has asked for an ombre by name more than once, so
       that is the language used.

       ONE GRADIENT, ALL FIVE COLOURS, NO SIXTH. Every stop below is a variable
       declared six lines up; nothing new is defined. It is simply the five
       palette colours in one line, ordered dark to light so the ramp reads as
       a ramp - olive, sage, cornflower, periwinkle, peach.

       WHAT IT MEANS: ALL FIVE AT ONCE = THE WHOLE COMPANY. Every page of this
       site leads with ONE hue and that hue is that page's identity (rule 2 in
       this section). The ombre is the deliberate exception, and it is reserved
       for the two blocks that do not belong to any one page: the invitation,
       and the "send us your colours" call to action at the foot of every
       category page. Both are the brand asking the visitor for something, both
       are identical wherever they appear, and neither is making a category's
       argument - so both take the whole palette rather than any page's share
       of it. That is its meaning, and it is why it appears nowhere else:
       used decoratively it would be the rainbow rule 1 forbids.

       IT DOES NOT DEPEND ON ANYTHING ELSE ON THE PAGE. These five values are
       declared six lines above and are the brand's own; the rule is built from
       the palette, not sampled from any particular section's artwork, so
       changing what the landing page opens on cannot strand it.

       IT IS NEVER A GROUND AND NEVER CARRIES TEXT. It is drawn at 4-6px, so
       no contrast pair is introduced by it. */
    --ombre: linear-gradient(90deg,
        var(--c-olive) 0%,
        var(--c-sage)  26%,
        var(--c-corn)  52%,
        var(--c-peri)  76%,
        var(--c-peach) 100%);

    /* THE PAGE'S SIGNATURE HUE. Periwinkle unless <body> says otherwise, so a
       page that forgets to choose still looks like the brand. */
    --hue:      var(--c-peri);
    --hue-wash: var(--c-peri-wash);
    --hue-tint: var(--c-peri-tint);
    --hue-deep: var(--c-peri-deep);

    /* ---- AND THE TYPE THAT GOES ON THE HUE AT FULL STRENGTH ------------
       Task P155-#73. Toby asked for the area behind each page title to be a
       SOLID BLOCK of that page's colour, which means that for the first time
       every hue in this palette has to carry large type at full strength.
       Five of the six are light enough to take black; olive is not.

       So the pairing is a TOKEN rather than a decision taken six times. A
       block reads --hue-on for its title and --hue-on-soft for the sentence
       under it, and the one hue that inverts inverts in one place.

           ground              --hue-on       --hue-on-soft
           --c-peri  #ab96ff   #000  8.54:1   #2e2e2e   5.52:1
           --c-corn  #93c3ff   #000 11.49:1   #2e2e2e   7.43:1
           --c-olive #4d6001   #fff  7.03:1   #ffffff   7.03:1
           --c-peach #faaa63   #000 10.99:1   #2e2e2e   7.11:1
           --c-sage  #97b49d   #000  9.33:1   #2e2e2e   6.03:1
           --c-ochre #e2aa00   #000  9.98:1   #2e2e2e   6.46:1
           --c-navy  #001e57   #fff 15.87:1   #ffffff  15.87:1

       OLIVE'S SOFT VALUE IS WHITE, NOT A GREY, AND NAVY'S IS WHITE FOR THE
       SAME REASON. --on-dark-mute #b3b3b3 is 3.35:1 on #4d6001 and fails AA
       outright, so the olive block carries white for both roles. On navy
       #b3b3b3 does technically pass at 7.57:1, and white is still what the
       block carries: a second grey introduces a tonal step nothing else in the
       table has, and 15.87:1 for both roles is the value already proven on
       olive. Every other number above clears 4.5:1 for body text and all seven
       clear 3:1 for the display title. */
    --hue-on:      #000000;
    --hue-on-soft: #2e2e2e;

    /* ---- AND THE HUE THAT GOES ON THE BLACK TOPBAR ----------------------
       Task P155-#81. Toby, 23 Sep 2026: "for the underlines on the nav bar
       i'd like it to be the accent colour as the page."

       THE BAR IS #000000 (`#masthead { background: var(--ink) }`, and the
       phone panel that drops out of it is the same value), so the underline
       under a nav item is a non-text graphical indicator sitting on black and
       its floor is 3:1. Five of the seven hues clear that on the nose at full
       strength; TWO DO NOT, and they fail for opposite reasons to the --hue-on
       table above rather than by the same rule:

           hue                 base       on #000   the value used here
           --c-peri  #ab96ff   base        8.54:1   --c-peri
           --c-corn  #93c3ff   base       11.49:1   --c-corn
           --c-peach #faaa63   base       10.99:1   --c-peach
           --c-sage  #97b49d   base        9.33:1   --c-sage
           --c-ochre #e2aa00   base        9.98:1   --c-ochre
           --c-olive #4d6001   base        2.99:1   --c-olive-tint  11.41:1
           --c-navy  #001e57   base        1.32:1   --c-navy-tint    8.96:1

       OLIVE MISSES BY 0.01 AND NAVY IS NOT CLOSE. Olive at 2.99:1 is under the
       floor by an amount nobody can see, and navy at 1.32:1 is a dark blue
       line on black — the underline simply would not be there. Both take
       their --hue-tint, which is the lighter value this palette already
       carries for exactly this job, and both then clear the floor three times
       over. The lowest number anywhere in the column above is 8.54:1.

       WHAT STILL READS THIS TOKEN, AFTER TASK P155-#82. #81 used it for the
       nav's underlines; #82 replaced those with a per-DESTINATION mapping
       (section 1), because Toby wanted each nav item to keep its own colour on
       every page rather than the whole bar to follow the page. So the values
       in the table above are still exactly the values the bar uses — section 1
       now names each one directly, per link, instead of resolving it through
       this token.

       THE TOKEN IS NOT DEAD AND SHOULD NOT BE DELETED. The collapsed phone
       panel's current-page rule still reads it (section 1, under the 1040px
       breakpoint), and that one is genuinely about the PAGE rather than about
       a link — see the note there. Adding an eighth hue therefore still means
       one line in the block below; it additionally means one line in section
       1's mapping if that hue's page is given a top-level nav item.

       The nav's SECOND dark ground is the submenu's #1a1a1a, where the same
       seven values run 7.08:1 to 9.52:1 and so clear the floor there too. */
    --hue-on-dark: var(--c-peri);
}
body.hue-peri  { --hue: var(--c-peri);  --hue-wash: var(--c-peri-wash);  --hue-tint: var(--c-peri-tint);  --hue-deep: var(--c-peri-deep);  --hue-on: #000000; --hue-on-soft: #2e2e2e; --hue-on-dark: var(--c-peri); }
body.hue-corn  { --hue: var(--c-corn);  --hue-wash: var(--c-corn-wash);  --hue-tint: var(--c-corn-tint);  --hue-deep: var(--c-corn-deep);  --hue-on: #000000; --hue-on-soft: #2e2e2e; --hue-on-dark: var(--c-corn); }
body.hue-olive { --hue: var(--c-olive); --hue-wash: var(--c-olive-wash); --hue-tint: var(--c-olive-tint); --hue-deep: var(--c-olive-deep); --hue-on: #ffffff; --hue-on-soft: #ffffff; --hue-on-dark: var(--c-olive-tint); }
body.hue-peach { --hue: var(--c-peach); --hue-wash: var(--c-peach-wash); --hue-tint: var(--c-peach-tint); --hue-deep: var(--c-peach-deep); --hue-on: #000000; --hue-on-soft: #2e2e2e; --hue-on-dark: var(--c-peach); }
body.hue-sage  { --hue: var(--c-sage);  --hue-wash: var(--c-sage-wash);  --hue-tint: var(--c-sage-tint);  --hue-deep: var(--c-sage-deep);  --hue-on: #000000; --hue-on-soft: #2e2e2e; --hue-on-dark: var(--c-sage); }
body.hue-ochre { --hue: var(--c-ochre); --hue-wash: var(--c-ochre-wash); --hue-tint: var(--c-ochre-tint); --hue-deep: var(--c-ochre-deep); --hue-on: #000000; --hue-on-soft: #2e2e2e; --hue-on-dark: var(--c-ochre); }
/* NAVY IS THE ONE DARK HUE, so it is the one line here that inverts --hue-on,
   exactly as olive does four lines up. Task P155-#76. */
body.hue-navy  { --hue: var(--c-navy);  --hue-wash: var(--c-navy-wash);  --hue-tint: var(--c-navy-tint);  --hue-deep: var(--c-navy-deep);  --hue-on: #ffffff; --hue-on-soft: #ffffff; --hue-on-dark: var(--c-navy-tint); }

/* -------------------------------------------------------------------------
   THE SECTION PATTERNS.

   Toby: "Vary the formats (where the text is, how big, what stuff looks like,
   the colour of the background) across the page (all pages) like the landing
   page."

   A SMALL SET, REUSED — NOT A COMPONENT PER SECTION. Two axes that combine,
   which is what makes eight classes cover a hundred and forty sections:

     BACKGROUND   .bg-paper .bg-alt .bg-wash .bg-tint .bg-ink .bg-deep .bg-accent
     SHAPE        .p-hero .p-prose .p-panel .p-split .p-centre .p-cards .p-cta

   A section is one of each: `<section class="sec p-split bg-wash">`. Adding a
   colour to the site therefore never means adding a component, and the rule
   "no more than three consecutive sections share a background AND a shape" is
   checkable by reading the class attributes down a page, which is exactly how
   it was checked.
   ------------------------------------------------------------------------- */
.sec { padding: 76px 0; }
.sec + .sec { padding-top: 76px; }
.sec > .wrap { position: relative; }
/* The prose column inside a section has already been padded by the section. */
.sec .post-body { padding: 0; }
.sec .post-body > *:first-child { margin-top: 0; }
@media (max-width: 860px) { .sec, .sec + .sec { padding: 46px 0; } }

/* ---- backgrounds ---------------------------------------------------------
   WHITE IS THE GROUND. COLOUR IS AN EDGE, A MARK OR A NUMERAL. Task P155-#70.

   Toby, 22 Sep 2026: "Sometimes you integrate colours very poorly, it looks
   like someone has just highlighted part of the website."

   He is describing a real defect and the cause is this block. Four consecutive
   tasks were each specified as "give section X background colour Y", and four
   classes here were the mechanism, so the site became a stack of flat
   full-bleed slabs: 78 of them across fifteen pages, measured with computed
   styles before this task. The same periwinkle tint sat behind "Our Process"
   and behind "Sock designs" for no reason connected to either, which is what
   makes a wash read as a highlighter mark rather than as design.

   WHAT CHANGED, AND IT IS THE METHOD AND NOT A NEW PALETTE. The five colours,
   their washes, tints and deeps are all exactly as they were. What is retired
   is the idea that a section is DISTINGUISHED BY BEING FILLED:

     .bg-wash    KEPT as --hue-wash. See "the line" below - a wash is 86% white
                                      and cannot jar against anything. This is
                                      the quiet step of the ladder and it is
                                      still a flat ground, on purpose.
     .bg-tint    was a flat --hue-tint ->  AN OMBRE that runs
                                      wash -> tint -> wash down the section, so
                                      its top and bottom edges are the SAME
                                      VALUE as a `.bg-wash` section and its
                                      middle is visibly the colour. It is still
                                      a coloured band - a visitor scrolling
                                      sees it - but it has no edge to be hard
                                      against its neighbours. It also keeps a
                                      4px full-bleed rule of the page hue at
                                      full strength along its top.
     .bg-deep    was --hue-deep   ->  --ink. A dark band is now structural
                                      black rather than a dark wash of the page
                                      hue. This also removes every dark
                                      coloured slab at a stroke, including the
                                      dark purple one Toby singled out on the
                                      landing page, and it is the reason no
                                      page needs --c-peach-deep any more.
     .bg-accent  was --c-peri at FULL strength -> --c-peri-wash, with the
                                      five-colour ombre rule across its top.
                                      The closing call to action still gets a
                                      field of colour, because asking for the
                                      order is content that earns one; what it
                                      no longer does is put the loudest value in
                                      the palette edge-to-edge against the black
                                      footer. See the .p-cta block in section 13.

   THE LINE, AND IT IS A LINE ABOUT VALUE AND NOT ABOUT AMOUNT. Toby, 22 Sep
   2026, after seeing a first pass of this task: "please still vary section
   colours". He is right, and the correction is worth stating as a rule so the
   next task does not have to rediscover it:

       A WASH MAY BE A FLAT FULL-BLEED GROUND. It is mixed 86% into white; the
       step from paper to a wash is a whisper and cannot read as a highlighter.
       A TINT OR ANYTHING STRONGER MAY NOT. It has to arrive as an ombre, be
       bounded inside a frame, or be black.

   That is the whole of it, and it is what makes "use less flat colour" and
   "lean into it" the same instruction rather than opposite ones. What was
   removed is not colour, it is HARD EDGES AT HIGH SATURATION. Every page still
   runs a four-step tonal ladder in its own single hue - paper, wash, the
   tint-ombre, black - so a visitor scrolling sees the sections change.

   THE RHYTHM IS TONAL AND SHAPED, NOT STAMPED. Seven section SHAPES already
   carry half of it - hero, prose, panel, split, centre, cards, cta - and they
   are untouched. The other half is the ladder above: down a category page the
   sequence reads ombre-hero / wash / black / tint-ombre / wash / black /
   tint-ombre / wash / the closing wash under its spectrum rule. Nine sections,
   four values, one hue, and not one hard edge between two saturated fields.

   AND THAT IS THE TEST THAT MATTERS. Every junction left on the site is
   paper-to-wash, wash-to-wash, wash-to-black, black-to-wash or a 4px rule with
   a wash on both sides of it. The only places two STRONG values still meet are
   black against white, which is the brand's own structural pair and has been
   since the day the live site was measured. */
.bg-paper  { background: var(--paper); }
.bg-alt    { background: var(--paper-alt); }
.bg-wash   { background: var(--hue-wash); }
/* AND IT IS FLAT AGAIN, WHICH IS THE INSTRUCTION. Task P155-#73.
   Toby, 22 Sep 2026: "No weird faded gradients please."

   P155-#70 made this a 180-degree ramp - wash at the top, tint through the
   middle, wash again at the bottom - so that a tinted section never met white
   at a hard edge. The ramp is what he is objecting to. It is a flat fill of
   --hue-tint now, with the 4px rule of the page's hue along its top edge that
   it has always had; the rule is what separates it from what is above, and a
   drawn line does that job without the section having to dissolve.

   CONTRAST IMPROVES NOWHERE AND WORSENS NOWHERE THAT MATTERS: the ramp's
   darkest point was the tint, every pair on it was measured against the tint,
   and the tint is now the whole ground. Black on the six tints: periwinkle
   15.3:1, cornflower 16.9:1, olive 11.4:1, peach 16.5:1, sage 15.9:1, ochre
   15.9:1. --ink-soft: 9.89 / 10.9 / 7.38 / 10.7 / 10.3 / 10.3:1. */
.bg-tint   {
    background: var(--hue-tint);
    box-shadow: inset 0 4px 0 var(--hue);
}
.bg-accent { background: var(--c-peri-wash); }

/* REVERSED GROUNDS. Black type never appears on either of these and white type
   never appears on the five light grounds; the two sets are kept apart by these
   two rules rather than by remembering.

   THE GREY IS FOR BLACK ONLY, AND THIS IS A CORRECTION. The first draft of
   this block put --on-dark-mute (#b3b3b3) on BOTH grounds, on the stated
   belief that it was "4.6:1 on the darkest deep". Task P155-#64 measured every
   pair in this file with the WCAG relative-luminance formula rather than
   believing the comment, and it is not: #b3b3b3 is 3.48:1 on --c-corn-deep,
   3.61:1 on peach, 3.98:1 on sage and 4.19:1 on periwinkle. Four of the five
   deeps fail AA for body text with it and the fifth (olive, 7.07:1) passes
   only by being nearly black. So the grey stays on --ink, where it is 10.0:1,
   and every deep ground carries WHITE body text instead: 7.30:1 at its worst
   (cornflower) and 14.82:1 at its best (olive). */
.bg-ink  { background: var(--ink); color: var(--on-dark); }
/* A DARK SECTION IS BLACK, NOT A DARK WASH OF THE PAGE HUE. Task P155-#70.
   This was `var(--hue-deep)` and it painted the dark purple, dark olive and
   brown slabs. Black is the brand's own structural weight and is what Toby
   means when he says the "Why Socks?" band and the contact band are deliberate
   and can stay. The two rules are now the same colour on purpose; every
   type-colour rule below already treated them as one pair. */
.bg-deep { background: var(--ink); color: var(--on-dark); }
.bg-ink h1, .bg-ink h2, .bg-ink h3, .bg-deep h1, .bg-deep h2, .bg-deep h3 { color: var(--on-dark); }
.bg-ink .post-body { color: var(--on-dark-mute); }
.bg-deep .post-body { color: var(--on-dark); }
.bg-ink .post-body h2, .bg-ink .post-body h3,
.bg-deep .post-body h2, .bg-deep .post-body h3 { color: var(--on-dark); }
.bg-ink .post-body p, .bg-ink .post-body li { color: var(--on-dark-mute); }
.bg-deep .post-body p, .bg-deep .post-body li { color: var(--on-dark); }
.bg-ink .post-body strong, .bg-deep .post-body strong { color: var(--on-dark); }
.bg-ink .post-body a, .bg-deep .post-body a { color: var(--on-dark); }
/* The hover on a link in body text fills it with the accent, which would put
   white on periwinkle at 2.5:1. On a dark ground the fill takes black type. */
.bg-ink .post-body a:hover, .bg-deep .post-body a:hover { color: #000; }
.bg-ink .sec-lede, .bg-ink .seg-lede { color: var(--on-dark-mute); }
.bg-deep .sec-lede, .bg-deep .seg-lede { color: var(--on-dark); }
/* THE SAME PAIR AGAIN, AT p-hero's SPECIFICITY. Task P155-#68.
   `.sec.p-hero .sec-lede` sets --ink-soft and is three classes deep; the two
   rules immediately above are two, so on a p-hero the dark-ground rule LOST and
   the lede rendered #2e2e2e on the ground - 1.2:1 on the cornflower deep, which
   is invisible. No page had ever combined p-hero with a reversed ground before
   this task, so the clash was latent rather than broken; blog.html, post.html
   and two category heads now do combine them. Restating the pair at matching
   specificity fixes every current and future p-hero on a dark ground at once,
   which is better than each page overriding it. */
.sec.p-hero.bg-ink .sec-lede, .sec.p-hero.bg-ink .seg-lede { color: var(--on-dark-mute); }
.sec.p-hero.bg-deep .sec-lede, .sec.p-hero.bg-deep .seg-lede { color: var(--on-dark); }

/* A GHOST BUTTON ON A DARK GROUND. `.btn.ghost` is a black outline with black
   type, which on `.bg-ink` is black on black and on `.bg-deep` is very nearly
   it. blog.html and post.html used to carry a per-page override for exactly
   this (`.blog-empty .btn.ghost`); with those two pages now built from the
   shared section shapes the override belongs here, where every dark section on
   the site gets it. White on black is 21:1 and white on the deeps is 7.30:1 at
   worst. */
.bg-ink .btn.ghost, .bg-deep .btn.ghost { color: var(--on-dark); border-color: var(--on-dark); }
.bg-ink .btn.ghost:hover, .bg-deep .btn.ghost:hover { background: var(--on-dark); color: #000; }
/* ON BLACK the periwinkle is 8.54:1 and is the right mark. ON A DEEP GROUND it
   is not: --c-peri measures 3.57:1 on the periwinkle deep, 2.97:1 on the
   cornflower one, 3.08:1 on the peach one and 3.40:1 on the sage one - all
   under the 4.5:1 an 11.5px label needs, and three of the four under even the
   large-text bar. Task P155-#68 puts far more sections on a deep ground than
   #64 did, so the pair is fixed rather than inherited: a deep ground takes the
   same hue's TINT, which is 5.86:1 at its worst across the five (cornflower)
   and 8.05:1 at its best (olive). */
/* Same correction as the split rule above: the eyebrow on a black section is
   the PAGE's tint rather than periwinkle on every page. Every one of the five
   tints clears 10:1 on black, far past the 4.5:1 an 11.5px label needs. */
.bg-ink .eyebrow { color: var(--hue-tint); }
.bg-deep .eyebrow { color: var(--hue-tint); }
.bg-ink hr, .bg-deep hr, .bg-ink .post-body hr, .bg-deep .post-body hr { border-top-color: var(--dark-line); }

/* Black type on the five light grounds, stated rather than inherited, so a
   ground change can never silently drag --ink-mute onto a tint. */
.bg-wash, .bg-tint, .bg-accent { color: var(--ink); }
.bg-tint .post-body, .bg-accent .post-body { color: var(--ink-soft); }
.bg-wash .post-body { color: var(--ink-soft); }

/* ---- shape 1: the page head --------------------------------------------
   A full-bleed wash with the page's h1 on it and a 5px rule of the page's own
   hue underneath. The rule is the whole reason the head does not simply run
   into the section below it. */
.sec.p-hero { padding: 72px 0 56px; border-bottom: 5px solid var(--hue); }
/* ---------------------------------------------------------------------------
   THE ONE COLOUR FIELD A PAGE IS ALLOWED, AND IT DISSOLVES. Task P155-#70.

   Every category page opened on `.p-hero.bg-tint`: a flat slab of the page's
   tint with a hard bottom edge onto white. `.bg-tint` is white with a top rule
   now, which would have left the heads of nine pages with no identity at all,
   and a page's signature hue is a settled thing Toby has asked for by name.

   So the hero keeps a colour field and stops being flat. It is an OMBRE - the
   page's tint at the top, its wash at the middle, paper at the bottom - which
   means the section has no bottom edge to be hard: by the time it reaches the
   5px hue rule that closes it, the ground under that rule is already white.
   The rule reads as a drawn line on paper rather than as the seam between two
   fills, which is the whole difference between this and what was here before.

   `box-shadow: none` cancels the 4px inset top rule `.bg-tint` would otherwise
   add; a hero sits directly under the sticky bar and does not need announcing.

   MEASURED AT THE TOP OF THE OMBRE, where the ground is darkest and every
   worst case therefore lives - black on the five tints: periwinkle 15.3:1,
   cornflower 16.9:1, olive 11.4:1, peach 16.5:1, sage 15.9:1. The lede is
   --ink-soft: 9.89 / 10.9 / 7.38 / 10.7 / 10.3:1. Both clear AA on all five.
   --------------------------------------------------------------------------- */
/* SUPERSEDED BY `.bg-block`, AND FLATTENED WHERE IT STANDS. Task P155-#73.
   Every page head on this site is `.p-hero.bg-block` now - a solid block of
   the page's colour - so no page reaches this rule any more. It is flattened
   rather than deleted so that it cannot put a fade back on a head the next
   time somebody writes `bg-tint` on one: the tint, flat, with no ramp and no
   inset rule. */
.sec.p-hero.bg-tint {
    background: var(--hue-tint);
    box-shadow: none;
}

/* ---------------------------------------------------------------------------
   `.bg-block` - THE SOLID BLOCK BEHIND A PAGE TITLE. Task P155-#73.

   Toby, 22 Sep 2026: "Make the section behind the titles in block colour and
   use these colours as the theme throughout the page, varying the format and
   usage. No weird faded gradients please."

   ONE TREATMENT, SIX HUES, AND NOTHING ELSE DIFFERENT. Every page head on this
   site is this rule: a flat fill of --hue at FULL strength, edge to edge, with
   a clean bottom edge where it stops. No ramp, no overlay, no inset rule, no
   second value anywhere in the band. Two pages differ from another two only in
   which colour `body` named. That is what makes five differently-coloured
   pages read as one site rather than five paint jobs.

   WHY FULL STRENGTH RATHER THAN THE TINT. "Block colour" is the instruction
   and the tint is not one - it is the colour diluted 62% into white, which is
   what the heads were already wearing and is the thing he is asking to change.
   Restraint rule 3 in section 12 said no hue is ever a large ground at full
   strength except the periwinkle; Toby has now overruled that for this one
   defined place, exactly as he overruled "the black bands stay black" in #68.
   It applies to the TITLE AREA and to nothing else: no section below a head
   takes a full-strength ground, which is the distinction between this and the
   arbitrary slabs P155-#70 removed.

   THE TYPE PAIRING IS A TOKEN, NOT A JUDGEMENT PER PAGE. --hue-on and
   --hue-on-soft are declared beside the palette in section 12 with every ratio
   measured. Black on five of the seven hues, white on the two dark ones, olive
   and navy.

   THE BOTTOM EDGE IS THE BLOCK'S OWN. `.sec.p-hero` draws a 5px rule of --hue
   under a head, which on a head that IS --hue would be a 5px continuation of
   itself - invisible, and five pixels of drift on an edge that is supposed to
   be clean. It is removed on a block; where the colour stops is the edge.

   MEASURED, EVERY PAGE, TITLE AND LEDE:
       how-it-works  --c-olive #4d6001  white  7.03:1   lede white   7.03:1
       faq           --c-ochre #e2aa00  black  9.98:1   lede #2e2e2e 6.46:1
       our-work      --c-corn  #93c3ff  black 11.49:1   lede #2e2e2e 7.43:1
       gallery       --c-navy  #001e57  white 15.87:1   lede white  15.87:1
       blog          --c-peri  #ab96ff  black  8.54:1   lede #2e2e2e 5.52:1
       category/legal pages on --c-sage #97b49d  black 9.33:1 / #2e2e2e 6.03:1
   --------------------------------------------------------------------------- */
/* THE SELECTORS BELOW ARE LONGER THAN THEY LOOK LIKE THEY NEED TO BE, and it
   is not decoration. Three rules already in this file set a colour on exactly
   these elements at two or three classes deep - `.sec.p-hero .seg-lede` is
   --ink-soft, `.sec .eyebrow` is --hue-deep - and both are LATER in the file
   than this section. At `.bg-block .eyebrow` they simply won, and the olive
   block came out with a dark grey lede on a dark green ground (1.6:1) while
   the blog's eyebrow came out periwinkle-deep on periwinkle (3.4:1). Both
   were measured on the rendered page, not reasoned about. Naming the head's
   own class alongside `.bg-block` puts every rule here at three classes or
   more, which settles it by specificity rather than by source order and keeps
   it settled if the file is ever reordered. */
.bg-block { background: var(--hue); color: var(--hue-on); }
.bg-block h1,
.bg-block h2,
.bg-block .post-title { color: var(--hue-on); }
.sec.bg-block .eyebrow,
.work-head.bg-block .eyebrow,
.case-head.bg-block .eyebrow,
.blog-head.bg-block .eyebrow { color: var(--hue-on); }
/* `body ` on the front of this group, and that is the same argument again one
   step further: `.sec.p-hero .seg-lede` is --ink-soft at three classes, which
   TIES with `.sec.bg-block .seg-lede` and then wins on source order, and the
   olive head came back with #2e2e2e on #4d6001 - 1.94:1, unreadable. The
   element selector settles the tie without moving the block. */
body .sec.bg-block p,
body .sec.bg-block .sec-lede,
body .sec.bg-block .seg-lede,
body .sec.bg-block .post-stand,
body .work-head.bg-block p,
body .work-head.bg-block .work-intro,
body .case-head.bg-block p,
body .case-head.bg-block .case-summary,
body .case-head.bg-block .crumbs,
body .case-head.bg-block .crumbs a,
body .blog-head.bg-block p { color: var(--hue-on-soft); }
.bg-block a { color: var(--hue-on); }
.sec.p-hero.bg-block { box-shadow: none; border-bottom: 0; }
.blog-head.bg-block, .work-head.bg-block, .case-head.bg-block { border-bottom: 0; }

/* THE BUTTON ON A BLOCK GOES BLACK, ON ALL SIX. `.btn` is periwinkle with
   black type everywhere else on this site, and a periwinkle button sitting on
   a periwinkle, cornflower or ochre block is a button you have to look for.
   Black is 21:1 against white type on every one of the six grounds and is the
   brand's own primary, so the one thing in the block meant to be clicked is
   the one thing that is not the block's colour. It inverts to paper on hover,
   which is 21:1 against black type on all six. */
.bg-block .btn { background: var(--ink); border-color: var(--ink); color: var(--on-dark); }
.bg-block .btn:hover { background: var(--paper); border-color: var(--paper); color: var(--ink); }

.sec.p-hero h1 { font-size: clamp(42px, 6.6vw, 78px); max-width: 17ch; }
.sec.p-hero .sec-lede, .sec.p-hero .seg-lede {
    font: 400 clamp(18px, 2vw, 21px)/1.55 var(--body);
    color: var(--ink-soft); margin: 20px 0 0; max-width: 60ch;
}
.sec.p-hero .hero-acts { display: flex; flex-wrap: wrap; gap: 12px; margin: 30px 0 0; }
.sec.p-hero .hero-acts .btn { text-decoration: none; }
@media (max-width: 860px) { .sec.p-hero { padding: 40px 0 34px; } }

/* ---- shape 2: the prose column ------------------------------------------
   The default and the quietest. A single measure, text left, headings at the
   article size. This is what every section on these pages used to be. */
.sec.p-prose .sec-in { max-width: 780px; }

/* ---- shape 3: the panel -------------------------------------------------
   The content sits in a card of the page's tint with a 5px rule of the hue
   down its left edge — the shape `.facts` already had in segment.css,
   generalised so anything can take it. The card is what stops a run of prose
   sections reading as one undifferentiated column. */
.sec.p-panel .sec-in { max-width: 900px; }
/* THE FRAME CLOSES ON ALL FOUR SIDES. Task P155-#69. Toby, 22 Sep 2026: "Make
   sure there are proper boarders around the 'we'd love to add you to our list'
   ... (and any other page its on)." The panel was bounded on ONE side - a 5px
   rule at the left and three open edges, which reads as a bounded block only
   because its ground differs from the section's. "Borders around" means all
   four, so the same --hue-deep now runs right round at a hairline and the left
   edge keeps its 5px weight. One declaration changed; the shape, the ground,
   the padding and the type are untouched, so every existing `.p-panel` section
   on the nine commercial pages gains the same closed frame rather than this
   one block getting a private one.

   AND THE SHAPE IS NOW REACHABLE FROM A LANDING-PAGE BAND. The home page is
   built from `.band`, not `.sec` (see the long note in section 13), so the
   invitation could not have taken this card without either duplicating its
   declarations or inventing a component. It takes the selector instead - the
   same rule, one more place it applies - which is exactly what section 12 says
   was done to `.facts` to make `.p-panel` in the first place. */
/* THE CARD IS FRAMED, NOT FILLED. Task P155-#70.
   Its ground was --hue-tint, which made it one more coloured rectangle - a
   smaller version of exactly the thing Toby objects to, sitting inside a
   section that was itself filled. It is white now and the colour moved into
   its EDGE: a hairline frame in the hue's deep shade on three sides and the
   page's hue at FULL STRENGTH, 5px, down the left. That left edge is the most
   saturated the hue ever appears on a light ground anywhere on the site, which
   is the trade this whole task makes - the colour is smaller, louder and in a
   place that means something, instead of larger, weaker and behind the words.

   The border was already 1px/5px in exactly this arrangement; only the fill is
   gone and only the left edge changed colour. */
.sec.p-panel .panel,
section.band.invite .panel {
    background: var(--paper);
    border: 1px solid var(--hue-deep);
    border-left: 5px solid var(--hue);
    padding: 36px 40px;
    color: var(--ink-soft);
}
.sec.p-panel .panel > *:first-child,
section.band.invite .panel > *:first-child { margin-top: 0; }
.sec.p-panel .panel > *:last-child,
section.band.invite .panel > *:last-child { margin-bottom: 0; }
.sec.p-panel .panel h2 { font-size: clamp(24px, 3vw, 31px); color: var(--ink); }
@media (max-width: 860px) {
    .sec.p-panel .panel,
    section.band.invite .panel { padding: 26px 22px; }
}

/* ---- shape 4: the split -------------------------------------------------
   Heading on the left in its own column, body on the right. The heading is set
   LARGER here than in a prose section, because it is carrying a column on its
   own rather than sitting above a paragraph — that size change is half of what
   makes the two shapes read differently at a glance. It sticks while its own
   body scrolls past, and stops sticking the moment the columns stack. */
.sec.p-split .sec-in {
    display: grid;
    grid-template-columns: minmax(0, 320px) minmax(0, 1fr);
    gap: 0 clamp(34px, 5vw, 76px);
    align-items: start;
    max-width: 1120px;
}
.sec.p-split .split-head { position: sticky; top: calc(var(--topbar-h, var(--topbar-fallback)) + 26px); }
.sec.p-split .split-head h2 {
    font-size: clamp(30px, 4.2vw, 46px);
    margin: 0;
    text-wrap: balance;
}
.sec.p-split .split-head .split-rule { display: block; width: 56px; height: 5px; background: var(--hue); margin: 20px 0 0; }
/* ON A DARK GROUND THE MARK IS THE PAGE'S OWN TINT, NOT PERIWINKLE.
   Task P155-#70. This said var(--c-peri) for every page, which meant the one
   coloured mark on a black section was the same purple on the olive pages, the
   cornflower pages and the sage ones - page identity dropped the moment a
   section went dark. The hue's TINT carries it instead and is visible on black
   in all five: periwinkle 15.9:1, cornflower 16.4:1, olive 10.2:1, peach
   15.4:1, sage 14.0:1. Decoration, so none of those is a required pair, but an
   invisible mark would be a pointless one. */
.sec.p-split.bg-ink .split-head .split-rule, .sec.p-split.bg-deep .split-head .split-rule { background: var(--hue-tint); }
.sec.p-split .post-body { max-width: 680px; }
@media (max-width: 900px) {
    .sec.p-split .sec-in { grid-template-columns: minmax(0, 1fr); gap: 18px; }
    .sec.p-split .split-head { position: static; }
    .sec.p-split .split-head h2 { font-size: clamp(27px, 6vw, 36px); }
}

/* ---- shape 5: the centred block ----------------------------------------
   Narrow measure, text centred, heading between the prose size and the split
   size. Used once or twice a page for the sentence that has to be read rather
   than skimmed. */
.sec.p-centre .sec-in { max-width: 760px; margin: 0 auto; text-align: center; }
.sec.p-centre .post-body { max-width: 68ch; margin: 0 auto; }
.sec.p-centre .post-body h2 { font-size: clamp(28px, 3.9vw, 41px); }
.sec.p-centre .post-body ul, .sec.p-centre .post-body ol { text-align: left; display: inline-block; }
.sec.p-centre .eyebrow { margin-bottom: 14px; }

/* ---- shape 6: the card grid --------------------------------------------
   Two or three across at desktop, one on a phone. Every card is the same
   paper rectangle with a hairline; only the grid changes. */
.sec.p-cards .sec-in { max-width: 1120px; }
.sec.p-cards .card-grid {
    display: grid; gap: 18px;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    margin: 34px 0 0;
}
.sec.p-cards .card {
    background: var(--paper);
    border: 1px solid var(--line);
    border-top: 4px solid var(--hue);
    padding: 26px 26px 10px;
}
.sec.p-cards .card > *:first-child { margin-top: 0; }
.sec.p-cards .card h3 { font-size: clamp(18px, 2vw, 21px); margin: 0 0 .6em; }
.sec.p-cards .card p { font-size: 16.5px; line-height: 1.6; color: var(--ink-soft); }

/* ---- shape 7: the closing ask ------------------------------------------
   Full-bleed, centred, and the only large flat area of a hue at full strength
   anywhere on the site. Black on --c-peri is 8.54:1. */
.sec.p-cta { padding: 64px 0; text-align: center; }
.sec.p-cta .sec-in { max-width: 700px; margin: 0 auto; }
.sec.p-cta h2 { font-size: clamp(28px, 4vw, 44px); margin: 0 0 .45em; text-wrap: balance; }
.sec.p-cta p { margin: 0 auto 1.4em; max-width: 52ch; color: var(--ink-soft); font-size: 17.5px; }
.sec.p-cta p:last-child { margin-bottom: 0; }
.sec.p-cta .btn { background: var(--ink); border-color: var(--ink); color: var(--on-dark); text-decoration: none; }
.sec.p-cta .btn:hover { background: var(--paper); border-color: var(--ink); color: var(--ink); }
/* ---------------------------------------------------------------------------
   THE CALL TO ACTION IS THE OTHER PLACE THE OMBRE IS ALLOWED. Task P155-#70.

   `.p-cta.bg-accent` closes all nine commercial pages and was a full-bleed slab
   of #ab96ff at FULL strength sitting directly on top of the black footer - two
   saturated fields meeting at a hard edge, which is the single loudest instance
   of the effect Toby objects to.

   IT KEEPS A FIELD OF COLOUR, BECAUSE THIS BLOCK EARNS ONE. It is the ask, it
   is the last thing on the page, and a call to action that looks like every
   other section is a call to action nobody answers. What changes is the VALUE:
   `.bg-accent` is --c-peri-wash now (section 12), the same periwinkle mixed 86%
   into white. Still unmistakably a coloured block, still the only section on a
   category page that is not in that page's own hue - and no longer the loudest
   value in the palette run edge to edge against black.

   WHAT ELSE CARRIES IT. A 6px ombre rule across the top of the section - the
   same five-colour line the invitation uses, and used here for the same reason:
   this block is the brand asking for the order, not the page making its
   argument, and it is identical at the foot of all nine pages. Under it the
   heading is black at 21:1 and the button goes back to PERIWINKLE, which is
   what `.btn` is everywhere else on the site and what the live societalsocks.com
   puts behind "Get Started". It had to invert to black while the ground was
   periwinkle; on white it does not, so the one element in the block that is
   meant to be clicked is the one element that is coloured.

   MEASURED:  h2 --ink on --c-peri-wash      18.70:1
              p  --ink-soft on --c-peri-wash 12.10:1
              .btn #000 on --c-peri           8.54:1
              .btn:hover --on-dark on --ink  21.00:1
   --------------------------------------------------------------------------- */
.sec.p-cta.bg-accent { position: relative; padding-top: 70px; }
.sec.p-cta.bg-accent::before {
    content: ""; position: absolute; top: 0; left: 0; right: 0;
    height: 6px; background: var(--ombre);
}
.sec.p-cta.bg-accent .btn { background: var(--c-peri); border-color: var(--c-peri); color: #000; }
.sec.p-cta.bg-accent .btn:hover { background: var(--ink); border-color: var(--ink); color: var(--on-dark); }

.sec.p-cta.bg-ink .btn, .sec.p-cta.bg-deep .btn { background: var(--c-peri); border-color: var(--c-peri); color: #000; }
.sec.p-cta.bg-ink .btn:hover, .sec.p-cta.bg-deep .btn:hover { background: var(--paper); border-color: var(--paper); color: #000; }
.sec.p-cta.bg-ink p { color: var(--on-dark-mute); }
.sec.p-cta.bg-deep p { color: var(--on-dark); }
@media (max-width: 860px) { .sec.p-cta { padding: 44px 0; } }

/* ---- the eyebrow, in the page's own colour ------------------------------
   The label above a heading is the cheapest place on the page to put a colour:
   it is short, it is bold, and it is never body text. --hue-deep clears 7.3:1
   against white at its worst (cornflower) and the wash grounds are paler than
   white is dark, so every combination of it clears AA for its size. */
.sec .eyebrow { color: var(--hue-deep); }
/* AND NOT ON A DARK GROUND. Task P155-#68. `.sec .eyebrow` and `.bg-deep
   .eyebrow` are both two classes deep, and this one is LATER in the file, so
   it won every time: on a `.sec.p-hero.bg-deep` the eyebrow came out
   --hue-deep on --hue-deep, which is 1:1 - the same colour twice, an invisible
   label. Measured on the rendered blog and gallery pages by the audit this
   task ran, not reasoned about. Restating both dark cases at three classes
   settles it wherever the two rules meet.
       --hue-tint on --hue-deep   5.86:1 worst (cornflower), 8.05:1 best
       --c-peri   on --ink        8.54:1 */
.sec.bg-deep .eyebrow { color: var(--hue-tint); }
.sec.bg-ink .eyebrow  { color: var(--c-peri); }
.bg-tint .eyebrow { color: var(--hue-deep); }

/* A NUMBERED SECTION. The numeral is the colour; the words are not. */
.sec .post-body h2 .num, .num-mark {
    font-family: var(--alt); font-weight: 700;
    color: var(--hue-deep);
}

/* ---- rules and marks ---------------------------------------------------- */
.hue-rule { border: 0; border-top: 4px solid var(--hue); width: 64px; margin: 0 0 26px; }
.sec.p-centre .hue-rule { margin-left: auto; margin-right: auto; }

/* The article link treatment already fills on hover with --accent. Inside a
   section whose ground IS a hue the fill would vanish, so it inverts instead. */
.bg-tint .post-body a:hover, .bg-accent .post-body a:hover { background: var(--ink); color: var(--on-dark); }
/* =========================================================================
   13. THE LANDING PAGE AND THE OUTER PAGES, IN THE SAME COLOUR. Task P155-#64.

   Section 12 above painted the seven commercial pages, which are built out of
   `.sec` + a shape + a background. The home page is not: it is older, it is
   the page Toby pointed at when he said "vary the formats ... like the landing
   page", and its bands (`.band`, `.band.alt`, `.band.dark`) are the shapes the
   seven patterns were abstracted FROM. Rewriting it onto `.sec` would have
   been a structural change dressed up as a colour pass, and task P155-#65 had
   just finished cutting three sections out of it.

   SO THE COLOUR COMES TO THE BANDS INSTEAD. Two new grounds, `.band.wash` and
   `.band.tint`, read the same --hue-* variables as everything in section 12,
   which is what keeps the home page and the segment pages one system rather
   than two that happen to share a hex code. Nothing about the band's SHAPE
   changes here; this file adds grounds and marks and no layout.

   WHAT THE LANDING PAGE GETS, AND WHAT IT DELIBERATELY DOES NOT:
     * The process band moves off flat grey onto the periwinkle wash.
     * The testimonials band moves onto the periwinkle tint.
     * Every section head grows a 56px rule of the page's hue above its h2 -
       the same mark `.split-rule` makes on the segment pages.
     * The six category links each carry the hue of the PAGE THEY LEAD TO, so
       schools is olive, clubs cornflower, universities periwinkle,
       fundraising peach and businesses sage before the visitor arrives.
     * The step numerals take the hue's deep shade.
     * The black bands stay black. "Why Socks?", the invitation and the
       contact block are the live site's own structure and its weight, and
       colouring them would be taking it too far.
     * The opening keeps its six-colour gradient untouched. It is already the
       most colourful thing on the site and it did not need help.
   ========================================================================= */
/* THE LANDING PAGE'S BANDS TAKE THE SAME CORRECTION AS THE SECTION SHAPES.
   Task P155-#70. These two painted the lilac band behind "Our Process", the
   near-lilac one behind "Sock designs" and the peach one behind "In their
   words" - three of the four flat colour fills Toby listed when he said the
   page "looks like someone has just highlighted part of the website".

   They now match `.bg-wash` and `.bg-tint` exactly (see the long note in
   section 12): wash is plain white, tint is white with a 4px full-bleed rule
   of the band's own hue along its top edge. One system for the landing page
   and the category pages, which is what it was before and still is. */
section.band.wash { background: var(--hue-wash); }
/* FLAT, FOR THE REASON GIVEN AT `.bg-tint` IN SECTION 12. Task P155-#73. */
section.band.tint {
    background: var(--hue-tint);
    box-shadow: inset 0 4px 0 var(--hue);
}

/* ---------------------------------------------------------------------------
   TWO MORE GROUNDS, AND THE BLACK BANDS MAY NOW TAKE COLOUR. Task P155-#68.

   Toby, 22 Sep 2026: "Take the colours further for the different sections.
   Lean into it."

   #64's pass ended with "the black bands stay black... colouring them would be
   taking it too far", and said so in the note immediately above. That was a
   judgement call and Toby has now overridden it, so the two grounds this file
   was missing exist:

     .band.deep    the page's hue at its DEEP shade, white type. A dark band
                   that is a colour rather than an absence of one.
     .band.accent  the periwinkle at FULL strength, black type. The loudest
                   ground on the site and the one to use once per page.

   They are the band-level twins of `.bg-deep` and `.bg-accent`, which the
   section-shape pages have had since #64 - same two colours, same two type
   colours, so a visitor moving between the landing page and a category page
   meets one system and not two.

   MEASURED. Periwinkle is the landing page's hue, so these are its numbers;
   every other page's hue is checked in the table in section 12.
       .band.deep   #4d4473   h2/white              8.79:1
                              body/white            8.79:1   <- NOT --on-dark-mute
                              eyebrow/--hue-tint    6.40:1
       .band.accent #ab96ff   h2/black              8.54:1
                              body/--ink-soft       5.52:1
                              eyebrow/black         8.54:1

   THE BODY TEXT ON .deep IS PURE WHITE AND THAT IS NOT AN OVERSIGHT.
   --on-dark-mute (#b3b3b3) is the grey the BLACK bands use for body copy, where
   it is 10.0:1. On the periwinkle deep it is 4.19:1 - under the 4.5:1 AA wants
   for body text. A deep ground is a dark ground with a colour in it, so it is
   lighter than black by definition and the mute grey does not survive the move.
   Every `.band.deep` paragraph is therefore --on-dark. */
/* A DARK BAND IS BLACK. Task P155-#70, matching `.bg-deep` in section 12.
   This painted #4d4473 behind "Why Socks?" - the "dark purple band" in Toby's
   list of slabs. Black is what he means by "the black Why Socks? band ... is
   deliberate and can stay", and it is the brand's own structural weight rather
   than a fifth wash of the page hue. */
section.band.deep {
    background: var(--ink);
    color: var(--on-dark);
}
section.band.deep h2, section.band.deep h3, section.band.deep a { color: var(--on-dark); }
section.band.deep p, section.band.deep .band-head p { color: var(--on-dark); }
section.band.deep .eyebrow { color: var(--hue-tint); }
/* The 56px mark over an h2: --hue on --hue-deep is the same colour twice and
   would be invisible, so a deep band takes the tint for it exactly as it does
   for the eyebrow. Decoration, so no contrast pair is introduced. */
section.band.deep .band-head h2::before { background: var(--hue-tint); }
/* The band is black after P155-#70, so the periwinkle body text that used to
   need 8.79:1 against #4d4473 now sits on 21:1, and --on-dark-mute is usable
   here exactly as it is on `.band.dark`. The rules below are left as they were
   - pure white on black is never wrong - and only this note changes. */
/* A ghost button is a black outline with black type, which on a deep ground is
   very nearly black on black. Same correction as `.bg-deep .btn.ghost` makes
   for the section-shape pages. White on the five deeps is 7.30:1 at worst. */
section.band.deep .btn.ghost { color: var(--on-dark); border-color: var(--on-dark); }
section.band.deep .btn.ghost:hover { background: var(--on-dark); color: #000; }
/* A SOLID BUTTON IS PERIWINKLE WITH BLACK TYPE (8.54:1), and `section.band.deep
   a { color: var(--on-dark) }` two lines up would otherwise repaint it white,
   which is 2.46:1 on that periwinkle. `section.band.dark` has carried exactly
   this correction since task P155-#64; the deep ground needs it for the same
   reason and it was missing until the audit for this task found it on
   our-work.html. `:not(.ghost)` leaves the outlined button alone, which the
   rule above has already put on white. */
section.band.deep a.btn:not(.ghost) { color: #000; }
section.band.deep a.btn:not(.ghost):hover { color: var(--ink); }

section.band.accent {
    background: var(--c-peri);
    color: var(--ink);
}
section.band.accent h2, section.band.accent h3 { color: var(--ink); }
section.band.accent p, section.band.accent .band-head p { color: var(--ink-soft); }
section.band.accent .eyebrow { color: var(--ink); }
section.band.accent .band-head h2::before { background: var(--ink); }
/* The filled button is periwinkle-on-periwinkle here, so it inverts: black
   fill, white type, 21:1, and the ghost button keeps its black outline. */
section.band.accent .btn { background: var(--ink); border-color: var(--ink); color: var(--on-dark); }
section.band.accent .btn:hover { background: var(--paper); border-color: var(--ink); color: var(--ink); }

/* ---------------------------------------------------------------------------
   "OUR CLIENTS" IS WHITE, AND IT WAS SKY BLUE FOR ABOUT AN HOUR. Task P155-#68.

   Toby, 22 Sep 2026, first: "Make the background blue on the our work section
   (skyblue)." It was built that way - --c-corn at full strength, #93c3ff, with
   `mix-blend-mode: multiply` on the logos so that none of them painted the
   white rectangle its own image file carries. Black on it measured 11.49:1 and
   the wall hint was promoted from --ink-mute (3.66:1, a fail) to --ink-soft
   (7.43:1).

   Toby, 22 Sep 2026, later the same day: "Our clients section should have a
   white background." That is the newer instruction and it wins. The sky blue,
   the hue re-point, the multiply and the --ink-soft promotion have all gone
   with it, and this band is plain --paper again.

   HE IS RIGHT, AND THE REASON IS IN WHAT THE BLUE NEEDED. Every client logo we
   hold is an OPAQUE image with a white ground - which is exactly why task
   P155-#25 refused to put this band on black. On ANY coloured ground each logo
   paints its own rectangle, redrawing the holder Toby has twice had removed.
   `multiply` hid that, but multiply also DARKENS what it keeps, so a logo
   brought back into colour on hover came back deepened rather than as itself -
   and "hovering brings that logo into colour" is a success criterion of this
   whole goal. A white ground needs no trick: the logos sit on the page with
   nothing drawn around them, which is what the wall was always for.

   There is nothing left to measure here. The heading is --ink on --paper at
   21:1 and the hint is --ink-mute at 6.7:1, both exactly as they were before
   this task touched the section.
   --------------------------------------------------------------------------- */

/* =========================================================================
   THE GALLERY PAGE. Task P155-#68.
   Toby, 22 Sep 2026: "Add a 'Gallery' Button to the top menu which takes you
   to a picture gallery that I can edit from the CRM."

   A MASONRY-ISH GRID THAT CROPS NOTHING. The pictures are whatever Toby
   uploads - a photograph of a boxed order is landscape, a single sock on a
   bench is portrait - so a fixed aspect ratio would either crop half of them
   or letterbox the rest. `grid-auto-rows` with `align-items: start` lets each
   figure be exactly as tall as its own picture and the columns simply run
   ragged at the bottom, which is what a gallery looks like.

   THE COLUMNS ARE `auto-fill`, NOT `auto-fit`. With two pictures in the list
   auto-fit would stretch them to half the page each; auto-fill keeps them at
   their natural column width and leaves the rest of the row empty, so a
   gallery Toby is still filling looks like a gallery with two pictures in it
   rather than like two enormous ones.

   NOTHING HERE NAMES A COLOUR except --paper-alt as the reserved box behind an
   image that has not decoded yet, which is the same grey the design grid and
   the post covers already use.
   ========================================================================= */
.gallery-in { width: 100%; max-width: 1120px; margin: 0 auto; }
.gallery-grid {
    list-style: none; margin: 0; padding: 0;
    display: grid; gap: 22px;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    align-items: start;
}
.gallery-grid li { margin: 0; }
.gallery-grid figure { margin: 0; }
.gallery-grid img {
    width: 100%; height: auto; display: block;
    background: var(--paper-alt);
}
.gallery-grid figcaption {
    margin: 10px 0 0;
    font: 700 13px/1.4 var(--alt); letter-spacing: .01em; color: var(--ink);
}
@media (max-width: 620px) {
    .gallery-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 14px; }
}

/* ---------------------------------------------------------------------------
   THE TESTIMONIALS BAND IS THE PEACH ONE. Task P155-#67.

   Toby, 22 Sep 2026: the testimonials heading "should be a different
   colour." (He renamed that heading to "Testimonials" later the same day,
   task P155-#72; the band it names is the same one.)

   IT WAS THE PERIWINKLE TINT and the process band above it is the periwinkle
   WASH, so the two sections on the landing page that carry any colour at all
   were carrying the same one, one step apart in strength. Read down the page
   that is not two colours, it is one colour that has faded.

   WHY PEACH AND NOT ONE OF THE OTHER THREE. Of the five in the palette:
     * CORNFLOWER is the nearest neighbour of the periwinkle - both are the
       blue-violet end of the same gradient - so it would have repeated the
       problem rather than fixed it.
     * OLIVE is the one ground in this file whose tint cannot carry --ink-mute
       (3.63:1), and it is the heaviest of the five. Under a black band it
       reads as a second dark band.
     * SAGE is a genuine alternative and was the runner-up; it is simply
       quieter, and this section sits between white and black where a quiet
       green is the least of the five that anything happens to.
     * PEACH is the only WARM colour in the palette, and the rest of this page
       is cool - a periwinkle wash, white, black. One warm band is what makes
       the testimonials a place on the page rather than another section, and
       the colour is still the brand's own: it is the gradient's top-right
       stop, read off societalsocks.com by task P155-#62.

   IT DOES NOT CLASH WITH WHAT IT SITS BETWEEN. Above it is the client wall on
   plain white and below it the invitation on black - two neutrals, so there is
   no second hue anywhere near it to fight with. The process band, the only
   other coloured ground on the page, is four sections further up.

   MEASURED, NOT EYEBALLED. --c-peach-tint is #fddfc4 and every pair in this
   section clears AA with room to spare (4.5:1 is the bar for body text, 3:1
   for large):
       h2 "Testimonials"          --ink  #000000   16.53:1
       the quote itself, 18px     --ink-soft       10.69:1
       the attribution, 13px bold --ink  #000000   16.53:1
       the role under it, 13px    --ink-soft       10.69:1  (promoted from
                                  --ink-mute by the rule below, which already
                                  applies to every `.band.tint`)

   HOW IT IS DONE: the section re-points the four --hue-* variables to the
   peach ones for itself and its children, exactly as `body.hue-peach` does
   for a whole page. The `.band.tint` rules above and below are untouched and
   still say `var(--hue-tint)`, so this adds no second way of colouring a band
   - the 56px mark over the heading and anything else that reads the page hue
   follow it to peach without a rule of their own. No new colour is defined
   anywhere; all four values come from the five-colour palette in section 12.
   --------------------------------------------------------------------------- */
#words {
    --hue:      var(--c-peach);
    --hue-wash: var(--c-peach-wash);
    --hue-tint: var(--c-peach-tint);
    --hue-deep: var(--c-peach-deep);
}

/* --ink-mute is 4.87:1 on the periwinkle tint, which clears AA but only just,
   and 3.63:1 on the olive one. Small print on a TINT is --ink-soft instead,
   which is 9.89:1 at its worst across the five. */
section.band.tint .band-head p,
section.band.tint .quote .role,
section.band.tint .wall-hint { color: var(--ink-soft); }

/* THE MARK ABOVE A SECTION HEADING. 56x5px of the page's hue, the same bar the
   split sections draw, so a visitor moving from the home page to a category
   page meets the same furniture. It is decoration and carries no text, so no
   contrast pair is introduced by it. */
.band-head h2::before {
    content: ""; display: block;
    width: 56px; height: 5px;
    background: var(--hue);
    margin: 0 0 22px;
}
/* On black, the page's own hue may be olive, which is 2.99:1 on #000 and would
   be an invisible bar. Every dark ground on this site uses the periwinkle for
   its marks for exactly that reason - it is 8.54:1 on black. */
section.band.dark .band-head h2::before { background: var(--c-peri); }

/* THE STEP NUMERALS. 01, 02, 03 were --ink-mute; they are the page's deep
   shade now, which is 7.83:1 on the periwinkle wash they sit on. They are the
   numeral only - the sentence beside each one is untouched black. */
.band.wash .step .n, .band.tint .step .n { color: var(--hue-deep); }
/* THE SOFTENED BORDER IS GONE WITH THE GROUND IT WAS FOR. Task P155-#70.
   `border-color: rgba(0,0,0,.14)` existed because --line (#d6d6d6) disappears
   on a coloured tint. Both bands are white now, so --line is correct again -
   and the shorthand was also repainting the 4px hue rule `.steps` grew in
   section 15, which is the mark the whole step list hangs from. Only the row
   dividers are named here; the top rule is left to section 15. */
.band.wash .step, .band.tint .step { border-bottom-color: var(--line); }

/* ---------------------------------------------------------------------------
   THE SIX CATEGORY ROWS: THE DESTINATION'S COLOUR WHEN ACTIVE, AND OTHERWISE
   EXACTLY AS THEY WERE. Task P155-#68.

   Toby, 22 Sep 2026: "In the 'who we make them for section' extend the
   allocated accent colour across the button rather than the purple (eg,
   businesses is green) WHEN ACTIVE (otherwise as it was is great)."

   THE RESTING ROW IS UNTOUCHED, and that is the instruction rather than a
   convenience: a 5px rule on the left edge in the destination page's hue, the
   label black and the sentence beside it --ink-soft, a 1px hairline between
   rows. An earlier pass this same task filled every row with its hue at rest as
   well; Toby has said the resting design is already right, so it is back
   exactly as it was. (The BAND behind the rows did move, from white to the
   periwinkle tint, as part of the colour pass he asked for in the same list -
   but that is the section's ground, not the rows.)

   WHAT CHANGED IS THE ACTIVE STATE. It used to fill with the PERIWINKLE - the
   same purple for all six, whatever colour the row's own edge was, so the one
   moment the row actually filled with colour contradicted the allocation the
   edge was expressing. An active row now fills EDGE TO EDGE with its own
   destination page's hue: Businesses reads sage green, Schools olive, Clubs
   cornflower, Universities periwinkle, Fundraising peach, and How it works
   olive because how-it-works.html is the olive page.

   HOVER AND KEYBOARD FOCUS ARE THE SAME STATE. This list has no selected tab -
   every row is a link to another page - so "active" here is the row the
   visitor is pointing at or has tabbed to. `:focus-visible` is paired with
   `:hover` rather than left to the outline, so a keyboard user gets the same
   answer to "which one am I on" that a mouse user gets.

   ONE CUSTOM PROPERTY PER ROW, NOT ONE RULE PER STATE. Each `li` declares
   --seg-active and, where it needs one, --seg-on; the three rules under them
   read those two and nothing else. A seventh category is one line.

   OLIVE IS THE ONE THAT INVERTS, AND IT IS MEASURED RATHER THAN ASSUMED.
   --c-olive is #4d6001, which is DARK - black on it is 2.99:1 and --ink-soft is
   1.93:1, both well under AA. Its rows therefore take WHITE type when active.
   Every one of the twelve states was measured on the rendered page with the
   :hover pseudo-state forced, not inferred from this file:

       row            RESTING (on white -      ACTIVE
                      see the note below)
                      body / label             ground    body    label
       Schools       13.58:1 / 21.00:1         #4d6001   7.03:1  7.03:1 <- inverts
       Clubs         13.58:1 / 21.00:1         #93c3ff   7.43:1 11.49:1
       Universities  13.58:1 / 21.00:1         #ab96ff   5.52:1  8.54:1
       Fundraising   13.58:1 / 21.00:1         #faaa63   7.11:1 10.99:1
       Businesses    13.58:1 / 21.00:1         #97b49d   6.03:1  9.33:1
       How it works  13.58:1 / 21.00:1         #4d6001   7.03:1  7.03:1 <- inverts

   THE BAND BEHIND THEM IS WHITE NOW. Task P155-#69. Toby, 22 Sep 2026: "Change
   the background of the 'who we make them for' section to white." It was the
   periwinkle tint #dfd7ff, where the resting numbers were 9.89 and 15.30; on
   white they are 13.58 and 21.00, so every resting pair improved and nothing
   needed re-colouring to survive the move. The section head on it: h2 --ink
   21.00:1, the sentence under it --ink-mute 6.69:1. The 56px rule over the h2
   and the six left-edge rules are decoration and introduce no text pair - the
   only one worth naming is --c-olive on white at 7.03:1, the palest-on-white
   any of the six edges gets, which is well clear even though it need not be.

   THE ROWS THEMSELVES ARE UNTOUCHED BY THAT CHANGE. Toby's "otherwise as it was
   is great" is about the ROW, and the row still renders exactly as it did: no
   fill of its own at rest, a 5px rule at its left edge, a hairline under it.
   What changed is the colour it inherits from the band behind it.

   AA wants 4.5:1 for body text and 3:1 for large; the lowest number anywhere
   in that table is 5.52 and it clears the stricter bar.

   The order below is DOCUMENT order, not id order. See the note in index.html
   about why "How it works" is li5 and sits last. */
.seg-links li { border-left: 5px solid transparent; padding-left: 0; transition: border-color .15s; }
.seg-links li:nth-child(1) { border-left-color: var(--c-olive); --seg-active: var(--c-olive); --seg-on: var(--on-dark); }
.seg-links li:nth-child(2) { border-left-color: var(--c-corn);  --seg-active: var(--c-corn);  }
.seg-links li:nth-child(3) { border-left-color: var(--c-peri);  --seg-active: var(--c-peri);  }
.seg-links li:nth-child(4) { border-left-color: var(--c-peach); --seg-active: var(--c-peach); }
.seg-links li:nth-child(5) { border-left-color: var(--c-sage);  --seg-active: var(--c-sage);  }
.seg-links li:nth-child(6) { border-left-color: var(--c-olive); --seg-active: var(--c-olive); --seg-on: var(--on-dark); }
.seg-links a { padding-left: 18px; }
.seg-links a:hover, .seg-links a:focus-visible {
    background: var(--seg-active);
    color: var(--seg-on, var(--ink-soft));
    padding-left: 32px; padding-right: 16px;
}
.seg-links a:hover span, .seg-links a:focus-visible span { color: var(--seg-on, var(--ink)); }

/* ---------------------------------------------------------------------------
   "WHO WE MAKE THEM FOR" FITS ON ONE SCREEN. Task P155-#71.
   Toby, 22 Sep 2026: "make it so who we make them for section one visual
   height."

   THE PROBLEM WAS THE DEFAULTS, NOT THE CONTENT. Nothing in this section was
   sized for it: `section.band` pays 96px of padding top and bottom to every
   band on the site, `h2` is clamp(34px, 5.2vw, 58px), `.band-head` adds 46px
   under itself, and each of the six rows pays 24px of padding on both sides of
   a 22px label. On a 1366x650 laptop that adds up to about 790px of section
   inside 543px of screen, so a visitor met the heading and four rows and had
   to scroll for Businesses and How it works - which is the whole point of the
   block, since the six rows ARE the section.

   IT IS TIGHTENED, NOT TRUNCATED. All six rows are still here, every word of
   every row is still here, nothing is display:none and nothing is clipped.
   What changed is that eleven measurements in this section now scale with the
   VIEWPORT HEIGHT instead of being fixed, so the section spends the height it
   has rather than the height a 96px default assumed.

   WHY vh AND NOT A MEDIA QUERY. A media query would have needed a breakpoint
   per screen shape and would have jumped between them. `clamp(min, Nvh, max)`
   gives a continuous answer: on a tall screen every number sits at its
   COMFORTABLE cap and the section looks very much as it did, and on a short
   one each falls smoothly towards its floor. The floors are the part that
   matters and they are all legibility floors, not arbitrary ones:

       row label      never below 16px bold   (was clamp(18px, 2vw, 22px))
       row sentence   never below 14.5px      (was a flat 16.5px)
       intro line     never below 14px        (was a flat 17px)
       heading        never below 28px        (was clamp(34px, 5.2vw, 58px))

   Nothing is below the 14px this site treats as its body minimum anywhere else,
   and the type only reaches those floors on a screen under about 700px tall.

   MEASURED, with the sticky bar's 107px taken off the viewport:

       1920 x 1080   section 631px   into 973px of screen
       1366 x  650   section 418px   into 543px of screen

   A PHONE IS EXPLICITLY NOT INCLUDED and cannot be. Below 720px the rows turn
   into two stacked lines each and the sentences wrap to two or three, so six
   rows plus a heading cannot fit 744px of phone screen without type nobody can
   read. The rules below leave the phone readable and let it scroll, which is
   the right answer there.

   THE BAND IS STILL WHITE AND THE ROWS STILL COLOUR ONLY WHEN ACTIVE. Nothing
   below touches a background, a border colour or a hover state - this is
   spacing and type size and nothing else. */
#segments { padding: clamp(28px, 5vh, 64px) 0; }
#segments .band-head { max-width: 720px; margin-bottom: clamp(14px, 2.6vh, 34px); }
#segments .band-head h2 {
    font-size: clamp(28px, 4.2vh, 46px);
    line-height: 1.06;
}
#segments .band-head h2::before {
    width: 48px; height: 4px;
    margin-bottom: clamp(8px, 1.4vh, 18px);
}
#segments .band-head p {
    font-size: clamp(14px, 1.8vh, 17px);
    margin-top: clamp(8px, 1.2vh, 14px);
}
#segments .seg-links a {
    padding-top: clamp(9px, 1.7vh, 20px);
    padding-bottom: clamp(9px, 1.7vh, 20px);
    font-size: clamp(14.5px, 1.75vh, 16.5px);
}
#segments .seg-links a span { font-size: clamp(16px, 2.1vh, 21px); }

/* THE PAGE HEADS ON THE OUTER PAGES. blog.html, our-work.html, privacy.html
   and exclude.html open on a head that was flat white. It takes the page's
   wash and the hue rule under it, which is the `.sec.p-hero` treatment
   expressed for the three stylesheets that predate it. */
/* AND THE WASH RAMPS TO PAPER RATHER THAN STOPPING DEAD. Task P155-#70, the
   same correction `.sec.p-hero.bg-tint` gets in section 13 and for the same
   reason: at a flat fill the 5px hue rule below was the seam between two
   coloured areas, and on a ramp that reaches white it is a line drawn on paper.
   The wash is unchanged - it is still --hue-wash at the top of the band - so
   every ratio measured against it is a floor. Black on the five washes is
   17.0:1 at worst (olive) and the ramp only ever moves towards 21:1.
   our-work.css overrides `.work-head, .case-head` with its own corn-to-peri
   ombre and is linked after this file, so this rule reaches `.blog-head`
   (privacy.html, exclude.html) and is the fallback for the other two. */
/* AND IT IS A FALLBACK ONLY. Task P155-#73. Every one of these four pages
   carries `.bg-block` on its head now, which is the site-wide solid-block
   treatment in section 13; this rule survives for a head that forgets to ask
   for one, and `:not(.bg-block)` keeps it from painting a wash over a block
   simply because this stylesheet says it later. */
.blog-head:not(.bg-block), .work-head:not(.bg-block), .case-head:not(.bg-block) {
    background: var(--hue-wash);
    border-bottom: 5px solid var(--hue);
}

/* A LIST OF CARDS ON A COLOURED GROUND still needs a white card, or the card
   stops being a card. */
.band.wash .made-grid figure, .band.tint .made-grid figure { background: var(--paper); }

/* -------------------------------------------------------------------------
   14. WHERE THE REST OF THE COLOUR LIVES. Task P155-#64.

   our-work.html and shop.html link their own stylesheet AFTER this one, and
   both style the component this pass wanted to mark - `.work-card` sets a
   `box-shadow` outline and `.gate-card` sets the `border` shorthand, either of
   which would flatten a `border-top` declared here no matter how the rules
   were ordered in this file. So the client-card rule is at the foot of
   our-work.css and the private-shop door is at the foot of shop.css, each
   reading the same --hue-* variables declared in section 12 above. There is
   one palette; it is just applied in the file that owns the component.
   ------------------------------------------------------------------------- */

/* -------------------------------------------------------------------------
   15. TWO PLACES THE COLOUR GOES THAT COST NOTHING IN CONTRAST.
   Task P155-#64.

   Both of these colour a LINE, never a letter. The body text on every page of
   this site is black on a light ground or white on a dark one and none of it
   changed in this task; what changed is the furniture around it, which is
   where a business-to-business site can afford colour and where a paragraph
   cannot.

   1. THE UNDERLINE ON A LINK IN RUNNING TEXT. blog.css sets `.post-body a` as
      black with a 1px underline. The word stays black - 21:1 on paper, 15.3:1
      on the strongest tint - and the underline becomes the page's deep shade,
      which is 6.4:1 against the tint at its worst and is not carrying the
      meaning on its own: the underline is still there, and still an underline,
      for anyone who cannot see the difference. The hover fill is untouched.

   2. THE FAQ CHEVRON. Two borders rotated 45 degrees, drawn in the page's deep
      shade instead of black. It is a disclosure marker beside a black heading
      that already says the same thing, so nothing is lost if the colour is
      not perceived.
   ------------------------------------------------------------------------- */
.sec .post-body a { text-decoration-color: var(--hue-deep); text-decoration-thickness: 2px; }
.sec.bg-ink .post-body a, .sec.bg-deep .post-body a { text-decoration-color: var(--c-peri); }

.sec .post-body .faq-item summary::after {
    border-right-color: var(--hue-deep);
    border-bottom-color: var(--hue-deep);
}
.sec.bg-ink .post-body .faq-item summary::after,
.sec.bg-deep .post-body .faq-item summary::after {
    border-right-color: var(--c-peri);
    border-bottom-color: var(--c-peri);
}

/* -------------------------------------------------------------------------
   16. A SOLID BUTTON ON A BLACK BAND KEEPS ITS BLACK TYPE. Task P155-#64.

   Found by measurement, not by eye, and it is older than this task. Three
   pages put a solid `.btn` inside a `section.band.dark`: the home page's
   "enquiries@societalsocks.com" in the contact block, and the closing ask on
   our-work.html and on a case-study page. `.btn` sets black on the periwinkle,
   which is 8.54:1 - but `section.band.dark a { color: var(--on-dark) }` is one
   element more specific than `.btn`, so it won, and what actually rendered was
   WHITE on the periwinkle at 2.46:1. That is the single worst pair anywhere on
   this site and it was on the main contact button.

   The hover was the same bug wearing a different hat: `.btn:hover` turns the
   button black, which on a black band is a button you cannot see at all. On a
   dark ground it inverts to white instead.

   GHOST BUTTONS ARE NOT TOUCHED. `.btn.ghost` is transparent with white type,
   which is 21:1 on black and correct; `:not(.ghost)` says so explicitly and
   `#contact .btn.ghost` outranks this rule in any case.
   ------------------------------------------------------------------------- */
section.band.dark a.btn:not(.ghost) { color: #000; }
section.band.dark a.btn:not(.ghost):hover {
    background: var(--on-dark); border-color: var(--on-dark); color: #000;
}

/* =========================================================================
   14. THE INVITATION, FRAMED. Task P155-#69.

   Toby, 22 Sep 2026: "Make sure there are proper boarders around the 'we'd love
   to add you to our list' at the bottom of the landing page (and any other page
   its on)."

   IT IS ON THREE PAGES AND IT WAS TWO DIFFERENT THINGS. On index.html it was
   `section.band.accent` - periwinkle at full strength, edge to edge, with
   `padding-bottom: 0` so the heading ran off its own bottom edge straight into
   the black contact band. On our-work.html and our-work-client.html it was
   `section.band.deep` plus a per-page `.work-cta` padding and headline size,
   which on the old peach hue painted #714c2d: the brown. Neither had a border
   of any kind, and the two did not match each other.

   ONE TREATMENT, AND IT IS A SHAPE THE SITE ALREADY OWNS. The band drops to
   paper and the words move inside `.panel` - shape 3 of the seven section
   patterns, the card the nine commercial pages have carried since P155-#64.
   Nothing here is a new component: the ground, the padding, the 5px left rule
   and the hairline round the other three sides are all set by the shared
   `.sec.p-panel .panel` rule in section 12, which this block joins by selector.
   All three pages render the same element with the same classes, so the frame
   cannot drift page to page.

   THE PANEL'S GROUND IS THE PERIWINKLE, NOT THE PAGE'S TINT, AND THAT IS THE
   ONE THING THIS BLOCK OVERRIDES. A `.p-panel` card takes --hue-tint, which is
   a different colour on every page - correct for a card inside a page's
   argument, wrong for a block that has to be recognisably the SAME invitation
   wherever a visitor meets it. --c-peri is the brand accent and is the one hue
   the restraint rules in section 12 allow at full strength, so the loud colour
   #68 gave this block survives; it is simply bounded now instead of bleeding.
   The frame goes to --ink for the same reason: on the periwinkle, --hue-deep
   would be a different edge colour on each of the three pages.

   MEASURED, and every pair is the same on all three pages because the ground is:
       h2 --ink on --c-peri                     8.54:1
       filled .btn  --on-dark on --ink          21:1
       ghost  .btn  --ink on --c-peri           8.54:1
       ghost hover  --on-dark on --ink          21:1
   A filled button would otherwise be periwinkle on periwinkle (1:1), which is
   the same inversion `section.band.accent .btn` used to make and it is carried
   over here rather than re-derived.

   THE HEADLINE SIZE IS UNIFIED, DOWNWARD. index.html set clamp(32px, 5.4vw,
   60px) and the two work pages clamp(28px, 4.2vw, 46px). Inside a bounded card
   the 60px version wrapped to three lines and pushed the frame into a shape
   nothing else on the site has, so all three take the smaller clamp. No word
   changed; only the type size.
   ========================================================================= */
/* ---------------------------------------------------------------------------
   AND THEN THE FRAME BECAME THE HIGHLIGHTER. Task P155-#70.

   Toby, 22 Sep 2026, on the result of #69: "Sometimes you intergrate colours
   very poorly, it looks like someone has just highlighted part of the website."
   The invitation was the clearest instance on the site and he named it: a 900px
   rectangle of flat #ab96ff floating in white with a heading in it and nothing
   else. A border around a highlighter mark is a bordered highlighter mark.

   WHAT AN INVITATION ACTUALLY LOOKS LIKE. Not a filled box. A statement set
   large and centred, with space around it and a rule above and below - the
   shape of a printed card, a masthead, a wedding invitation. So the fill goes
   entirely, the words go to the middle, and the two rules that bracket them are
   THE OMBRE: all five palette colours in one 6px line, declared once in section
   12 and used in exactly two places on the site.

   WHY ALL FIVE HERE AND ONE HUE EVERYWHERE ELSE. Rule 2 in section 12 says a
   page is never a rainbow, and it still is not: every SECTION of every page
   takes that page's single hue. This block is the exception that proves it,
   because it is the only thing on the site that is not speaking for a page. It
   is the brand asking the visitor to join it and it is the same words in the
   same shape on index.html, our-work.html and our-work-client.html - so it
   takes the whole palette rather than whichever page it happens to be sitting
   at the bottom of. All five colours, drawn as a line and never as a field.

   IT IS THE SAME MARKUP AS BEFORE. `section.band.invite .panel` on all three
   pages, `data-copy` ids untouched, not one word changed. Only the paint.

   MEASURED, on white, and the same on all three pages:
       h2 --ink on --paper                      21:1
       filled .btn  --on-dark on --ink          21:1
       ghost  .btn  --ink on --paper            21:1
       ghost hover  --on-dark on --ink          21:1
   The ombre rules carry no text and are 6px, so they introduce no pair. Every
   number here is better than the 8.54:1 the periwinkle ground gave.
   --------------------------------------------------------------------------- */
section.band.invite { background: var(--paper); padding: 84px 0; }
section.band.invite .panel {
    /* The shared `.p-panel` card is overridden back to nothing: no ground, no
       frame, no left rule. What is left is the measure and the space. */
    background: none;
    border: 0;
    color: var(--ink);
    position: relative;
    max-width: 820px;
    margin: 0 auto;
    padding: 46px 0;
    text-align: center;
}
/* The two rules. Absolutely positioned so they span the panel's full measure
   regardless of how the heading wraps, and so neither is part of the flow that
   `.panel > *:first-child { margin-top: 0 }` reasons about. */
section.band.invite .panel::before,
section.band.invite .panel::after {
    content: ""; position: absolute; left: 0; right: 0;
    height: 6px; background: var(--ombre);
}
section.band.invite .panel::before { top: 0; }
section.band.invite .panel::after  { bottom: 0; }
section.band.invite .panel h2 {
    color: var(--ink);
    font-size: clamp(30px, 4.6vw, 52px);
    max-width: 22ch;
    margin: 0 auto;
    text-wrap: balance;
}
/* The two work pages put buttons under the heading; index.html does not. The
   flex row is centred so the block reads as one centred composition on all
   three rather than centred on one and left-aligned on the others. */
section.band.invite .contact-actions {
    margin-top: 30px;
    display: flex; flex-wrap: wrap; gap: 12px; justify-content: center;
}
section.band.invite .btn { background: var(--ink); border-color: var(--ink); color: var(--on-dark); text-decoration: none; }
section.band.invite .btn:hover { background: var(--paper); border-color: var(--ink); color: var(--ink); }
section.band.invite .btn.ghost { background: transparent; color: var(--ink); border-color: var(--ink); }
section.band.invite .btn.ghost:hover { background: var(--ink); color: var(--on-dark); }
@media (max-width: 860px) {
    section.band.invite { padding: 52px 0; }
    section.band.invite .panel { padding: 34px 0; }
}

/* -------------------------------------------------------------------------
   TWO SECTIONS ARE WHITE ON PURPOSE, NOT BY OMISSION. Task P155-#69.

   Toby named both: "Our clients section should have a white background" and
   "Change the background of the 'who we make them for' section to white".
   A plain `.band` paints nothing and the body's --paper shows through, so both
   already RENDERED white - but `getComputedStyle(el).backgroundColor` reported
   `rgba(0, 0, 0, 0)`, which is "unpainted", not "white". The difference matters
   the next time somebody colours the bands: an unpainted section silently takes
   whatever is put behind it, a white one does not. These two were asked for by
   name, so they say so.

   No pair is introduced: --ink on --paper is 21:1 and --ink-mute (the logo wall
   hint) is 6.69:1, which is what both were already rendering against.
   ------------------------------------------------------------------------- */
section.band#work, section.band#segments { background: var(--paper); }

/* TWO WHITE BANDS NOW MEET, AND A HAIRLINE IS WHAT SEPARATES THEM.
   Task P155-#75.

   Toby's running order puts "Who we make them for" immediately above "Our
   Clients". Both are white BY NAME - he asked for each of them in so many
   words - so neither may be given a ground to tell it from its neighbour, and
   without something between them the two read as one very tall white section
   with two headings in it.

   A HAIRLINE OF --line, FULL BLEED, ACROSS THE TOP OF #work. It is the rule
   this site already draws between the rows of a list and between two
   testimonials; it is not a new device and it is not a colour. Both
   backgrounds are still --paper and `getComputedStyle` still reports
   rgb(255, 255, 255) on each, which is what the rule above exists to
   guarantee. The border is on #work rather than under #segments because
   #segments is also the page's own section and is followed by the footer on
   nothing; the divider belongs to the band that arrives. */
section.band#work { border-top: 1px solid var(--line); }

/* =========================================================================
   15. COLOUR IN THE THINGS THEMSELVES. Task P155-#70.

   Sections 12, 13 and 14 above take colour OFF the grounds. On its own that
   would leave a white site, and the instruction was not "use less colour" -
   Toby asked in P155-#68 to "lean into it" and in #70 to stop it looking like
   a highlighter. Those pull the same way, not opposite ways: colour should
   appear MORE often and in MORE places, and flat coloured rectangles far less.

   So this section puts the hue back, in the things that make up a page rather
   than behind them. Six roles, and each one is a place the eye already goes:

     1. DISPLAY NUMERALS    the 01/02/03 down "Our Process"
     2. RULES AND MARKS     the rule over the steps, over the testimonials,
                            the 56px marks (already in section 12)
     3. BORDERS AND EDGES   the 5px left edge of every framed card (section 12),
                            the 4px section rule on `.bg-tint`
     4. BUTTONS AND STATES  the periwinkle CTA button on white (section 13)
     5. SMALL MARKS         the peach rule above each testimonial
     6. THE OMBRE           the invitation and the CTA (sections 12/14)

   THE ONE RULE THAT GOVERNS ALL OF THEM: a hue at FULL STRENGTH may be a rule,
   an edge or a mark, and may never be TEXT. Four of the five measure under
   4.5:1 on white (periwinkle 2.0, cornflower 1.7, peach 2.1, sage 2.4); only
   olive clears it, at 7.03. Anything that is read takes --hue-deep, whose worst
   case across the five is 7.30:1. Every declaration below obeys that split, and
   it is why the numerals are deep and the rules are not.
   ========================================================================= */

/* ---- 1. DISPLAY NUMERALS ------------------------------------------------
   "01 02 03" were 15px Space Grotesk in --ink-mute: small grey labels beside
   the sentences, invisible as soon as the lilac slab behind them went. They are
   display numerals now - the Sora face the counter and the section headings
   use, at 28px, in the page's deep shade. The step is one of the few places on
   a landing page where a number is genuinely the subject, and this is the same
   move the opening makes with 8,242 at a tenth of the size.
   --ink-mute -> --hue-deep also RAISES contrast on white, from 6.7:1 to 8.79:1
   on the periwinkle page. */
.steps { border-top: 4px solid var(--hue); }
.step { grid-template-columns: 62px 1fr; align-items: baseline; }
.step .n {
    font: 700 28px/1.1 var(--display);
    letter-spacing: -0.02em;
    color: var(--hue-deep);
}
@media (max-width: 860px) {
    .step { grid-template-columns: 48px 1fr; }
    .step .n { font-size: 23px; }
}

/* ---- 5. THE TESTIMONIAL MARK -------------------------------------------
   The testimonials band was a full-bleed peach slab. It is white now and the
   peach moved into two marks: the 4px rule the grid hangs from, and a short
   36px rule over each individual quote. A quotation mark drawn as a rule -
   small, repeated, and the only peach on the page apart from the section's own
   56px mark and the ombre.

   PEACH MEANS "SOMEBODY ELSE IS SPEAKING" and it is used for nothing else on
   the landing page. #words re-points --hue to peach for itself (section 13), so
   both rules below read `var(--hue)` and follow it without naming a colour.
   Both are decoration and carry no text; the quote itself is --ink-soft on
   white, 13.6:1, up from 10.7:1 on the peach tint it used to sit on. */
#testimonial-list { border-top: 4px solid var(--hue); }
.quote blockquote::before {
    content: ""; display: block;
    width: 36px; height: 4px;
    background: var(--hue);
    margin: 0 0 20px;
}

/* ---- THE UNUSED ACCENT BAND ---------------------------------------------
   `section.band.accent` is the landing page's twin of `.bg-accent` and is not
   currently on any page - the invitation was the last thing using it and moved
   off it in P155-#69. It is corrected rather than deleted so that it cannot
   reintroduce a full-strength periwinkle slab the next time somebody reaches
   for it: the periwinkle WASH as its ground and the ombre rule on top, exactly
   as `.p-cta.bg-accent` in section 13. The type-colour rules above it still
   apply: --ink is 18.70:1 on that wash and --ink-soft is 12.10:1. */
section.band.accent {
    background: var(--c-peri-wash);
    position: relative;
}
section.band.accent::before {
    content: ""; position: absolute; top: 0; left: 0; right: 0;
    height: 6px; background: var(--ombre);
}
section.band.accent .band-head h2::before { background: var(--hue); }
section.band.accent .btn { background: var(--c-peri); border-color: var(--c-peri); color: #000; }
section.band.accent .btn:hover { background: var(--ink); border-color: var(--ink); color: var(--on-dark); }

/* ---- THE JUNCTIONS -------------------------------------------------------
   Task P155-#70, and this is the half of the fix that is about what happens
   BETWEEN two sections rather than inside either of them.

   Toby: a hard edge between two saturated areas is what makes colour look
   pasted on. After the three sections above there are only three kinds of
   ground left on the site - white, black, and a gradient that ends in white -
   so the only junction that can still produce one is a ruled section arriving
   directly underneath something already saturated. It happens in two places:

     * a `.bg-tint` / `.band.tint` immediately after a BLACK section. Its 4px
       hue rule would sit flush against the black edge, which is a coloured
       line on a black field and reads as a seam. It occurs on six pages -
       schools, universities, how-it-works, how-it-works-pricing, clubs and
       businesses - wherever a dark split is followed by a tinted prose block.
     * `#process` immediately after `#opening` on the landing page, where the
       rule would land directly against the bottom edge of whatever the opening
       is carrying. The opening is the loudest thing on the site by design and
       that is not going to become less true, so this suppression is keyed on
       ADJACENCY rather than on what the opening happens to contain.

       P155-#75 REORDERED THE LANDING PAGE AND BOTH OF THOSE CASES ARE NOW
       EMPTY ON IT, which is the point of keying them on adjacency. `#opening`
       is followed by `#why` (`.band.deep`), and `#why` is followed by
       `#segments` (a plain white band), so on index.html not one of the five
       selectors below matches anything any more. None of them has started
       applying to a section it was not meant for - they simply stop firing.
       They are kept because the six category pages still hit the first case,
       and because the landing page's order is Toby's and may move again.

       THE ONE JUNCTION THE NEW ORDER DID CREATE is `#words` -> `#process`:
       peach tint under periwinkle tint. That is exactly what the 4px rule is
       for - two different light tints with a drawn line between them - so it
       is deliberately NOT suppressed.

   IN BOTH CASES THE RULE IS DROPPED RATHER THAN MOVED, because in both cases
   the thing above has already done the dividing. A black band ends in the
   hardest edge the brand owns and needs no help announcing that it has
   finished; the opening is the loudest block on the site whatever it holds. Adding a second
   divider on top of either is the "highlighting" instinct in miniature, and
   the section below simply begins on white - which is the breathing room the
   brief asks for between two areas of colour.

   Every remaining junction on the site is therefore white-to-white,
   white-to-black, black-to-white, gradient-to-white, or a 4px rule with white
   on both sides of it. There is no longer anywhere that two saturated fields
   meet. */
#opening + .band.tint,
.band.dark + .band.tint,
.band.deep + .band.tint,
.bg-ink  + .bg-tint,
.bg-deep + .bg-tint { box-shadow: none; }

/* =========================================================================
   16. THE PAGE'S COLOUR, CARRIED DOWN THE PAGE IN DIFFERENT FORMS.
   Task P155-#73.

   Toby, 22 Sep 2026, in the same message that allocated a colour to each page:
   "use these colours as the theme throughout the page, varying the format and
   usage."

   THE BLOCK IS THE TITLE AREA AND NOTHING ELSE. If the hue then vanished the
   moment the block ended, the page would be a coloured hat on a white page -
   which is the "arbitrary slab" P155-#70 spent a whole task removing. So the
   hue has to recur below the block, and the thing that stops the recurrence
   becoming a stack of slabs is that it takes a DIFFERENT FORM each time.

   THE FORMS ALREADY IN THIS FILE, all reading `var(--hue)` and none of them a
   ground: the 5px left edge of a panel, the 56px mark above a split heading,
   the 4px rule along the top of a card, the 4px rule over the steps and the
   testimonial grid, the 64px `.hue-rule`, the eyebrow in --hue-deep, the step
   numerals in --hue-deep, the 2px underline under a link in running text, and
   the flat --hue-tint ground with its 4px top rule. This section adds the
   forms those leave missing, page by page, plus the one correction that six
   colours force.

   AND SINCE P155-#76 IT ALSO CARRIES THE ONE RULE A DARK HUE NEEDS. Navy is
   the seventh colour and the first whose title block is dark; rule 4 at the end
   of this list is the whole of what that costs.

   THE CORRECTION: TWO HUES HAVE NO COLOURED DEEP. --c-peach-deep and
   --c-ochre-deep are both #000000 - peach since P155-#70 retired the brown,
   ochre from the day it was added, for the same reason (see section 12). Every
   rule in this file that expresses the hue as TYPE or as a fine line reads
   --hue-deep, so on the gallery and the FAQs those rules come out black and
   the page loses its colour below the block. The three rules immediately below
   send those DECORATIVE cases to --hue at full strength instead. They are all
   lines and marks, never letters: #faaa63 is 2.10:1 on white and #e2aa00 is
   2.10:1, so neither may ever be read, and neither is being asked to be.
   ========================================================================= */

/* 1. THE LINK UNDERLINE, on the two pages whose deep is black. The word stays
      black at 21:1 and the underline stops being a black line under black
      type, which is not a form of anything. */
body.hue-peach .sec .post-body a,
body.hue-ochre .sec .post-body a { text-decoration-color: var(--hue); }

/* 2. THE FAQ CHEVRON, same reasoning. It is a disclosure marker beside a
      heading that already says the same thing in words. */
body.hue-peach .sec .post-body .faq-item summary::after,
body.hue-ochre .sec .post-body .faq-item summary::after {
    border-right-color: var(--hue);
    border-bottom-color: var(--hue);
}

/* 3. THE EYEBROW, which is the one piece of TYPE the correction cannot fix -
      the colour is unreadable at 2.10:1 and stays black. It gains a rule
      instead: the label in black at 21:1 with a 3px bar of the hue under it.
      Type plus a mark, rather than coloured type. */
body.hue-peach .sec:not(.bg-block):not(.bg-ink):not(.bg-deep) .eyebrow,
body.hue-ochre .sec:not(.bg-block):not(.bg-ink):not(.bg-deep) .eyebrow {
    display: inline-block;
    color: var(--ink);
    border-bottom: 3px solid var(--hue);
    padding-bottom: 6px;
}

/* 4. THE BUTTON INSIDE A TITLE BLOCK, ON THE ONE HUE THE BLOCK IS DARK.
      Task P155-#76. `.bg-block .btn` is a BLACK pill with white type, which
      works on the six light hues and disappears on navy: #000000 against
      #001e57 is 1.32:1, well under the 3:1 WCAG 1.4.11 asks of the boundary of
      a control, so the button would read as a hole in the block rather than as
      something to press. It inverts to the white pill the block already uses
      for its hover state - #ffffff on #001e57 is 15.87:1 for the edge and
      black on white is 21:1 for the label - and hovers to black, which is the
      same two values swapped.

      GALLERY, THE ONLY NAVY PAGE TODAY, HAS NO BUTTON IN ITS BLOCK. This rule
      is here so that the next page given `hue-navy` cannot inherit an
      invisible one. */
body.hue-navy .bg-block .btn { background: var(--paper); border-color: var(--paper); color: var(--ink); }
body.hue-navy .bg-block .btn:hover { background: var(--ink); border-color: var(--ink); color: var(--on-dark); }

/* ---- HOW IT WORKS: THE FACTS PANEL GETS A BULLET ------------------------
   The panel already carries a 5px left edge of the olive. Its six sentences
   were an unmarked stack of paragraphs; each now has a 12x4px dash of the hue
   in the margin beside it. A bullet is a form nothing else on this site uses,
   it is decoration beside text that is already a complete sentence, and it
   turns "the numbers behind the six steps" into something that reads as a set
   of six rather than as a block. Reaches how-it-works.html and
   how-it-works-pricing.html, the two pages with a `.panel.facts`. */
.sec.p-panel .panel.facts p { position: relative; padding-left: 26px; }
.sec.p-panel .panel.facts p::before {
    content: ""; position: absolute; left: 0; top: .66em;
    width: 14px; height: 4px; background: var(--hue);
}

/* ---- THE FAQs: THE JUMP NAV AND THE OPEN QUESTION -----------------------
   Two forms, and both are functional rather than applied.

   THE JUMP LINKS took a grey 2px underline that went black on hover. The
   underline is the ochre now, so the navigation block at the top of the page
   is where the colour first recurs after the title block. That RESTING form is
   the one Toby singled out — "the active underline is currently yellow (yay
   that looks awesome)" — and it is the one line of this component that task
   P155-#81 deliberately did not touch. The link text is unchanged black at
   21:1 and the underline is decoration.

   THE HOVER AND THE SELECTED STATE ARE A FILLED HIGHLIGHT NOW, not a black
   underline, and they live in segment.css with the rest of the component
   rather than here. The line that used to sit under this one set the hover
   underline to --ink from a higher-specificity selector, which would have
   silently beaten the new rules; it has gone rather than been re-pointed,
   because two files setting the same state is how this component drifted in
   the first place. What is left below is the RESTING colour and nothing else.

   WHICH OTHER PAGES GOT THE SAME TREATMENT, since Toby asked for "all the
   other similar pages" and the honest answer is a short one. THREE components
   on this site let a visitor pick one thing out of a set, and they were each
   handled differently on purpose:

       .faq-jump   the menu he was looking at. faq.html ALONE — no other page
                   in this directory carries one. CHANGED.
       .post-toc   the contents list on a blog post, in blog.css. The same
                   idea as .faq-jump and it hover-highlighted in --accent too,
                   so it now reads --hue like this one. It is written by
                   tools/build-site.js onto a BUILT post and appears nowhere
                   on the staging site, so the change is real but unverifiable
                   here; it renders identically today because every post is
                   hue-peri and --hue on a peri page IS --accent. CHANGED.
       .seg-links  the "Who we make them for" rows on the landing page.
                   DELIBERATELY LEFT. It already fills an active row edge to
                   edge — not with the page's hue but with each category's OWN
                   destination colour, signed off in task P155-#72 — and
                   flattening it to one hue would undo that.

   The site's other link lists — the footer columns, the breadcrumb on a client
   page — are navigation, not a selection, and have no selected state to
   colour. The generalisation Toby asked for is real but it is in the WRITING:
   every rule in the highlight block names var(--hue), never --c-ochre, so the
   day a second page carries a jump menu it highlights in that page's colour
   with no CSS written for it.

   AN OPEN QUESTION TAKES A 4px LEFT EDGE. A `.faq-item` is a hairline-ruled
   row; opening one now marks it down its left side in the page's colour, so
   the colour is doing a job - it says which of thirty questions is the one you
   opened - rather than decorating. Closed rows are untouched, which keeps the
   list quiet until somebody uses it. */
/* `body` is on the front so it out-specifies segment.css, which sets the same
   property on the same selector and is linked after this file. */
body .post-body .faq-jump a { border-bottom-color: var(--hue); }
/* Toby, 24 Sep 2026: "for the FAQ jump links please make the underline black
   when its highlights". Hovered, focused or current: the fill stays the hue,
   the rule under the words goes to ink. */
body .post-body .faq-jump a:is(:hover, :focus-visible, [aria-current]) { border-bottom-color: var(--ink); }
.post-body .faq-item:has(details[open]) {
    border-left: 4px solid var(--hue);
    padding-left: 18px;
}

/* ---- OUR WORK: THE "SEE THE WORK" MARK ----------------------------------
   The cards already carry a 4px rule of the cornflower across the top and wash
   to --hue-wash on hover. The third form is the small bold link at the foot of
   each card, which was black: it is --hue-deep #425873, 7.30:1 on white and
   7.11:1 on the hover wash, and it is the page's one piece of coloured type. */
.work-card .go { color: var(--hue-deep); }

/* ---- THE GALLERY: A RULE, A CAPTION MARK AND A FRAME --------------------
   The most photographic page on the site and the one with the least furniture
   to colour, so the orange arrives in three places that are not each other.

   A 4px RULE ACROSS THE TOP OF THE GRID, which is also the thing that stops
   the pictures starting abruptly under the block.

   A 20x3px MARK BEFORE EVERY CAPTION. Small, repeated, and the same idea as
   the quotation mark drawn as a rule on the landing page.

   A FRAME ON THE PICTURE YOU ARE POINTING AT. `outline` with a negative
   offset, so it is drawn INSIDE the image's own rectangle and the grid does
   not move by a pixel when it appears. It is flat, it has no radius and it is
   not a shadow, so the flatness rule holds. 4px of #faaa63 against the grey
   placeholder behind a picture is 2.06:1 - it is a pointer affordance that
   duplicates the cursor and the caption, not an indicator anything depends
   on, and the link itself keeps its focus ring. */
.gallery-sec .gallery-in { border-top: 4px solid var(--hue); padding-top: 34px; }
.gallery-grid figcaption { position: relative; padding-top: 12px; }
.gallery-grid figcaption::before {
    content: ""; position: absolute; top: 0; left: 0;
    width: 20px; height: 3px; background: var(--hue);
}
.gallery-grid img { transition: outline-color 120ms linear; outline: 4px solid transparent; outline-offset: -4px; }
.gallery-grid li:hover img, .gallery-grid a:focus-visible img { outline-color: var(--hue); }

/* ---- GALLERY: GRADUAL LOADING --------------------------------------------
   Toby, 24 Sep 2026: "Make the gallery load more gradually please? OR have a
   spinner". gallery.js places each picture as its row arrives; it fades in
   once decoded. The spinner sits under the grid until the last one lands. */
.gallery-grid .gallery-item { opacity: 0; transform: translateY(8px); transition: opacity .45s ease, transform .45s ease; }
.gallery-grid .gallery-item.is-in { opacity: 1; transform: none; }
.gallery-loading { display: flex; align-items: center; justify-content: center; gap: 12px; padding: 40px 0 64px; background: var(--paper); color: var(--ink-soft); font: 600 14px/1 var(--alt); letter-spacing: .04em; }
.gallery-loading[hidden] { display: none; }
.gallery-spinner { width: 26px; height: 26px; border-radius: 50%; border: 3px solid var(--line); border-top-color: var(--hue, var(--ink)); animation: gallery-spin .8s linear infinite; }
@keyframes gallery-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
    .gallery-grid .gallery-item { transition: opacity .2s linear; transform: none; }
    .gallery-spinner { animation-duration: 2.4s; }
}
