/*
 * stacks-article.css
 *
 * Plugin-bundled stylesheet for the `stacks_article` single template.
 * Every rule is scoped under `.sjp-article` so nothing leaks into the
 * host theme's other pages.
 *
 * Visual target: the existing hand-built article pages on
 * stacksjournal.org (e.g. /article/Marrocco-26004/), a Divi-Builder
 * two-column layout. We can't inherit Divi's per-module CSS
 * (`.et_pb_text_N h2`) because those instance IDs only exist when a
 * human drags boxes around — we reproduce the intent with our own
 * scoped rules so every auto-pushed article gets the same look
 * without manual builder work.
 *
 * Layout overview:
 *
 *   ┌─ .sjp-article ──────────────────────────────────────────────┐
 *   │ .sjp-grid (CSS grid: main 1fr + aside 280px, gap 60px)      │
 *   │   ┌─ main ────────────────────────┐  ┌─ aside ───────────┐  │
 *   │   │ Subject tag pills (gold)      │  │ Acceptance + PR   │  │
 *   │   │ Title                          │  │ Peer-Review btn   │  │
 *   │   │ Authors (with ORCID icons)     │  │ Badges            │  │
 *   │   │ Collaborators (italic)         │  │ DOI + dates       │  │
 *   │   │ ─── thin grey divider ─────    │  │ Funding           │  │
 *   │   │ Affiliations | Citation (2col) │  │ Conflicts         │  │
 *   │   │ ─── thin grey divider ─────    │  │ Data & Code btn   │  │
 *   │   │ [PDF] [Contact] [Share]        │  └───────────────────┘  │
 *   │   │ ─── thin grey divider ─────                              │
 *   │   │ Abstract + keywords                                      │
 *   │   │ Body content                                             │
 *   │   │ References                                               │
 *   │   │ Corresponding Authors                                    │
 *   │   └──────────────────────────────────────────────────────────┘
 *   └──────────────────────────────────────────────────────────────┘
 *
 * Color tokens mirror the editorial app / Marrocco:
 *   --sjp-purple      #7448FF   accent (section H2s, buttons, badges)
 *   --sjp-purple-dark #2D2163   button hover
 *   --sjp-title-dark  #111827   title + subheadings
 *   --sjp-body        #2c2c2c   body text (matches Divi customizer)
 *   --sjp-muted       #6b7280   dates, secondary labels
 *   --sjp-tag-bg      #FFDE99   gold pills
 *   --sjp-rule        #e5e7eb   thin grey dividers
 */

.sjp-article {
    /* ⚠️ A LONG URL MUST WRAP, OR THE WHOLE PAGE PANS SIDEWAYS ON A PHONE.
       One unbreakable token is enough. On the journal's first app-published
       article (hogland-26014) reference 24 is a 91-character GitHub URL; with
       overflow-wrap: normal it forced its paragraph to 767px, and because every
       ancestor up to <body> is overflow: visible, that width reached the page.
       MEASURED 2026-10-22 at 375px: the page was 805px wide and scrolled 430px
       sideways, so a reader had to pan to read EVERY paragraph, not just the
       reference. It hit tablets too (45px at 768) and on desktop the URL ran
       79px past its column into the gap before the sidebar.

       Set HERE, on the article root, because it inherits. This had been fixed
       three times locally — citation links, the sidebar DOI, ORCIDs — each where
       it was noticed, while the body and the reference list, where most URLs
       actually live, were never covered.

       ⚠️ break-word, NOT anywhere. They differ in exactly one way that matters
       here: `anywhere` also shrinks an element's MIN-CONTENT width, and auto
       table layout sizes columns from min-content — so `anywhere` would let a
       wide table squeeze its columns down to fit instead of scrolling inside its
       <figure>. `break-word` only breaks a token that would otherwise overflow
       its line, and leaves table sizing alone. Verified: both tables on 26014
       still scroll within their figures with this rule on. */
    overflow-wrap: break-word;

    --sjp-purple:      #7448FF;
    --sjp-purple-dark: #2D2163;
    --sjp-title-dark:  #111827;
    --sjp-body:        #2c2c2c;
    --sjp-muted:       #6b7280;
    --sjp-tag-bg:      #FFDE99;
    /* The abstract block's cream. Identical across ten published articles. */
    --sjp-cream:       #fff8e5;
    --sjp-rule:        #e5e7eb;
    /* ⚠️ MEASURED GEOMETRY, and the three numbers are interdependent: the live
       article's row is 1080px wide and splits 760 text + 50 gutter + 270
       sidebar. max-width is 1128 because .sjp-article adds 24px of padding on
       each side, so the INNER width lands on 1080. Change one of these and the
       text column stops matching the published articles. */
    --sjp-max-width:   1128px;
    --sjp-sidebar-w:   270px;
    --sjp-gap:         0px;

    max-width: var(--sjp-max-width);
    /* ⚠️ NO TOP MARGIN, AND THE 32px THAT WAS HERE DID THE OPPOSITE OF WHAT IT
       LOOKED LIKE. Nothing between this element and <body> has padding or a
       border — #et-main-area, #et-boc and #page-container are all bare — so a
       top margin here COLLAPSES THROUGH ALL OF THEM and lands outside body,
       pushing the WHOLE PAGE down, the site header with it. It never put space
       between the header and the article at all; it put 32px of blank above
       the Stacks logo.

       Measured at 1440px, logged out: body's rect started at 32 on our page and
       at 0 on a published article, and the logo sat at 59 against 27. David,
       2026-10-07: "the sticky header up top is too thick above the logo ... but
       maybe this is just a function of this preview?" It was not the preview —
       it was this line, and it would have survived publication.

       ⚠️ The header-to-article gap is UNCHANGED by this. Both the header and
       this article moved together while the margin was escaping, so removing
       it moves them both back up; the clearance is still the 122px of
       padding-top below. Do not "compensate" by adding it to the padding. */
    margin: 0 auto 64px auto;
    padding: 0 24px;
    font-family: 'Open Sans', Helvetica, Arial, Lucida, sans-serif;
    color: var(--sjp-body);
    line-height: 23px;
    font-size: 15px;
    /* ⚠️ MEASURED, not chosen: the journal's published articles run Open Sans
       at weight 500, not 400. Inherited here so every block gets it without
       repeating itself. This is the single edit that most changes how the page
       reads — and it is INERT unless weight 500 is in the Google Fonts request
       (see the wp_enqueue_scripts callback in the plugin). Without the font
       file the browser synthesises 500 from 400 and the difference is
       invisible. */
    font-weight: 500;
    /* An explicit white: a theme with a tinted body background would otherwise
       show through the article. */
    background: #ffffff;
    box-sizing: border-box;
    /* ⚠️ CLEARS DIVI'S FIXED HEADER. Without this the top ~95px of the article
       renders UNDERNEATH it: the title's first line, the subject tags, and the
       whole top of the sidebar — including "Accepted by N of N reviewers" and
       the View Peer Review button. David reported exactly that on 2026-10-06.

       Why it happens here and not on the hand-built articles: those are the
       `project` post type and Divi serves them a header at top:0 h:104, with
       their content inside Divi sections that carry their own top spacing.
       Ours is `stacks_article`, whose header template sits at top:32 h:95
       (bottom 127), and #page-container has padding-top:0 on both. Nothing
       above us reserves the space, so we reserve it.

       ⚠️ PADDING, NOT MARGIN. Measured on the live page: with padding-top the
       header stays put and the title clears it by 79px — which is exactly the
       clearance the published articles have (Maschler title y=183, header
       bottom 104). Margin was tried first and measured as moving the header
       too; that turned out to be the injected style racing the site's own
       layout scripts, but padding is the honest expression anyway, because the
       space belongs INSIDE the article rather than between it and something
       else.

       ⚠️ If Divi's header template for this post type changes height, re-measure.
       In the browser: `.et_pb_section_1_tb_header.menu-custom` bounding rect,
       against `.sjp-title`. Target is a 79px gap. */
    /* 131px, not 140: measured again 2026-10-07 and ours cleared the header by
       88px where Maschler-26010 clears it by 79. David: "The white space at the
       top of the header is too large." */
    /* ⚠️ 92px CLEARS THE HEADER BY ~40, LESS THAN THE PUBLISHED 79. 131px
       matched Maschler-26010 exactly and David still called it too tall on
       2026-10-07: "The header is still too long, vertically on the base." A
       deliberate departure — he is judging the page whole, not matching the old
       ones. ⚠️ Do not restore 131 on the grounds that it no longer matches
       published; that is the point. */
    /* ⚠️ 122px. MEASURED SIDE BY SIDE WITH Maschler-26010 at 1440px on
       2026-10-07, the comparison David asked for: from the bottom of the site
       nav to the top of the subject-tag row, Maschler has 73px and ours had
       30px. The page started 43px higher than the reference and read cramped.

       ⚠️ THIS PARTLY REVERSES THE 92px, KNOWINGLY. History is 131 -> 92
       because David called 131 "too long, vertically on the base"; 122 lands
       between, giving ~60px of nav clearance against the reference's 73. If
       it reads tall again the lever is this number alone. */
    padding-top: 122px;
}
.sjp-article *,
.sjp-article *::before,
.sjp-article *::after {
    box-sizing: inherit;
}

/* ⚠️ THIS RULE WAS FIGHTING THE HOUSE STYLE, AND WINNING.

   It was added to "neutralize the host Divi customizer's global
   h1..h6 { line-height: 0.8em }", on the assumption that 0.8 was the theme
   being unhelpful. Measured on the live site 2026-10-06, at a pinned 1440px
   width, across the oldest published article and the newest: every section
   heading is 26px/20.8px and every H3 is 20px/16px. Both are 0.8. Divi was
   right and this rule was the deviation — it is what made David report that
   the new article's text "looks too big".

   ⚠️ AND IT IS NOT THE SAFETY NET ITS OLD COMMENT CLAIMED. `.sjp-article h2`
   is (0,1,1); `.sjp-abstract-heading` is (0,1,0). The blanket rule is MORE
   specific than the specific rules it was supposed to back up, so it
   overrode them. That is why .sjp-abstract-heading declared line-height
   20.8px and the browser computed 32.5px — proven by injecting the same
   declaration at one class higher, which took immediately.

   Keep the value explicit rather than deleting the rule and inheriting
   Divi's 0.8em: the article should not depend on a theme customizer setting
   that someone can change in the WordPress admin without touching code. */
.sjp-article h1,
.sjp-article h2,
.sjp-article h3,
.sjp-article h4,
.sjp-article h5,
.sjp-article h6 {
    line-height: 0.8;
}

/* ── Grid: main + sidebar ────────────────────────────────────────── */
.sjp-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) var(--sjp-sidebar-w);
    gap: var(--sjp-gap);
    align-items: start;
}
/* ⚠️ The 50px gutter between text and sidebar belongs on the MAIN column, as on
   the target (.et_pb_column_0 { padding-right: 50px }). Putting it in the grid
   gap instead would also inset the sidebar's own right edge, narrowing it below
   the measured 270px. */
.sjp-main   { min-width: 0; padding-right: 50px; }
/* ⚠️ align-self: stretch DEFEATS THE GRID'S align-items: start FOR THIS COLUMN
   ONLY, AND THE STICKY TOC DEPENDS ON IT. A sticky element can only travel
   inside its containing block: with the sidebar sized to its own content it is
   a few hundred pixels tall, so the TOC would unstick and scroll away a
   screenful into a 13,000px article. Stretching the sidebar to the row height
   gives it the whole article to travel. Nothing moves visually — the blocks
   still stack from the top; only the box is taller. */
.sjp-sidebar { min-width: 0; align-self: stretch; }

/* Thin grey horizontal divider between major blocks. */
.sjp-divider {
    border: 0;
    border-top: 1px solid var(--sjp-rule);
    margin: 28px 0;
}

/* ── Subject tag pills (gold, above the title) ───────────────────── */
/* ⚠️ EVERY VALUE HERE WAS MEASURED off the live article's .tag rule, including
   the two that look like mistakes. 9.45px and 2.7px are literal values in the
   journal's child theme, not roundings of 9.5 and 3 — a "tidied" 10px/3px pill
   is visibly wider and softer beside the published articles. A "Development"
   pill measures 93x25. */
.sjp-subject-tags {
    display: flex;
    flex-wrap: wrap;
    /* row-gap 13px, column-gap 9px — the theme sets them separately. */
    gap: 13px 9px;
    margin-bottom: 18px;
}
/* ⚠️ 12px, not the child theme's 9.45px. Picked by David off a side-by-side of
   15/13/12/11/9.45px against the byline (2026-10-12) — see the longer note in
   apps/web/app/globals.css. ⚠️ The same rule lives there; change both or the
   two surfaces drift. */
.sjp-subject-tag {
    background: var(--sjp-tag-bg);
    color: var(--sjp-body);
    padding: 6px 15px;
    border-radius: 2.7px;
    font-size: 12px;
    /* ⚠️ 700, REVERSING THE EARLIER 500 ("matching the byline; 700 at the same
       px still read as larger"). David asked for it directly, 2026-10-13:
       "please bold the Stacks at the top". The 12px — which was the real
       complaint behind the 500 — is unchanged.
       ⚠️ MIRRORED in apps/web/app/globals.css; change both together.
       ⚠️ UNVERIFIED against a real WordPress render — the plugin is not
       installed, so this has only been seen in the Next.js copy. */
    font-weight: 700;
    line-height: normal;
    letter-spacing: normal;
    text-align: center;
    /* Capitalize, so a tag typed in any case reads as a label. */
    text-transform: capitalize;
    box-shadow: 0 1px 2px -1px rgba(16, 24, 40, 0.10),
                0 1px 3px 0 rgba(0, 0, 0, 0.10);
}

/* ── Title ───────────────────────────────────────────────────────── */
/* `!important` on line-height because the host Divi customizer ships a
   global `h1,h2,h3,h4,h5,h6 { line-height: 0.8em }` rule that — despite
   lower specificity — can win depending on load order on some builds,
   and 0.8em collapses multi-line titles into an unreadable cascade. */
/* ⚠️ 26px/700, NOT 32px/800. The target's title is the same size as its section
   headings — it is an <h2> there — and ours being 32px at weight 800 is what
   made David's article run to three lines and read noticeably heavier than the
   published ones. Colour is the body colour, not the near-black we had. */
.sjp-article .sjp-title {
    font-size: 26px;
    font-weight: 700;
    line-height: 1.3 !important;
    color: var(--sjp-body);
    /* ⚠️ 14px, SO THE FOUR HEADER LINES ARE EVENLY SPACED. Measured ink to
       ink on the live page: title->authors was 25px while
       authors->collaborators was 15 and collaborators->special issue 16, so
       the title floated away from its own byline. David: "can we decrease the
       space between the title and the author byline ... just the same amount
       of space as between the authors, collaborators, and special issues?"
       ⚠️ This supersedes the 24px set earlier the same day; that was chosen
       before the other three gaps were evened up and is no longer consistent
       with them. */
    margin: 0 0 14px 0;
    padding: 0;
    text-align: left;
    /* No negative tracking: the target sets none, and tightening a 26px title
       makes it read as a different typeface beside the published articles. */
    letter-spacing: normal;
}

/* ── Author byline ───────────────────────────────────────────────── */
.sjp-authors {
    font-size: 17px;
    line-height: 1.6;
    color: var(--sjp-body);
    /* 12px, up from 6 — the byline and the collaborators line were close
       enough to read as one block. ⚠️ Sibling margins COLLAPSE, so this 12
       and .sjp-collaborators' own 4px top margin resolve to 12, not 16. */
    margin: 0 0 12px 0;
}
.sjp-author { white-space: nowrap; }
/* ⚠️ THE THEME ALSO RAISES <sup>, SO THIS WAS LIFTED TWICE. Measured on the
   live byline: vertical-align: super (ours) AND position: relative with
   top: -8.8px, which is the theme's own `sup` rule — nothing in this file sets
   position or top. The two stack, which is why David said "the superscript is
   way too high". Neutralising the theme's offset leaves exactly one raise, the
   one we chose.

   ⚠️ Only the BYLINE marker is touched. .sjp-affiliations sup is a different
   class and is left alone. */
