/* ==========================================================================
   Wisdom Quotes & Stories — Ghost theme
   Forked from wisdom-love-quotes v1.0.0. Structure and accessibility work are
   inherited; the palette is measured from this site's own logo and THE AD
   PIPELINE IS REMOVED ENTIRELY.

   NO ADS, BY DECISION. This site is not on our AdSense — its ads.txt is
   media.net (8CU808IUG) and pub-7439041255533808 appears only as a media.net
   RESELLER line. Rather than renumber an AdSense slot that does not exist here,
   the loader, the in-article unit, the archive units, their CSS and their
   package.json settings were all DELETED. ads.txt is still served unchanged, so
   media.net authorisation survives for whenever monetisation is decided.

   The site was nearly skipped entirely: it was recorded as a 1-post parked
   domain because its table prefix is a2mps2_ and wp_posts is a stale 2018 decoy
   holding one row. It actually has 1,244 posts — the largest in the fleet.
   ========================================================================== */

/* ==========================================================================
   SELF-HOSTED WEBFONTS — added 2026-08-05
   --------------------------------------------------------------------------
   Until now this theme shipped NO font files and no @font-face, and the site's
   Ghost heading_font/body_font settings are empty — so it rendered in whatever
   system font the visitor happened to have. The WordPress theme this site
   replaced self-hosted Poppins and Lora; that typographic identity was dropped
   at migration, on this site and on all eleven others.

   Both faces are SIL OFL. Served from the theme, never fonts.googleapis.com —
   a third-party stylesheet on the critical path costs a DNS lookup, a TLS
   handshake and a round trip before the first byte of font CSS arrives, and it
   hands the visitor's IP to a third party on every page view.

   Only latin and latin-ext ship. unicode-range means latin-ext downloads only
   if a page actually contains a character in it. Lora is the variable release,
   so 400..700 is one file; Poppins has no variable release, so 600 and 700 are
   separate. Per page that is ~50 KB, not the 138 KB on disk.

   THE FALLBACK METRICS ARE MEASURED, NOT GUESSED. font-display:swap trades
   FOIT for CLS — text paints in the fallback then reflows when the webfont
   lands. Rendering both faces at 200px and comparing advance widths gave:

       Lora    vs Georgia    103.1%   (x-height 104.2%)  <- chosen
       Lora    vs Times      112.0%
       Poppins vs Arial      112.7%   (x-height 106.7%)  <- chosen
       Poppins vs Segoe UI   113.0%

   Georgia sits within 3.1% of Lora, which is why it leads the serif stack.
   size-adjust closes the remainder so the swap barely moves the text.
   ========================================================================== */