.sjp-author-markers {
    font-size: 11px;
    margin-left: 1px;
    vertical-align: super;
    line-height: 0;
    position: static;
    top: auto;
}
/* The ORCID mark belongs to the author, not to the affiliation number sitting
   between them: 8px of gap (a text space plus the icon's own 4px) read as a
   break. David: "too much space after the superscript and before the orcid."

   ⚠️ 4px, AND NOW IT IS THE WHOLE GAP. It was 0 here because a text space
   from the template's line breaks supplied ~4.4px; the template now echoes
   each author on one line (that same whitespace was also putting a space
   between the icon and the comma after it), so the gap has to be declared.
   The review page uses the same 4px. */
.sjp-authors .sjp-orcid { margin-left: 4px; }
.sjp-orcid {
    display: inline-flex;
    align-items: center;
    margin-left: 4px;
    vertical-align: middle;
    text-decoration: none;
}
.sjp-orcid svg { display: block; }

/* A word carrying an explanation, with an (i) after it and a tooltip the
   stylesheet draws itself.

   ⚠️ IT NO LONGER RELIES ON THE BROWSER'S `title`, and that is the whole point.
   A native tooltip waits roughly a second before appearing, cannot be styled,
   and gives no hint of what kind of thing it is. David, 2026-10-15: "The
   tooltip on the webiste -- can it show an i (as in info) on hover and have
   the popup come up faster?" This appears after 120ms and carries a visible
   info mark, so a reader knows there is something to read before they hover.

   ⚠️ THE `title` ATTRIBUTE IS GONE, replaced by aria-describedby pointing at
   the bubble (2026-10-19). It was drawing a SECOND, slower tooltip under this
   one. Assistive tech reads the bubble through aria-describedby instead, so
   the explanation is still announced; apps/web/lib/collaborator-credit.ts owns
   the wording and the review page carries the same markup.

   ⚠️ THE TEXT IS WRITTEN TWICE BY THE TEMPLATE — into the word's `title` and
   into the .sjp-hint-bubble span inside it, since CSS cannot read a `title`.
   (An earlier version of this note said a data-sjp-hint attribute; there is
   none.) If one is edited the other must be. */
.sjp-article .sjp-hinted {
    position: relative;
    cursor: help;
    /* No dotted underline: the (i) is the affordance now, and the two together
       read as clutter on an italic line. */
    text-decoration: none;
}
.sjp-article .sjp-hinted::after {
    content: "i";
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 13px;
    height: 13px;
    margin-left: 3px;
    border-radius: 50%;
    background: var(--sjp-purple);
    color: #fff;
    /* Upright and not italic: this line is set in italic, and a slanted serif
       "i" in a 13px circle reads as a smudge. */
    font-family: Georgia, 'Times New Roman', serif;
    font-style: normal;
    font-weight: 700;
    font-size: 10px;
    line-height: 1;
    vertical-align: 1px;
}
.sjp-article .sjp-hinted .sjp-hint-bubble {
    position: absolute;
    left: 0;
    bottom: calc(100% + 8px);
    z-index: 20;
    width: max-content;
    max-width: 280px;
    padding: 8px 10px;
    /* ⚠️ STYLED AS THE BROWSER'S OWN TOOLTIP. This was a dark bubble with the
       native `title` tooltip appearing underneath it a second later — the same
       sentence twice, in two boxes. David, 2026-10-19, kept the dark one's
       SPEED and the native one's LOOK. The `title` is gone from the template,
       so this is the only tooltip on the word now. */
    border-radius: 4px;
    background: #F6F6F6;
    color: #2C2C2C;
    border: 1px solid #B9B9B9;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
    font-family: 'Open Sans', Helvetica, Arial, Lucida, sans-serif;
    font-style: normal;
    font-weight: 400;
    font-size: 13px;
    line-height: 18px;
    /* ⚠️ visibility, NOT display, so the 120ms transition can run at all — a
       display change is not animatable and the bubble would snap in. opacity
       alone would leave it hoverable while invisible. */
    opacity: 0;
    visibility: hidden;
    transition: opacity 120ms ease-out, visibility 0s linear 120ms;
    pointer-events: none;
}
.sjp-article .sjp-hinted:hover .sjp-hint-bubble,
.sjp-article .sjp-hinted:focus-within .sjp-hint-bubble {
    opacity: 1;
    visibility: visible;
    transition: opacity 120ms ease-out, visibility 0s;
}

/* The article type, above the credential badges. Small caps in the muted ink,
   so it labels the group without competing with the badges' own icons. */
.sjp-article .sjp-article-type {
    margin: 0 0 10px 0;
    padding: 0;
    font-size: 11px;
    font-weight: 700;
    /* ⚠️ THE BRAND PURPLE, NOT --sjp-muted. David, 2026-10-15: "Can we
       updddate Research Article to be the brand purple on both the pdf and the
       website". The PDF's half is the articleType paragraph in
       apps/web/components/StacksArticle/StacksArticleCover.tsx. */
    color: var(--sjp-purple);
    /* ⚠️ NO text-transform AND NO letter-spacing — both were here and both were
       removed on 2026-10-15. David: "please make it Research Article (instead of
       all caps). Same on the website." The tracking went with the caps rather
       than being kept: .06em exists to open up capitals and reads loose in mixed
       case. The PDF's half of this is the articleType paragraph in
       apps/web/components/StacksArticle/StacksArticleCover.tsx; the two surfaces
       have to agree or a reader comparing them sees two different journals.

       The label arrives already in title case from publishedLabel()
       (apps/web/lib/article-types.ts), so nothing upstream changes. */
}

/* Collaborators (italic line below the byline) */
/* ⚠️ DIVI SETS `p { padding-bottom: 1em }` AND IT LEAKS INTO OURS. Any <p> of
   ours that does not state its own padding inherits 15px from the theme. Found
   by auditing every paragraph in the article on 2026-10-07: three places were
   affected and two of them were unintended.

   - .sjp-collaborators got 15px it never asked for, so the gap below the
     collaborators line was 25px (15 padding + 10 margin) against 6px above it.
     David: "Too much space between collaborators and special issue byline.
     (In general that spacing looks off)."
   - .sjp-statement-body p already spaces itself with `margin: 0 0 15px`, and
     Divi ADDED 15px of padding on top, double-spacing the back matter at 30px.
   - .sjp-content p is the one case where 15px is OURS and deliberate.

   ⚠️ A new <p> in this stylesheet inherits the theme's 15px unless it says
   otherwise. State padding explicitly on anything that is not body prose. */
.sjp-collaborators { padding: 0; }
.sjp-statement-body p { padding-bottom: 0; }

/* ⚠️ THE 12px BOTTOM MARGIN EVENS UP THE THREE BYLINE ROWS, which is what
   David meant by "the spacing for the authors, collaborators, and special
   issue ... seem a little off". Measured ink to ink: ours ran 15px then 11px
   where Maschler-26010 runs 16px then 15px — the unevenness was the tell, not
   the size. ⚠️ Sibling margins COLLAPSE against .sjp-special-issue's 10px top
   margin, so this 12 decides the gap, not 12+10. */
.sjp-collaborators {
    font-style: italic;
    font-size: 15px;
    color: var(--sjp-body);
    margin: 4px 0 14px 0;
}

/* ── Affiliations + Citation two-column meta block ───────────────── */
.sjp-meta-columns {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 32px;
}
.sjp-affiliations-block > h3,
.sjp-citation-block > h3 {
    font-size: 17px;
    font-weight: 700;
    color: var(--sjp-title-dark);
    margin: 0 0 10px 0;
    line-height: 1.3;
}
.sjp-affiliations {
    list-style: none;
    padding: 0;
    margin: 0;
    font-size: 14px;
    line-height: 1.55;
    color: var(--sjp-body);
}
.sjp-affiliations li { margin-bottom: 4px; }
/* ⚠️ NO margin-right. The markup already puts whitespace between </sup> and
   the text, so the 4px was stacked ON TOP of a space — ~8px from the digit to
   the first letter against 3px on Maschler-26010, which sets no margin at all.
   David: "is there too much of a space between the affiliation number and the
   actual affiliation". */
.sjp-affiliations sup { font-size: 11px; }
.sjp-citation-body {
    font-size: 14px;
    line-height: 1.55;
    color: var(--sjp-body);
    margin: 0;
}
.sjp-citation-body em { font-style: italic; }
.sjp-citation-body a {
    color: var(--sjp-purple);
    text-decoration: underline;
    text-underline-offset: 2px;
    word-break: break-all;
}

/* ── Buttons (primary filled + outline) ──────────────────────────── */
/* Matches Marrocco's `.article-download-button` + outline "share"
   variants: purple #7448FF, fills #2D2163 on hover.              */
.sjp-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 12px 22px;
    border-radius: 4px;
    /* 14px, measured on Download PDF across Maschler-26010, cameron-26009 and
       Tucker-26003. Ours was 15px. */
    font-size: 14px;
    font-weight: 700;
    line-height: 1;
    text-decoration: none;
    cursor: pointer;
    transition: background-color 200ms ease, color 200ms ease, border-color 200ms ease;
    border: 2px solid var(--sjp-purple);
    letter-spacing: 0.01em;
}
.sjp-btn-primary {
    background: var(--sjp-purple);
    color: #ffffff;
}
.sjp-btn-primary:hover,
.sjp-btn-primary:focus-visible {
    background: var(--sjp-purple-dark);
    border-color: var(--sjp-purple-dark);
    color: #ffffff;
    text-decoration: none;
}
.sjp-btn-outline {
    background: transparent;
    color: var(--sjp-purple);
}
.sjp-btn-outline:hover,
.sjp-btn-outline:focus-visible {
    background: var(--sjp-purple-dark);
    border-color: var(--sjp-purple-dark);
    color: #ffffff;
    text-decoration: none;
}
.sjp-btn-block {
    display: flex;
    width: 100%;
}

.sjp-three-buttons {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 12px;
}
.sjp-three-buttons .sjp-btn {
    width: 100%;
}

/* ── Abstract ─────────────────────────────────────────────────────────────────
 *
 * ⚠️ THE COMMENT THAT USED TO BE HERE SAID "plain, no cream box — matches
 * Marrocco". THAT WAS WRONG, and it is why this block was never built.
 * Marrocco-26004 carries the cream box like all the others: the rule is
 * BYTE-IDENTICAL across ten published articles (Seeger, Marrocco, Tucker,
 * Piasecki, Mengak, Maschler, Jespersen, Bliss-Ketchum, Farmer, cameron), which
 * makes it a shared preset — house style, not one article's choice. The plugin
 * README has advertised this block as shipped since Phase 4; it never existed.
 *
 * ⚠️ THE PHOTO IS OUTSIDE THE CREAM BLOCK AND THE CAPTION IS INSIDE IT. Measured:
 * the photo's bottom edge and the block's top edge meet at 0px, and that flush
 * seam is what makes the two read as a single object. Hence the ZERO top padding
 * below — it is load-bearing, not an oversight.
 *
 * Measured at 1024px and re-confirmed at 1440px.
 */
/* ⚠️ THE 15px BOTTOM MARGIN IS HALF OF THE GAP ABOVE "Introduction", and it is
   here rather than on the heading so that EVERY H2 is spaced the same way.

   A body paragraph ends with 15px of its own padding, so a mid-article heading
   already sits 15px below the text above it. The abstract panel ended flush, so
   the first heading sat 15px tighter than all the others. Giving the panel the
   same 15px a paragraph gives means one rule — the 20px on .sjp-content > h2 —
   puts every section heading at the same 35px below whatever precedes it.

   35px is measured, not chosen: on Maschler-26010, the reference David gave,
   the beige panel's bottom is 4091 and the Introduction heading's top is 4126.
   David, 2026-10-07: "Can we add a little more space between the abstract beige
   and the start of introduction like there is on here." */
.sjp-abstract { margin: 8px 0 15px 0; }

.sjp-abstract-photo {
    margin: 30px 0 0 0;
    padding: 0;
}
.sjp-abstract-photo img {
    display: block;
    width: 100%;
    height: auto;
    border-radius: 0;
    /* Two stacked shadows, as the target sets them — a single softer shadow
       reads as a card, these read as a print plate. */
    filter: drop-shadow(0 1px 2px rgba(16, 24, 40, 0.10))
            drop-shadow(0 1px 3px rgba(0, 0, 0, 0.10));
}

.sjp-abstract-inner {
    background: var(--sjp-cream);
    /* ⚠️ Top padding is ZERO on purpose: see the flush seam above. */
    padding: 0 50px 40px 22px;
}

.sjp-abstract-photo-caption {
    font-size: 15px;
    line-height: 23px;
    color: var(--sjp-body);
    /* ⚠️ Italic from CSS, never from an <em> wrapped around author HTML — that is
       the unbalanced tag that italicised an entire article on 2026-10-06. */
    font-style: italic;
    margin: 0;
    padding: 15px 0 40px;
}
.sjp-abstract-photo-caption strong { font-style: normal; font-weight: 700; }

.sjp-abstract-heading {
    color: var(--sjp-purple);
    font-weight: 700;
    /* 26px, matching every other section heading. It was 22px here while
       .sjp-content > h2 already carried a comment saying "26px, not 22px —
       measured". The two disagreed with each other. */
    font-size: 26px;
    line-height: 20.8px;
    margin: 0 0 10px 0;
}

/* ⚠️ ONLY WHEN THERE IS NO GRAPHICAL ABSTRACT. The cream panel's padding is
   `0 50px 40px 22px` — zero at the top — and that is correct for the published
   articles, because their panel opens with the graphical abstract image and its
   caption, which is what puts their Abstract heading 117px down (measured on
   Maschler-26010). An article without one, like Hogland 26014, left the heading
   flush against the panel's top edge. :first-child is precisely that case: the
   heading is the first thing in the panel only when no photo precedes it.

   40px to match the panel's own bottom padding, so the cream block is evenly
   inset top and bottom. */
/* ⚠️ PADDING ON THE PANEL, NOT MARGIN ON THE HEADING — and the difference is
   the entire bug. The heading IS the panel's first child, so its margin-top
   COLLAPSES THROUGH a parent that has no top padding or border: the 40px
   escaped the cream box and pushed the whole box down instead of opening space
   inside it. David, 2026-10-07: "Beige color at the top of the abstract is not
   going up beyond the word abstract." Exactly that.
   Padding cannot collapse, so the beige now starts above the heading. */
.sjp-abstract-heading:first-child {
    margin-top: 0;
}
.sjp-abstract-inner:has(> .sjp-abstract-heading:first-child) {
    padding-top: 40px;
}
.sjp-abstract-body {
    /* ⚠️ 15px/23px, NOT the 14px the target's module declares. Every paragraph
       there carries an inline font-size:15px that wins, so 14px is a dead value
       in the source. Copying it would have made our abstract smaller than the
       published ones for a reason that does not exist. */
    font-size: 15px;
    line-height: 23px;
    color: var(--sjp-body);
}
.sjp-abstract-body p { margin: 0; padding: 0 0 15px; }
.sjp-abstract-body p:last-child { padding-bottom: 0; }

.sjp-keywords {
    font-size: 15px;
    line-height: 23px;
    margin: 30px 0 0 0;
    color: var(--sjp-body);
}

@media (max-width: 640px) {
    /* The 50px right padding is a desktop proportion; on a phone it eats the
       measure. */
    .sjp-abstract-inner { padding: 0 18px 28px 18px; }

    /* ⚠️ The blocks that SHARE these insets — .sjp-content, .sjp-statement,
       .sjp-references — get their phone override in the "Narrow viewports"
       section near the end of this file, NOT here. It cannot live beside this
       rule: their base rules are declared further down, and a media query adds
       no specificity, so an override placed here loses to them on source order
       and silently does nothing. This one only works because .sjp-abstract-inner
       happens to be declared above it. */
}

/* ── Main body content ───────────────────────────────────────────── */
.sjp-content {
    font-size: 15px;
    line-height: 23px;
    color: var(--sjp-body);
    margin-top: 16px;
    /* ⚠️ THE SAME INSETS AS .sjp-abstract-inner, AND THAT IS THE WHOLE POINT.
       Measured on the published articles: body text sits at x=202 in a 688px
       column, which is exactly the abstract panel's CONTENT box (the panel is
       x=180 w=760 with padding 0 50px 40px 22px). Our body text was at x=180
       in a 760px column — 20px further left and 72px wider — so the Abstract
       and Introduction did not line up and the longer measure read as heavier
       type even though every font size already matched.

       Expressing it as the same insets rather than a width means the two stay
       aligned by construction: change the panel's padding and the body follows. */
    padding: 0 50px 0 22px;
}
.sjp-content > h2 {
    /* 26px, not 22px — measured on the live article's section headings
       (Abstract, Introduction, Methods and Materials…), Open Sans 700 in
       #7448FF. The 30px below the heading is padding so it cannot collapse
       into the first paragraph's spacing. */
    color: var(--sjp-purple);
    font-weight: 700;
    font-size: 26px;
    /* 0.8, measured. See the blanket heading rule near the top of this file
       for why this was 1.25 and what that cost. */
    line-height: 0.8;
    text-align: left;
    margin: 0;
    /* ⚠️ 20px ABOVE, AND IT PAIRS WITH THE 15px ON .sjp-abstract / ON A
       PARAGRAPH to make 35px above every section heading.

       This is a DEPARTURE FROM THE PUBLISHED ARTICLES, deliberately. Measured on
       Maschler-26010: its H2s have padding-top 0 and sit 15px below the previous
       paragraph — the published set is just as tight. David, 2026-10-07, looking
       at the back matter: "Acknowledgments, Author Contributions etc. They look
       a little tight. Can we add an extra space before new section headers and
       have this be throughout the manuscript?" ⚠️ Do not restore 0 on the
       grounds that it no longer matches published; that is the point.

       ⚠️ PADDING, NOT MARGIN. A top margin here would collapse through
       .sjp-content, which has no padding-top of its own, and the first heading
       would lose the gap entirely. That collapse has bitten this file before —
       see the note on the 30px below.

       H3 is deliberately NOT changed: it already sits 15+24=39px below a
       paragraph, and raising it further would give a SUBSECTION more air than a
       section, inverting the hierarchy. */
    /* 34px, not 20. David saw the first attempt live and asked for "1.5x what
       is there now": the measured ink-to-ink gap was 29px, so the target is
       ~43px. 20 -> 34 is the +14 that gets there, and it keeps the pairing
       with the 15px on .sjp-abstract / on a paragraph. */
    padding: 34px 0 30px;
}
/* ⚠️ H3 IS ITALIC AND NOT BOLD — DAVID'S HOUSE STYLE, SET 2026-10-06, and a
   deliberate departure from the published articles rather than a match.

   The published set could not be matched because it does not agree with
   itself: of the 12 articles with any H3, four are all-bold, four all-plain,
   and four are MIXED WITHIN A SINGLE ARTICLE. The reason is that the bold was
   never ours — it is the author's own <b> inside the heading, which is why it
   varies article to article and paragraph to paragraph. Measured: the H3
   element is weight 700 on every article; a wrapped one computes to 900.

   ⚠️ 900 IS NOT A WEIGHT WE SHIP. The Google Fonts request asks for
   0,400;0,500;0,600;0,700;0,800 and 1,400;1,500 — so 900 was being synthesised
   or substituted by the browser, the exact failure the font comment in the
   plugin warns about. Italic at 500 is a face we actually serve.

   David, 2026-10-06: "I think H2 gets bold, and H3 are italicized but not
   bold." H2 keeps 700; this is the other half of that.

   ⚠️ KEEP IN STEP WITH components/StacksArticle/StacksArticleBody.tsx, which
   styles `.stacks-body-static h3` for the author's proof and the PDF. If the
   two drift, the website and the PDF set subsections differently — the same
   class of split that the table borders had. */
/* ⚠️ H3 IS BOLD AND UPRIGHT. THIS REVERSES THE RULE SET ON 2026-10-06, which
   read "H2 gets bold, and H3 are italicized but not bold" and is quoted at
   length in the note above — treat that note as history, not as current.

   David restated the whole ladder on 2026-10-07, once sub-sections were
   actually appearing on the page: "headers like discussion should be purple
   and the bigger font, then the ones nested under them should just be bolded,
   and then the ones nested under those should be italics (no bold)."

   So: H2 purple and large, H3 bold, H4 italic. Each level is distinguishable
   from the one above without any two sharing a treatment, which the old
   scheme did not manage — H3 and H4 were both italic and differed only by
   weight. */
.sjp-content h3 {
    color: var(--sjp-title-dark);
    font-style: normal;
    font-weight: 700;
    font-size: 20px;
    line-height: 0.8;
    margin: 24px 0 8px 0;
}
/* ⚠️ AUTHOR BOLD INSIDE A HEADING IS NEUTRALISED, NOT REMOVED.

   Headings carry their own weight; an author's <b> on top of that pushed H3 to
   900 — a weight we do not ship (the Google Fonts request stops at 800), so the
   browser synthesised it. Measured across nine published articles: 39 of 39
   bold-wrapped H3s bold the ENTIRE heading, none bold only part of it. So this
   is never "this word matters", it is "I made the heading bold", which the
   heading style already does. Nothing semantic is lost.

   ⚠️ NEUTRALISED IN CSS RATHER THAN STRIPPED FROM THE MARKUP, deliberately.
   Table borders had to be rewritten because they arrive as INLINE STYLES, which
   beat any stylesheet. Bold arrives as a TAG, which CSS beats easily — so there
   is no reason to run another regex over author HTML, and the author's markup
   stays in the stored manuscript and the published post. Delete these two rules
   and the old behaviour is back; nothing has been thrown away.

   ⚠️ <em> IS LEFT ALONE, and set upright inside the italic H3 so it stays
   visible. Italic-inside-italic is invisible, and the standard typographic move
   is to flip it to roman. No published heading uses italics today (0 of 199
   H2s and H3s measured), but this journal publishes ecology — a species name in
   a subsection heading is a matter of time. */
.sjp-content h2 b, .sjp-content h2 strong,
.sjp-content h3 b, .sjp-content h3 strong,
.sjp-content h4 b, .sjp-content h4 strong {
    font-weight: inherit;
    font-style: inherit;
}
/* ⚠️ H3 no longer needs its <em> flipped upright. That rule existed because
   italic inside an italic heading is invisible; H3 is upright now, so an
   author's <em> — a species name in a subsection heading — should simply be
   italic. H4 still needs it, being italic itself. */
.sjp-content h4 em, .sjp-content h4 i {
    font-style: normal;
}

/* ⚠️ H4 IS ITALIC AND NOT BOLD — the third rung of the ladder above. It was
   italic AND bold, which made it heavier than the H3 it sits under.
   ⚠️ `inherit` rather than a number: the body around it is weight 500 here and
   400 in the PDF, and "the same weight as the text, but italic" is the
   intent. A literal 400 would read LIGHTER than the body on the website. */
.sjp-content h4 {
    color: var(--sjp-title-dark);
    font-style: italic;
    font-weight: inherit;
    font-size: 15px;
    line-height: 1.3;
    margin: 20px 0 6px 0;
}
.sjp-content p,
.sjp-content ul,
.sjp-content ol {
    /* Measured from the live article: 15px/23px, weight 500, and the gap
       between paragraphs is BOTTOM PADDING of 15px rather than a margin.
       Padding rather than margin matters at a page break in print and stops
       adjacent margins collapsing into each other. */
    margin: 0;
    padding: 0 0 15px;
    font-size: 15px;
    line-height: 23px;
    font-weight: 500;
}
.sjp-content ul,
.sjp-content ol { margin-left: 24px; }
.sjp-content blockquote {
    border-left: 3px solid var(--sjp-purple);
    margin: 18px 0;
    padding: 4px 0 4px 16px;
    color: var(--sjp-body);
    font-style: italic;
}
.sjp-content a {
    color: var(--sjp-purple);
    text-decoration: underline;
    text-underline-offset: 2px;
}
.sjp-content a:hover { text-decoration-thickness: 2px; }

/* Figures — plain image + caption, no tint background */
.sjp-content figure {
    margin: 28px 0;
    padding: 0;
    background: transparent;
    border: 0;
}
.sjp-content figure img,
.sjp-content p > img {
    max-width: 100%;
    height: auto;
    display: block;
    margin: 0 auto;
    border-radius: 2px;
}
.sjp-content figure figcaption {
    font-size: 13px;
    color: #374151;
    margin-top: 10px;
    line-height: 1.55;
}

/* ⚠️ LINKS IN THE BACK MATTER HAD NO RULE AT ALL, so every reference DOI and the
   peer-review link fell through to the Divi theme's default anchor styling —
   rgb(137,102,255), weight 500, no underline. That is a DIFFERENT PURPLE from
   the journal's #7448FF, close enough that nobody notices and wrong on every
   article. Measured against Maschler-26010, where reference links are 15px
   weight 700 in the journal purple and underlined.

   ⚠️ The published peer-review sentence carries no underline on the anchor
   itself — it wraps the words in a <u> instead, so it renders underlined either
   way. Underlining here matches what a reader sees without reproducing the
   markup quirk. */
.sjp-statement-body a,
.sjp-references-body a {
    color: var(--sjp-purple);
    font-weight: 700;
    text-decoration: underline;
    text-underline-offset: 2px;
}

/* ── Back matter ──────────────────────────────────────────────────────────
 * The mandatory statements, Transparent Peer Review and Recommended Citation.
 *
 * ⚠️ THE SAME INSETS AS .sjp-content, for the same reason. Back matter sits
 * OUTSIDE .sjp-content in the markup, so it did not inherit the body column and
 * rendered at x=180 while the article ran at x=202 — a 22px step that reads as
 * a misalignment down the left margin. Measured on the live page after 0.6.8.
 *
 * ⚠️ Back-matter headings are FULL section headings, 26px, not a smaller class.
 * Measured on Maschler-26010: Acknowledgments, Funding Information, Author
 * Contributions, Data Availability, Recommended Citation and References are all
 * 26px, the same as Introduction and Results.
 */
.sjp-statement {
    padding: 0 50px 0 22px;
    /* ⚠️ NO margin-top. The air above a back-matter heading is padding ON THE
       HEADING, exactly as it is for a body section — see .sjp-content > h2.
       It was 32px here, and the result was measured, not guessed:
         body sections          43px above, 24px below
         Acknowledgments        41px        (the content's own paragraph
                                             margin happened to pad it)
         Author Contributions   26px        17px tighter
         Funding                26px        17px tighter
         References             34px         9px tighter
       So the FIRST back-matter heading looked about right and every one after
       it was visibly bunched — which is what David saw: "the spacing above the
       h2 text sections at the end of the article does not have the additional
       space/padding that the Intro/Methods/Discussion do."
       ⚠️ A margin ALONE cannot do the job: between two statements it collapses
       with the previous one's, so the gap never adds up to the body's. The
       padding below does the work; the 15px here supplies the one piece
       padding cannot.

       The 15px that padding cannot supply is added BETWEEN back-matter blocks
       only — see the sibling rule below. */
}

/* ⚠️ BETWEEN BACK-MATTER BLOCKS ONLY, AND THE SELECTOR IS THE WHOLE POINT.
   A body heading sits 34px of its own padding below the previous paragraph's
   15px bottom margin: 49px. Between two statements that 15px does not exist,
   because .sjp-statement-body p:last-child zeroes it — so 34px was all there
   was and the gap came out 15px short (measured: 28px of ink against a body
   section's 43px).
   ⚠️ IT MUST NOT APPLY TO THE FIRST ONE. Putting the 15px on .sjp-statement
   itself was tried and MEASURED AT 58px for Acknowledgments — 15px too much.
   The margin below the body's last paragraph does NOT collapse with it as
   expected; the two stack. Rather than depend on a collapse that this file
   has now been wrong about twice, the 15px is given only where the previous
   sibling is known to end flush. */
.sjp-statement + .sjp-statement,
.sjp-statement + .sjp-references { margin-top: 15px; }
.sjp-statement > h2 {
    color: var(--sjp-purple);
    font-weight: 700;
    font-size: 26px;
    line-height: 0.8;
    /* ⚠️ THE SAME 34/30 AS .sjp-content > h2, AND IT MUST TRACK IT. The point
       is that a reader cannot tell a back-matter heading from a body one by
       its spacing; if that rule's padding changes, change this with it.
       Padding, not margin, for the reason given there — a margin collapses
       through the bare wrappers between here and <body>. */
    margin: 0;
    padding: 34px 0 30px;
}
.sjp-statement-body {
    font-size: 15px;
    line-height: 23px;
    color: var(--sjp-body);
}
.sjp-statement-body p { margin: 0 0 15px; }
.sjp-statement-body p:last-child { margin-bottom: 0; }

/* ── References ──────────────────────────────────────────────────── */
/* No margin-top of its own: it gets 15px from the sibling rule above when it
   follows a statement, and the body's own paragraph margin when it does not. */
.sjp-references { padding: 0 50px 0 22px; }
.sjp-references > h2 {
    color: var(--sjp-purple);
    font-weight: 700;
    /* The same 34/30 as every other section heading — see .sjp-statement > h2. */
    margin: 0;
    padding: 34px 0 30px;
    /* 26px, not 22px. Measured on Maschler-26010: Acknowledgments, Funding
       Information, Author Contributions, Data Availability, Recommended
       Citation and References are ALL 26px — back matter is not a smaller
       class of heading in this journal. */
    font-size: 26px;
    margin: 0 0 12px 0;
}
/* ⚠️ MEASURED FROM THE LIVE ARTICLE, and the answer is that references are just
   body text: 15px/23px, weight 500, #2c2c2c, 15px of bottom padding, NO hanging
   indent. Ours was 14px/1.6, which read as a smaller, denser block than the
   article it belongs to.

   ⚠️ font-style IS DELIBERATELY NOT SET HERE. On one of our articles the whole
   reference list renders italic, and that italic is in the AUTHOR'S typeset
   manuscript, not in this stylesheet — a different article's references are
   upright. Forcing `font-style: normal` would flatten the authors' own
   formatting, including the italics that conventionally mark a journal title.
   Normalising reference style belongs to the Crossref reference-matching work,
   where a matched reference can be re-rendered from structured data; it is not
   a CSS problem and must not be solved here. */