/* Lora — body copy and the quotations. Variable, 400..700. */
@font-face{ font-family:'Lora'; font-style:normal; font-weight:400 700; font-display:swap;
  src:url('../fonts/lora-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face{ font-family:'Lora'; font-style:normal; font-weight:400 700; font-display:swap;
  src:url('../fonts/lora-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }
@font-face{ font-family:'Lora'; font-style:italic; font-weight:400 700; font-display:swap;
  src:url('../fonts/lora-italic-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face{ font-family:'Lora'; font-style:italic; font-weight:400 700; font-display:swap;
  src:url('../fonts/lora-italic-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }

/* Poppins — headings and UI. No variable release, so 600 and 700 are separate. */
@font-face{ font-family:'Poppins'; font-style:normal; font-weight:600; font-display:swap;
  src:url('../fonts/poppins-600-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face{ font-family:'Poppins'; font-style:normal; font-weight:600; font-display:swap;
  src:url('../fonts/poppins-600-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }
@font-face{ font-family:'Poppins'; font-style:normal; font-weight:700; font-display:swap;
  src:url('../fonts/poppins-700-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face{ font-family:'Poppins'; font-style:normal; font-weight:700; font-display:swap;
  src:url('../fonts/poppins-700-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }

/* Metric-matched fallbacks. These carry the MEASURED ratios above, so the
   swap-in barely moves the text instead of reflowing the page. */
@font-face{ font-family:'Lora Fallback'; src:local('Georgia'), local('Times New Roman');
  size-adjust:103.1%; ascent-override:100.5%; descent-override:27.5%; }
@font-face{ font-family:'Poppins Fallback'; src:local('Arial'), local('Helvetica');
  size-adjust:112.7%; ascent-override:105.0%; descent-override:35.0%; }

:root {
    /* --- palette, MEASURED FROM THE MASTER LOGO -----------------------------
       REPLACED 2026-08-05. The previous palette here was violet, and it was
       wrong: it was quantised from a 502x82 script WORDMARK recovered off the
       live site, which is a derived asset, not the brand. The master is
       Logos/Wisdom Quotes and Stories.png — a 1600x1600 illustrated circular
       badge: navy tree-and-figure silhouette, sunset sky, teal ocean, amethyst
       and rose crystals. It shares NO colour with the wordmark.

       Derived with Material Color Utilities for the hue family and WCAG 2.x for
       the tone. Full method, area shares and matrix:
       05 Wisdom All Websites/Wisdom Quotes and Stories/Brand Kit/02-Color/palette.md

       Token NAMES below are the legacy forked ones (brown/rust/gold/navy) kept
       as aliases so nothing downstream breaks. None of them describe their
       colour. The canonical names are declared first; migrate references to
       those and then delete the alias block.                                   */

    /* canonical — these are what the colours actually are */
    --wqs-navy-true:   #022A5C;  /* 14.11:1 on paper — silhouette, 6.1% of mark */
    --wqs-navy-deep:   #04182F;  /* 17.84:1 on paper                            */
    --wqs-ink-soft:    #4C6F81;  /*  5.39:1 on paper — meta, bylines            */
    --wqs-ocean:       #0293CE;  /*  3.46:1 — FILL / large text / focus ring     */
    --wqs-ocean-ink:   #0072A1;  /*  5.36:1 on paper — the working ocean        */
    /* THE OCEAN INVERTED. Added 2026-08-06 with the navy footer. The kit's own
       matrix is explicit that the ocean is the ONE colour in this palette that
       does not survive being moved onto the navy: --wqs-ocean is 4.08:1 there and
       --wqs-ocean-ink, being darker still, is 2.64:1 — under the 3:1 WCAG 1.4.11
       asks of a focus ring. This is the lifted variant the kit specifies for that
       case, at 5.23:1 on navy. It is FILL-ONLY on white (2.70:1). */
    --wqs-ocean-lift:  #34A7E3;  /*  5.23:1 on navy — the ocean, for dark grounds */
    --wqs-sky:         #C5EAFF;  /*  1.27:1 — FILL ONLY. Takes navy ink         */
    --wqs-apricot:     #FFCD9C;  /*  1.45:1 — FILL ONLY. The feature band       */
    --wqs-amethyst:    #6F449D;  /*  7.02:1 on paper — text-safe RAW            */
    --wqs-plum:        #4C1656;  /* 13.49:1 on paper                            */
    --wqs-rose:        #B0387B;  /*  5.68:1 on paper — text-safe RAW            */
    --wqs-blush:       #FFB1BA;  /*  1.71:1 — FILL ONLY                         */
    --wqs-sun:         #EDE9E1;  /*  1.21:1 — FILL ONLY                         */

    /* legacy alias block — same names the rest of this file already uses */
    --wqs-ink:        var(--wqs-navy-true);   /* was #23007B violet            */
    --wqs-brown:      var(--wqs-amethyst);    /* NOT brown. was #845DC4        */
    --wqs-brown-soft: var(--wqs-plum);        /* hover. was #50308D            */
    --wqs-navy:       var(--wqs-ink-soft);    /* was #525057, a neutral grey   */
    --wqs-rust:       var(--wqs-sky);         /* NOT rust. was #D9B2FF lilac   */
    --wqs-rust-ink:   var(--wqs-ocean-ink);   /* was #845DC4                   */
    --wqs-gold-ink:   var(--wqs-rose);        /* NOT gold. was #300097 violet  */
    --wqs-cream:      var(--wqs-apricot);     /* the feature band. was #E2C8FA */
    --wqs-surface:    #F2F9FF;  /*  card face, a breath of sky on white        */
    --wqs-paper:      #FFFFFF;  /*  page ground                                */
    --wqs-white:      #FFFFFF;

    /* TITLE INK — 2026-08-05, by request. Every heading on a LIGHT ground uses
       this: the base h1-h4 rule and the .quote-card-title / .post-title /
       .archive-title / .section-title / .feed-title / .error-title overrides,
       which until now were a mix of --wqs-ink navy and --wqs-amethyst.
       16.92:1 on white and ~16:1 on the card surface.
       STILL NOT APPLIED TO .site-title, but the reason changed on 2026-08-06.
       It used to be a contrast problem: the header bar was #300060 and #300050
       measured 1.05:1 on it — the same colour, near enough, and invisible. The
       bar is white now, where this token would be perfectly legible. It is kept
       off the site title on brand grounds instead: #300050 is a hand-picked
       violet with no counterpart in the logo, and the header was re-inked to the
       measured palette by request, so .site-title takes --wqs-ink #022A5C.
       The footer headings still use white — they are on the footer's dark
       ground, which is now --wqs-navy-true and takes white at 14.11:1.
       OPEN, AND SAID ONCE: every heading on a light ground still carries this
       violet while the header now carries the badge's navy. Aligning them is a
       site-wide repaint and was not what was asked for. */
    /* REPOINTED 2026-08-06 to the brand's own ink: #300050 -> --wqs-navy-true
       #022A5C. This was the LAST hand-picked colour in the stylesheet — a violet
       specified by eye, matching nothing in the logo — and it was setting every
       heading on every light ground: h1-h4, card titles, post titles, archive
       titles, section and feed titles, the error title.
       17.84:1 would have been available via --wqs-navy-deep; navy-true at
       14.11:1 is used instead so the headings match the site title in the header
       and the footer ground exactly, rather than being a fourth near-black.
       Header, footer and every heading now come from the same three logo
       colours. */
    --wqs-title:      var(--wqs-navy-true);

    /* THE BAND TRAP, SIXTH SITE, SIXTH SET OF COLOURS. Measured on the apricot
       band #FFCD9C: --wqs-ink 9.72, --wqs-plum 9.29 and --wqs-amethyst 4.84 all
       pass; --wqs-ink-soft 3.71, --wqs-rose 3.91 and --wqs-ocean-ink 3.69 all
       FAIL AA while passing comfortably on the page. On the sky tint #C5EAFF
       --wqs-rose is 4.49 — a miss by 0.01, which is exactly how this trap keeps
       getting through. All three still clear the 3:1 a focus ring needs, so the
       ring is unconstrained and only TEXT is.
       HARD RULE: the band carries --wqs-ink, --wqs-plum or --wqs-amethyst.      */

    --wqs-text:       var(--wqs-ink);
    --wqs-muted:      var(--wqs-ink-soft);  /*  5.39:1 — meta, bylines         */
    /* ACCENT REPOINTED 2026-08-06, by request: amethyst -> the badge's ocean.
       #6F449D is a real kit token and it is text-safe (7.02:1), so this is not a
       contrast fix — it is a BRAND fix. The area table in palette.md lists what
       the artwork is actually made of: white, navy #022A5C, sky, apricot and
       ocean #0293CE. Amethyst never appears in it; it entered the palette as a
       SEED, survived into the token list, and then quietly became the colour of
       every link, tag pill, chip, pagination button and in-content heading on a
       site whose logo contains no violet at all.
       --wqs-ocean-ink #0072A1 is the working, text-safe form of that ocean:
       5.36:1 on paper and 5.04:1 on the --wqs-surface card face, so every place
       the old accent was legible the new one still is. Checked against each
       ground it actually lands on, not just against white.
       SEVENTEEN RULES now read this token instead of naming a colour directly.
       That was the real defect — the alias block at the top of this file exists
       precisely so a repalette is one line, and half the stylesheet was bypassing
       it. Repointing this token is now the whole change. */
    --wqs-accent:     var(--wqs-ocean-ink); /*  5.36:1 — links, chips, pills    */

    --wrap:      1180px;
    --wrap-narrow: 720px;
    --radius:    10px;
    --gap:       clamp(1rem, 2.5vw, 1.75rem);
    --tap:       44px;         /* WCAG 2.5.8 target size                  */

    /* Ghost still injects --gh-font-heading / --gh-font-body if an owner picks a
       font in admin, and that must keep winning — it is the site owner's choice.
       Below it now sits the BRAND pairing rather than a bare system stack:
       Poppins for display, Lora for body, per the brand kit. Note this inverts
       the old fallbacks, which had a serif on headings and a sans on body. */
    --font-body: var(--gh-font-body, 'Lora', 'Lora Fallback', Georgia, "Iowan Old Style", "Times New Roman", serif);
    --font-head: var(--gh-font-heading, 'Poppins', 'Poppins Fallback', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif);

    /* Shadow tint follows --wqs-ink (#022A5C, the silhouette navy). */
    --shadow-sm: 0 1px 2px rgba(2,42,92,.06), 0 2px 8px rgba(2,42,92,.05);
    --shadow-md: 0 4px 16px rgba(2,42,92,.10);
}

body.accent-deep { --wqs-accent: var(--wqs-rose); }

/* --- reset -------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
    margin: 0;
    background: var(--wqs-paper);
    color: var(--wqs-text);
    font-family: var(--font-body);
    font-size: 1.0625rem;
    line-height: 1.65;
    -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; height: auto; display: block; }
/* <picture> defaults to display:inline, which would become the img's containing
   block and break every rule below that sizes or centres the img directly
   (.quote-card-img height:100%, .post-media img margin-inline:auto). */
picture { display: block; }
a { color: var(--wqs-accent); text-decoration-thickness: 1px; text-underline-offset: 2px; }
/* Hover DARKENS toward the brand ink rather than shifting hue: ocean-ink 5.36:1
   at rest -> navy-true 14.11:1 on hover. Plum #4C1656 was the old pair to the
   amethyst accent and is the same violet family that has just been removed
   everywhere else. */
a:hover { color: var(--wqs-navy-true); }
h1, h2, h3, h4 { font-family: var(--font-head); line-height: 1.22; color: var(--wqs-title); margin: 0 0 .5em; }

/* WCAG 1.4.11 asks 3:1 of a focus indicator, not the 4.5:1 a text colour needs.
   outline-offset puts a 2px gap between the ring and the element, so the ring's
   neighbour on both sides is the container background, never a button fill —
   which is why the ring on an --wqs-brown button is not the comparison that
   matters. RE-MEASURED 2026-08-05 for the badge palette: --wqs-rust-ink is now
   the ocean ink #0072A1, and it rings at 5.36:1 on the white page, 5.04:1 on a
   card and 3.69:1 on the apricot .sections band — the band figure STILL clears
   the 3:1 that 1.4.11 asks, so the ring is correct on a band where TEXT in the
   same colour (3.69, under 4.5) is not. That is precisely why 1.4.11 and 1.4.3
   set different thresholds, and why the band rule above constrains text without
   touching the focus ring.
   Twice now this comment has carried another palette's numbers and read as
   verified — first an orange-on-cream pair from a different site, then the
   violet 4.83/3.19 set from the wrong logo. Re-measure it on every repalette. */
:focus-visible {
    outline: 3px solid var(--wqs-ocean-ink);
    outline-offset: 2px;
    border-radius: 3px;
}

.skip-link {
    position: absolute; left: -9999px; top: 0; z-index: 100;
    background: var(--wqs-ink); color: #fff; padding: .75rem 1.25rem;
}
.skip-link:focus { left: 0; }

.wrap { width: min(100% - 2rem, var(--wrap)); margin-inline: auto; }
.wrap-narrow { width: min(100% - 2rem, var(--wrap-narrow)); }

/* --- header ------------------------------------------------------------- */
/* HEADER GROUND CHANGED AGAIN 2026-08-06, by request: #300060 -> WHITE.
   History of this one bar: white -> #300050 (v1.7.1) -> #300060 (v2.0.0) ->
   white. A turn to white was also asked for and withdrawn mid-turn at v1.9.1,
   so this is the second time it has been requested and the first time it ships.

   THE SAME RULE APPLIES IN REVERSE, AND IT IS THE WHOLE OF THIS CHANGE. Every
   element in this bar had been re-inked to WHITE-ON-VIOLET at v1.7.1. On a white
   ground those inks measure:

     .site-title       --wqs-white  #FFFFFF   1.00:1   invisible
     .site-tagline     --wqs-white  #FFFFFF   1.00:1   invisible
     nav links         --wqs-white  #FFFFFF   1.00:1   invisible
     nav hover         #FFCB9A                1.44:1   invisible
     .nav-toggle-bar   --wqs-white  #FFFFFF   1.00:1   invisible
     .nav-toggle-label --wqs-white  #FFFFFF   1.00:1   invisible
     header icons      --wqs-white  #FFFFFF   1.00:1   invisible
     borders           rgba(255,255,255,.12-.30)        invisible

   Repointing the background alone would have produced an empty white strip with
   a logo in it. Changing the ground is never the change; it is half of a pair.

   WHICH COLOURS THE BAR TAKES NOW — the request was "brandkit colours according
   to logo", so the inks below are the MEASURED palette from the 1600x1600 badge
   (Brand Kit/02-Color/palette.md), not the #300050/#300060/#FFCB9A literals that
   had accumulated here. None of those three appears anywhere in the artwork:
   #300060 was specified by hand, and the violet family entered this theme from a
   palette built off a derived wordmark that was later superseded. The badge is
   navy silhouette, sky, ocean and apricot. So on white:

     .site-title       --wqs-ink       #022A5C  14.11:1  AAA   the mark's own ink
     .site-tagline     --wqs-ink-soft  #4C6F81   5.39:1  AA    the kit's secondary
     nav links         --wqs-ink       #022A5C  14.11:1  AAA
     nav hover         --wqs-ocean-ink #0072A1   5.36:1  AA    the working ocean
     nav hover pill    --wqs-sky       #C5EAFF    fill         + navy ink 11.14:1
     .nav-toggle-bar   --wqs-ink       #022A5C  14.11:1        (needs 3:1)
     .nav-toggle-label --wqs-ink       #022A5C  14.11:1  AAA
     header icons      --wqs-ink       #022A5C  14.11:1
     icon hover        --wqs-sky fill + --wqs-ink glyph        11.14:1

   Every figure is from the kit's own contrast matrix, measured against paper
   #FFFFFF. The bar is now MORE legible than it was on violet, not less.

   THE FOCUS RING IMPROVES AND IS STILL THE THING TO WATCH. --wqs-ocean-ink was
   3.03:1 on #300060 — over WCAG 1.4.11's 3:1 with almost nothing to spare. On
   white it is 5.36:1. That margin is why the same token can now also carry nav
   hover text, which needs 4.5:1 and could not have had it on the old bar.

   THE FOOTER FOLLOWED LATER THE SAME DAY. It was left on #300060 when this bar
   went white, which broke the open-and-close-on-one-colour symmetry that
   justified darkening the header at v1.7.1. It is now --wqs-navy-true #022A5C —
   the badge's own silhouette navy — so the page is white at the top and the
   logo's navy at the bottom, and BOTH grounds are measured brand colours for the
   first time. See the .site-foot rule for what that move cost (the focus ring).

   SEPARATION IS A SHADOW, NOT A BORDER — 2026-08-06, by request. The 3px sky
   rule this shipped with earlier the same day is gone. The job is unchanged and
   it still has to be done: this bar is STICKY over a white page, so with no
   ground contrast and no edge it would dissolve into the content scrolling under
   it. --shadow-sm does it without drawing a line, and it is already tinted with
   --wqs-ink rather than being neutral grey, so the lift reads as the brand's
   navy rather than as a generic drop shadow.
   A shadow also degrades better than the border did: the sky rule was a fixed
   3px band that sat there whether or not anything was under it, whereas this
   only reads where content passes beneath. */
.site-head {
    --wqs-head: var(--wqs-paper);
    background: var(--wqs-head);
    box-shadow: var(--shadow-sm);
    position: sticky; top: 0; z-index: 40;
}
/* 68px -> 90px by request. The wrap measurements recorded further down were all
   taken at 68px and REMAIN VALID: they are about the bar's WIDTH — whether the
   brand lockup and the button fit on one line — and a taller bar changes neither
   term. Nothing keys off this height. */
.site-head-inner {
    display: flex; align-items: center; gap: 1rem;
    min-height: 90px; padding-block: .5rem;
    flex-wrap: wrap;
}
.site-brand {
    display: flex; align-items: center; gap: .7rem;
    text-decoration: none; color: inherit; margin-right: auto;
    min-height: var(--tap);
}
/* CHANGED 2026-08-05 — the logo is now the BADGE, not the wordmark.
   The wordmark was a 502x82 violet script quantised from a derived asset; it
   shares no colour with this brand and clashed with the badge palette once that
   shipped. The master mark is a square illustrated badge, so this reverts to the
   46px circle the rest of this lineage uses.
   CONSEQUENCE THAT MUST NOT BE MISSED: the wordmark carried the site's NAME, and
   .site-title was hidden precisely because of that. A square badge says nothing,
   so the title is now VISIBLE again — badge + name + tagline is a lockup, which
   is what a 6:1 landscape header slot actually wants. Dropping the badge in
   without un-hiding this would have left the header nameless. */
/* 46 -> 50px by request, 2026-08-06. THE WIDTH/HEIGHT ATTRIBUTES IN default.hbs
   MOVED WITH IT, BOTH OF THEM. A circular mark's size lives in three places on
   this site — this rule, the <img> in the {{#if @site.logo}} branch, and the
   <img> in the {{else}} fallback — and the attributes are what reserve the box
   before the file lands. Changing only the CSS leaves the browser reserving 46px
   for a 50px image, which is a layout shift on every page load: exactly the
   thing those attributes exist to prevent, reintroduced by a cosmetic edit.
   The FOOTER lockup (.foot-brand-logo) is deliberately left at 46px — it is a
   separate element and was not part of the request. */
.site-logo { height: 50px; width: 50px; object-fit: contain; border-radius: 50%; flex: none; }
.site-brand-text { display: flex; flex-direction: column; }
.site-title {
    /* 19px by request, 2026-08-06 — was 1.2rem. Stated in px rather than rem
       because the ask was in px; note this makes the brand name the one piece of
       type on the site that no longer scales with the reader's root font size.
       At the default 16px root it is a 0.2px change, so nothing moved visually;
       the difference only appears for a reader who has enlarged their base font,
       where the title now stays put while the rest of the page grows. */
    font-family: var(--font-head); font-size: 19px; font-weight: 700;
    /* The ACCENT, matching enlighteningquotes.com, which sets its site title in
       --eq-brown rather than its ink. Amethyst is 7.02:1 on paper — AAA — so the
       match costs nothing in contrast. */
    /* RE-INKED AGAIN for the white bar, 2026-08-06. White was correct on the
       violet ground and is 1.00:1 on this one. --wqs-ink #022A5C is the badge's
       own silhouette-and-wordmark navy — 6.13% of the artwork by area, the
       largest non-white share — and 14.11:1 on paper.
       NOT --wqs-title #300050, which every other heading on a light ground uses:
       that is a hand-picked violet with no counterpart in the logo, and the
       request was for the kit's colours. The two are close in tone (16.92 vs
       14.11) so this is a hue decision, not a contrast one. Flagged rather than
       quietly propagated: the body headings still carry #300050. */
    color: var(--wqs-ink); line-height: 1.1;
    letter-spacing: -.01em;
}
/* --wqs-ink-soft #4C6F81 — the kit's own secondary ink, 5.39:1 on paper, AA at
   this size. It is what the palette lists for "meta, bylines", which is exactly
   the role a tagline plays under the site name. The footer's #C9BAB2 was chosen
   against the violet and is 1.61:1 here. */
/* 13px by request, 2026-08-06 — was .78rem (12.48px at a 16px root), so this is
   a half-pixel larger and, like .site-title above, no longer scales with the
   reader's root font size. It stays --wqs-ink-soft: at 5.39:1 that clears AA,
   and WCAG has no minimum font size, so 13px is a legibility judgement rather
   than a compliance one. */
.site-tagline { font-size: 13px; color: var(--wqs-ink-soft); line-height: 1.25; }

/* The button now holds a visible "Menu" label under the bars, so it can no
   longer be a fixed 44x44 square — a hard height would clip the text and a hard
   width would squeeze it. min-* keeps the WCAG 2.5.8 floor while letting the
   box grow to whatever the word needs; align-items centres the bars over the
   label now that the two have different natural widths (previously the bars
   were the only children and stretched edge to edge). */
.nav-toggle {
    display: inline-flex; flex-direction: column;
    justify-content: center; align-items: center; gap: 4px;
    min-width: var(--tap); min-height: var(--tap);
    /* Was rgba(255,255,255,.30) — a white wash, invisible on a white bar. Same
       shape, same alpha, the ink flipped to the brand navy. */
    background: none; border: 1px solid rgba(2,42,92,.30); border-radius: 8px;
    cursor: pointer; padding: 6px 10px;
}
/* The bars lost their stretch when align-items became centre, so they now need
   an explicit width — without one an empty span with no content is 0px wide and
   the burger disappears entirely. */
.nav-toggle-bar { width: 22px; }
.nav-toggle-label {
    font-family: var(--font-head);
    font-size: .68rem; font-weight: 700; line-height: 1;
    letter-spacing: .03em;
    color: var(--wqs-ink);
    margin-top: 2px;
}
/* The bars ARE the control — WCAG 1.4.11 wants 3:1 of them. On the white bar
   --wqs-ink is 14.11:1, so the burger is the highest-contrast thing in the
   header. This is also the only element here that has now been re-inked three
   times (amethyst -> white -> navy); it is re-measured each time rather than
   assumed to have survived, because at 2px tall it is the first thing to
   disappear and the last thing anyone notices. */
.nav-toggle-bar { display: block; height: 2px; background: var(--wqs-ink); border-radius: 2px; }

/* {{navigation}} emits its own <ul class="nav"> — style that, never wrap it
   in another <ul> or you get nested lists and stray bullet markers. */
.site-nav { flex-basis: 100%; display: none; }
.site-nav.is-open { display: block; }
/* CHILD combinator, not descendant. .site-nav now holds two lists — {{navigation}}'s
   <ul class="nav"> as a direct child, and .social-icons nested inside .site-social.
   Written as `.site-nav ul` and `.site-nav li a` these rules also captured the icon
   row: flex-direction:column stacked the three icons vertically in the open mobile
   menu, and min-height/padding/border-radius turned the 38px circles into 38x44
   pills with a hairline under each. Measured, not guessed. */
.site-nav > ul {
    list-style: none; margin: .5rem 0 0; padding: 0;
    display: flex; flex-direction: column;
}
.site-nav > ul li { list-style: none; }
.site-nav > ul li a {
    display: flex; align-items: center; min-height: var(--tap);
    padding: 0 .25rem; text-decoration: none; font-weight: 600;
    /* NAVY at rest on the white bar — 14.11:1, the mark's own ink. Was white,
       which is 1.00:1 here.
       The divider follows: rgba(255,255,255,.14) was a white hairline in the
       open mobile menu and is now invisible, so it becomes the same navy wash at
       the same weight. */
    color: var(--wqs-ink); border-bottom: 1px solid rgba(2,42,92,.14);
}
/* HOVER ON THE PLAIN BAR — --wqs-ocean-ink #0072A1, 5.36:1 on paper, clear of
   the 4.5:1 hover TEXT needs. It is the palette's "working ocean", derived from
   the #0293CE ocean in the badge, and it is a genuine hue shift away from navy
   rather than a tonal nudge, so the hover reads on a phone where there is no
   pill behind it.
   The old #FFCB9A apricot was the right answer on a dark bar and is 1.44:1 on
   white — the single worst survivor of this inversion.
   NOTE the desktop block below OVERRIDES this colour when the sky pill appears:
   ocean-ink on --wqs-sky measures 4.23:1, which fails AA. Same colour, different
   ground, different verdict — the pair has to be read together. */
.site-nav > ul li a:hover { color: var(--wqs-ocean-ink); }

/* In the stacked mobile menu the icon row sits under the links, separated by the
   same hairline the links use. .social-icons is already a flex row, so it stays
   horizontal while the nav list is a column. */
.site-social { padding-top: .5rem; }

/* CENTRE THE BURGER WHEN THE BAR WRAPS — 2026-08-05, by request.

   Measured in a same-origin iframe harness (Chrome will not open a window under
   ~500px, so a screenshot cannot see this):

     412px+   brand + button share one row, button hard right, bar 68px
     390px    button WRAPS to its own row, x=16-60 — hard LEFT, bar 122px
     360px    same, bar 122px
     320px    same, bar 149px

   So on most real phones the button was sitting alone on a second line against
   the left edge, under the brand.

   justify-content, NOT a margin-inline:auto on the button. An auto margin would
   also fire on the ONE-row layout at 412-799px, where .site-brand's
   margin-right:auto currently pushes the button to the right edge — the two
   autos would share the free space and drag the button inward at widths where
   nothing was wrong. Auto margins consume free space BEFORE justify-content is
   applied, so on the single-row layout this rule is inert and the button stays
   right; on the wrapped line the button is the only item, carries no auto
   margin, and centres. One rule, correct on both sides of the wrap, with no
   breakpoint guess about where the wrap happens — which depends on the length
   of the site title and would drift if it were ever edited. */
@media (max-width: 799px) {
    /* Fallback only. The rules below now keep the button ON the brand's row at
       every width we ship, so this should never fire — it is kept because the
       wrap point depends on the length of the site title, and if that is ever
       edited to something much longer the button dropping to a CENTRED second
       row is a far better failure than it dropping to a left-aligned one. */
    .site-head-inner { justify-content: center; }

    /* KEEP THE BUTTON BESIDE THE BRAND — 2026-08-05, by request.
       It was wrapping to its own row on every phone width (320-412px), because
       the brand lockup plus a 54px button exceeds the line. The wrap is fixed
       by letting the BRAND shrink rather than by forbidding the wrap:
       flex-wrap:nowrap would also apply to .site-nav, which has flex-basis:100%
       precisely so the open menu takes its own full-width line — the menu would
       have been crushed onto the header row instead.
       min-width:0 ALONE DOES NOT WORK, and this cost a deploy to learn: a
       wrapping flex container decides where to break the line using each item's
       HYPOTHETICAL main size — for the brand, its max-content width, the full
       unwrapped title — and only then shrinks whatever ended up on the line.
       min-width:0 changes how far an item may shrink; it does not change the
       size the line-breaking step measured. The button was already on line two
       by the time shrinking was considered.
       flex-basis:0 is what actually fixes it: the brand's hypothetical size
       becomes 0, so the line-breaker sees 0 + 54px and keeps both together,
       then flex-grow:1 hands the brand every remaining pixel and the title
       wraps inside it. min-width:0 is still required alongside — without it the
       auto minimum re-imposes the content width. */
    .site-brand { flex: 1 1 0; min-width: 0; }
    .site-brand-text { min-width: 0; }
    /* Never let the button itself be the thing that shrinks. */
    .nav-toggle { flex: none; }
}

/* Below ~500px there is not enough room for the tagline AND a title AND the
   button on one line: the title would wrap to three lines to make space. The
   tagline is the descriptive half of the lockup and the site name is the
   identifying half, so the tagline is what goes. It returns at 500px, where the
   measurements show everything already fits. */
@media (max-width: 499px) {
    .site-tagline { display: none; }
}

@media (min-width: 800px) {
    .nav-toggle { display: none; }
    /* flex, NOT block: .site-nav now has two children — {{navigation}}'s <ul> and
       the icon row — and block would stack them, dropping the icons onto a second
       line under the menu. */
    /* .is-open must be repeated here. A media query adds NO specificity, so the
       base `.site-nav.is-open { display: block }` (0,2,0) outranks a bare
       `.site-nav` (0,1,0) even inside this block. With one child that was
       harmless; now .site-nav has two, and block stacks them — a reader who opens
       the menu on a phone and then rotates to landscape got a 101px header with
       the icons on their own row. Measured by forcing .is-open at every width. */
    .site-nav,
    .site-nav.is-open { display: flex; align-items: center; gap: .6rem; flex-basis: auto; }
    .site-nav > ul { flex-direction: row; gap: .35rem; margin: 0; }
    .site-nav > ul li a { border-bottom: 0; padding-inline: .85rem; border-radius: 8px; }
    /* THE HOVER PILL IS A PAIR — fill AND ink, set together, because changing
       one without the other is what this file keeps recording as the recurring
       mistake (the footer tag-pill's amber, the header's own v1.7.1 inversion).
       rgba(255,255,255,.12) on a white bar is no pill at all, so the fill becomes
       --wqs-sky #C5EAFF, a measured logo colour (upper sky, 7.7% of the artwork
       across two buckets).
       And the ink goes BACK to --wqs-ink for the duration of the pill: the base
       rule's --wqs-ocean-ink is 5.36:1 on white but only 4.23:1 on sky, under the
       4.5 it needs. Navy on sky is 11.14:1 (kit matrix, AAA). So the link is navy
       at rest, ocean on hover with no pill, and navy again on hover with one —
       which looks inconsistent written down and is the only combination where
       every state passes on the ground it actually sits on. */
    .site-nav > ul li a:hover { background: var(--wqs-sky); color: var(--wqs-ink); }
    .site-social { padding-top: 0; border-left: 1px solid rgba(2,42,92,.18); padding-left: .6rem; }
}

/* The icons only join the inline nav row once there is room for them.
   Measured against PRODUCTION's six-item menu — staging carries only Home+About,
   so measuring there would have passed this and shipped the regression. At 900px
   the six items plus three icons wrapped .site-nav onto a second line and the
   header went 71 -> 125px; at 1024 it fits. Below 800 they live in the collapsed
   menu, which has room, so only this middle band is excluded.
   Separately and NOT caused by this: the live header ALREADY wraps to 125px at
   820px with six items and no icons. Pre-existing, left alone here. */
@media (min-width: 800px) and (max-width: 1023px) {
    .site-nav .site-social { display: none; }
}

/* --- hero — REMOVED 2026-08-05 ------------------------------------------
   .hero, .hero-title, .hero-sub and the `.hero + .sections { margin-top: 0 }`
   adjacency rule are all gone, by request. index.hbs no longer emits the
   section, so every one of those rules was dead weight on the critical path.
   The homepage now opens on the category strip.
   The strip's own top margin is cancelled on the homepage below, where it is
   the first thing under the header — see .home-template .sections.
   ------------------------------------------------------------------------ */

/* Visually hidden, still announced. Carries the homepage <h1> that the hero
   used to hold. clip-path does the work in every current browser; the legacy
   `clip` stays for anything that predates it. white-space:nowrap stops a
   1px-wide box wrapping the title into a tall invisible column, which is the
   classic way this pattern still ends up affecting layout. */
.visually-hidden {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0; border: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
}

/* --- grid of quote cards ------------------------------------------------ */
/* CARD GRID — informativequotes.com's treatment, adopted 2026-08-05.
   auto-fill/minmax replaces the old 1 / 2 / 3 breakpoint ladder. The ladder
   stepped at 560px and 960px regardless of how much room the cards actually
   had, so between breakpoints they stretched. minmax(240px, 1fr) lets the
   count follow the available width instead, which is why IQ's grid holds up
   at widths the ladder never anticipated. */
.quote-grid {
    display: grid;
    /* FOUR ACROSS ON DESKTOP, explicitly.
       auto-fill alone will not do this: it fits as many columns as the width
       allows, so the count swings with the viewport. At a 240px minimum a
       1024px laptop dropped to 3; lowering it to 220 fixed that but then let a
       FIFTH column in at 1280px. There is no single minimum that yields exactly
       four at every desktop width — the constraint is a count, so it is stated
       as a count below. auto-fill still governs the fluid range under 1000px,
       where stepping 3 -> 2 -> 1 is the right behaviour. */
    grid-template-columns: repeat(auto-fill, minmax(min(220px, 100%), 1fr));
    gap: 1.25rem;
    /* align-items:start REMOVED to match informativequotes.com, whose cards
       stretch so a row's bottom edges line up. With it, a two-line title made
       one card taller than its neighbours and the row's borders stopped
       aligning — the single most visible difference against IQ's grid. */
    padding-block: clamp(1.5rem, 4vw, 2.75rem);
}
/* The desktop cap. 1000px is where four 220px columns plus their gaps first
   fit (940px of track + 60px of gap), so this never squeezes them narrower
   than the fluid rule would have. */
@media (min-width: 1000px) {
    .quote-grid { grid-template-columns: repeat(4, 1fr); }
}

/* CARD — informativequotes.com is FLAT: a hairline border and no resting
   shadow. MOTION — enlighteningquotes.com's lift, kept exactly.
   The two combine well: with no shadow at rest the hover shadow has somewhere
   to travel from, so the lift reads more clearly here than it does on EQ,
   where the card already carries shadow-sm. */
.quote-card {
    background: var(--wqs-white);
    border: 1px solid rgba(2,42,92,.10);
    border-radius: var(--radius);
    overflow: hidden;
    display: flex; flex-direction: column;
    transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
.quote-card:hover,
.quote-card:focus-within {
    transform: translateY(-3px);
    box-shadow: var(--shadow-md);
    border-color: rgba(2,42,92,.20);
}

.quote-card-link { text-decoration: none; color: inherit; display: block; }
/* CARD IMAGES — REBUILT 2026-08-05 after measuring the real artwork.

   This was `aspect-ratio: 1/1` with `object-fit: cover`, and it was cropping
   every card. Eight consecutive homepage images, measured at the 600w
   derivative actually served:

     600x450  1.33      600x486  1.23      600x400  1.50
     598x298  2.01      600x384  1.56      600x392  1.53
     584x438  1.33      600x389  1.54

   NOT ONE IS SQUARE. All landscape, median ~1.5. `cover` on a square box was
   therefore discarding the left and right of every single one — on the 2.01
   image, about half its width. And this site's artwork is QUOTE GRAPHICS with
   the words baked into the picture, so what `cover` threw away was the start
   and end of the sentence. That is why cards read "rn something / everyone who
   / through our".

   THE FIX IS THE BOX, NOT THE FIT MODE. aspect-ratio moved 1/1 -> 3/2, the
   median of the real library rather than a square nothing in it actually is.
   `cover` stays, so the well is always completely filled with no letterbox
   bars — but it now crops against a box the artwork nearly matches, and that
   changes the cost by an order of magnitude. Measured per sample:

     ratio    cropped at 1/1      cropped at 3/2
     1.50        33.3%               0%      exact fit
     1.53        34.6%               2.0%
     1.54        35.1%               2.6%
     1.56        35.9%               3.8%
     1.33        24.8% (sides)      11.3%   (top/bottom)
     1.23        18.7% (sides)      18.0%   (top/bottom)
     2.01        50.2%              25.4%
     ------------------------------------------------
     mean        ~33%                ~9%

   So the average card loses 9% of an edge instead of a third of its width, and
   four of the eight are within 4% of untouched. The two survivors — the 1.23
   portrait and the 2.01 panorama — are genuinely the wrong shape for any single
   grid and are the only ones that still lose a meaningful edge.

   `contain` was tried first and rejected: it truncates nothing, but it leaves
   bars on most cards, and a grid of quote graphics floating in coloured
   rectangles reads as broken rather than careful.

   object-position centre: these graphics set their type centred, so a centre
   crop takes margin off both sides rather than eating one edge of a sentence. */
.quote-card-media { aspect-ratio: 3 / 2; background: var(--wqs-surface); }
/* the picture must fill the aspect-ratio box, or the img's height:100% has no
   definite height to resolve against and the card collapses */
.quote-card-media picture { width: 100%; height: 100%; }
.quote-card-img {
    width: 100%; height: 100%;
    object-fit: cover;
    object-position: center;
}

/* CARD BODY — informativequotes.com's .card-body: the padding lives here now,
   not on the title, because the body holds three stacked things (pill, title,
   date) rather than a title alone. */
.quote-card-body { padding: .85rem .9rem 1rem; }

/* TOPIC PILL — informativequotes.com's .topic-badge, sitting ABOVE the title.
   Amethyst on the sky surface measures 6.61:1 — AA. The old treatment was a
   bare uppercase link UNDER the title in the soft ink. */
.quote-card-badge {
    display: inline-block;
    font-size: .72rem; font-weight: 700; letter-spacing: .03em;
    text-transform: uppercase;
    color: var(--wqs-accent);
    background: var(--wqs-surface);
    padding: .2rem .5rem;
    border-radius: 4px;
}

/* Title to informativequotes.com's .card-title: 1rem, tight margins, set in the
   heading face. Poppins at 1rem reads larger than the old Lora 1.02rem, so this
   is not a reduction in practice. */
.quote-card-title {
    font-size: 1rem; line-height: 1.4;
    margin: .35rem 0 .4rem;
    color: var(--wqs-title); font-family: var(--font-head); font-weight: 700;
    transition: color .18s ease;
}
.quote-card-link:hover .quote-card-title,
.quote-card-link:focus-visible .quote-card-title { color: var(--wqs-accent); }

/* Date line — informativequotes.com's .card-meta. This site's cards carried no
   date at all before. */
.quote-card-meta { color: var(--wqs-ink-soft); font-size: .8rem; }

/* --- archive heads ------------------------------------------------------ */
.archive-head {
    background: var(--wqs-surface);
    padding-block: clamp(1.75rem, 5vw, 2.75rem);
    border-bottom: 1px solid rgba(2,42,92,.12);
    text-align: center;
}
.archive-kicker,
.post-kicker {
    display: inline-block;
    font-size: .74rem; letter-spacing: .1em; text-transform: uppercase;
    font-weight: 700; color: var(--wqs-ink-soft); text-decoration: none;
    margin-bottom: .4rem;
}
.post-kicker { min-height: 28px; }
.archive-title { font-size: clamp(1.7rem, 4.5vw, 2.5rem); color: var(--wqs-title); }
.archive-desc { max-width: 55ch; margin-inline: auto; color: var(--wqs-muted); }
.archive-count { font-size: .9rem; color: var(--wqs-muted); margin: .25rem 0 0; }

/* --- single post -------------------------------------------------------- */
/* NB: must NOT be `.post` — Ghost's {{post_class}} stamps `post` onto every
   card in the grid, so a `.post` rule leaks padding into every card. */
.single { padding-block: clamp(1.75rem, 5vw, 3rem); }
.post-head { text-align: center; margin-bottom: 1.75rem; }
.post-title { font-size: clamp(1.6rem, 4.5vw, 2.4rem); color: var(--wqs-title); }
.post-meta { color: var(--wqs-muted); font-size: .9rem; margin: 0; }

.post-media { margin: 0 0 1.75rem; }
.post-media img {
    margin-inline: auto; border-radius: var(--radius);
    box-shadow: var(--shadow-md); background: var(--wqs-surface);
}
.post-media figcaption {
    text-align: center; font-size: .85rem; color: var(--wqs-muted); margin-top: .6rem;
}

.post-content { font-size: 1.09rem; }
/* Reset via the child combinator only. A plain `.post-content p { margin: 0 }`
   out-specifies `> * + *` and silently removes every paragraph gap. */
.post-content > * { margin: 0; }
.post-content > * + * { margin-top: 1.15em; }
/* The imported WordPress content wraps whole paragraphs in <strong>, so this
   must NOT recolour — tinting it turned every post body into bold brown. */
.post-content strong { color: inherit; }
.post-content h2, .post-content h3 { margin-top: 1.8em; color: var(--wqs-accent); }
.post-content img { border-radius: var(--radius); margin-inline: auto; }
.post-content blockquote {
    margin: 1.6em 0; padding: .6em 0 .6em 1.25em;
    border-left: 4px solid var(--wqs-sky);   /* decorative use of gold — not text */
    color: var(--wqs-ink); font-style: italic;
}
.post-content a { color: var(--wqs-accent); }
.post-content ul, .post-content ol { padding-left: 1.4em; }
.post-content hr { border: 0; border-top: 1px solid var(--wqs-surface); margin: 2em 0; }

/* --- Koenig editor cards ------------------------------------------------ */
/* Ghost's editor emits these widths; they must be styled or content breaks
   out of, or is trapped inside, the narrow column. */
.post-content .kg-card { margin-block: 1.6em; }

.post-content .kg-width-wide {
    width: min(100vw - 2rem, 1000px);
    max-width: none;
    margin-inline: calc(50% - min(50vw - 1rem, 500px));
}
.post-content .kg-width-full {
    width: 100vw;
    max-width: none;
    margin-inline: calc(50% - 50vw);
}
.post-content .kg-width-full img,
.post-content .kg-width-wide img { width: 100%; border-radius: 0; }
.post-content .kg-width-wide img { border-radius: var(--radius); }

.post-content .kg-image-card figcaption,
.post-content .kg-embed-card figcaption {
    text-align: center; font-size: .85rem; color: var(--wqs-muted); margin-top: .6rem;
}
.post-content .kg-embed-card { display: flex; flex-direction: column; align-items: center; }
.post-content .kg-gallery-row { display: flex; gap: .6rem; }
.post-content .kg-gallery-image img { width: 100%; }
.post-content .kg-bookmark-card a {
    display: flex; flex-direction: column; border: 1px solid var(--wqs-surface);
    border-radius: var(--radius); overflow: hidden; text-decoration: none; color: inherit;
}
.post-content .kg-bookmark-content { padding: 1rem; }
.post-content .kg-bookmark-title { font-weight: 700; color: var(--wqs-accent); }
.post-content .kg-bookmark-description { color: var(--wqs-muted); font-size: .92rem; }

/* The seven classes below are what gscan reported as ERRORS on wisdom-quotes-4u
   from v1.4.1 onward, and that theme still ships with them. They are a consequence
   of card_assets:false: with Ghost's own cards.min.css suppressed, the theme owes
   a style for every Koenig class, including cards this site will never use.
   wq4u's position was that Ghost activates the theme anyway because a gscan error
   is not fatal. That is true and it is still the wrong trade — a theme carrying
   seven permanent "known-ignorable" errors teaches you to skim the next scan, and
   the next scan is where a real regression shows up.
   Styled properly rather than stubbed: this site has 0 gallery cards and 0 bookmark
   cards today, but if one is ever added it should look deliberate instead of
   unstyled. Costs ~20 lines and makes the scan mean something. */
.post-content .kg-gallery-container { display: flex; flex-direction: column; gap: .6rem; }
.post-content .kg-bookmark-container { display: flex; flex-direction: column; }
.post-content .kg-bookmark-metadata {
    display: flex; align-items: center; flex-wrap: wrap; gap: .4rem;
    margin-top: .6rem; font-size: .85rem; color: var(--wqs-muted);
}
.post-content .kg-bookmark-icon { width: 20px; height: 20px; margin-right: .25rem; }
.post-content .kg-bookmark-author { font-weight: 600; color: var(--wqs-ink); }
.post-content .kg-bookmark-publisher { color: var(--wqs-muted); }
.post-content .kg-bookmark-publisher::before { content: "•"; margin-right: .4rem; }
.post-content .kg-bookmark-thumbnail { flex-shrink: 0; }
.post-content .kg-bookmark-thumbnail img {
    width: 100%; height: 100%; object-fit: cover; border-radius: 0;
}
.post-content .kg-button-card { display: flex; justify-content: center; }
.post-content .kg-btn {
    display: inline-flex; align-items: center; min-height: var(--tap);
    padding: 0 1.5rem; background: var(--wqs-accent); color: var(--wqs-white);
    border-radius: 999px; text-decoration: none; font-weight: 600;
}

.post-tags { margin-top: 2rem; display: flex; flex-wrap: wrap; gap: .5rem; }

.tag-pill {
    display: inline-flex; align-items: center;
    /* 30px, down from 36, by request. Used by the footer category cloud and by
       .post-tags under a post. Still clears WCAG 2.5.8 AA, which asks 24x24 —
       checked, because this is a tap target and shrinking one is the direction
       that breaks it. It is below the 44px this theme uses elsewhere, so it is
       now the smallest target on the site. */
    min-height: 30px; padding: 0 .85rem;
    background: var(--wqs-surface); color: var(--wqs-accent);
    border-radius: 999px; text-decoration: none;
    font-size: .85rem; font-weight: 600;
}
.tag-pill:hover { background: var(--wqs-accent); color: var(--wqs-white); }

/* --- share -------------------------------------------------------------- */
/* Plain links to each network's share endpoint — no vendor JavaScript. A share
   widget would add third-party script to a page that already carries AdSense,
   and INP is the metric ad scripts damage most.
   Buttons are 44px, well clear of the WCAG 2.5.8 AA floor of 24x24. */
.share {
    margin-top: 2.25rem; padding-top: 1.5rem;
    border-top: 1px solid var(--wqs-surface);
    display: flex; align-items: center; flex-wrap: wrap; gap: .75rem 1rem;
}
.share-label {
    font-weight: 700; font-size: .9rem; color: var(--wqs-muted);
    text-transform: uppercase; letter-spacing: .04em;
}
.share-list { list-style: none; display: flex; flex-wrap: wrap; gap: .5rem; margin: 0; padding: 0; }
.share-list li { list-style: none; }
.share-btn {
    display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
    width: var(--tap); height: var(--tap); padding: 0;
    background: var(--wqs-surface); color: var(--wqs-accent);
    border: 0; border-radius: 999px; cursor: pointer;
    text-decoration: none; font: inherit;
}
.share-btn svg { width: 20px; height: 20px; display: block; }
/* Brand colours on hover only. Each is at least 3:1 against white, which is the
   non-text contrast requirement for an icon. */
.share-whatsapp:hover  { background: #128C7E; color: var(--wqs-white); }
.share-facebook:hover  { background: #1877F2; color: var(--wqs-white); }
.share-x:hover         { background: #000000; color: var(--wqs-white); }
.share-pinterest:hover { background: #BD081C; color: var(--wqs-white); }
/* Added 2026-08-05. White glyph on each brand fill measures 5.69:1 (LinkedIn)
   and 5.11:1 (Instagram) — both well past the 3:1 WCAG 1.4.11 asks of a
   non-text indicator. Instagram's real mark is a gradient; a flat #C13584 is
   used here for the same reason the footer does, so the hover states stay one
   consistent shape rather than one of them carrying a gradient. */
.share-linkedin:hover  { background: #0A66C2; color: var(--wqs-white); }
.share-instagram:hover { background: #C13584; color: var(--wqs-white); }
/* .share-copy, .share-copy.is-copied, .share-copied and
   .share-copy .share-copied:empty removed 2026-08-06 with the button itself.
   Four dead rules left behind is how a stylesheet ends up with selectors nobody
   can find the markup for; they go out with the element in the same change.
   .share-btn keeps `border: 0`, `cursor: pointer` and `font: inherit` even
   though every remaining item is an <a>. Those three exist to normalise a
   <button> and are now inert — left in place because this partial is the fleet
   template and the other eleven themes still carry the button. */

/* --- comments ----------------------------------------------------------- */
/* Ghost's native comments, added 2026-08-06. The UI itself is a web component
   Ghost injects and mounts here; almost none of its internals are stylable from
   this file, so what follows styles the CONTAINER and the surrounding copy only.
   Do not add rules targeting the component's internal classes — they are
   generated by a bundled build and rename between Ghost releases, so such rules
   look fine for a while and then silently stop applying.

   The one hook Ghost does honour is the site's accent colour, which the comments
   UI reads for its buttons and links. That comes from the accent_color setting,
   not from this stylesheet.

   Width matches the post body: this sits inside .wrap-narrow, so it inherits the
   720px measure and lines up with the paragraphs above rather than running the
   full page width like the related-posts grid below it. */
.post-comments {
    /* 2.5rem -> 1.75rem, 2026-08-06 by request: the gap above Comments and
       above More like this was wider than the space between paragraphs inside
       them, which made each section look detached from the post it belongs to.
       Trimmed on both so the two keep matching. */
    margin-top: 1.75rem;
    padding-top: 1.5rem;
    border-top: 1px solid var(--wqs-surface);
}
/* The section heading reuses .section-title so it matches "More like this"
   directly beneath it — two headings of the same rank should not be two
   different sizes. Margin is tightened because the note sits under it. */
.post-comments .section-title { margin-bottom: .35rem; }
/* Explains the sign-in step BEFORE the reader hits it. Ghost's own empty state
   just shows a "Sign in" button with no indication that it is passwordless, and
   on a site with no membership offering that button reads like a paywall. --wqs-muted
   is 5.39:1 on the page, AA at this size. */
.post-comments-note {
    margin: 0 0 1.25rem;
    color: var(--wqs-muted);
    font-size: .92rem;
    line-height: 1.55;
}

/* FACEBOOK COMMENTS WILL NOT BE FULL WIDTH WITHOUT THIS, AND data-width="100%"
   IS NOT ENOUGH ON ITS OWN. The SDK renders into an <iframe> and writes an
   INLINE width in px onto both the iframe and its wrapping <span>, computed once
   from the container at first paint. Inline styles beat every class rule, so the
   override has to carry !important, and it has to name the span as well as the
   iframe or the iframe is 100% of a span that is still 550px.
   The consequence of leaving it: the thread renders at a fixed width and never
   reflows, so it is correct at exactly one viewport and wrong at every other —
   including after a phone rotates. */
/* The Facebook width-override rules that used to sit here are GONE with the
   plugin — they existed only to fight the inline px width Meta's SDK writes onto
   its own iframe, and Comentario renders in the light DOM at natural width. */

/* The SDK injects nothing until it loads, so the section would otherwise jump
   from zero height to the full thread — layout shift on every post page, on the
   metric ad and social embeds damage most. A min-height reserves roughly the
   height of the login prompt / empty composer, which is what most readers see.

   THE BORDER IS OURS, NOT FACEBOOK'S — 2026-08-06, by request. The plugin runs
   inside a cross-origin iframe, so nothing inside it can be styled from here;
   the frame is what gets the box. Same hairline and radius as .quote-card so the
   comment area reads as part of this theme rather than a pasted-in widget.
   Background is --wqs-surface, not white: Facebook renders its own thread on
   white, so a tinted well keeps the two visually separate once content loads. */
/* HIDE THE SIGN-IN BAR — it is a dead control on this domain.
   The domain runs auth_anonymous=1 with auth_local, auth_sso and every federated
   provider OFF, so the button opens a dialogue offering nothing. A control that
   leads nowhere is worse than no control, and "no sign-in" was the requirement.

   TARGETING `.comentario-profile-bar`, NOT `.comentario-btn-primary` — the
   submit button carries that same class, so hiding by button style would have
   removed the ONE control that posts a comment. Checked the parent chain in the
   live DOM before writing this rather than guessing from the class name.

   Comentario renders in the LIGHT DOM (verified: no shadowRoot), which is the
   only reason this rule can reach inside the widget at all. If a future version
   moves to shadow DOM this stops applying — and the sign-in button reappearing
   is the symptom to watch for.

   A MALFORMED COMMENT SILENTLY ATE THIS RULE FOR THREE DEPLOYS, and the second
   cause was writing about the first. CSS COMMENTS HAVE NO ESCAPE SEQUENCE: the
   two characters star-slash end a comment wherever they appear, including inside
   backticks. My explanation of the bug literally contained them, so the comment
   closed on the line describing the problem and the prose after it became
   invalid CSS — and the parser discards the next rule along with it. The
   symptom was maddening — the rule was in the served file, the selector matched
   the element in the live DOM, sibling rules from the SAME file applied, and
   computed display stayed `flex`. I misread that as Comentario's runtime
   stylesheet winning on source order. It was not: its rule is
   `.comentario-toolbar{display:flex}`, (0,1,0) and no !important, so this
   (0,2,0) selector outranks it with or without one.
   **When a rule does not apply but its neighbours do, suspect the comment above
   it before you suspect the cascade.** !important is kept only as cheap
   insurance against a future runtime sheet, not because it is needed today. */
.post-comments .comentario-profile-bar { display: none !important; }

/* NO OUTER WELL — 2026-08-06, by request. The widget host is a plain block; the
   only bordered thing is the comment box itself, styled below. min-height stays
   because the widget is client-rendered and the section would otherwise jump
   from 0 to full height on every post page. */
.post-comments comentario-comments {
    display: block;
    min-height: 140px;
    border: 0;
    background: none;
    padding: 0;
}

/* THE COMMENT BOX — this is the one element that carries a border now. Same
   hairline, radius and surface tint as .quote-card so it reads as part of the
   theme rather than a pasted-in widget. */
/* THE HOST IS THE "OUTER BOX" THE EYE SEES, not the widget root. Comentario puts
   a 1px border and a white fill on .comentario-add-comment-host and nests the
   real placeholder inside it, so stripping the widget root alone still left a
   visible frame. Measured in the live DOM, not guessed. */
.post-comments .comentario-add-comment-host {
    border: 0 !important;
    background: none !important;
    padding: 0 !important;
}
.post-comments .comentario-add-comment-placeholder {
    /* The host is display:flex, so the placeholder shrink-wrapped its own text
       and came out 198px wide inside a 720px column. flex-basis is what fixes
       that — width:100% alone loses to flex sizing. */
    flex: 1 1 100%;
    width: 100%;
    box-sizing: border-box;
    border: 1px solid rgba(2,42,92,.18) !important;
    border-radius: var(--radius);
    background: var(--wqs-surface);
    padding: .9rem 1rem;
    min-height: 84px;
    cursor: text;
}
.post-comments .comentario-add-comment-placeholder:hover {
    border-color: var(--wqs-accent) !important;
}

/* PLACEHOLDER TEXT REPLACED WITHOUT JAVASCRIPT. The element is a <div> whose
   text node reads "Add a comment", so there is no `placeholder` attribute to
   set and no way to edit the string from CSS directly. Collapsing the div's own
   font to 0 hides the original text, and ::before supplies ours at normal size.
   The alternative was a MutationObserver re-writing the node after every render
   — more code, and it would flash the old wording first.
   IF THE WIDGET EVER RENDERS AN EMPTY BOX, this is the rule to suspect: a future
   Comentario version that nests the text in a child element would have that
   child inherit font-size:0. */
.post-comments .comentario-add-comment-placeholder { font-size: 0 !important; }
.post-comments .comentario-add-comment-placeholder::before {
    content: "Share your thoughts...";
    font-size: 1rem;
    font-family: var(--font-body);
    color: var(--wqs-muted);
}

/* "POWERED BY COMENTARIO" REMOVED — asked for, and checked against the licence
   first rather than just hidden. Comentario is MIT: the licence obliges us to
   keep the copyright notice in copies of the SOURCE, which we do — the container
   runs the unmodified official image and its LICENSE file is untouched. MIT does
   not require a visible badge in a deployed UI, so hiding it is permitted.
   No supported config toggle exists (checked every key in /api/config for
   powered / branding / footer / logo — none), so CSS is the only route. */
.post-comments .comentario-footer { display: none !important; }

/* SUBMIT BUTTON RELABELLED "Add Comment" to "Post Comment" — asked for.
   Same font-size-zero plus pseudo-element technique as the placeholder above,
   because the label is a text node with no attribute to rewrite.
   SCOPED TO THE EDITOR FOOTER, not to .comentario-btn-primary on its own: that
   class is also worn by the Sign in button, so an unscoped rule would relabel a
   second control. ::after rather than ::before so the text lands after the
   button's submit icon rather than in front of it. The icon is an SVG child and
   is unaffected by font-size zero. */
.post-comments .comentario-comment-editor-footer .comentario-btn-primary {
    font-size: 0 !important;
}
.post-comments .comentario-comment-editor-footer .comentario-btn-primary::after {
    content: "Post Comment";
    font-size: .9rem;
    font-family: var(--font-head);
    font-weight: 700;
}

/* RSS BUTTON REMOVED — asked for. Targeting the WRAPPER, not the button:
   `.comentario-thread-buttons` was measured in the live DOM and contains exactly
   one child, the RSS button. The sibling `.comentario-sort-buttons` holds
   Votes/Oldest/Newest and is untouched, so sorting still works if it is ever
   un-hidden. Hiding by `.comentario-btn-link` would have taken those too. */
.post-comments .comentario-thread-buttons { display: none !important; }

/* --- ads ---------------------------------------------------------------- */

/* --- related / sections ------------------------------------------------- */
.related { background: var(--wqs-surface); border-top: 1px solid rgba(2,42,92,.10); }
/* --wqs-ink, NOT --wqs-brown. This heading sits on the .sections band
   (--wqs-cream #E3E8E4), where the page accent --wqs-brown (--heart-ink) is
   only ~3.80:1 and fails AA — while being a correct ~4.8:1 twenty pixels above
   it on the white page. Fourth fleet site to hit this, with different colours
   every time, which is why the constraint is written on the token. */
/* .feed-title shares this exactly — the homepage's "Latest Posts" and "Top
   Stories" headings are the same rank and the same treatment as the category
   strip's title, and giving them their own near-identical rule is how two
   headings that should match drift apart six months later. */
/* These two SHED the heading face, 2026-08-05 by request: "font family like menu
   font". The menu sets no font-family at all — .site-nav > ul li a only sets
   weight — so it inherits --font-body, i.e. Lora, the serif. Checked rather than
   assumed, because the obvious guess is that a nav uses the heading face.
   So these headings now run in the BODY face while every other h1-h4 on the site
   stays Poppins. That is the intent, not an oversight.
   Weight 700 as asked, up from 500 (unitless — CSS font-weight takes no px).
   Lora ships here as the VARIABLE release covering 400..700, so 700 is the top
   of its real range: a genuine bold instance, not a browser-synthesised fake,
   and not clamped. Anything above 700 WOULD be synthesised on this face. */
.section-title,
.feed-title {
    font-size: clamp(1.3rem, 3vw, 1.7rem); color: var(--wqs-title);
    font-family: var(--font-body); font-weight: 700;
    text-align: center; padding-top: clamp(1.5rem, 4vw, 2.25rem); margin-bottom: 0;
}

/* The second homepage grid. A hairline separates it from the pagination that
   ends the Latest Posts collection above — without it the two grids read as one
   long feed interrupted by a stray pair of buttons. */
.feed-section { border-top: 1px solid rgba(2,42,92,.10); }
/* .feed-more is GONE, not left behind: the "See all stories" text link it styled
   was replaced by a .pagination button, which carries its own spacing. A rule
   for markup that no longer exists is the kind of thing that survives three more
   redesigns and then confuses someone. */

/* Section navigation. Ghost tags are flat, so a "section" is just a tag worth
   promoting. Counts are shown because they are the honest signal of where the
   archive actually is — and because they make the strip useful rather than
   decorative. Chips are 44px tall for the same tap-target reason as .share-btn. */
/* The one Morning Mist surface. It was --wqs-surface, the same tone as .related,
   .archive-head, the card media wells and the tag pills — so the strip had no
   identity of its own. Mist separates it from all of those, and it is the only
   place in the theme that carries the cool half of the badge.
   Renders on index.hbs and tag.hbs only, so a post page still has exactly one
   band (.related, cream) and the home and tag pages have exactly one each. */
/* REPAINTED 2026-08-05: the band was --wqs-apricot #FFCD9C and is now WHITE, by
   request. Consequences worth stating rather than discovering:

   * THE BAND TRAP NO LONGER APPLIES HERE. Every warning in the palette block
     above about --wqs-ink-soft / --wqs-rose / --wqs-ocean-ink failing AA on the
     apricot was about THIS element — it was the only apricot surface in the
     theme. On white they measure 5.39, 5.68 and 5.36 and all pass. The token
     stays declared: it is still the documented brand fill, the deploy script
     asserts it, and the note above is the record of why it is not used here.
   * The strip is now the same colour as the page, so the two hairlines below
     are the ONLY thing separating it from the grid. They are load-bearing now,
     not decoration — do not remove them along with the background.
   * .section-chip is white on white as a result, and reads purely by its own
     hairline border, exactly like a .quote-card does on the same ground. */
.sections {
    margin-top: 3rem;
    background: var(--wqs-paper);
    border-block: 1px solid rgba(2,42,92,.10);
    padding-bottom: clamp(1.5rem, 4vw, 2.25rem);
}
/* Replaces `.hero + .sections`. With the hero gone there is no adjacent sibling
   to key on, and the strip is the first thing in <main> on the homepage — 3rem
   of blank white under the header before the first heading. Keyed on Ghost's
   own body class rather than :first-child so that the invisible <h1> sitting
   above it in the markup cannot break the match. */
.home-template .sections { margin-top: 0; }
.section-list {
    list-style: none; margin: 1.25rem 0 0; padding: 0;
    display: flex; flex-wrap: wrap; gap: .6rem; justify-content: center;
}
.section-list li { list-style: none; }
.section-chip {
    display: inline-flex; align-items: center; gap: .5rem;
    min-height: var(--tap); padding: 0 .5rem 0 1rem;
    background: var(--wqs-white); color: var(--wqs-accent);
    border: 1px solid rgba(2,42,92,.12); border-radius: 999px;
    text-decoration: none; font-weight: 600; font-size: .92rem;
}
.section-chip:hover { background: var(--wqs-accent); color: var(--wqs-white); border-color: var(--wqs-accent); }
.section-chip-count {
    font-size: .75rem; font-weight: 700;
    background: var(--wqs-surface); color: var(--wqs-muted);
    border-radius: 999px; padding: .15rem .55rem;
}
.section-chip:hover .section-chip-count { background: rgba(255,255,255,.18); color: var(--wqs-white); }

/* --- mobile sizing for the topic strip (2026-08-05, by request) -----------
   799px is this theme's existing mobile boundary — the width at which the nav
   collapses — so the strip changes shape at the same point everything else
   does rather than at a second, invented breakpoint.

   min-height:20px reads like a WCAG 2.5.8 failure — the AA target floor is
   24x24 — and it is NOT one. Measured on the live page at 320/360/390/412:
   the chips render 91x28. min-height is a FLOOR, not a height, and the count
   badge nested inside carries .15rem of its own vertical padding, so the box
   settles at 28px on its own. 4px of headroom, not a 4px shortfall.
   Recorded because the declaration looks alarming out of context and the next
   person to read it will otherwise "fix" a rule that is already compliant. */
@media (max-width: 799px) {
    .section-chip {
        padding: 0 .5rem 0 .5rem;
        min-height: 20px;
        font-size: 14px;
    }
    .section-chip-count { font-size: 13px; }
}

/* --- pagination --------------------------------------------------------- */
.pagination {
    display: flex; align-items: center; justify-content: center; gap: 1rem;
    padding-block: 2rem 3rem; font-size: .95rem;
}
.pagination a {
    display: inline-flex; align-items: center; min-height: var(--tap);
    /* The arrow is its own <span> and this is a FLEX container, so the plain
       space between it and the label in the markup is DISCARDED — flex turns
       each text run into an anonymous item and drops the whitespace between
       them. The buttons rendered "<-Previous Page" jammed together. A gap, not
       an &nbsp;, because the arrow span is aria-hidden and a hard space living
       inside hidden content is not one the visible label can depend on. */
    gap: .4rem;
    padding: 0 1.1rem; background: var(--wqs-white);
    border: 1px solid var(--wqs-accent); border-radius: 999px;
    color: var(--wqs-accent); text-decoration: none; font-weight: 600;
}
.pagination a:hover { background: var(--wqs-accent); color: var(--wqs-white); }
.page-number { color: var(--wqs-muted); }

.btn {
    display: inline-flex; align-items: center; justify-content: center;
    min-height: var(--tap); padding: 0 1.5rem;
    background: var(--wqs-accent); color: var(--wqs-white);
    border-radius: 999px; text-decoration: none; font-weight: 600;
}
.btn:hover { background: var(--wqs-ink); color: var(--wqs-white); }

/* --- error -------------------------------------------------------------- */
.error-page { text-align: center; padding-block: clamp(2.5rem, 8vw, 5rem); }
.error-code { font-family: var(--font-head); font-size: 4rem; color: var(--wqs-ocean-ink); margin: 0; }
.error-title { font-size: clamp(1.4rem, 4vw, 2rem); color: var(--wqs-title); }
.error-help { color: var(--wqs-muted); }

/* --- footer ------------------------------------------------------------- */
/* FOOTER GROUND CHANGED 2026-08-05: was var(--wqs-ink) #022A5C, the silhouette
   navy, now #300050 by request — a deep violet.

   Set as its OWN token on .site-foot rather than by repointing --wqs-ink, which
   would have been the quick way to do it and would have repainted the body text,
   every heading, the skip link and the .btn hover state along with it. The
   footer is the only surface that moves.

   NOT a measured brand colour — every other value in this file came out of the
   master badge, this one was specified directly. Its nearest relative in the
   kit is --wqs-plum #4C1656; it is darker than that and darker than the navy it
   replaces (relative luminance 0.0121 vs 0.0244), which is why every ratio
   below IMPROVED rather than needing a rework:

     #E8DED8  body copy   10.67 -> 12.78:1
     #F0E4D2  links       11.24 -> 13.48:1
     #C9BAB2  copyright    7.49 ->  8.98:1
     #FFFFFF  headings    14.11 -> 16.92:1
     #F3E9DD  on the .10 white wash used by the pills and icon discs, 11.16:1

   Re-measured, not assumed — a darker ground is only safe for TEXT, and this
   file has been wrong about inherited contrast numbers twice before. */
/* FOOTER GROUND 2026-08-06, by request: #300060 -> --wqs-navy-true #022A5C.
   Same reasoning as the header the same day. #300060 was specified by hand and
   appears nowhere in the artwork; #022A5C IS the artwork — it is the tree-and-
   figure silhouette and the arced wordmark, 6.13% of the mark by area and the
   largest non-white share in it. The palette doc names it as one of the two
   canonical dark GROUNDS and says outright that "dark mode is the badge,
   enlarged": every light colour in the logo already sits on this navy inside the
   logo, and each clears AAA against it (8.23-11.65).

   THE TEXT ALL SURVIVES, CHECKED RATHER THAN ASSUMED. Everything in this footer
   is --wqs-white, which was 16.22:1 on the violet and is 14.11:1 here. Lower, and
   still AAA at every size in this region, so not one ink needed moving.

   ONE THING DID NOT SURVIVE, AND IT IS NOT TEXT. The global focus ring is
   --wqs-ocean-ink, which measures 5.36:1 on the white page and 3.03:1 on the old
   violet — and only 2.64:1 on this navy, under the 3:1 WCAG 1.4.11 requires of a
   non-text indicator. So a keyboard user tabbing into the footer would have lost
   the ring at the exact moment they needed it. The kit predicts this precisely:
   the ocean is the single colour in the palette that fails against the navy, and
   it ships a lifted variant for it. Overridden below, scoped to this region only.

   That is the third time on this site a ground change has turned out to be a
   PAIR. Header v1.7.1 (light inks -> dark bar), header v2.5.0 (dark inks -> white
   bar), and now the footer, where the casualty was the one element nobody looks
   at until they are navigating by keyboard. */
.site-foot {
    --wqs-foot: var(--wqs-navy-true);
    background: var(--wqs-foot); color: var(--wqs-white);
    margin-top: 3rem; padding-block: clamp(2rem, 5vw, 3rem);
}
/* See above. Scoped to the footer: on the white page and the white header
   --wqs-ocean-ink is 5.36:1 and is the better ring, so this must not be global.
   outline-color alone, so the 3px width and 2px offset stay in one place. */
.site-foot :focus-visible { outline-color: var(--wqs-ocean-lift); }
.site-foot a { color: var(--wqs-white); }
.foot-heading { color: var(--wqs-white); font-size: 1.05rem; margin-bottom: .9rem; }

/* --- footer widgets (added 2026-08-05) ---------------------------------- */
/* Three widgets: about + social, recent posts, category list.
   One column on a phone, two in the middle band, three on desktop. The about
   column is given slightly more track because it holds a paragraph while the
   other two hold lists — at an even 1fr the prose column set to ~28 characters
   and read as a ragged strip. */
.foot-widgets {
    display: grid;
    grid-template-columns: 1fr;
    gap: 2rem;
    padding-bottom: 2rem;
}
@media (min-width: 640px) {
    .foot-widgets { grid-template-columns: repeat(2, 1fr); gap: 2rem 2.5rem; }
    /* The category cloud is the widest of the three and the only one that wraps
       freely, so at two columns it takes the full second row rather than being
       squeezed into a half-width well beside nothing. */
    .foot-widget-tags { grid-column: 1 / -1; }
}
@media (min-width: 1000px) {
    .foot-widgets { grid-template-columns: 1.15fr 1fr 1.1fr; gap: 2.5rem; }
    .foot-widget-tags { grid-column: auto; }
}

/* Brand lockup at the top of widget 1 — badge + site name, mirroring the header
   so the footer closes with the same mark the page opened on. */
.foot-brand {
    display: inline-flex; align-items: center; gap: .7rem;
    text-decoration: none;
    margin-bottom: .9rem;
    min-height: var(--tap);
}
.foot-brand-logo {
    width: 46px; height: 46px;
    border-radius: 50%; object-fit: contain; flex: none;
    /* The badge is a full-bleed illustration with its own ground, so this well
       never shows through it — it is here for the {{else}} branch's bundled
       logo.png, which is transparent and would otherwise dissolve into the navy
       footer. Same reason .foot-post-media carries one. */
    background: rgba(255,255,255,.10);
}
/* Stacks the name over the tagline, mirroring .site-brand-text in the header. */
.foot-brand-text { display: flex; flex-direction: column; }
.foot-brand-name {
    /* An explicit colour on the span, not on the anchor: `.site-foot a` is
       (0,1,1) and out-specifies a bare `.foot-brand`, so setting it there would
       silently lose. A declaration on the child beats an inherited value
       whatever the specificity. */
    font-family: var(--font-head); font-weight: 700;
    font-size: 1.05rem; line-height: 1.2;
    color: var(--wqs-white);
}
/* Same size and role as .site-tagline in the header, in the footer's muted tone
   — #C9BAB2 measures 7.49:1 on the navy, so it stays AAA even at .78rem. No
   font-family: it inherits the body face, exactly as the header tagline does. */
.foot-brand-tagline {
    font-size: .78rem; line-height: 1.25;
    color: var(--wqs-white);
}
/* Underline the NAME only. The tagline is a descriptor, not the link's subject,
   and underlining both makes the lockup read as one long link on hover. */
.foot-brand:hover .foot-brand-name { text-decoration: underline; }

/* Sub-heading inside a widget — smaller than .foot-heading so the widget keeps
   one dominant title ("About Us") and this reads as a level below it, which is
   also what the h2/h3 nesting says. */
.foot-subheading {
    font-family: var(--font-head);
    color: var(--wqs-white);
    font-size: .92rem; font-weight: 600;
    margin: 1.25rem 0 .6rem;
}

.foot-about-text {
    margin: 0;
    color: var(--wqs-white);   /* 16.22:1 on the #300060 footer */
    font-size: .95rem;
    line-height: 1.7;
    max-width: 42ch;
}

/* RECENT POSTS. Two class selectors on the link colour deliberately: the
   `.site-foot a` rule above is (0,1,1) and would otherwise out-specify a bare
   `.foot-post-link`. Same trap the .foot-social comment records. */
.foot-posts {
    list-style: none; margin: 0; padding: 0;
    display: flex; flex-direction: column; gap: .85rem;
}
.foot-posts li { list-style: none; }
.site-foot .foot-post-link {
    display: flex; align-items: center; gap: .75rem;
    text-decoration: none; color: var(--wqs-white);
    /* the 50px circle still clears WCAG 2.5.8's 24x24; this holds the row height
       for a post whose title is a single short line */
    min-height: 50px;
}
/* 50px, down from 56, by request. THREE places carry this number and all three
   must move together: this box, the <img> inside it, and the width/height
   ATTRIBUTES in default.hbs. Changing the width alone turns the circle into an
   oval, because height is a separate declaration and border-radius:50% follows
   whatever box it is given. The HTML attributes matter too — they are what
   reserves the space before the image arrives, and leaving them at 56 would
   reintroduce the layout shift they exist to prevent.
   size="xs" is still right: 150w over a 50px circle is 3x, ample on any screen. */
.foot-post-media {
    display: block; flex: none;
    width: 50px; height: 50px;
    border-radius: 50%; overflow: hidden;
    /* the well shows through for a post with no feature image, so the row keeps
       its shape instead of collapsing to a bare title */
    background: rgba(255,255,255,.10);
}
.foot-post-media img {
    width: 50px; height: 50px;      /* explicit: the global `img { height:auto }`
                                       would otherwise letterbox a non-square
                                       source inside the circle */
    object-fit: cover; border-radius: 50%;
}
.foot-post-title {
    font-family: var(--font-head); font-weight: 600;
    font-size: .92rem; line-height: 1.35;
    /* Titles on this site run long — several exceed 90 characters. Unclamped,
       one of them turns a 56px row into a 5-line block and the five circles
       stop reading as a list. Two lines, then ellipsis. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.site-foot .foot-post-link:hover .foot-post-title,
.site-foot .foot-post-link:focus-visible .foot-post-title {
    color: var(--wqs-white); text-decoration: underline;
}
.site-foot .foot-post-link:hover .foot-post-media { background: rgba(255,255,255,.20); }

/* margin-bottom was 2rem, spacing the cloud off the social row that used to sit
   under it. The widget grid owns that spacing now. */
.tag-cloud { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .5rem; }
.site-foot .tag-pill {
    background: rgba(255,255,255,.10); color: var(--wqs-white);
}
/* Hover fill moved to the brand palette 2026-08-06: #FFA744 -> --wqs-apricot
   #FFCD9C. The amber was picked by eye on 2026-08-05 and was the last hand-
   chosen hex left in the stylesheet after the accent and title repaint.
   APRICOT IS THE RIGHT BRAND ANSWER RATHER THAN MERELY A NEARBY ONE: it is the
   sunset band in the badge, 3.27% of the artwork by area, and it is the same
   warm family the amber was reaching for — so this keeps the look that was asked
   for while making it a measured colour instead of an invented one.
   THE PAIR STILL HOLDS, AND IT IS WHY THIS RULE SETS BOTH PROPERTIES. Apricot is
   a light fill (1.45:1 on white — the palette marks it FILL ONLY), so the label
   must stay dark. --wqs-ink on apricot is 9.72:1 straight off the kit's contrast
   matrix, up from 8.76:1 on the amber. Leave the colour off and the label would
   inherit the footer's white and sit at ~1.4:1 — invisible at the exact moment
   of interaction, which is the failure this rule has already been fixed for once. */
.site-foot .tag-pill:hover { background: var(--wqs-apricot); color: var(--wqs-ink); }
/* SPLIT ROW 2026-08-06, by request: legal links LEFT, copyright RIGHT.
   This undoes the centring of 2026-08-05, which was itself put in because the
   copyright was a lone line of type pinned to one corner with nothing opposite
   it. There is something opposite it now, so a split row is what it wanted all
   along.
   The v1.4.0 comment warned that `justify-content` on a row would centre a nav
   and the copyright as a GROUP, leaving the copyright off-centre. The same trap
   applies to space-between: with three children the copyright lands in the
   MIDDLE, not the right. That is why default.hbs wraps the legal nav and any
   future secondary nav in .foot-meta-left — this row has exactly two children by
   construction, so the split cannot drift with content. */
.foot-meta {
    border-top: 1px solid rgba(255,255,255,.14); padding-top: 1.25rem;
    display: flex; flex-wrap: wrap; align-items: center;
    justify-content: space-between;
    gap: .75rem 1.5rem;
    text-align: left;
}
.foot-meta-left { display: flex; flex-direction: column; gap: .5rem; }
.foot-nav ul { list-style: none; display: flex; flex-wrap: wrap; gap: .25rem 1rem; margin: 0; padding: 0; }
.foot-nav li { list-style: none; }
.foot-nav li a { display: inline-flex; align-items: center; min-height: 36px; }
/* Same shape as .foot-nav — this is the hardcoded sibling of it, so the two must
   look identical whether the links come from the theme or from Ghost's secondary
   navigation. Colour is inherited from `.site-foot a` (white, 16.22:1 on the
   #300060 footer) and is deliberately not restated: one declaration, not two
   that drift.
   1.25rem of column gap rather than .foot-nav's 1rem: four long labels sit
   closer to each other than three short ones read as. 36px rows keep each label
   clear of the WCAG 2.5.8 24x24 floor when they wrap to two lines on a phone. */
.foot-legal ul { list-style: none; display: flex; flex-wrap: wrap; gap: .25rem 1.25rem; margin: 0; padding: 0; }
.foot-legal li { list-style: none; }
.foot-legal a { display: inline-flex; align-items: center; min-height: 36px; font-size: .88rem; }
.foot-copy { margin: 0; font-size: .88rem; color: var(--wqs-white); }

/* Below 640px the two halves stack, and a left-aligned block over a
   right-aligned one reads as a mistake rather than a layout. So the split only
   exists where there is room for it and everything re-centres underneath.
   640px is the same breakpoint .foot-widgets uses to go from one column to two,
   so the footer changes shape once rather than twice. */
@media (max-width: 639px) {
    .foot-meta { flex-direction: column; align-items: center; text-align: center; }
    .foot-meta-left { align-items: center; }
    .foot-legal ul { justify-content: center; }
}

/* --- social icons ------------------------------------------------------- */
/* One partial serves both places: a compact three in the header nav and the full
   set in the footer. Ported from wisdom-life-quotes v1.2.1. Class names match that
   theme deliberately, so the fleet has one vocabulary for this component. */
.social-icons {
    display: flex; flex-wrap: wrap; gap: .25rem;
    list-style: none; margin: 0; padding: 0;
}
.social-icons li { list-style: none; }
.social-icon {
    display: inline-flex; align-items: center; justify-content: center;
    /* WCAG 2.5.8 is AA at 24x24. 38 clears it with margin without making the
       header taller than the 46px logo. The footer set gets --tap below. */
    width: 38px; height: 38px;
    border-radius: 50%;
    color: inherit; text-decoration: none;
}
.social-icon svg { width: 19px; height: 19px; display: block; }

/* Header: the bar is WHITE again as of 2026-08-06, so these go back to the nav's
   ink — --wqs-ink #022A5C, 14.11:1 — after a spell at white for the violet bar.
   THE HOVER HAD TO BE REBUILT, NOT JUST RE-INKED. It was "invert to a white disc
   with the accent as the glyph", which is a real inversion on a dark bar and is
   NO CHANGE AT ALL on a white one — a white disc on white, and the only visible
   difference would have been the glyph shifting navy to amethyst. So the disc
   takes --wqs-sky, matching the nav's hover pill exactly, and the glyph stays
   navy at 11.14:1. One hover treatment for both halves of the nav row.
   FOUR icons here now, not three — Pinterest joined the compact set. They sit in
   a --tap-tall flex row inside .site-nav, which has no fixed count, so the extra
   circle needs no rule; but see the 800-1023px block below, which hides this row
   entirely because six nav items plus icons wrapped the bar. That measurement
   was taken with three icons and the fourth only widens the case for hiding
   them, so the breakpoint still holds. */
.site-social .social-icon { color: var(--wqs-ink); }
.site-social .social-icon:hover { background: var(--wqs-sky); color: var(--wqs-ink); }

/* Footer: two class selectors, so this beats the .site-foot a colour rule above. */
/* The row now sits INSIDE widget 1, under the about paragraph, rather than as a
   standalone block above .foot-meta. It carried margin-top:1.1rem to open that
   gap; the "Join Our Community" heading now sits between the two and owns the
   spacing on both sides. Left at 1.1rem this would COLLAPSE with the heading's
   .6rem bottom margin to 1.1rem — adjacent siblings collapse to the larger — and
   push the icons away from the heading that labels them. */
.foot-social { margin-top: 0; }

/* EIGHT ICONS IN A COLUMN THAT IS NO LONGER FULL WIDTH.
   As a wrapping flex row this broke 7 + 1 at every desktop and tablet width
   measured — 1440, 1024 and 800 — because eight 44px targets plus their gaps
   need 408px and the about track is 284-389px across that range. One icon
   stranded on its own line reads as a bug rather than a layout.
   There is no width that fixes it: shrinking the icons to 38px still overflows
   at 1024, and widening the track starves the category cloud. So the count is
   stated instead of left to the container — a fixed 4x2 block, which needs
   200px and therefore fits every column this grid can produce, down to a 320px
   phone. Targets stay at 44px, well clear of the 24x24 WCAG 2.5.8 AA floor.
   max-content, not 1fr: 1fr would stretch each cell to fill the track and space
   the circles apart differently in every column width. */
.foot-widget-about .social-icons {
    display: grid;
    grid-template-columns: repeat(4, max-content);
    /* 15px — 8 -> 10 -> 20 -> 15 by request. Stated in px rather than rem
       because the ask was in px and because this is the space between two
       fixed-size circles, not type; it should not scale with the root font size.
       Applies on BOTH axes, so the two rows separate by the same 15px.
       Fits comfortably: a 4x2 block of 44px targets is 4*15 + ... i.e.
       4*44 + 3*15 = 221px, and the narrowest column this grid produces is
       ~284px.
       THIS IS THE ONLY GAP FOR THIS ROW. `.foot-social .social-icons` below used
       to set one too; both selectors are (0,2,0) and both match, so the value
       was decided purely by source order — the kind of pair that gets edited in
       the wrong place six months later. Its declaration was removed. */
    gap: 15px;
    justify-content: start;
}
/* gap deliberately NOT set here — see .foot-widget-about .social-icons, which is
   the single source of truth for this row's spacing. Two equal-specificity rules
   both setting it meant source order silently decided the winner. */
.site-foot .social-icon {
    width: var(--tap); height: var(--tap);
    background: rgba(255,255,255,.10); color: var(--wqs-white);
}
.site-foot .social-icon svg { width: 20px; height: 20px; }
/* Hover inverts to a white disc with the brand colour as the glyph, rather than
   the brand-coloured disc wisdom-life-quotes uses. That theme's footer is a light
   surface; this one is near-black, where X's #000 and Tumblr's #36465D as
   backgrounds read as a hole punched in the page. Same pattern, different ground. */
.site-foot .social-icon:hover      { background: var(--wqs-white); }
.site-foot .social-facebook:hover  { color: #1877F2; }
.site-foot .social-instagram:hover { color: #C13584; }
.site-foot .social-x:hover         { color: #000000; }
.site-foot .social-pinterest:hover { color: #BD081C; }
.site-foot .social-youtube:hover   { color: #FF0000; }
.site-foot .social-tumblr:hover    { color: #36465D; }
/* Flickr is new in this theme — wisdom-quotes-4u has no Flickr account, so the
   partial it was forked from had no icon and this file had no rule. #FF0084 is
   Flickr's pink; it sits on the WHITE hover disc, not on the navy footer, so the
   comparison that matters is 3.76:1 on white — clear of the 3:1 WCAG 1.4.11 asks
   of a non-text indicator, and the tightest margin of any icon in this row.
   Measured, not estimated: against --wqs-ink it would read 4.48:1, which is the
   more flattering number and the wrong one. Do not quote it. */
.site-foot .social-flickr:hover    { color: #FF0084; }
.site-foot .social-email:hover     { color: var(--wqs-accent); }

/* --- motion ------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
    * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
    /* :focus-within added alongside :hover — the card now lifts on keyboard focus
       too, and killing only the hover transform would leave keyboard users with
       the motion this query exists to suppress. */
    .quote-card:hover,
    .quote-card:focus-within { transform: none; }
}

/* --- single-post sidebar ------------------------------------------------ */
/* Added 2026-08-06 by request: a right sidebar on single posts only, carrying
   Facebook follow, subscribe, categories and archives.

   THE ARTICLE MEASURE IS NOT TOUCHED. The left track is capped at
   --wrap-narrow, the same 720px the post body has always used, so line length
   is identical to before at every width. The sidebar is added beside the
   article inside the wider --wrap, not carved out of it.

   Everything below inherits the existing tokens - no new colours are introduced
   and no contrast pair here is new. The pairs in use are --wqs-text and
   --wqs-title on --wqs-paper, and --wqs-accent on --wqs-surface, all of which
   are measured further up this file.                                          */

.post-layout {
    --side-w: 400px;   /* sized to the Facebook frame; see widget-facebook.hbs */
    --side-gap: 2.5rem;
}

/* STACKED IS THE DEFAULT, and the wide layout is the override. Written this way
   round because the stacked case is the one most of this site's readers get -
   the traffic is overwhelmingly mobile - so it is the case that should need no
   media query to be correct. */
/* A FLEX COLUMN, PURELY SO THE STACKED ORDER CAN BE SET INDEPENDENTLY OF THE DOM.
   Changed 2026-08-06 by request: on a phone the comments must come DIRECTLY under
   the post, before the sidebar starts.

   The DOM order is main, sidebar, extra — and it has to stay that way, because on
   the wide layout the sidebar is a grid item spanning both rows and reordering the
   source would put it after the comments it is supposed to run alongside. `order`
   changes the visual sequence without touching the source, which is exactly the
   case it exists for.

   Source order still degrades sensibly: with CSS off, a reader gets post, sidebar,
   comments — all present, none trapped. And because `order` does not affect the
   accessibility tree, the tab sequence follows the DOM; that is the known cost of
   this technique, and it is acceptable here because all three regions are landmarked
   and reachable, rather than a control being separated from what it controls. */
.post-layout { display: flex; flex-direction: column; }
.post-main    { order: 1; }
.post-extra   { order: 2; }   /* comments — directly under the article on a phone */
.post-sidebar { order: 3; }

.post-main,
.post-extra,
.post-sidebar {
    width: min(100%, var(--wrap-narrow));
    margin-inline: auto;
}
.post-sidebar { margin-top: 2.75rem; }

/* 1200px = 720 article + 40 gap + 400 sidebar + the 2rem .wrap gutter, rounded
   up. Below it the grid does not apply at all and the three children are plain
   stacked blocks in DOM order: article, sidebar, comments. */
@media (min-width: 1200px) {
    .post-layout {
        display: grid;
        grid-template-columns: minmax(0, var(--wrap-narrow)) var(--side-w);
        column-gap: var(--side-gap);
        /* The pair is 1160px inside an 1180px wrap, so it needs centring.
           justify-content on the GRID, not margin auto on the items - auto
           margins are consumed before the tracks are placed. */
        justify-content: center;
        align-items: start;
    }
    /* Explicit placement, because the sidebar has to span both rows: the
       comments sit under the article in column 1 while the sidebar continues
       past them. Implicit placement would put the comments beside the sidebar.
       These also make the `order` values above irrelevant here — explicit
       placement wins over order — so the wide layout is unaffected by the
       phone-only reordering. */
    .post-main    { grid-column: 1; grid-row: 1; }
    .post-sidebar { grid-column: 2; grid-row: 1 / span 2; margin-top: 0; }
    .post-extra   { grid-column: 1; grid-row: 2; }

    .post-main,
    .post-extra,
    .post-sidebar { width: auto; margin-inline: 0; }
}

/* STICKY SIDEBAR, PINNED BY ITS BOTTOM EDGE — 2026-08-06, chosen by request
   after the alternatives were measured.

   THE PROBLEM THIS SOLVES: the column is 909px and the viewport is 895px. A
   top-pinned sticky element that is TALLER than the viewport hangs its lower
   part off the bottom of the screen and scrolling can never bring it back —
   measured overflowing by 120px, with the archives dropdown simply unreachable.
   Two other fixes were tried and rejected: an inner `max-height` + `overflow-y`
   worked but put a second scrollbar inside the column (removed by request), and
   shrinking the sidebar to fit worked but cost the Facebook timeline (restored
   by request).

   WHY `bottom` FIXES IT. With `bottom: 1rem` the column scrolls with the page
   until its BOTTOM edge reaches 1rem above the viewport floor, and pins there.
   So reading down the page walks the reader through the whole column — the
   widgets below the fold arrive as you scroll rather than being stranded — and
   from then on it holds position. Everything stays reachable at any viewport
   height, with no inner scrollbar and no height cap. The cost, stated: once
   pinned, the TOP of the column (the Facebook widget) sits above the viewport,
   so it is the bottom widgets that stay on screen. Scrolling back up returns it.

   `top: auto` is REQUIRED, not tidiness — it undoes the previous top offset.
   Sticky honours BOTH edges if both are set, and a box with `top` and `bottom`
   on an element taller than the gap between them stays stuck to `top`, which
   would have silently reproduced the exact bug this replaces.

   No `align-self` is needed: the grid already sets `align-items: start`, so the
   column begins at the top of its area and only leaves it on scroll.

   Desktop only — inside the min-width:1200px block, because on a phone the
   sidebar is a stacked block and pinning it would mean nothing. */
@media (min-width: 1200px) {
    /* The aside stretches to fill its whole grid area — article + comments +
       related — purely to give the sticky child room to travel. `align-items:
       start` on the grid would otherwise size it to its content and there would
       be nothing to stick inside; that was measured failing to pin at all. */
    .post-sidebar { align-self: stretch; }
    .post-sidebar-inner {
        /* MEASURED HEIGHT OF THE COLUMN. It is a hardcoded approximation and the
           one fragile number in this rule: if a widget is added, removed or
           resized, re-measure and update it. Being wrong is not fatal — too
           small leaves a gap under the pinned column, too large hides a little
           of its bottom — but it will drift. Re-measured 2026-08-06 after the
           timeline was removed: 945 -> 700. THE STALE 945 WAS A LIVE BUG for one
           deploy — with the column at 698 the min() still resolved to the short-
           viewport branch (-66px), so it pinned ABOVE the header instead of under
           it, on a screen it now fits comfortably. Exactly the drift predicted
           two lines up. UPDATE THIS WHENEVER A WIDGET CHANGES.
           THE FACEBOOK WIDGET WAS RESIZED SEVEN TIMES ON 2026-08-07 AND THIS
           NUMBER HAD TO FOLLOW EVERY ONE. Live readings, in order:

               700  no timeline
              1070  370px frame        1100  400        1040  340
              1000  300                 920  280 + slimmer chrome
              1360  40:60 split         851  400px widget   <- HERE

           For the first few it was simply 698 + the visible frame, and then the
           card's own chrome started moving too and the shortcut died. Predicting
           it has been wrong twice (915 vs a measured 920; 850 vs 851), which is
           the whole argument: RE-MEASURE, DO NOT CALCULATE, whenever anything in
           this column changes. A stale value makes min() take the short-viewport
           branch on a screen that fits and pins the column ABOVE the site
           header — that has shipped once. */
        --side-h: 851px;

        position: sticky;
        /* THE OFFSET-TOP TRICK, and why `bottom` is NOT used here.
           `bottom` on a sticky box engages while scrolling UP — it stops the box
           falling below the scrollport floor. It does nothing on the way DOWN,
           which is the direction that matters for a sidebar. Setting
           `bottom: 1rem` was tried and measured never pinning once, at any
           scroll position, and it still did not pin when the column was
           temporarily shrunk below the viewport height — which ruled out height
           as the cause and identified the semantics as the cause.

           min() of two offsets gives the right behaviour at both extremes with
           no JavaScript:
             - TALL viewport, where the column fits: the first term wins and this
               is ordinary top-sticky, pinned under the 90px site header.
             - SHORT viewport, where the column is taller than the screen: the
               second term is smaller (often negative), so the column scrolls up
               with the page until its BOTTOM sits 1rem above the viewport floor,
               and pins there. The reader walks down through the whole column on
               the way, so nothing is stranded — which was the entire problem. */
        top: min(calc(90px + 1rem), calc(100vh - var(--side-h) - 1rem));
    }
    /* THE INNER SCROLL WAS REMOVED 2026-08-06 BY REQUEST — plain sticky, no
       max-height, no overflow. Stating the consequence once rather than
       rediscovering it later: this column runs to roughly 1,100px and a laptop
       viewport is ~800-900px, so on a short screen the bottom of the sidebar
       (the archives dropdown) sits below the fold and STAYS there — a sticky
       element taller than the viewport pins its top, so scrolling the page no
       longer brings its lower part into view. That is the trade for having no
       scrollbar inside the column, and it was chosen deliberately. It is not
       visible on a tall monitor, where the whole column fits.
       If it becomes a problem, the fix is `align-self: end` with `top: auto;
       bottom: 1rem`, which pins the BOTTOM instead — everything stays reachable
       and there is still no inner scrollbar, at the cost of the Facebook widget
       scrolling out of view. */
}

/* 2rem -> 3.25rem by request, 2026-08-06. The four widgets are visually
   distinct things (a Facebook frame, a form, and two dropdowns) and at 2rem the
   accent rule under each heading read as though it belonged to the widget
   ABOVE it rather than the one below. The gap is now clearly larger than any
   space inside a widget, which is what makes the grouping unambiguous.
   :last-child keeps the column from ending in dead space. */
.side-widget { margin-bottom: 1.75rem; }
.side-widget:last-child { margin-bottom: 0; }

.side-heading {
    position: relative;
    font-family: var(--font-head);
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--wqs-title);
    margin: 0 0 .6rem;
    padding-bottom: .5rem;
    border-bottom: 1px solid rgba(2,42,92,.10);
}
/* A short accent rule sitting on the hairline. Decorative only - it carries no
   information, so its contrast is comfort rather than a requirement. */
.side-heading::after {
    content: "";
    position: absolute;
    left: 0; bottom: -1px;
    width: 44px; height: 2px;
    background: var(--wqs-accent);
}

/* --- sidebar: facebook --- */
/* One card holding OUR header and Facebook's cropped frame, so the two read as a
   single widget rather than as a heading with an embed stuck under it. The
   border and radius live here; the frame inside carries neither. */
.fb-card {
    border: 1px solid rgba(2,42,92,.14);
    border-radius: var(--radius);
    background: var(--wqs-white);
    overflow: hidden;
}
/* ── THE COVER FILLS THE CARD'S FULL WIDTH — `cover`, NOT `contain` ─────────
   Asked 2026-08-07: the artwork should span the widget edge to edge. `contain`
   had been used to show the whole 800x451 image inside a shorter box, which
   worked but letterboxed ~187px of tinted bar either side. `cover` fills the
   width instead and takes the difference off the top and bottom.

   ⚠⚠ THIS DOES CROP, AND THAT IS A REVERSAL — an earlier instruction in the
   same conversation was "don't crop the cover". It is not a contradiction that
   can be engineered away: at full card width the uncropped artwork is 224px
   tall, so keeping it whole AND filling the width AND making the profile block
   SMALLER are mutually exclusive. Filling the width won. Do not "restore"
   `contain` without re-reading that trade — it will silently grow the profile
   block by 100px+ or bring the side bars back.

   THE BAND IS 45%, CHOSEN FROM A SIDE-BY-SIDE at the real 398px width:
   35% cut the URL line, 55% clipped the tree crown, 65% cut the top of
   "Wisdom". 45% holds the butterflies, both wordmark lines, the URL and the
   meditating figure's head. A 100px box shows ~45% of the frame, so there is
   far more latitude here than the old 44px band had (~2 points) — but
   RE-CHECK IT IF THE HEIGHT MOVES, because the two are one decision and a
   stale position cut the figure's head the last time the box shrank.

   ⚠ THIS HEIGHT IS A BUDGET LINE, NOT A LOOK:

       2 border + 100 cover + 60 header + 49 buttons + 150 feed = 361 -> 400 widget

   Every pixel added here comes straight out of the feed below. The three moving
   parts are this height, .fb-embed-clip's height, and the iframe's requested
   height (clip + 130).

   The width/height ATTRIBUTES on the <img> (800/451) reserve the ratio before
   the file lands, so there is no layout shift. */
.fb-card-cover {
    display: block;
    width: 100%;
    height: 100px;
    object-fit: cover;
    object-position: center 45%;
    background: var(--wqs-surface);
}
/* THE AVATAR DRIVES THIS ROW'S HEIGHT — the name and follower lines together
   come to ~39px, so whichever is taller wins and it is always the avatar.
   Sized down to 38 during the shrink pass, then back to 48 when the profile
   block was asked to grow again; the row follows it either way.

   ⚠ THE AVATAR SIZE LIVES IN THREE PLACES — this box, and the width AND height
   ATTRIBUTES on both <img> branches in widget-facebook.hbs. Change only the CSS
   and you get an oval on a slow connection; leave the attributes stale and you
   reintroduce the layout shift they exist to prevent. */
.fb-card-head {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .5rem .7rem;
    border-bottom: 1px solid rgba(2,42,92,.10);
}
.fb-card-avatar {
    flex: 0 0 auto;
    width: 42px; height: 42px;
    border-radius: 50%;
    background: var(--wqs-surface);
}
/* min-width:0 so a long site title wraps instead of forcing the row wider than
   the card — the same flex default that bit the archive rows. */
.fb-card-text { min-width: 0; }
.fb-card-name {
    display: block;
    font-family: var(--font-head);
    font-weight: 700;
    font-size: .95rem;
    line-height: 1.25;
    color: var(--wqs-title);
    text-decoration: none;
}
.fb-card-name:hover { color: var(--wqs-accent); text-decoration: underline; }
/* This is the line Facebook could not render legibly. --wqs-muted is 5.39:1 on
   white, AA at this size. */
.fb-card-followers {
    margin: .15rem 0 0;
    font-family: var(--font-head);
    font-size: .83rem;
    color: var(--wqs-muted);
}
/* Replaces Facebook's own Follow + Share bar, which the crop removed. Its own
   row on the surface tint, mirroring the bar Facebook drew there. */
/* The hit target has never gone below 32px through any of the resizes — WCAG
   2.5.8 wants 24x24 and the padding is what moves, not the target. Back to 38
   with the rest of the profile block. */
.fb-card-actions {
    display: flex;
    gap: .5rem;
    padding: .45rem .7rem;
    background: var(--wqs-surface);
    border-bottom: 1px solid rgba(2,42,92,.10);
}
.fb-card-btn {
    flex: 1 1 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 34px;
    padding: 0 .8rem;
    border: 1px solid var(--wqs-accent);
    border-radius: 6px;
    background: var(--wqs-white);
    color: var(--wqs-accent);
    font-family: var(--font-head);
    font-size: .85rem;
    font-weight: 600;
    text-decoration: none;
}
.fb-card-btn:hover { background: var(--wqs-accent); color: var(--wqs-white); }

/* Share, as a square icon button beside Follow. flex:0 0 auto with a matching
   36px min-height keeps it square and stops it stretching to share the row —
   Follow takes the remaining width. 36px clears WCAG 2.5.8's 24x24 comfortably;
   it is the same height as the button it sits next to, so the row reads as one
   control group rather than two sizes. */
.fb-card-icon {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    min-height: 34px;
    border: 1px solid var(--wqs-accent);
    border-radius: 6px;
    background: var(--wqs-white);
    color: var(--wqs-accent);
    text-decoration: none;
}
.fb-card-icon:hover { background: var(--wqs-accent); color: var(--wqs-white); }
.fb-card-icon svg { display: block; }
/* Follow is the action being asked for, so it carries the fill and Share is the
   outline — same primary/secondary pairing as the subscribe buttons, so the two
   widgets teach the reader one visual language rather than two. */
.fb-card-btn-primary {
    background: var(--wqs-accent);
    color: var(--wqs-white);
}
.fb-card-btn-primary:hover { background: var(--wqs-navy-true); border-color: var(--wqs-navy-true); }

/* THE CROP. The frame is requested 280px tall and pulled up by 130px inside a
   150px window, so Facebook's broken 130px header is clipped off and the
   timeline begins at the top of the box: 280 - 130 = 150 visible.
   line-height:0 kills the inline-descender gap an iframe would otherwise leave.

   THE THREE NUMBERS ARE LOAD-BEARING TOGETHER — this height, the frame's height
   below, and the `height=` in the iframe URL. Change the visible height without
   changing the requested one by the same amount and the last post is cut
   mid-card; change it the other way and a strip of Facebook's broken header
   reappears at the top of the box.

   ── 150 IS WHAT THE 400px WIDGET LEAVES ─────────────────────────────────────
   The widget height is the fixed input; this is subtraction, not taste:

       widget 400 - heading 29 - gap 10                       = card 361
       card 361 - 2 border - 100 cover - 60 head - 49 buttons = feed 150

   Feed sizes OBSERVED on this Page, so any future change has a known cost:

     522  several complete posts with images
     400  post header + caption + most of an image
     280  post header + caption + a sliver
     150  poster + timestamp + ~3 caption lines   <- HERE
     118  poster + timestamp + ~2 lines
     100  poster + timestamp + one line

   ⚠ AN EARLIER VERSION OF THIS TABLE SAID "150 = a post header on its own".
   That was inherited from a 2026-08-06 note and never actually measured; at 150
   the feed shows three lines of caption. The genuinely useless size was ~90.

   The plugin spends a FLAT 130px on the header we discard (read off its own
   returned markup — a 652 request returns 130 + a 521 pane), so the feed pane is
   req minus 131 and every pixel given or taken lands entirely on the feed.

   ⚠ FACEBOOK'S DOCUMENTED 500px MAXIMUM IS NOT ENFORCED. Requests of
   500/530/600/700 return feed panes of 369/399/469/569 — linear, no clamp. So
   this height is a free choice; an earlier version of this comment said 500 was
   a ceiling and that was wrong. Measure before believing the docs here. */
.fb-embed-clip {
    height: 150px;
    overflow: hidden;
    line-height: 0;
    background: var(--wqs-surface);
}
.fb-embed-frame {
    display: block;
    border: 0;
    width: 400px;
    height: 280px;
    max-width: 100%;
    margin-top: -130px;   /* clips Facebook's own header — see widget-facebook.hbs */
}

/* --- sidebar: subscribe --- */
.side-sub-intro {
    margin: 0 0 .85rem;
    font-size: .93rem;
    line-height: 1.55;
    color: var(--wqs-muted);
}
.side-sub-form { display: flex; flex-direction: column; gap: .6rem; }
.side-sub-input {
    width: 100%;
    min-height: 44px;
    padding: .55rem .8rem;
    font: inherit;
    font-size: .95rem;
    color: var(--wqs-text);
    background: var(--wqs-white);
    border: 1px solid rgba(2,42,92,.22);
    border-radius: var(--radius);
}
.side-sub-input::placeholder { color: var(--wqs-muted); opacity: 1; }
/* Two actions side by side. They are NOT equal weight: Subscribe is the primary
   filled button and takes the larger share, Unsubscribe is a quiet outline. A
   destructive-ish action drawn as loudly as the one you want people to take is
   how you get accidental clicks, and this pair sits under a single shared field
   where the only thing distinguishing them is their appearance.
   flex-wrap plus a sensible basis means they stack rather than squeeze on the
   narrowest phones, where 2 x 44px targets do not fit one row. */
.side-sub-actions { display: flex; flex-wrap: wrap; gap: .5rem; }
.side-sub-btn {
    flex: 1 1 auto;
    min-width: 8.5rem;
    min-height: 44px;
    border: 1px solid transparent;
    border-radius: var(--radius);
    background: var(--wqs-accent);
    color: var(--wqs-white);
    font-family: var(--font-head);
    font-size: .95rem;
    font-weight: 600;
    cursor: pointer;
}
.side-sub-btn:hover { background: var(--wqs-navy-true); }
.side-sub-btn[disabled] { opacity: .6; cursor: default; }

/* --wqs-accent on white is 5.36:1, so the outline treatment keeps AA for the
   label and clears the 3:1 that WCAG 1.4.11 asks of the border as a boundary.
   Hover fills it rather than shifting the text colour, so the two buttons meet
   at the same visual weight only at the moment of intent. */
.side-sub-btn-secondary {
    flex: 0 1 auto;
    background: transparent;
    color: var(--wqs-accent);
    border-color: var(--wqs-accent);
}
.side-sub-btn-secondary:hover {
    background: var(--wqs-accent);
    color: var(--wqs-white);
}

/* THE HONEYPOT MUST NOT USE display:none. Some bots specifically skip fields
   that are display:none, which defeats the point; but a field merely moved
   offscreen stays in the tab order, so the markup ALSO carries tabindex="-1"
   and aria-hidden. Both halves are needed - this rule alone would leave a
   keyboard trap, the attributes alone would leave a visible box. */
.side-sub-hp {
    position: absolute;
    left: -9999px;
    width: 1px; height: 1px;
    overflow: hidden;
}

/* Empty at rest, so it occupies no space and cannot shift the layout when a
   message arrives - :empty is what makes that true without JavaScript having to
   toggle a class for it. */
.side-sub-msg {
    margin: 0;
    font-size: .88rem;
    line-height: 1.5;
    color: var(--wqs-muted);
}
.side-sub-msg:empty { display: none; }
.side-sub-form.is-error .side-sub-msg { color: var(--wqs-rose); }
.side-sub-form.is-success .side-sub-msg { color: var(--wqs-navy-true); font-weight: 600; }

/* --- sidebar: the two dropdowns --- */
/* Shared by categories and archives so the pair reads as one control type. */
.side-drop {
    border: 1px solid rgba(2,42,92,.14);
    border-radius: var(--radius);
    background: var(--wqs-white);
}
.side-drop-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    min-height: 44px;
    padding: .55rem .85rem;
    cursor: pointer;
    font-family: var(--font-head);
    font-size: .93rem;
    font-weight: 600;
    color: var(--wqs-title);
    list-style: none;
}
/* Removes the default triangle in both engines. The custom chevron below
   replaces it and, unlike the native marker, can be rotated on open. */
.side-drop-toggle::-webkit-details-marker { display: none; }
.side-drop-toggle::marker { content: ""; }

.side-drop-chevron {
    flex: 0 0 auto;
    width: 8px; height: 8px;
    border-right: 2px solid var(--wqs-accent);
    border-bottom: 2px solid var(--wqs-accent);
    transform: rotate(45deg) translate(-2px, -2px);
    transition: transform .15s ease;
}
.side-drop[open] > .side-drop-toggle .side-drop-chevron {
    transform: rotate(225deg) translate(-2px, -2px);
}

/* A HEIGHT CAP IS MANDATORY ON THESE TWO, not a nicety: the categories panel is
   28 rows and the archives panel is 7 years of up to 12 months. Uncapped, an
   open dropdown would be several screens tall and would push the comments far
   down the page on a phone. Capped and scrollable, it behaves like the control
   it is drawn as. */
.side-drop-panel {
    max-height: 320px;
    overflow-y: auto;
    overscroll-behavior: contain;
    border-top: 1px solid rgba(2,42,92,.14);
    padding: .35rem;
}

.side-drop-panel ul { list-style: none; margin: 0; padding: 0; }

.side-cat-name, .side-month-link > span:first-child { min-width: 0; }

.side-cat-link,
.side-month-link {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .6rem;
    min-height: 36px;
    padding: .3rem .55rem;
    border-radius: 6px;
    text-decoration: none;
    font-size: .9rem;
    color: var(--wqs-text);
}
.side-cat-link:hover,
.side-month-link:hover {
    background: var(--wqs-surface);
    color: var(--wqs-accent);
}

/* The count badge. --wqs-muted is 5.39:1 on the page and on the card surface,
   so it is AA at this size; it is deliberately quieter than the label beside
   it, which is the thing being chosen. */
.side-count {
    flex: 0 0 auto;
    font-family: var(--font-head);
    font-size: .78rem;
    font-weight: 600;
    color: var(--wqs-muted);
    background: var(--wqs-surface);
    border-radius: 999px;
    padding: .1rem .5rem;
}

/* --- sidebar: archives, the nested year level --- */
.side-year-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    min-height: 38px;
    padding: .3rem .55rem;
    border-radius: 6px;
    cursor: pointer;
    font-family: var(--font-head);
    font-size: .92rem;
    font-weight: 700;
    color: var(--wqs-title);
    list-style: none;
}
.side-year-toggle::-webkit-details-marker { display: none; }
.side-year-toggle::marker { content: ""; }
.side-year-toggle:hover { background: var(--wqs-surface); }
.side-months { padding-left: .5rem; margin-bottom: .35rem; }
.side-month-all { font-weight: 600; color: var(--wqs-accent); }

/* --- archive pages ------------------------------------------------------ */
.archive-page { padding-block: clamp(1.75rem, 5vw, 3rem); }
.archive-head { text-align: center; margin-bottom: 1.75rem; }
.archive-kicker {
    margin: 0 0 .35rem;
    font-family: var(--font-head);
    font-size: .78rem;
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--wqs-accent);
}
.archive-title { font-size: clamp(1.6rem, 4.5vw, 2.4rem); color: var(--wqs-title); margin: 0 0 .35rem; }
.archive-count { margin: 0; color: var(--wqs-muted); font-size: .92rem; }
.archive-up { font-weight: 600; }

.archive-list { list-style: none; margin: 0; padding: 0; }
.archive-item + .archive-item { border-top: 1px solid rgba(2,42,92,.10); }
.archive-link {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    min-height: 44px;
    padding: .7rem .5rem;
    text-decoration: none;
    color: var(--wqs-text);
}
.archive-link:hover { background: var(--wqs-surface); color: var(--wqs-accent); }
/* min-width:0 is load-bearing, not tidiness. A flex item defaults to
   min-width:auto, which refuses to shrink below its content, so a long post
   title beside a fixed-width date would push the row wider than the page on a
   phone. Titles here run to 60+ characters, so this is the common case rather
   than an edge one. The same reason applies to .side-cat-name below. */
.archive-item-title { font-weight: 600; min-width: 0; }
/* The date must never wrap or shrink - it is the column the eye scans down. */
.archive-item-date {
    flex: 0 0 auto;
    font-family: var(--font-head);
    font-size: .82rem;
    color: var(--wqs-muted);
    white-space: nowrap;
}
.archive-empty { text-align: center; color: var(--wqs-muted); }

/* --- "More like this" — 3 smaller cards ---------------------------------- */
/* Changed 2026-08-06 by request: three posts instead of four, with smaller
   artwork.

   THE IMAGES ARE MADE SMALLER BY NARROWING THE GRID, NOT BY CROPPING HARDER.
   Dropping to three cards in the full 1180px wrap would have made each card
   WIDER and the images BIGGER — the opposite of what was asked. Shrinking
   .quote-card-media's aspect-ratio instead would have cropped more off each
   image, and this site's artwork is quote graphics with the words baked into
   the picture, so a taller crop eats the start and end of the sentence. That
   exact defect is recorded against this theme already.

   So the grid is capped to the 720px article measure and centred. Three cards
   at ~226px are meaningfully smaller than four at ~277px, nothing is cropped
   any harder than before, and the section now lines up with the column of text
   above it instead of running wider than the article it belongs to. */
@media (min-width: 700px) {
    .related .quote-grid { grid-template-columns: repeat(3, 1fr); }
}

/* IT LIVES INSIDE THE ARTICLE COLUMN NOW, so the full-bleed treatment has to go.
   `.related` above still carries `background: var(--wqs-surface)` and a top
   border from when it was a page-width band below everything; left alone, that
   band would now be a tinted rectangle floating inside a 720px column with the
   page white on either side of it.

   Reset to match .post-comments directly above it instead — a hairline rule and
   space, nothing else. The two sections under the article are then the same
   shape as each other, which is the point of moving this one up here.

   The full-width band rule is not deleted: `.related` is a shared class and
   removing its background would change any future page that still uses it
   full-bleed. Overridden in place, scoped to this context. */
.post-extra .related {
    background: none;
    border-top: 1px solid var(--wqs-surface);
    margin-top: 1.75rem;
    padding-top: 1.5rem;
}
/* The max-width caps that centred this on the 720px measure are GONE — the
   column IS 720px now, so they were a no-op at best and would fight any future
   change to the article measure. */

/* --- THE SIDEBAR ON EVERY TEMPLATE BUT THE HOME PAGE --------------------- */
/* Added 2026-08-07 by request. The sidebar rendered only from post.hbs; it now
   also renders from page, tag, author, archive, archive-year, archive-month
   and error. index.hbs is deliberately the one exception — the home page keeps
   its full-width layout.

   NOTHING ABOVE THIS COMMENT WAS CHANGED, and that is the design of the patch:
   those templates now emit the same .post-layout / .post-main / .post-sidebar
   structure post.hbs already emitted, so they inherit the stacked-by-default
   flex column, the 1200px grid breakpoint and the whole sticky rule verbatim.
   Reusing the primitives rather than writing a parallel set is what keeps one
   sidebar looking like one sidebar across seven templates. Only the two cases
   below need anything new.

   THE COST, STATED PLAINLY RATHER THAN DISCOVERED LATER: the Facebook iframe
   and the two {{#get}} queries behind the categories and archives dropdowns
   now run on every category, author, archive and 404 page as well as every
   post. Those pages previously loaded no third-party frame at all. That is the
   price of the request, not a bug — but it is the number to watch if listing
   pages get slower.                                                          */

/* THE LISTING VARIANT — tag, author, and the 404's recent cards.
   These carry a card GRID, not a 720px reading measure, so capping their
   column at --wrap-narrow the way a post is capped would be actively wrong: it
   would squeeze the four-across grid into 720px on every screen from 1000px
   up, which is long before the sidebar is even beside it. `--list` keeps the
   content column full-width while the layout is stacked, and hands it the
   whole remaining track once the two-column grid engages. */
.post-layout--list .post-main { width: 100%; }

@media (min-width: 1200px) {
    /* 1fr, not the capped --wrap-narrow track a post gets: a grid should use
       whatever the sidebar leaves it (1180 - 400 - 40 = 740px) rather than stop
       at 720 and strand the remaining 20. A post stops at 720 because a reading
       measure is the point; a card grid has no such reason. */
    .post-layout--list { grid-template-columns: minmax(0, 1fr) var(--side-w); }
    .post-layout--list .post-main { width: auto; }

    /* THREE ACROSS, NOT FOUR, AND ONLY HERE.
       .quote-grid pins itself to repeat(4, 1fr) from 1000px up. That is right
       for a full-width band and wrong inside a 740px column: four tracks there
       are ~185px each, narrower than the 220px minimum the fluid rule below
       1000px never goes under. Three tracks are ~246px, comfortably above it.
       Scoped to .post-main inside --list so the home page's grid, the related-
       posts grid and the section strips are all untouched. */
    .post-layout--list .post-main .quote-grid { grid-template-columns: repeat(3, 1fr); }
}