.sjp-references-body {
    font-size: 15px;
    line-height: 23px;
    font-weight: 500;
    color: var(--sjp-body);
}
.sjp-references-body p,
.sjp-references-body li {
    margin: 0;
    /* ⚠️ THE 15px BOTTOM PADDING IS THE MEASURED ONE; the left padding and the
       negative text-indent are the hanging indent, added 2026-10-15. David
       asked for it on both surfaces: "can we make references on the PDF use a
       hanging indent so that any second or more lines of one is indented? Same
       thing on the webpage."

       ⚠️ THIS SUPERSEDES THE "NO hanging indent" IN THE NOTE ABOVE, which was
       measured off the live article and was accurate then. A hanging indent is
       what makes a bibliography scannable: the surnames form an unbroken
       column and every continuation line steps clear of it.

       ⚠️ 3em, AND THE SAME VALUE AS THE PDF — see components/ArticlePreview.tsx.
       It shipped at 1.5em the same day and David came straight back: "Hanging
       indent isn't large enough on both the pdf and the webpage. Can you try
       doubling it?" 3em is that doubling, kept identical on both surfaces so a
       reader comparing the PDF with the website sees one bibliography.

       Still an em and not APA's half inch: 0.5in is a MANUSCRIPT rule written
       for double-spaced 12pt copy, and a fixed inch eats the measure of a
       typeset column. An em scales with the type, so the two surfaces stay in
       step at any size. (At this 15px body, 3em is ~45px — wider than APA's
       half inch would be, which is the direction David asked for.) */
    padding: 0 0 15px 3em;
    text-indent: -3em;
}
/* ⚠️ NO LIST INDENT. Measured: reference entries sat at x=224 while every other
   line on the page sat at x=202, because of this 22px. The published articles set
   references flush with the body column, with no hanging indent.

   The markers are already suppressed and authors type their own numbering into
   the text, so the <ol> is carrying no meaning here — it is just what the editor
   produced. */
.sjp-references-body ol,
.sjp-references-body ul {
    /* ⚠️ NO !important, AND THE COMMENT THAT USED TO BE HERE WAS WRONG. It
       claimed Divi padded this list at a higher specificity and that the 40px
       offset came from the theme. MEASURED on the live article 2026-10-15:
       Divi's rule is `#left-area ol, .entry-content ol { list-style-type:
       decimal; list-style-position: inside; padding: 0 0 23px }` — the left
       padding is ZERO, and on this page our own rule wins anyway
       (list-style-type computes to `none`). The theme was never the cause; see
       the li > p rule below for what actually was.

       The declarations still belong here: they state what the list should be,
       and they are what makes list-style-type compute to none. */
    padding-left: 0;
    margin-left: 0;
    list-style: none;
}

/* ⚠️ THE HANGING INDENT MUST BE APPLIED ONCE, AND THIS IS WHERE IT WAS BEING
   APPLIED TWICE.

   The rule above sets `padding-left: 3em; text-indent: -3em` on BOTH
   `.sjp-references-body p` AND `.sjp-references-body li`. An imported entry
   arrives as `<li><p>…</p></li>` — so the li took the 3em and the p inside it
   took another 3em, and the whole entry sat 3em right of the References
   heading with its continuation lines 6em in. David, 2026-10-15: "On the
   website, the references are too farr indended. It should be flush with their
   header."

   MEASURED on the live article, left edges in px:

       heading   first line   continuation
          46          91           136      before
          46          46            91      after

   ⚠️ THE SPLIT IS DELIBERATE: the li positions the block (padding-left), the p
   pulls its own first line back (text-indent, which it keeps). Zeroing the
   text-indent here instead would flatten the hanging indent altogether,
   because text-indent on the li does nothing to a block child.

   ⚠️ It also halves the gap between entries, from 30px to 15px — both elements
   were carrying the 15px bottom padding too, and 15px is the measured value.

   ⚠️ A diagnosis was published before this one and was wrong (it blamed Divi).
   The difference was measuring the rendered page instead of a harness built
   around an assumption. */
.sjp-references-body li > p {
    padding: 0;
}

/* ── Corresponding authors (bottom of main column) ───────────────── */
.sjp-corresponding { margin-top: 32px; }
.sjp-corresponding > h2 {
    color: var(--sjp-purple);
    font-weight: 700;
    /* 26px, same as References and every other section heading. */
    font-size: 26px;
    margin: 0 0 10px 0;
}
.sjp-corresponding ul {
    list-style: none;
    padding: 0;
    margin: 0;
}
.sjp-corresponding li {
    margin-bottom: 6px;
    font-size: 14px;
}
.sjp-corresponding-email {
    color: var(--sjp-muted);
    text-decoration: none;
    margin-left: 6px;
}
.sjp-corresponding-email:hover {
    color: var(--sjp-purple);
    text-decoration: underline;
}

/* ── Version strip (V2+) ─────────────────────────────────────────── */
.sjp-version-strip {
    margin-top: 40px;
    padding: 12px 16px;
    background: #f5f3ff;
    border: 1px solid #e5e1ff;
    border-radius: 4px;
    font-size: 13.5px;
    color: var(--sjp-body);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
}
.sjp-version-label { font-weight: 700; color: var(--sjp-purple); }
.sjp-version-prev {
    color: var(--sjp-purple);
    text-decoration: underline;
    text-underline-offset: 2px;
    margin-left: auto;
}

/* ═════════════════════════════════════════════════════════════════
 * SIDEBAR
 * Each .sjp-sidebar-block is separated by a thin grey rule drawn
 * via border-top. Keeps the sidebar tidy without extra <hr> tags.
 * ═════════════════════════════════════════════════════════════════ */
.sjp-sidebar {
    display: flex;
    flex-direction: column;
    gap: 0;
    font-size: 14px;
}
/* ── The article's metadata, printed a second time for narrow screens ──
   The template captures the sidebar's blocks once and prints them twice: in the
   sidebar (wide screens) and inside the article column right after Download /
   Contact / Share (.sjp-article-info-inline, <=980px). See the long note above
   the grid in single-stacks_article.php.

   Hidden by default; the "Narrow viewports" section turns it on and turns the
   original sidebar off at the SAME breakpoint, so a reader never sees both.

   Inset like the body text, so its left edge lines up with the abstract and the
   paragraphs around it rather than sitting flush like a foreign card — the same
   0 50px 0 22px .sjp-content uses, and it follows them to 18/18 on a phone.
   David asked for it to "match the article". */
.sjp-article-info-inline {
    display: none;
}
.sjp-article-info-inline > .sjp-sidebar {
    padding: 0 50px 0 22px;
}

/* ⚠️ ARTICLE-SIZED TEXT IN THE INLINE COPY ONLY. The sidebar is set at 11px on
   purpose — David's choice for the narrow right-hand column ("should also be
   the same size as the other stuff on the right panel", 2026-10-15) — and the
   desktop sidebar keeps it. But in the article column on a phone, beside a 15px
   abstract, an 11px funding paragraph reads as fine print. David asked for the
   moved block to "match the article", so here it takes the body text's 15/23,
   and its small purple labels step up to 14px so they still read as labels
   above it.

   ⚠️ SPECIFICITY, NOT POSITION, MAKES THESE WIN. The rules they override set
   11px at up to (0,2,0) — `.sjp-article .sjp-article-type` — and are scattered
   through the sidebar section. Prefixing with `.sjp-article-info-inline
   .sjp-sidebar` adds two classes, so each override outranks its original
   wherever either one sits, and none of them can match the desktop sidebar,
   which is not inside .sjp-article-info-inline. */
.sjp-article-info-inline .sjp-sidebar dl,
.sjp-article-info-inline .sjp-sidebar dt,
.sjp-article-info-inline .sjp-sidebar dd,
.sjp-article-info-inline .sjp-sidebar .sjp-badges span,
.sjp-article-info-inline .sjp-sidebar .sjp-sidebar-doi,
.sjp-article-info-inline .sjp-sidebar .sjp-funding-body,
.sjp-article-info-inline .sjp-sidebar .sjp-coi p,
.sjp-article-info-inline .sjp-sidebar .sjp-copyright p {
    font-size: 15px;
    line-height: 23px;
}
.sjp-article-info-inline .sjp-sidebar .sjp-article-type,
.sjp-article-info-inline .sjp-sidebar .sjp-funding > h4,
.sjp-article-info-inline .sjp-sidebar .sjp-coi > h4 {
    font-size: 14px;
    line-height: 20px;
}

.sjp-sidebar-block {
    padding: 22px 0;
    border-top: 1px solid var(--sjp-rule);
}
.sjp-sidebar-block:first-child {
    border-top: 0;
    padding-top: 0;
}
.sjp-sidebar-block:last-child {
    padding-bottom: 0;
}

/* Acceptance line at the very top of the sidebar */
.sjp-acceptance {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 14px;
    font-size: 16px;
    color: var(--sjp-title-dark);
}
.sjp-icon-check { flex: 0 0 auto; }

/* Badges — one per line with icon + label */
.sjp-badges {
    display: flex;
    flex-direction: column;
    gap: 14px;
}
.sjp-badge {
    display: flex;
    align-items: center;
    gap: 12px;
    font-size: 15px;
    color: var(--sjp-body);
}
.sjp-icon-badge { flex: 0 0 auto; }
/* The licence badge is the one badge that is a link. It takes NEITHER the
   theme's link colour NOR an underline: it sits in a column of three that has
   to keep reading as one group, and David ruled on the underline after seeing
   it live on all three surfaces (2026-10-20) — "No need for the link to CC By
   4.0 ... to have the underline." The CC mark beside it is the affordance.
   ⚠️ text-decoration: none is LOAD-BEARING here, not a default: Divi
   underlines anchors globally, so removing this line brings the underline back.
   ⚠️ Declared AFTER .sjp-badge: Divi styles anchors globally and a rule
   placed earlier at equal specificity loses on source order. That exact
   trap already cost a release on .sjp-sidebar-doi a, noted below. */
.sjp-badge a {
    color: inherit;
    font-weight: inherit;
    text-decoration: none;
}

/* 15px and BOLD, measured on Maschler-26010: the sidebar DOI is the one link in
   that column the reader is meant to copy, and the published articles weight it
   accordingly. Ours was 14px/500 and underlined; theirs carries no underline —
   the weight and the colour do the work. */
/* ⚠️ 11px, MATCHING THE FUNDING LABEL — a departure from the published 15px.
   David, 2026-10-07, pointing at the DOI and the dates: "This part is still too
   large in the text. This too. It should match the size of Funding information
   etc." The whole right column now sits at one size. Bold and purple stay: it
   is still the link a reader copies. */
.sjp-sidebar-doi {
    /* ⚠️ 22px MATCHES .sjp-sidebar-block's OWN PADDING, which is what David
       meant by "the same amount of space as after funding information" — the
       DOI and the Submitted/Accepted/Published rows live in ONE block, so
       nothing separated them and they read as a single clump. 10px was a
       paragraph gap; 22px is the break the rest of the column uses. */
    margin: 0 0 22px 0;
    font-size: 11px;
    line-height: 15.4px;
    word-break: break-all;
}

/* The rest of the column at the same 11px — the date rows, the three status
   badges and the copyright line. David asked for the copyright by name: "should
   also be the same size as the other stuff on the right panel". */
.sjp-sidebar dl, .sjp-sidebar dt, .sjp-sidebar dd,
.sjp-badges, .sjp-badges p, .sjp-badges div, .sjp-badges span,
.sjp-copyright, .sjp-copyright p {
    font-size: 11px;
    line-height: 15.4px;
}
/* ⚠️ UNDERLINED, AND EDITED HERE RATHER THAN BY ADDING A RULE ABOVE. A second
   `.sjp-sidebar-doi a` block earlier in the file loses to this one on source
   order at equal specificity — which is exactly what happened on the first
   attempt: the underline was declared, computed to `none`, and the probe
   caught it. David, 2026-10-07: "the DOI in the right panel should be
   underlined". The purple alone was carrying "this is a link". */
.sjp-sidebar-doi a {
    color: var(--sjp-purple);
    font-weight: 700;
    text-decoration: underline;
}

.sjp-history-list {
    display: grid;
    grid-template-columns: max-content 1fr;
    column-gap: 10px;
    row-gap: 3px;
    margin: 0;
    /* ⚠️ SET LIKE .sjp-statement-body, DELIBERATELY AND EXPLICITLY. David,
       2026-10-15: "Looks like the submitted/accepted/publsihed text on the
       website is a different font than the funding informaiotn. It should be
       the funding infomraiton."

       It was 14px with no family of its own, so it inherited whatever the
       theme gives a <dl> in that column — which is not what a <p> next to it
       inherits. Rather than hunt the ancestor, this states the requirement:
       the dates are set like the statements they sit beside. ⚠️ Keep the three
       values in step with .sjp-statement-body above; they are a pair now. */
    font-family: 'Open Sans', Helvetica, Arial, Lucida, sans-serif;
    font-size: 15px;
    line-height: 23px;
}
.sjp-history-list dt {
    color: var(--sjp-body);
    font-weight: 400;
}
.sjp-history-list dd {
    margin: 0;
    color: var(--sjp-body);
    font-weight: 400;
}

/* ⚠️ 11px AND BOLD. 0.6.13 set this to weight 500 and David caught it: "headers
   for conflicts of interest and funding information don't match -- they aren't
   bolded maybe?" He was right and my measurement was wrong — the probe had
   matched a WRAPPER, not the label. Measured properly, the published markup is
   `<strong>Funding Information:</strong>` computing to 11px weight 700 in the
   journal purple. Size was right; weight was mine to get wrong. */
/* ⚠️ CONFLICTS OF INTEREST IS NOT A SEPARATE BLOCK VISUALLY. David, against
   the reference: "No line between funding information and conflict of
   interest." On Maschler-26010 the two statements run together under one rule;
   ours drew .sjp-sidebar-block's border-top between them and split a pair that
   belongs together. The rule goes and its 22px of padding with it, so COI
   reads as the next paragraph of the same block. ⚠️ Only COI is exempted —
   every other sidebar block keeps its rule. */
.sjp-sidebar-block.sjp-coi {
    border-top: 0;
    padding-top: 0;
}
.sjp-funding > h4,
.sjp-coi > h4 {
    color: var(--sjp-purple);
    font-weight: 700;
    font-size: 11px;
    margin: 0 0 6px 0;
    line-height: 1.4;
}
/* 11px/15.4, measured across the published sidebar: the statement text, the
   date rows, the badge rows and the copyright line are all 11px there, where
   ours ran at 14px. David: "Text size on the right panel is off." */
.sjp-funding-body,
.sjp-coi p {
    font-size: 11px;
    line-height: 15.4px;
    color: var(--sjp-body);
    margin: 0;
}
.sjp-funding-body p:last-child { margin-bottom: 0; }

/* ═════════════════════════════════════════════════════════════════
 * STICKY MINI-NAV
 * The host theme's `script.js` calls
 *   $('.sticky-nav').addClass('show') when scrollTop > 50
 * globally on every page. We emit a `.sticky-nav` element in our
 * template with the same inner structure the theme's Marrocco page
 * uses (.sticky-container > .content > .title/.authors/.collaborators
 * + .container-buttons). The visual rules below are scoped under our
 * own `.sjp-sticky-nav` class so they don't collide with the theme's
 * legacy `.single-project .sticky-nav` ruleset on Divi-built pages.
 *
 * Hidden by default; `.show` (toggled by the theme JS) animates it
 * into view at the top of the viewport.
 * ═════════════════════════════════════════════════════════════════ */
/* display:none is the single source of truth for "hidden" — no
   transform shenanigans that a cascading rule elsewhere could
   override. Our JS toggles `.sjp-sticky-nav--show` to reveal. */
.sjp-sticky-nav {
    display: none;
}
/* ⚠️ Z-INDEX TOKENS. The sticky bar and the share dialog MUST be ordered
   relative to each other, and they are 180 lines apart in this file. Defining
   both here is what stops the next edit to one of them silently inverting the
   pair — which is exactly what happened: the bar held max-int and the dialog
   held 999999, so clicking Share in the sticky bar opened the dialog
   UNDERNEATH the bar, with the bar's own buttons still clickable on top of
   the scrim. */
:root {
    --sjp-z-sticky: 9000;
    --sjp-z-modal: 9100;
}
.sjp-sticky-nav.sjp-sticky-nav--show {
    display: block;
    position: fixed;
    /* ⚠️ NO `!important` ON `top`. The `body.admin-bar` rule below has to be
       able to win, and !important outranks specificity no matter how specific
       the other selector is. Phase 4.5 added `top: 0 !important` here and
       silently killed that offset, so for every logged-in viewer the bar has
       been rendering underneath the WordPress admin bar ever since. That is
       the "sticky header is a little off" David reported on 2026-10-05.
       If a theme ever genuinely overrides `top`, raise specificity — do not
       reach for !important again. */
    top: 0;
    left: 0;
    right: 0;
    width: 100%;
    /* ⚠️ 9000, NOT max-int. The old 2147483647 was chosen to beat "any theme's
       fixed header", but it is not how this bar wins — the site header is
       HIDDEN while ours is showing (see the body.sjp-has-sticky-nav rule
       below), so there is nothing to out-stack. What max-int actually did was
       put the bar above our own share dialog. A number with room above it is
       what makes the pair orderable. */
    z-index: var(--sjp-z-sticky);
    background: #ffffff;
    border-bottom: 1px solid #e5e7eb;
    box-shadow: 0 2px 12px rgba(16, 24, 40, 0.12);
    /* 24px horizontal matches .sjp-article's own gutter, so the sticky title
       lines up with the article text instead of sitting inboard of it.
       Reverted from Phase 4.5's 18px/28px, which was deliberately bulked to
       "visually cover a typical site nav" — we no longer cover the site nav,
       we hide it. */
    /* ⚠️ 10px, DOWN FROM 14. David, 2026-10-07: "Sticky header is way too long
       and covering up stuff." The bar grew when the title was allowed to wrap
       in 0.6.15 — a 2-line title is most of its height. Trimmed here, in the
       title's size and in its leading; see .sjp-sticky-title. MEASURED at
       1440px with Hogland's title: 109px -> 98px. (A comment here first
       claimed 92; that was an estimate written before the measurement and it
       was wrong. The number above is the measured one.)

       ⚠️ ~98px IS THE FLOOR while the bar shows a wrapped title, the authors
       AND the collaborators with air between them — a 2-line title at 14px is
       34 of those pixels and the two byline rows are another 43. Going below
       it means DROPPING A LINE, not tuning: hiding .sjp-sticky-collaborators
       on desktop (it is already hidden under 900px) takes the bar to ~78px.
       That is David's call, and it trades against the breathing room he asked
       for in the same message. */
    padding: 10px 24px;
    font-family: 'Open Sans', Helvetica, Arial, Lucida, sans-serif;
    color: #111827;
    animation: sjp-sticky-slide-in 200ms ease both;
}
@keyframes sjp-sticky-slide-in {
    from { transform: translateY(-100%); }
    to   { transform: translateY(0); }
}
/* Slip below the WP admin bar when it is present (logged-in viewers only).
   ⚠️ DESKTOP ONLY, and the breakpoint is not arbitrary: WordPress core
   switches #wpadminbar to `position: absolute` at <=782px, so on a phone the
   admin bar scrolls away with the page. Offsetting a FIXED bar by 46px there
   would leave a 46px see-through gap once the admin bar had scrolled off.
   Above 782px the admin bar is fixed and 32px tall, so the offset is required
   or it clips our title.
   (WP core's admin-bar CSS is not in this repo; this is documented WP
   behaviour and is UNVERIFIED here — check it on a phone while logged in.) */
@media screen and (min-width: 783px) {
    body.admin-bar .sjp-sticky-nav.sjp-sticky-nav--show { top: 32px; }
}
/* Give the theme's main nav an opaque background on our post type.
   The Stacks child theme sets the nav's background to
   `rgba(255,255,255,0)` globally, so article content bleeds
   through it as you scroll. Force white so the nav is a proper
   opaque bar at rest (before our sticky nav takes over).
   Selectors cover both the legacy Divi header (`#main-header`) and
   Divi Theme Builder's own wrapper (`header.et-l.et-l--header`,
   present when the site has a TB header assigned — which
   stacksjournal.org does, per the `et-tb-has-header` body class). */
body.single-stacks_article #main-header,
body.single-stacks_article header.et-l.et-l--header,
body.single-stacks_article .et-l--header,
/* #top-header is Divi's secondary/top bar. It was in the HIDE rule below but
   not here, so at rest it stayed transparent and article text showed through
   it — the one strip the opaque fix missed. */
body.single-stacks_article #top-header {
    background-color: #ffffff !important;
}
/* When our sticky-nav is showing, hide the theme's nav so they
   don't fight for the top row. Body class `sjp-has-sticky-nav` is
   toggled by our scroll handler. */
body.sjp-has-sticky-nav #main-header,
body.sjp-has-sticky-nav header.et-l.et-l--header,
body.sjp-has-sticky-nav .et-l--header,
body.sjp-has-sticky-nav #top-header {
    /* ⚠️ `visibility`, NOT `display`. `display: none` takes an in-flow header
       out of the layout, so the whole page jumps upward by the header's height
       the instant you cross the scroll threshold — and jumps back when you
       scroll up again. `visibility: hidden` keeps the box and its space, so
       nothing moves. This hide, not z-index, is what guarantees exactly one
       bar on screen. */
    visibility: hidden !important;
}
/* REMOVED on 2026-10-05 (was:
       body.single-stacks_article #main-header.et-fixed-header
           { display: none !important; }
   described as "belt-and-braces: hide the Divi scroll-shrink variant").
   ⚠️ IT HID THE SITE HEADER ON DIVI'S SCROLL THRESHOLD, NOT OURS. Divi adds
   .et-fixed-header at its own scroll position, which is not our 50px. In the
   band where Divi had added the class but scrollTop was still <= 50, the site
   header was hidden and our bar had not yet appeared: a stretch of scrolling
   with NO header at all. It also applied on `single-stacks_article`
   unconditionally rather than only while our bar is showing.
   The rule above, keyed on our own body class, is the only hide we want —
   one threshold, one owner. */
.sjp-sticky-container {
    display: flex;
    align-items: center;
    gap: 24px;
    width: 100%;
    max-width: 1140px;
    margin: 0 auto;
}
.sjp-sticky-content {
    flex: 1 1 auto;
    min-width: 0;
}
/* ⚠️ THE STICKY TITLE WRAPS, IT DOES NOT TRUNCATE. It was nowrap with an
   ellipsis "so the sticky bar stays compact" — but these titles are long, so a
   reader saw the first half and "…". David, 2026-10-07: "Title in the sticky
   header should always wrap so you can see the whole thing." The bar grows
   instead; that is the trade he chose. */
.sjp-sticky-title {
    color: #111827;
    /* ⚠️ 18px, MATCHING THE REVIEW PAGE'S STICKY BAR. It was 14px — shrunk in
       0.6.15 to get the bar from 109px to 92 — and beside the transparent
       review page's bar, which sets its title at 18px, the published article's
       looked like the lesser of the two. David, 2026-10-15: "Text size in teh
       sticky header of the apps articles is smaller than that in teh publsihed
       article... The transparent peer review sticky header has the right size
       text."

       ⚠️ THE BAR GETS TALLER AGAIN, and that is the accepted cost. The
       two-line clamp below is what keeps it bounded; height was being bought
       with legibility. */
    font-size: 18px;
    font-weight: 700;
    line-height: 1.2 !important;
    margin: 0;
    /* ⚠️ TWO LINES MAXIMUM, and this is a CEILING, not a change to today's
       page. Hogland's title already wraps to exactly two. Without the clamp a
       longer title has nothing stopping it making the bar three or four lines
       tall, which is the complaint that produced this edit — the bar must have
       a known worst case. line-clamp needs the overflow:hidden to take. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: normal;
}
/* Near-black, like the byline. The collaborators line below stays grey, which
   is the distinction David asked for: "Authors in the sticky header should be
   black, but collaborators can stay grey." */
/* ⚠️ THE 6px AND 5px BELOW ARE AIR DAVID ASKED FOR IN THE SAME BREATH AS
   "too long", AND BOTH WERE HONOURED. The three lines were 2px and 0px apart —
   genuinely touching. The height came out of the padding and the title size
   instead, so the bar is 17px SHORTER than before while these gaps roughly
   trebled. David: "can we add a little breathing room between the title,
   authors, and collaborators". */
.sjp-sticky-authors {
    color: #111827;
    /* ⚠️ 15px, the review page's byline size — see .sjp-sticky-title. */
    font-size: 15px;
    font-weight: 400;
    line-height: 1.4;
    margin: 6px 0 0 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.sjp-sticky-collaborators {
    color: #6b7280;
    /* ⚠️ 15px, matching the authors line it sits under, and the same size the
       collaborators line takes in the byline above. It was 11px — two steps
       below the title — which read as a footnote rather than as credit. */
    font-size: 15px;
    font-style: italic;
    font-weight: 400;
    line-height: 1.4;
    margin: 5px 0 0 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.sjp-sticky-buttons {
    display: flex;
    gap: 10px;
    flex: 0 0 auto;
}
.sjp-sticky-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 9px 20px;
    border-radius: 4px;
    font-size: 14px;
    font-weight: 700;
    line-height: 1;
    text-decoration: none;
    border: 2px solid #7448FF;
    transition: background-color 180ms ease, color 180ms ease, border-color 180ms ease;
    white-space: nowrap;
}
.sjp-sticky-btn-primary {
    background: #7448FF;
    color: #ffffff;
}
.sjp-sticky-btn-primary:hover,
.sjp-sticky-btn-primary:focus-visible {
    background: #2D2163;
    border-color: #2D2163;
    color: #ffffff;
    text-decoration: none;
}
.sjp-sticky-btn-outline {
    background: transparent;
    color: #7448FF;
}
.sjp-sticky-btn-outline:hover,
.sjp-sticky-btn-outline:focus-visible {
    background: #2D2163;
    border-color: #2D2163;
    color: #ffffff;
    text-decoration: none;
}

@media (max-width: 780px) {
    /* On narrow screens: drop the collaborators line, tighten
       button sizes, and let the authors sit closer to the title
       to save vertical space. */
    .sjp-sticky-nav.sjp-sticky-nav--show { padding: 10px 16px; }
    .sjp-sticky-container { gap: 12px; }
    /* Scaled with the desktop sizes above, keeping the same step down. */
    .sjp-sticky-title { font-size: 16px; }
    .sjp-sticky-authors { font-size: 13px; }
    .sjp-sticky-collaborators { display: none; }
    .sjp-sticky-btn { padding: 8px 14px; font-size: 13px; }
}

@media (max-width: 480px) {
    /* On phones, hide buttons in the sticky bar — the full-width
       buttons in the article are a tap away when the reader scrolls
       back up. Keeping the title + authors line gives readers quick
       orientation without clutter. */
    .sjp-sticky-buttons { display: none; }
}

/* ═════════════════════════════════════════════════════════════════
 * SHARE MODAL
 * Self-contained: own positioning, own JS (see template). We emit
 * this on every stacks_article; hidden by default via the HTML
 * `hidden` attribute + opacity:0 transition. JS flips .show to
 * animate it in.
 * ═════════════════════════════════════════════════════════════════ */
/* Hidden by default via `hidden` attribute + display:none — both
   belt and braces against any theme rule that might set display on
   unscoped selectors. Shown when our JS removes the attribute AND
   adds `sjp-share-modal--show` (double gating so neither flips the
   modal on alone). */
.sjp-share-modal[hidden],
.sjp-share-modal { display: none; }
.sjp-share-modal.sjp-share-modal--show {
    display: flex !important;
    position: fixed;
    inset: 0;
    /* ⚠️ MUST STAY ABOVE --sjp-z-sticky. Until this was fixed the sticky bar
       held max-int and this held 999999, so clicking Share IN the sticky bar
       opened the dialog underneath it — scrim and all — with the bar's own
       buttons still live above the overlay. The two tokens are defined
       together at the top of the sticky section for exactly this reason. */
    z-index: var(--sjp-z-modal);
    align-items: center;
    justify-content: center;
    padding: 24px;
    /* ⚠️ Darker than it was (0.55). At that value the page behind stayed
       legible enough to compete with the dialog, so the dialog read as a card
       floating on the article rather than as the thing you are now using.
       A blur as well, where supported, so the backdrop reads as out of focus
       rather than merely tinted. */
    background: rgba(17, 24, 39, 0.72);
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
    opacity: 1;
    font-family: 'Open Sans', Helvetica, Arial, Lucida, sans-serif;
    animation: sjp-share-fade-in 200ms ease both;
}
@keyframes sjp-share-fade-in {
    from { opacity: 0; }
    to   { opacity: 1; }
}

.sjp-share-modal-inner {
    background: #ffffff;
    width: 100%;
    max-width: 560px;
    border-radius: 12px;
    padding: 28px 40px 40px 40px;
    box-shadow: 0 18px 50px rgba(17, 24, 39, 0.25);
    transform: translateY(6px);
    transition: transform 220ms ease;
}
.sjp-share-modal.sjp-share-modal--show .sjp-share-modal-inner { transform: translateY(0); }

.sjp-share-topbar {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    margin-bottom: 18px;
}
.sjp-share-logo { display: inline-flex; align-items: center; }
.sjp-share-close {
    appearance: none;
    background: transparent;
    border: 0;
    padding: 6px;
    cursor: pointer;
    border-radius: 4px;
    line-height: 0;
}
.sjp-share-close:hover { background: #f3f4f6; }
.sjp-share-close svg { display: block; }

.sjp-share-heading {
    font-size: 24px;
    font-weight: 700;
    color: #111827;
    margin: 4px 0 22px 0;
    line-height: 1.3;
}

.sjp-share-icons {
    display: flex;
    gap: 16px;
    margin-bottom: 26px;
}
.sjp-share-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 64px;
    height: 64px;
    border-radius: 50%;
    background: #ede9fe; /* light purple bubble */
    text-decoration: none;
    transition: background-color 160ms ease, transform 160ms ease;
}
.sjp-share-icon:hover,
.sjp-share-icon:focus-visible {
    background: #d8c9ff;
    transform: translateY(-1px);
}

.sjp-share-copy-label {
    font-size: 16px;
    color: #111827;
    margin: 0 0 10px 0;
}
.sjp-share-input {
    position: relative;
    display: flex;
    align-items: center;
    gap: 10px;
    border: 2px solid #d1d5db;
    border-radius: 8px;
    padding: 8px 8px 8px 44px;
}
.sjp-share-input-icon {
    position: absolute;
    top: 50%;
    left: 14px;
    transform: translateY(-50%);
}
.sjp-share-url {
    flex: 1 1 auto;
    min-width: 0;
    border: 0;
    outline: none;
    background: transparent;
    font-size: 15px;
    color: #374151;
    font-family: inherit;
    padding: 8px 0;
}
.sjp-share-copy-btn {
    appearance: none;
    background: #7448FF;
    color: #ffffff;
    border: 0;
    border-radius: 6px;
    padding: 10px 28px;
    font-size: 15px;
    font-weight: 700;
    cursor: pointer;
    transition: background-color 160ms ease;
    font-family: inherit;
}
.sjp-share-copy-btn:hover,
.sjp-share-copy-btn:focus-visible { background: #2D2163; }

@media (max-width: 520px) {
    .sjp-share-modal-inner { padding: 20px 20px 28px 20px; }
    .sjp-share-heading { font-size: 20px; }
    .sjp-share-icons { gap: 12px; }
    .sjp-share-icon { width: 56px; height: 56px; }
    .sjp-share-input { padding-left: 38px; }
    .sjp-share-input-icon { left: 10px; }
    .sjp-share-copy-btn { padding: 10px 18px; font-size: 14px; }
}

/* ── Narrow viewports ────────────────────────────────────────────── */
@media (max-width: 980px) {
    /* Sidebar falls below the main content. */
    .sjp-grid {
        grid-template-columns: minmax(0, 1fr);
        gap: 40px;
    }
    /* ⚠️ AND SO DOES THE REASON FOR .sjp-main's 50px RIGHT PADDING. That padding
       is the gutter between the text and the sidebar (see .sjp-main above). Once
       the grid is one column there is nothing beside the text to separate it
       from, but the padding stayed — from 980px all the way down to a phone,
       where it was 50 of 375px. It belongs to the same breakpoint as the
       collapse, so it lives inside the same rule set. Also widens the headline,
       which sits inside .sjp-main: 11 lines down to 7 at 375px. */
    .sjp-main {
        padding-right: 0;
    }

    /* ⚠️ SWAP THE TWO COPIES OF THE ARTICLE'S METADATA — at exactly the width
       where the sidebar stops being beside the text, so there is no width at
       which a reader sees both or neither.

       Hiding the original sidebar also hides its table of contents, on
       purpose. With the sidebar at the foot of a 44-screen article the TOC was
       the last thing on the page, and on a phone its sticky head took 78px of
       the screen once you got there. David, 2026-10-22: "we don't need a TOC".
       ⚠️ WIDE SCREENS KEEP IT — this rule only applies at <=980px.

       ⚠️ TWO CLASSES, NOT ONE, and that is load-bearing: .sjp-sidebar is
       `display: flex` further up this file at the same single-class
       specificity, so a bare `.sjp-sidebar { display: none }` would win or
       lose purely on where it sat. `.sjp-grid > .sjp-sidebar` outranks it
       wherever it is, and it matches ONLY the original — the inline copy's
       aside is inside <main>, not a direct child of the grid. */
    .sjp-article-info-inline {
        display: block;
    }
    .sjp-grid > .sjp-sidebar {
        display: none;
    }
    .sjp-meta-columns {
        grid-template-columns: 1fr;
        gap: 24px;
    }
    .sjp-three-buttons {
        grid-template-columns: 1fr;
        gap: 10px;
    }
}

@media (max-width: 640px) {
    /* ⚠️ AND EVERY BLOCK THAT SHARES ITS INSETS MUST FOLLOW IT, OR THEY STOP
       LINING UP. .sjp-content, .sjp-statement and .sjp-references deliberately
       copy the abstract panel's 0 50px 0 22px so that body text, back matter
       and references all start where the abstract's text starts — "aligned by
       construction", in .sjp-content's own words. This media query used to
       override ONLY the abstract, so on a phone the abstract's text sat at x=34
       and everything else at x=38: the alignment those insets exist to
       guarantee, broken at exactly the width where it was changed.

       MEASURED 2026-10-22 at 375px, before -> after:
         text left edge, abstract/body/back matter/references   34/38/38/38 -> 34/34/34/34
         body text width                                         221px -> 307px
         characters per line                                     27 -> 35

       Left AND right only: these blocks have no top or bottom padding of their
       own, and the shorthand would silently zero any added later.

       ⚠️ THIS BLOCK MUST STAY BELOW .sjp-content, .sjp-statement AND
       .sjp-references. It was first written beside the abstract's override,
       where it lost to all three on source order and did nothing — and a test
       that injected it at the END of the page passed anyway, because there it
       won. Proven against the real file instead; see the commit. */
    .sjp-content,
    .sjp-statement,
    .sjp-references,
    .sjp-article-info-inline > .sjp-sidebar {
        padding-left: 18px;
        padding-right: 18px;
    }
}

@media (max-width: 600px) {
    .sjp-article {
        /* ⚠️ ZERO TOP MARGIN, and it is not cosmetic. This was 16px, and Divi's
           header script reads the page's top offset and copies it onto the FIXED
           header as an inline `top: 16px`. That opened a 16px slit ABOVE the
           header that the article scrolled through — visible in David's
           screenshots as faint bars over the logo. MEASURED 2026-10-22: with this
           margin the header sat at top:16; without it, at 0, with no inline top
           at all. (The 2026-10-07 note on the base rule about margin "moving the
           header too" was this same mechanism.) */
        margin: 0 auto 40px auto;
        /* ⚠️ LEFT AND RIGHT ONLY. This was `padding: 0 16px`, and the shorthand
           ZEROED padding-top — the 122px that clears Divi's fixed header (see
           the base .sjp-article rule, tuned with David on 2026-10-07). On a
           phone the article therefore started underneath the fixed header, and
           four of hogland-26014's five subject ribbons were hidden behind it.
           Measured 2026-10-22; David spotted it.

           With the desktop 122px the ribbons sit 49px below the 73px phone
           header. The legacy articles leave 67 (cameron-26009: header bottom 73,
           first ribbon 140). ⚠️ THE 18px SHORTFALL IS DELIBERATE: on desktop
           David chose LESS clearance than the legacy articles ("too long,
           vertically on the base"), and vertical space is scarcer on a phone.
           If it reads cramped, a phone-only padding-top of 140px matches the
           legacy pages exactly. */
        padding-left: 16px;
        padding-right: 16px;
    }
    .sjp-title { font-size: 24px; }
    .sjp-abstract-heading { font-size: 19px; }
    .sjp-content > h2 { font-size: 19px; }
    .sjp-references > h2 { font-size: 19px; }
    .sjp-version-strip {
        flex-direction: column;
        align-items: flex-start;
    }
    .sjp-version-prev { margin-left: 0; }
}

/* ── Figures ─────────────────────────────────────────────────────────────────
 *
 * The markup these style is emitted by sjp_render_figure_html() in the plugin,
 * which replaces each <!--SJP_FIGURE:id--> marker in the body:
 *
 *   .sjp-content .imgWithCitation
 *       > a.img-glass > img
 *       > .citation > p > span.title + caption text
 *
 * ⚠️ EVERY NUMBER BELOW WAS MEASURED, NOT CHOSEN. Taken from the journal's own
 * published article https://stacksjournal.org/article/Seeger-26012/ at a 1440px
 * viewport on 2026-10-05, so a new article sits beside the existing ones without
 * a visible seam.
 *
 *   figure card   450px wide
 *   image         410px (card width minus 20px padding each side)
 *   caption       12px / 17.76px, #2c2c2c
 *   caption label 700 weight, #7448ff  — "Figure 1." in purple
 *
 * ⚠️ An earlier pass of this work recorded the image as 280px. That was measured
 * in a narrow preview pane, not at desktop width, and is wrong. If these numbers
 * are ever re-derived, measure at >=1440px.
 *
 * ⚠️ SCOPED UNDER .sjp-content ON PURPOSE. `.imgWithCitation` and `.citation`
 * are the live site's own class names, reused here so the two renderings match.
 * They are generic enough to collide with a theme's rules, so every selector is
 * anchored to our own container rather than left loose.
 */
.sjp-content .imgWithCitation {
  /* ⚠️ THE LAVENDER CARD IS THE FIGURE'S MOST RECOGNISABLE FEATURE and it was
     missing entirely. Measured off the live article: rgb(244,241,255), 20px
     padding, square corners, 450px cap. Without it a figure reads as a loose
     image dropped into the text rather than as a figure. */
  /* ⚠️ THE LAVENDER CARD STAYS. It is the figure's most recognisable feature and
     the published articles all have it.

     ⚠️ THE BUG WAS TRANSPARENCY, NOT THE COLOUR — and I had it backwards first.
     David, 2026-10-07: "i wanted it lavender, but it seems like the figures were
     being see-through. That's the change." Measured: Hogland's Figure 1 webp
     carries an alpha channel with 33.3% of its pixels transparent, so the card's
     lavender showed THROUGH the artwork wherever the author's own background was
     meant to be white. The fix belongs on the image, not the card — see
     `.sjp-content .imgWithCitation img` below.

     ⚠️ FULL TEXT WIDTH IS A DELIBERATE DEPARTURE from the published 450px card.
     David asked for it knowing that: "is it possible to make the figures on the
     webpage the width of the text? … (I know this is different than the current
     ones)". Clicking still opens the full-size original — the anchor is
     untouched. */
  background: #F4F1FF;
  max-width: 100%;
  margin: 28px 0;
  padding: 20px;
  box-sizing: border-box;
}


.sjp-content .imgWithCitation a.img-glass {
  display: block;
  /* The anchor opens the full-size image. ⚠️ It is NOT a lightbox here: the
     overlay behaviour on the live site comes from JavaScript in the Divi child
     theme, which never runs on this template. Clicking navigates. The class is
     kept so the markup matches, and so a lightbox can be added later without
     touching the renderer. */
  text-decoration: none;
  border: 0;
}

.sjp-content .imgWithCitation img {
  display: block;
  /* ⚠️ THE 410px CAP IS GONE. It came from the published 450px card (450 minus
     20px padding each side) and is what kept the image small inside a card that
     now spans the text column — leaving it would have given a 688px lavender
     card with a 410px image marooned in the middle.

     ⚠️ `width: auto`, NOT `100%`. With 100% a figure NARROWER than the column
     would be stretched up to fill it and render blurry. `auto` draws every
     image at its natural size and `max-width` pulls the oversized ones back to
     the column — so wide figures (the usual case: Hogland's are 936-1024px
     against a 648px content box) fill the width, and a small one stays small.

     ⚠️ THE WHITE PLATE. Author figures are often exported with a transparent
     background — matplotlib's default — and Hogland's Figure 1 measures 33.3%
     transparent pixels, so the lavender card showed THROUGH the artwork. David,
     2026-10-07: "i wanted it lavender, but it seems like the figures were being
     see-through." White is what the author saw when they drew it.

     In CSS rather than flattening the upload, deliberately: non-destructive,
     reversible, and the stored image stays exactly what the author sent. */
  width: auto;
  max-width: 100%;
  height: auto;
  margin: 0 auto;
  /* A white plate under the artwork — see the card rule above for why. */
  background: #ffffff;
}

.sjp-content .imgWithCitation .citation {
  margin-top: 10px;
}

/* ⚠️ The caption and its label share ONE line: on the live article
   .citation .title is display:inline and the caption text runs straight on from
   "Figure 1.". Ours broke to a new line because the caption arrives already
   wrapped in its own <p>, and a <p> cannot nest inside a <p> — the browser
   closes the first one, forcing the break. The renderer now unwraps it; these
   rules cover both the unwrapped case and any stray block the author left. */
/* ⚠️ 15px, THE BODY'S TEXT SIZE — a deliberate departure from the published
   12px, which was measured on Maschler-26010, cameron-26009 and Tucker-26003.
   David, 2026-10-07, resolving the contradiction in his own note ("same as the
   table text" vs "same as figure captions"): "I want them all at 15px." So
   every caption in the article — figure and table — now reads at the same size
   as the prose around it. */
.sjp-content .imgWithCitation .citation p {
  font-size: 15px;
  line-height: 23px;
  color: #2c2c2c;
  margin: 0;
  padding: 0;
}
.sjp-content .imgWithCitation .citation p > p {
  display: inline;
  margin: 0;
  padding: 0;
}

/* "Figure 1." — the only purple in a caption. */
.sjp-content .imgWithCitation .citation .title {
  font-weight: 700;
  color: #7448ff;
}

/* On a phone the fixed card width would overflow the gutter. */
@media (max-width: 520px) {
  .sjp-content .imgWithCitation {
    max-width: 100%;
    padding: 12px 0;
  }
}

/* ── Special issue note ──────────────────────────────────────────────────────
 * "* This article is part of a Special Issue from FOSS4GNA."
 * Measured from stacksjournal.org/article/Seeger-26012 at 1440px on 2026-10-06:
 * 12px/16.8px, weight 500, #2c2c2c; the link is bold purple and underlined.
 * Smaller than body text on purpose — it is a footnote to the byline, not part
 * of the article.
 */
.sjp-special-issue {
  font-size: 12px;
  line-height: 16.8px;
  font-weight: 500;
  color: #2c2c2c;
  margin: 10px 0 0;
  padding: 0;
}
.sjp-special-issue a {
  color: #7448ff;
  font-weight: 700;
  text-decoration: underline;
  text-decoration-color: #7448ff;
}

/* ORCIDs listed under the affiliations, as the published articles do.
   Same size as the affiliation text; they are reference data, not links. */
.sjp-author-orcids {
  margin-top: 10px;
  font-size: 14px;
  line-height: 1.55;
  color: var(--sjp-body);
  /* Long identifiers must not push the two-column meta row wider. */
  overflow-wrap: anywhere;
}

/* Table captions. The journal sets them italic with a bold "Table 1." label.
   ⚠️ The italic lives HERE and not in the content: it used to be an <em> wrapped
   around author-written HTML by publish-figures.ts, which produced invalid
   nesting and left the element open — tipping the rest of the article, and the
   sidebar after it, into italics. Style is presentation; it belongs in CSS. */
/* ⚠️ Scoped to figures that hold an IMAGE. The unscoped rule also hit table
   figures, whose caption sits ABOVE the table — see the table block below. */
.sjp-content figure:not([data-stacks-table]) figcaption {
  font-style: italic;
}
.sjp-content figcaption strong {
  font-style: normal;
  font-weight: 700;
}

/* ── Figure lightbox ─────────────────────────────────────────────────────────
 * The overlay opened by clicking a figure. One instance serves the whole page;
 * the template fills in the image and caption on click.
 *
 * ⚠️ Sits above the sticky bar, like the share dialog — see the z-index tokens.
 * Without that, clicking a figure while the sticky bar is showing opens the
 * enlarged image UNDERNEATH it.
 */
.sjp-img-overlay[hidden] { display: none !important; }
.sjp-img-overlay {
  position: fixed;
  inset: 0;
  z-index: var(--sjp-z-modal);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px 24px;
  background: rgba(17, 24, 39, 0.82);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}
.sjp-img-overlay-inner {
  margin: 0;
  max-width: 1000px;
  width: 100%;
  /* The caption must stay visible, so the IMAGE is what gives way on a short
     window — not the text explaining it. */
  max-height: 100%;
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-width: 0;
}
.sjp-img-overlay-img {
  display: block;
  max-width: 100%;
  max-height: 72vh;
  width: auto;
  margin: 0 auto;
  background: #ffffff;
  border-radius: 4px;
}
.sjp-img-overlay-caption {
  color: #ffffff;
  /* 15px/23px — the same size as the caption under the figure, the table
     captions and the body. David, 2026-10-07: "the font size is also still too
     small", about this overlay specifically. Every caption on the page is now
     one size; see the figcaption rules above. */
  font-size: 15px;
  line-height: 23px;
  /* ⚠️ LEFT, NOT CENTRED. A centred caption is fine for three words and hard
     to read for three lines, which is what a figure caption usually is. David,
     2026-10-07: "When a figure is clicked and expanded, can the caption be left
     justified?" */
  text-align: left;
  max-width: 760px;
  margin: 0 auto;
  overflow-y: auto;
}
.sjp-img-overlay-caption[hidden] { display: none; }
.sjp-img-overlay-close {
  position: absolute;
  top: 16px;
  right: 20px;
  width: 40px;
  height: 40px;
  font-size: 30px;
  line-height: 1;
  color: #ffffff;
  background: transparent;
  border: 0;
  cursor: pointer;
  border-radius: 6px;
}
.sjp-img-overlay-close:focus-visible { outline: 2px solid #ffffff; outline-offset: 2px; }

/* The inline figure is now a button in effect, so say so on hover. */
.sjp-content .imgWithCitation a.img-glass { cursor: zoom-in; }

@media (prefers-reduced-motion: no-preference) {
  .sjp-img-overlay { animation: sjp-fade-in 140ms ease both; }
}
@keyframes sjp-fade-in { from { opacity: 0; } to { opacity: 1; } }


/* ── Tables ───────────────────────────────────────────────────────────────────
 *
 * ⚠️ THE PLUGIN STYLESHEET HAD NO TABLE RULES AT ALL. The journal's table look
 * lives in apps/web/components/StacksTable.tsx (PUBLICATION_CSS) and drives the
 * app's own rendering and the PDF — it was never ported here, so a published
 * table arrived on the website completely unstyled: no caption bar, no frame, no
 * cell padding, no type scale.
 *
 * ⚠️ THE SELECTORS ARE NOT A COPY OF THAT FILE. PUBLICATION_CSS targets a
 * .stacks-table-pub wrapper with a .stacks-table-frame element, and the app
 * inlines a plain <figure data-stacks-table="…"> into the published body with
 * neither. Porting its selectors verbatim would have styled nothing. What is
 * carried across is its MEASUREMENTS, which that file documents as coming from
 * the journal's own typeset article (Tucker-26003-Article.docx, read from the
 * XML, where Word stores border weight in eighths of a point):
 *
 *     every rule      0.5pt   — ONE weight, no exceptions
 *     vertical rules  none
 *     cell padding    5pt on all four sides
 *     caption shading #f4f1ff, 5pt padding
 *
 * ⚠️ ONE WEIGHT EVERYWHERE IS THE POINT, and it is David's instruction: "I want
 * the line thickness to be consistent." Do not reintroduce a heavier frame or a
 * thicker rule under the header; three weights is what made the first attempt
 * read as not-quite-Tucker.
 *
 * ⚠️ THE DASHED ROW SEPARATORS ARE NOT OURS. They are the author's own cell
 * borders, preserved deliberately by the paste pipeline, and the rules below do
 * not remove them. If dashed separators should become house style the change
 * belongs in StacksTable.tsx too, or the website and the PDF will disagree.
 */
.sjp-content figure[data-stacks-table] {
  margin: 28px 0;
  padding: 0;
  overflow-x: auto;
}

/* The caption sits ABOVE a table, in its own shaded bar. */
/* ⚠️ THE SAME CAPTION TYPOGRAPHY AS A FIGURE, measured off the live site.
   A figure caption on this very page renders as
       <div class="citation"><p><span class="title">Figure 1.</span> text</p></div>
   at 12px/17.76 weight 500 UPRIGHT, label 12px weight 700 in the journal purple.
   Table captions were 14px ITALIC with the label in body colour, so figures and
   tables on one page were set differently.

   ⚠️ "UPRIGHT" CORRECTS A CLAIM THIS CODEBASE USED TO MAKE. A comment in
   lib/publish-figures.ts said "The caption is STILL italic on the published page
   — that is the journal's style." Measured on Maschler-26010, cameron-26009 and
   Tucker-26003: every published figure AND table caption is font-style: normal.
   The italic was ours, not the journal's.

   ⚠️ THE SHADED BAR AND THE ABOVE-THE-TABLE POSITION STAY. Those come from the
   journal's own typeset Word file (Tucker-26003-Article.docx) and from David's
   explicit choice to keep a real table's caption above it. Only the TYPE changes. */
.sjp-content figure[data-stacks-table] figcaption {
  background: #f4f1ff;
  padding: 5pt;
  margin: 0;
  /* ⚠️ 15px, THE TABLE'S OWN TEXT SIZE — not the 12px a figure caption uses.
     David: "Caption for tables should be the same font size as the table text."
     A table caption sits directly above rows of 15px text, so 12px read as a
     different voice from the thing it labels.
     ⚠️ FIGURE captions stay at 12px, the measured published value. His note also
     said "Same as figure captions", which contradicts the first half — he was
     asked which he meant rather than having one guessed at. */
  font-size: 15px;
  line-height: 23px;
  font-weight: 500;
  color: var(--sjp-body);
  /* ⚠️ ITALIC, LIKE EVERY OTHER CAPTION. This was the only one of the four
     caption renderings set upright: the web FIGURE caption is italic, and the
     PDF sets both its figure and table captions italic. David, 2026-10-07:
     "we should be consistent on whether the figure/table captions are
     italicized or not ... Can we try with always italicized?"
     The LABEL ("Table 1.") stays upright and bold — see the strong rule just
     below, which covers figure and table captions alike. */
  font-style: italic;
  border-top: 1pt solid #1A1A1A;
}
.sjp-content figure[data-stacks-table] figcaption strong {
  font-style: normal;
  font-weight: 700;
  color: var(--sjp-purple);
}

.sjp-content figure[data-stacks-table] table {
  border-collapse: collapse;
  /* ⚠️ !important, AND IT IS THE ONLY THING THAT WORKS HERE.
     applyHouseTableRules() strips width from every CELL but DELIBERATELY KEEPS
     IT ON THE TABLE (lib/table-html.ts, the `isTable ||` in the width filter),
     so a table pasted out of Word arrives with its own `style="width: 644px"`
     — and an inline style beats any ordinary selector. Measured on Hogland
     26014's Table 2: 644px inside a 1008px figure, so the caption band and the
     top rule ran 364px wider than the table under them. David, 2026-10-07:
     "the actual table doesn't go as wide as the header. Can we make them go
     across?"
     max-width too, or an inline max-width would cap the 100% straight back.
     ⚠️ Fixed IN CSS rather than in the sanitizer on purpose: this way it also
     corrects articles that are ALREADY PUBLISHED, without re-pushing them.
     Same judgement as the border-style reset — the author decides what the
     table looks like inside, the journal decides its outer geometry. */
  width: 100% !important;
  max-width: none !important;
  /* 15px/23px — the manuscript's own body size. David, 2026-10-07: "Can we make
     the tables have the same font size as the text in the manuscript?" It was
     14px, which read as a different, smaller voice from the prose around it. */
  font-size: 15px;
  line-height: 23px;
  color: var(--sjp-body);
  /* ⚠️ auto, NOT fixed. With a fixed layout every column takes an equal share,
     so a narrow heading like "# Predictors" wrapped onto two lines while a wide
     column sat half empty. `auto` lets the browser size each column to its
     content and is why the wrapping goes away. David: "is it possible to make it
     so they autosize so they limit the number of extra lines?" */
  table-layout: auto;
}

/* ⚠️ Every rule is drawn by a CELL, never by the figure or the caption. Inside
   border-collapse two adjacent cell borders MERGE to the heavier one, while a
   border drawn by a wrapping element STACKS with the cell's. A pasted table
   usually brings its own outer borders, so a wrapper-drawn frame landed on top
   of theirs and printed at double weight — which is exactly the complaint that
   produced the single-weight rule. */
.sjp-content figure[data-stacks-table] th,
.sjp-content figure[data-stacks-table] td {
  /* ⚠️ !important, BECAUSE THE AUTHOR'S CELL PADDING ARRIVES INLINE AND WINS
     OTHERWISE. The PDF has forced this since 2026-10-01 (padding: 5pt
     !important in components/StacksTable.tsx) and the website did not, so the
     same table was set with the journal's 5pt in one rendering and whatever
     Word left in the other — measured at 9.6px a side on Hogland's Table 2,
     which is ~23px of column width given away across four columns.
     Same class of split as the group rules and the alignment: a rule that
     existed in one stylesheet and not the other. */
  padding: 5pt !important;
  text-align: left;
  vertical-align: top;
  border: 0;
}
/* ⚠️ The rule ABOVE the header is heavier than the one below it. David,
   2026-10-07: "the top line should also be bolded." A table's top rule is its
   frame; the one under the header separates heading from body. 1pt over 0.5pt
   is the lightest pair that reads as different without reintroducing the
   three-weight look that was rejected earlier. */
/* ⚠️ THE CHILDREN TOO, OR THE LABELS ARE NOT ACTUALLY BOLD. Measured: a header
   cell computed to 700 while the <p> inside it computed to 500, because
   `.sjp-content p { font-weight: 500 }` beats inheritance — the third thing in
   this file that the prose rule reached into a table cell and changed. David
   asked for bold column labels on 2026-10-02 and they have been rendering at
   500 ever since. */
.sjp-content figure[data-stacks-table] thead th,
.sjp-content figure[data-stacks-table] thead td {
  font-weight: 700;
  /* ⚠️ ALL FOUR HEAVY RULES ARE 1pt AND MUST STAY EQUAL. David, 2026-10-07:
     "I want the extra thick lines to be the same in all 4 places -- top,
     bottom, and top and bottom of column headers (which should also be
     bolded)". The four are: the table's top (the figcaption's border-top),
     these two around the header row, and the foot (tbody's border-bottom).
     Change one and you change all four, or the table goes back to looking
     like three different weights. The only 0.5pt rules left inside a table
     are the author's own group rules, redrawn by applyHouseTableRules. */
  border-top: 1pt solid #1A1A1A;
  border-bottom: 1pt solid #1A1A1A;
}
/* ⚠️ THE WEIGHT REACHES THE CHILDREN; THE BORDERS MUST NOT.
   Word wraps each label in its own <p>, which takes the prose rule's weight
   500 and beats `thead th { font-weight: 700 }` — so the children need the
   weight. But when `thead th *` was simply bolted onto the selector list
   ABOVE, those children inherited its border-top and border-bottom too, and
   every header cell grew a second pair of rules inset by the cell's padding:
   a 126px line inside a 139px cell, with a visible gap at each column edge.
   David, 2026-10-07: "they added some weird lines". Keep these two rules
   separate — the borders belong to the cell, the weight to everything in it. */
.sjp-content figure[data-stacks-table] thead th *,
.sjp-content figure[data-stacks-table] thead td * {
  font-weight: 700 !important;
}
/* ⚠️ THE FOOT RULE IS DRAWN BY THE TABLE BOX, NOT BY THE LAST ROW'S CELLS, AND
   A ROWSPAN IS THE WHOLE REASON.

   It used to be `tbody tr:last-child th, td`. When the first column groups rows
   with rowspan — Hogland 26014's Table 2 is "Classification" over two rows then
   "Regression" over two — the grouping cell BELONGS TO THE EARLIER ROW. So the
   last row has one fewer cell, column 1 is never selected, and the rule starts
   partway across. David, 2026-10-07: "the table 2 line at the bottom isn't going
   all the way over."

   Measured on a 4-column repro with that exact rowspan shape: table 744px wide,
   old rule 455px starting 289px in; new rule 744px, gap 0.

   ⚠️ tbody, NOT THE TABLE ELEMENT. A row group's border participates in
   collapsing and merges with a cell's at the same weight — unlike a border on a
   wrapping div, which stacks (that is what the note above the cell rules
   warns about). The table element is avoided because the matching rule in
   components/StacksTable.tsx must survive two later resets there —
   `table { border: none }` and `table { border-style: none !important }` — and
   a table-box border renders as NOTHING under them. Verified by screenshot.
   Both stylesheets target tbody so they cannot drift. */
/* ⚠️ A MANUAL LINE BREAK IN A COLUMN HEADING IS SUPPRESSED HERE TOO, NOT ONLY
   IN THE PUBLISH PATH.

   applyHouseTableRules already replaces <br> inside a <th> with a space, but
   that only takes effect when an article is PUSHED — the published HTML is
   generated then and not re-rendered afterwards. Hogland 26014's "# Predictors"
   stayed on two lines across three rounds of this, because each fix shipped
   after the last push. This rule fixes every article ALREADY published, the
   moment the plugin updates, with nothing to re-push. Same reasoning as the
   table's width override directly below.

   ⚠️ IT RELIES ON THE WHITESPACE AROUND THE BREAK. Word writes
   `#<br>\n  Predictors`, so hiding the <br> leaves the newline, which collapses
   to one space and reads "# Predictors". A break with NO whitespace either side
   would join two words. That is why the publish path still inserts a real
   space: new pushes get the clean fix, this is the retrofit.

   ⚠️ HEADER CELLS ONLY. A <br> in a body cell can be real content. */
/* ⚠️ EVERY CELL LEFT-ALIGNED, INCLUDING ITS CHILDREN, AND !important IS WHAT
   MAKES IT STICK.

   The cell rule above already says text-align: left, and it was not enough:
   Word wraps each cell's contents in its own <p style="text-align:center">, so
   the alignment lives on a CHILD of the cell and an inline style beats any
   selector. Hogland 26014's Table 2 came out with Model and Texture left and
   the five numeric columns centred. David, 2026-10-07: "i thought we said
   everything would be left justified? Some of these are center. Can you make
   sure that's the rule for all tables?"

   Same judgement as the typeface, the rule weights and the table width: the
   author decides what is in the table, the journal decides how it is set.
   ⚠️ Keep in step with the matching rule in components/StacksTable.tsx. */
.sjp-content figure[data-stacks-table] th,
.sjp-content figure[data-stacks-table] td,
.sjp-content figure[data-stacks-table] th *,
.sjp-content figure[data-stacks-table] td * {
  text-align: left !important;
  /* ⚠️ THE SIZE IS SET ON THE CELLS AND THEIR CHILDREN, NOT ON THE TABLE, AND
     THAT IS THE WHOLE FIX. `.sjp-content p { font-size: 15px }` — the PROSE
     rule — matches the <p> that Word puts inside every cell, so it beat the
     table's own font-size by inheritance and every cell rendered at the body's
     15px no matter what the table said. Setting 15px on the table was
     therefore a no-op all along; changing it changed nothing, which is exactly
     what measuring four sizes showed.

     ⚠️ 12px MATCHES THE PDF, and that is deliberate. Both renderings set the
     same table in almost the same measure — Letter minus 0.75in margins is
     672px, the web column is 688px — but the PDF's cells were 9pt (12px) and
     the web's 15px, so the same table wrapped far more on the page. David:
     "This table is looking really funky ... It looks better in the pdf." It
     was not the layout; it was 25% larger type in the same width.

     ⚠️ THIS REVERSES "tables the same font size as the manuscript text"
     (2026-10-07, earlier the same day). Setting tables a step below body is
     the usual typographic practice and it is what the PDF already did; the two
     renderings now agree. If David prefers the larger type back, this is the
     line, and the cost is the wrapping he just flagged. */
  /* ⚠️ BACK TO 15px, THE MANUSCRIPT'S OWN SIZE. 12px matched the PDF and David
     saw it live: "shrunk the text size of the header rows and the rows in the
     tables." His original instruction stands — "the same font size as the text
     in the manuscript" — and the cost is the wrapping he flagged separately.
     The rule still has to live HERE, on the cells and their children, because
     the table's own font-size is a no-op: `.sjp-content p` reaches inside
     every cell and wins by inheritance. */
  font-size: 15px !important;
  line-height: 23px !important;
}

/* ⚠️ BODY CELLS AT WEIGHT 400, BECAUSE THE PROSE RULE WAS MAKING THEM 500 AND
   500 IS WIDER.

   Exactly the same trap as the font-size above, and found the same way: by
   asking the cascade which rule actually reaches the cell. `.sjp-content p,
   ul, ol { font-weight: 500 }` is the manuscript's body weight, and the <p>
   Word puts inside every table cell is a descendant of .sjp-content, so table
   text was being set a half-step heavier than the PDF's 400. Measured on one
   string, same 12px: 280px wide on the web against the PDF's 400-weight
   rendering.

   ⚠️ td ONLY, NOT th. Column labels are deliberately bold (2026-10-02), and an
   !important here on th would flatten them. */
.sjp-content figure[data-stacks-table] td,
.sjp-content figure[data-stacks-table] td * {
  font-weight: 400 !important;
}

.sjp-content figure[data-stacks-table] th br { display: none; }

.sjp-content figure[data-stacks-table] tbody {
  /* The fourth of the four heavy rules — see the thead note above. */
  border-bottom: 1pt solid #1A1A1A;
}

/* A table note, when the author gave one, sits under the rule. */
.sjp-content figure[data-stacks-table] > p {
  font-size: 12px;
  line-height: 1.5;
  color: var(--sjp-muted);
  margin: 8px 0 0;
  padding: 0;
}


/* ═════════════════════════════════════════════════════════════════
 * ON THIS PAGE — the sidebar table of contents
 *
 * Built by the script in single-stacks_article.php from the rendered article;
 * this file only dresses it. It is the LAST sidebar block, so it comes into
 * view under the copyright and then sticks — David, 2026-10-07: "a table of
 * contents that scrolls on the right after they pass the (c)".
 *
 * ⚠️ DELIBERATELY THE SAME CARD AS THE EDITORIAL POLICIES CONTENTS BOX, at
 * David's request ("i want the sticky TOC to look the sticky one we made for
 * the editorial policies"). The radius, border, shadow, 13px links and the
 * 2px left rule that fills in on the current entry are copied from
 * stacks-policies.css. ⚠️ If that card is restyled, restyle this one with it —
 * they are two files and nothing enforces the match.
 * ═════════════════════════════════════════════════════════════════ */
.sjp-toc-rail {
    /* ⚠️ --sjp-toc-top IS SET BY THE SCRIPT FROM THE BAR'S OWN BOTTOM EDGE, and
       the 130px here is only the value used before the bar has ever appeared.
       A constant cannot work: the bar's height has changed four times, and for
       a logged-in viewer the WordPress admin bar pushes it down another 32px —
       which is how this card ended up "a little buried" under it. */
    position: sticky;
    top: var(--sjp-toc-top, 130px);
    max-height: calc(100vh - var(--sjp-toc-top, 130px) - 24px);
    overflow-y: auto;
    background: #fff;
    border: 1px solid #d9d4e6;
    border-radius: 12px;
    box-shadow: 0 4px 18px rgba(45, 33, 99, 0.10);
    /* ⚠️ NO TOP PADDING ON THE CARD — it belongs to .sjp-toc-rail-head. The
       card scrolls and the head sticks inside it; with the padding here,
       entries scroll up through the gap ABOVE the stuck head. Same arrangement
       and the same reason as the Editorial Policies card. */
    padding: 0 12px 16px;
    /* ⚠️ Cancels .sjp-sidebar-block's rule and 22px of padding. It is a card,
       so the divider above it would read as a second, competing edge. */
    border-top: 1px solid #d9d4e6;
    margin-top: 22px;
}
.sjp-toc-rail[hidden] { display: none; }
/* The search bar, sticky at the top of the card as the list scrolls under it.
   David, 2026-10-07: "can we add the search bar too like in the editorial
   policies? ... Let's have it look just like the editorial policies one." The
   "Contents" label went with it — the box says what the card is. */
.sjp-toc-rail-head {
    position: sticky;
    top: 0;
    z-index: 2;
    background: #fff;
    padding-top: 14px;
    padding-bottom: 10px;
}
.sjp-toc-rail-search {
    padding: 0 2px 12px;
    border-bottom: 1px solid #ece8f5;
    /* The clear button is positioned against this box. */
    position: relative;
}
/* ⚠️ THE BROWSER'S OWN CLEAR CROSS IS HIDDEN, or Chrome shows a second one
   next to ours on an input[type=search]. */
.sjp-toc-rail-input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
.sjp-toc-rail-clear {
    position: absolute;
    top: 0;
    right: 2px;
    height: 34px;          /* the input's own height, so the cross is centred */
    width: 30px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: 0;
    padding: 0;
    font-size: 16px;
    line-height: 1;
    color: var(--sjp-muted);
    cursor: pointer;
}
.sjp-toc-rail-clear:hover { color: var(--sjp-purple); }
.sjp-toc-rail-clear[hidden] { display: none; }
/* The magnifying glass is an inline SVG data-URI: no extra file to ship and no
   broken image if one goes missing. Stroke is the muted grey, URL-encoded. */
.sjp-toc-rail-input {
    width: 100%;
    font: inherit;
    font-size: 13px;
    padding: 8px 10px 8px 32px;
    background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%235b5b66' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='10.5' cy='10.5' r='6.5'/%3E%3Cpath d='M20 20l-4.8-4.8'/%3E%3C/svg%3E") no-repeat 10px center;
    border: 1px solid #d9d4e6;
    border-radius: 8px;
    color: #2C2C2C;
    box-sizing: border-box;
}
.sjp-toc-rail-input:focus {
    outline: 2px solid var(--sjp-purple);
    outline-offset: 0;
    border-color: transparent;
}
/* While a search is running the contents list gives way to the results. */
.sjp-toc-rail-results[hidden] { display: none; }
.sjp-toc-searching .sjp-toc-rail-list { display: none; }
.sjp-toc-rail-results ul { list-style: none; margin: 0; padding: 0; }
.sjp-toc-rail-result {
    display: block;
    padding: 8px 10px;
    border-left: 2px solid transparent;
    border-radius: 4px;
    text-decoration: none;
}
.sjp-toc-rail-result:hover { background: #F4F1FF; border-left-color: var(--sjp-purple); }
.sjp-toc-rail-result-title { font-size: 13px; font-weight: 600; color: #2D2163; line-height: 1.35; }
.sjp-toc-rail-snippet { font-size: 12px; color: #2C2C2C; line-height: 1.45; margin-top: 2px; }
.sjp-toc-rail-snippet mark { background: #FFF3D6; color: inherit; border-radius: 2px; padding: 0 1px; }
.sjp-toc-rail-empty { font-size: 13px; color: var(--sjp-muted); padding: 8px 10px; }
/* The instance tally, immediately under the search divider. Grey and small:
   it is context for the list, not an entry in it. */
.sjp-toc-rail-tally {
    font-size: 11px;
    color: var(--sjp-muted);
    padding: 8px 10px 2px;
}
/* The section a run of snippets belongs to. A heading inside the result list,
   not a link — the snippets under it are the links. */
.sjp-toc-rail-result-section {
    font-size: 13px;
    font-weight: 600;
    color: #2D2163;
    padding: 10px 10px 2px;
}
/* "×4" beside a section holding more than one hit. */
.sjp-toc-rail-result-count { font-weight: 400; color: var(--sjp-muted); }
/* Snippets sit under their section heading, indented to show the grouping. */
.sjp-toc-rail-results .sjp-toc-rail-result { padding-left: 18px; }
.sjp-toc-rail-list { list-style: none; margin: 0; padding: 0; }
.sjp-toc-rail-list li { margin: 0; padding: 0; }
.sjp-toc-rail-list a {
    display: block;
    padding: 5px 10px;
    border-left: 2px solid transparent;
    font-size: 13px;
    line-height: 1.4;
    color: #2C2C2C;
    text-decoration: none;
}
.sjp-toc-rail-list a:hover { color: var(--sjp-purple); }

/* Subsections (H3) nest under their section and, as on the Editorial Policies
   card, only the ones you are currently inside are shown — the script adds
   .sjp-open to that group. A card listing every subsection of a long article
   is a wall of text you have to read to use. */
.sjp-toc-rail-sub { list-style: none; margin: 0; padding: 0; display: none; }
.sjp-toc-rail-sub.sjp-open { display: block; }
.sjp-toc-rail-sub-item a {
    padding-left: 22px;
    font-size: 12px;
    color: var(--sjp-muted);
}

/* "Figures and Tables" — a heading inside the list, not a link. The figures
   are listed together at the end rather than in amongst the sections, but each
   entry still points at where the figure sits in the manuscript. */
/* ⚠️ TYPED EXACTLY LIKE A SECTION ENTRY — same size, colour, weight and
   padding as "Discussion" above it. David, 2026-10-07: "the Figure and table
   header should look the same as the other main ones like discussion etc." It
   was 11px uppercase muted, which read as a different tier. It is still not a
   link, because there is nowhere for it to go; only the entries under it are.
   ⚠️ If .sjp-toc-rail-list a changes size or colour, change this with it — the
   whole point is that the two match. No border-top: "no need for the line
   seperation." */
/* ⚠️ `.sjp-toc-rail-list li.` — the plain class loses to `.sjp-toc-rail-list li`
   above, which zeroes padding, and the entry rendered with none. Caught by
   comparing the computed padding against a section entry's. */
/* ⚠️ NO PADDING ON THE li — the button inside provides it. Both carried
   `padding: 5px 10px`, so the label sat 10px further in than "Discussion",
   and the caret pushed it another 12. David: "Figures and tables are offset
   and that's not correct. They should be at the same margin as Discussion."
   The caret is now positioned on the RIGHT, so the text starts exactly where
   every other entry's does. */
/* ⚠️ ONE DECLARATION, AND THAT IS THE WHOLE POINT. This rule used to say
   `padding: 0` and then, four lines later, `padding: 5px 10px` again — the
   second won, the <li> kept its padding on top of the button's, and the fix
   shipped in plugin 0.6.29 for "Figures and tables are offset and that's not
   correct" did nothing. David saw it again on 0.6.30 and on 0.6.30 reported
   both halves of it: the label 12px further in than "Discussion", and "too
   much space" above and below it from the doubled 5px.
   Measured in scripts/check-article-toc.js, which fails on the old rule:
   offset 12px, row 10px taller. Do not add padding, a border or a margin
   here — the <button> inside carries all three, and anything added here is
   added to the button's. */
.sjp-toc-rail-list li.sjp-toc-rail-group {
    padding: 0;
}
.sjp-toc-rail-item a {
    padding-left: 22px;
    font-size: 12px;
    color: var(--sjp-muted);
}

/* The "Figures and Tables" disclosure. The button is typed exactly like a
   section entry — same size, colour and padding — so the card still reads as
   one list; only the caret marks it as something that opens. */
.sjp-toc-rail-toggle {
    display: block;
    width: 100%;
    text-align: left;
    background: none;
    border: 0;
    border-left: 2px solid transparent;
    /* The same 5px/10px every entry's <a> uses, so the labels line up. */
    padding: 5px 10px;
    position: relative;
    font: inherit;
    font-size: 13px;
    line-height: 1.4;
    color: #2C2C2C;
    cursor: pointer;
}
.sjp-toc-rail-toggle:hover { color: var(--sjp-purple); }
/* On the right, out of the text's way — a caret before the label would indent
   it past every other entry, which is the thing being fixed. */
.sjp-toc-rail-caret {
    position: absolute;
    right: 10px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 10px;
    color: var(--sjp-muted);
}
/* ⚠️ CLOSED IS THE DEFAULT, and it is the markup's default too — the script
   sets aria-expanded="false" when it builds the button, so the list is shut
   before any CSS loads rather than flashing open and collapsing. */
.sjp-toc-rail-media { display: none; }
.sjp-toc-rail-media.sjp-open { display: block; }
.sjp-toc-rail-media ul { list-style: none; margin: 0; padding: 0; }
.sjp-toc-rail-list a.sjp-toc-current {
    border-left-color: var(--sjp-purple);
    background: #F4F1FF;
    color: #513CA3;
    font-weight: 600;
}

/* ⚠️ Anchor targets must clear the sticky bar, or clicking an entry puts the
   heading UNDERNEATH it. Same measured variable as the card's own offset, so
   the two can never disagree. */
.sjp-abstract h2[id],
.sjp-content h2[id],
.sjp-content h3[id],
.sjp-content figure[id],
.sjp-statement h2[id],
.sjp-references h2[id] { scroll-margin-top: var(--sjp-toc-top, 130px); }

/* ── The gold flash on a contents jump ──────────────────────────────────────
   Applied by the TOC's click handler to whatever it scrolled to, and removed
   after 1.6s. It marks WHERE YOU LANDED, which a jump down a long article
   otherwise leaves you guessing at.

   ⚠️ The outline is drawn with box-shadow, not a border or outline property:
   a border would change the element's box and shove the text sideways by a
   pixel as it comes and goes, which is exactly the twitch this is meant to
   avoid. box-shadow paints outside the box and costs no layout.

   ⚠️ -4px/8px of spread so a heading's own line box is not hugged too
   tightly, and the figure case (a whole block) still reads as one wash. */
/* ⚠️ THE SUBJECT-PILL GOLD, #FFDE99 — the same --sjp-tag-bg as the pills
   above the title, and a deliberate change from the abstract panel's pale
   cream (--sjp-cream, #fff8e5) this used to use. David asked for the cream
   first, saw it in place, and reversed: "maybe it should be the same gold as
   the stacks at the top of the page so it stands out more". The cream is so
   close to white that a one-sentence wash on white barely registered.
   ⚠️ WRITTEN AS A LITERAL, NOT var(--sjp-tag-bg). The policies page carries
   an identical copy of this animation and has no such token, and the two
   must not drift. If --sjp-tag-bg changes, change both literals.
   ⚠️ 3.2s, AND THE SCRIPTS MUST AGREE. Both stacks-policies.js and the
   article template hold a FLASH_MS that removes the class — shorter than the
   animation and the wash is cut off mid-fade. David: "can it linger a little
   longer?"
   ⚠️ The outline is a box-shadow, not a border or outline: a border would
   change the box and shove the text sideways by a pixel as it comes and goes.
   3px of spread, not the 6px this had as a block-level wash — the highlight
   is now an inline run of text inside a 25px line box, and 6px bled into the
   lines above and below it. */
@keyframes sjp-flash-gold {
    0%   { background-color: #FFDE99; box-shadow: 0 0 0 3px #FFDE99; }
    80%  { background-color: #FFDE99; box-shadow: 0 0 0 3px #FFDE99; }
    100% { background-color: transparent; box-shadow: 0 0 0 3px rgba(255, 222, 153, 0); }
}
.sjp-flash {
    animation: sjp-flash-gold 3.2s ease-out 1;
    border-radius: 2px;
    /* ⚠️ A HIGHLIGHTED SENTENCE USUALLY WRAPS. Without `clone` the
       background and shadow are drawn once around the whole inline box, which
       leaves the first line's right-hand end and the last line's left-hand
       end unpainted; with it, every line fragment gets its own. */
    -webkit-box-decoration-break: clone;
    box-decoration-break: clone;
}
/* ⚠️ A flashing block is precisely what this setting exists to prevent. The
   element still gets the class; it simply does not animate. */
@media (prefers-reduced-motion: reduce) {
    .sjp-flash { animation: none; }
}

/* Phones: the sidebar is already stacked under the article, and a sticky card
   there would cover the text it indexes. Show it in place, unstuck. */
@media (max-width: 900px) {
    .sjp-toc-rail {
        position: static;
        max-height: none;
        overflow-y: visible;
    }
}

/* The byline asterisk's footnote, under the affiliations list. Set like an
   affiliation line rather than like body copy — it belongs to that block, and
   the review page (app/reviews/[id]/ArticleHeader.tsx) prints its own the same
   way. See the byline markers in single-stacks_article.php for why the mark
   exists and the collision it has with the special-issue note. */
.sjp-article .sjp-corresponding-note {
    margin: 10px 0 0 0;
    padding: 0;
    font-size: 15px;
    line-height: 23px;
    color: var(--sjp-body);
}
/* ── The corresponding author's envelope, in the byline and in this note ──

   ⚠️ A 14x10 ICON STANDING ON THE BASELINE, NOT A SUPERSCRIPT CHARACTER. It
   used to be the ✉ character inside the byline's .sjp-author-markers at 11px,
   with this note's <sup> pinned to match. David, 2026-10-15: "you made the
   little mail thing too small. Can you make it larger please. It doesn't have
   to be superscript. On both TPR and Article page." It is now an inline SVG
   (sjp_tpl_corresponding_mark() in the template explains why not the
   character). 10px tall sits between the byline's x-height and cap height,
   so it reads as a letter-sized mark; the old glyph was 5.9px of ink.

   ⚠️ THE SAME SIZE IN BOTH PLACES. Earlier the same day he asked for "the
   size of the mail icon the same in the bylien as in the affilations" — the
   mark is a pair, and a reader matches one to the other.

   ⚠️ MIRRORED in apps/web/app/globals.css under the same class name, for the
   transparent peer review page. Change both or the two stop matching. */
.sjp-article .sjp-corresponding-mark {
    display: inline-block;
    width: 14px;
    height: 10px;
    vertical-align: baseline;
}
/* 3px off whatever precedes it in the byline — the affiliation number, or
   the name when there is none. The note starts with the mark, so no gap
   there. */
.sjp-article .sjp-authors .sjp-corresponding-mark {
    margin-left: 3px;
}
