/*
 * stacks-article-listing.css — the one generated article list, served at
 * /articles/ and /article/. See ../../article-listing.php for why the listing
 * exists at all, and ../article-listing.php for the markup it styles.
 *
 * Every rule is scoped under `.sjp-listing`, like stacks-article.css, so
 * nothing here can reach the rest of the Divi site.
 *
 * ⚠️ EVERY COLOUR COMES FROM TheStacks-BrandGuide2022.pdf. NOTHING ELSE.
 *
 * The first version of this file had the brand purple right and everything
 * around it wrong: #6b7280, #e5e7eb and #111827 are Tailwind's default greys,
 * and #d6d3e8 was in no ramp at all. Tailwind's greys are blue-cast, so a
 * correct purple sat next to greys that pulled blue and the whole page read as
 * the wrong purple. The purple was never the problem.
 *
 *   PRIMARY   purple #7448FF   green #136F63   yellow #FFC64D
 *             dark   #2C2C2C   light #F3F1F4
 *   PURPLE    #0E0A28 #2D2163 #513CA3 #7448FF #A28FEC #CCC2F3 #F4F1FF
 *   GREY      #1A1A19 #626162 #868586 #AAA9AB #CECDCF #F3F1F4
 *   YELLOW    #2E1900 #FFA800 #FFB727 #FFDE99 #FFF1D1 #FFF8E5
 *
 * Contrast measured against white, not eyeballed — the ratio is why each one
 * was picked rather than its neighbour on the ramp:
 *
 *   --sjp-purple      #7448FF  5.11:1  links, the article type, the active chip
 *   --sjp-title-dark  #1A1A19 17.42:1  titles (grey ramp, darkest)
 *   --sjp-body        #2C2C2C 13.97:1  body text (the brand's own dark)
 *   --sjp-muted       #626162  6.17:1  dates, counts, authors. ⚠️ NOT #868586,
 *                                      which is 3.68:1 and fails for text this
 *                                      size — it is a large-text grey only.
 *   --sjp-tag-bg      #FFDE99          the gold topic pills
 *   --sjp-tag-ink     #2E1900 12.87:1  on the gold (yellow ramp, darkest)
 *   --sjp-rule        #E5E7EB          hairlines between cards. ⚠️ TAILWIND, not
 *                                      the ramp — it matches the article
 *                                      pages' own .sjp-divider. See the token.
 *   --sjp-field       #868586  3.68:1  the search box and chip BORDERS. ⚠️ A
 *                                      ramp step darker than the hairline on
 *                                      purpose: these are the edges of real
 *                                      controls, and WCAG 1.4.11 wants 3:1 for
 *                                      those. #CECDCF is 1.58:1 and would make
 *                                      the search box a ghost.
 *   --sjp-eyebrow     #513CA3  8.32:1  the card's type-and-date line (purple
 *                                      ramp, step 3). ⚠️ A SECOND purple on
 *                                      purpose — see the eyebrow rule.
 *
 * ⚠️ NO THUMBNAILS. The approved wireframe showed a figure per card; the
 * template's header records why they are not here and how to put them back.
 */

.sjp-listing {
    --sjp-purple:     #7448FF;
    --sjp-title-dark: #1A1A19;
    --sjp-body:       #2C2C2C;
    --sjp-muted:      #626162;
    --sjp-tag-bg:     #FFDE99;
    --sjp-tag-ink:    #2E1900;
    /* ⚠️ #E5E7EB IS A TAILWIND GREY AND IT IS HERE ON PURPOSE. Everything
       else in this file was moved OFF Tailwind's greys and onto the brand
       ramp earlier — this one line goes back, deliberately, because it has a
       different job: it has to match the hairline the ARTICLE pages draw.
       `.sjp-divider` in stacks-article.css is 1px solid #E5E7EB and is the
       second-commonest border on an article page (14 of them, measured on
       /article/davis-25012/). The brand ramp's lightest usable grey, #CECDCF,
       is 1.58:1 against white where #E5E7EB is 1.24:1 — visibly darker, which
       is exactly what David noticed.
       ⛔ Do not "restore" this to the ramp. Consistency with the rest of the
       site wins for a hairline; the ramp wins everywhere else in this file. */
    --sjp-rule:       #E5E7EB;
    --sjp-field:      #868586;
    /* The eyebrow's purple — purple ramp step 3, 8.32:1 on white (AAA).
       Deliberately NOT --sjp-purple: see the eyebrow rule. */
    --sjp-eyebrow:    #513CA3;
    /* The hover/focus colour every control on an article page converges on —
       purple ramp step 2. See the chip hover rule. */
    --sjp-purple-dark: #2D2163;

    /* ── The subject chips ──────────────────────────────────────────
       SETTLED, AND SETTLED THE SECOND TIME BY COMPARISON. ⛔ READ THE NEXT
       PARAGRAPH BEFORE CHANGING THESE VALUES TO AN OUTLINE.

       ⚠️ THE OUTLINE PATTERN WAS BUILT, SHIPPED TO A PREVIEW, LOOKED AT, AND
       REJECTED — 2026-10-29. The chips were made to match .sjp-btn-outline on
       the article pages exactly (transparent fill, #7448FF ink, 2px edge,
       weight 700); that is commit b0538f9e, and this block is its revert.
       David saw both renderings and said: "I think i like this view better
       than the all white. Sorry. Can we go back please?" — meaning the tinted
       fill below. So the tint is not an older state nobody revisited; it is
       the one that won a side-by-side.

       ⛔ DO NOT RE-APPLY THE OUTLINE. A request for it is in the session log
       twice, from the lead, written BEFORE David had seen it rendered. Those
       messages are honest and are now out of date. If someone asks for it
       again, point them here first and ask David — do not just do it, because
       doing it is what this revert undid.

       The tinted values themselves he asked for himself, in the worker
       session, 2026-10-29 at 21:11: "Selected shoudl be 7448FF. The
       unselected ones should be F4F1FF please". ⛔ This is not an open
       question and it is not a brief that went unapplied — both of which it
       has already been mistaken for.

       Three schemes exist in the commit log, so the next reader will find the
       other two and wonder:

         1. #F4F1FF fill / #513CA3 ink / #CCC2F3 edge          proposed
         2. #CCC2F3 fill / near-black ink / #513CA3 selected   built, 21:07
         3. #F4F1FF fill / near-black ink / #7448FF selected   ← David, 21:11

       ⚠️ HE DID NOT SEE (2) RENDERED BEFORE CHOOSING (3), and that is worth
       recording rather than smoothing over: (2) was committed at 21:07 and the
       preview was not rebuilt between 20:53 and 21:14, so it never reached a
       screen he was looking at. He asked for (3) on its own terms, not by
       comparison. If anyone wants him to compare them side by side, that is a
       real option nobody has taken — not a decision to re-open on his behalf.

       ⚠️ (3) IS A STEP BACK IN CONTRAST: white on #7448FF is 5.11:1, AA, where
       (2)'s white on #513CA3 was 8.32:1, AAA. What it buys is the brand's
       primary purple — the colour this page already uses for every action — as
       the selected state. It passes AA at 14px.

       ⛔ DO NOT SILENTLY "RESTORE" (2), and do not restore the outline
       either — see the top of this block. Ask David. An earlier version of THIS
       COMMENT still described (2) as pending while the tokens below were
       already (3); that is worse than no comment at all, because it reads as
       an unresolved question when it is settled, and it caused exactly that
       misreading. If these values change again, change this block in the same
       edit, and name who decided and when.

       The three ramp steps stay named for their job, so another try is one
       line each. */
    --sjp-chip-bg:       #F4F1FF;  /* purple ramp, lightest */
    --sjp-chip-ink:      #1A1A19;  /* 15.65:1 on the fill — AAA */
    --sjp-chip-edge:     #7448FF;  /* 5.11:1 on white. Matches the selected
                                      fill, which is #7448FF — true while (3)
                                      is shipped, false the moment it is not. */
    --sjp-chip-on-bg:    #7448FF;  /* the SELECTED chip */
    --sjp-chip-on-ink:   #FFFFFF;  /* 5.11:1 on it — AA. ⚠️ see the rule */

    max-width: 860px;
    margin: 0 auto;
    /* ⚠️ THE TOP PADDING CLEARS DIVI'S FIXED HEADER, AND THAT IS THE WHOLE
       REASON FOR THE NUMBER. Measured on stacksjournal.org 2026-10-30:
       div.et_pb_section_1_tb_header is `position: fixed`, top 0, HEIGHT 72px —
       at 1280px AND at 291px, so it does not shrink on narrow screens. The
       listing starts at the document top, so without this the first 72px of the
       page is behind the header: the h1 rendered at top 28 and its top 44px
       were covered, which read as the page having no title at all.
       ⚠️ 48px was correct in the Playground harness and wrong on the real site.
       Playground has no Divi, so NOTHING in either harness can catch this —
       it took David looking at the live page. The number is the header's 72px
       PLUS the gap the design wants, kept separate in the comment so a future
       reader can change one without losing the other.
       ⛔ If Divi's header height ever changes, this must change with it. */
    padding: 120px 24px 72px;   /* 72px header + 48px gap */
    font-family: 'Open Sans', Helvetica, Arial, sans-serif;
    color: var(--sjp-body);
    /* ⚠️ Divi sets line-height:0.8em on every heading site-wide (its customizer
       CSS). That is unreadable for the multi-line article titles below, so the
       headings here set their own and must keep doing so. */
    line-height: 1.6;
}

/* ⚠️ LOAD-BEARING, AND IT IS THE FILTER. The script hides a card by putting
   `hidden` on it, which works through the browser's own `[hidden]{display:none}`
   — a rule that ANY `display` declaration on the element beats, including ones
   Divi sets on list items site-wide. Without this line the search box appears to
   do nothing. !important because Divi's own rules are the thing being beaten. */
.sjp-listing [hidden] {
    display: none !important;
}

/*
 * ── TYPE SCALE ───────────────────────────────────────────────────────
 *
 * Matched to the live /articles/ page, measured with getComputedStyle at
 * 1280x900 — not read off a screenshot. All Open Sans.
 *
 *   ROLE                  LIVE            HERE          why they differ
 *   page heading          40/45  700      40px/1.125    —
 *   article title         25/25  700      25px/1.3      ⚠️ line-height
 *   byline                18/20  500      18px/1.45     ⚠️ line-height
 *   action link           18/30.6 700     18px/1.7      —
 *   topic pill            18/38  500      18px/1.3      pill padding does
 *                                                        the vertical work
 *   body / standfirst     18/30.6 500     18px/1.7      —
 *   subject section head  20/16  700      NOT USED      ⚠️ see below
 *
 * ⚠️ THREE PLACES THIS DELIBERATELY DOES NOT COPY THE LIVE PAGE:
 *
 * 1. LINE-HEIGHTS SMALLER THAN THE FONT SIZE. The live article title is
 *    25px type on a 25px line, and the byline 18px on 20px. That is Divi's
 *    site-wide `line-height: 0.8em` on headings leaking through, not a
 *    decision — it clips descenders and it is why this file already had to
 *    override Divi for the titles. Sizes matched; line-heights sane. A
 *    16-author byline (zaragoza-26013) wraps to three lines, and at 1.11
 *    those lines collide.
 *
 * 2. THE 20px SUBJECT HEADING IS NOT THIS PAGE'S EYEBROW. On the live page
 *    "GEOSPATIAL TECHNOLOGY" is a SECTION heading that groups the articles
 *    beneath it. This listing replaced that grouping with the filter chips,
 *    so it has no such element. The card's eyebrow — "RESEARCH ARTICLE ·
 *    29 OCTOBER 2026" — is a label the live page does not have at all, and
 *    setting it to 20px/700 would make it louder than the 18px byline and
 *    nearly the size of the 25px title. It is 14px: proportionate to the
 *    title it sits above, subordinate to everything else on the card.
 *
 * 3. THE CONTROLS KEEP THEIR OWN SIZE. The search box, the chips, the
 *    count and the sort toggle are this page's furniture and have no live
 *    counterpart. They stay at 14-16px so the ARTICLES are the loudest
 *    thing on the page, which is the whole point of the page.
 */

/* ── Heading ─────────────────────────────────────────────────────────── */

.sjp-listing__head {
    margin-bottom: 24px;
}

.sjp-listing__title {
    margin: 0 0 10px;
    /* 40px/700, the live page's own heading. ⚠️ 700 not 800: it was heavier
       here than on the live page, which is part of what made the two look
       like different pages. */
    font-size: 40px;
    font-weight: 700;
    line-height: 1.125;
    color: var(--sjp-title-dark);
}

.sjp-listing__standfirst {
    margin: 0;
    max-width: 44em;
    font-size: 18px;
    line-height: 1.7;
    color: var(--sjp-muted);
}

/* The one link in the hero. Underlined at rest for the same reason the card
   links are: on a page this grey, colour alone is not enough to mark it. */
.sjp-listing__standfirst-link {
    color: var(--sjp-purple);
    font-weight: 600;
    /* ⚠️ LONGHAND, NOT THE SHORTHAND. `text-decoration: underline 1px` shipped in 0.6.82
       and computed to NONE on the live site: a thickness inside the shorthand is not
       universally parsed, and an INVALID SHORTHAND IS DISCARDED WHOLE — which left
       Divi's `a { text-decoration: none }` standing. A longhand cannot be dropped as a
       unit, so the underline survives whatever Divi and Safari do.
       ⛔ Verify by COMPUTED STYLE on the live page, never by reading this file: the rule
       being here is exactly what misled us. */
    /* ⚠️ !important, AND IT IS NOT NEGOTIABLE HERE. The site carries a global
       `a { text-decoration: none !important }`. !important BEATS SPECIFICITY, so no
       selector we write can win without matching it — .sjp-listing__link is (0,1,0)
       against a bare (0,0,1) and still loses.
       ⚠️ 0.6.84 tried longhands instead, on my reading that the shorthand was being
       discarded. That diagnosis was WRONG: the longhands shipped, were served, and
       still computed to none, because the !important rule was the cause all along.
       ⛔ Verify by COMPUTED STYLE on the live page. Twice now the rule has been
       present in this file and absent from the rendered page. */
    text-decoration-line: underline !important;
    text-underline-offset: 2px;
}

.sjp-listing__standfirst-link:hover,
.sjp-listing__standfirst-link:focus-visible {
    text-decoration-thickness: 2px !important;
}

.sjp-listing__standfirst-link:focus-visible {
    outline: 2px solid var(--sjp-purple);
    outline-offset: 2px;
}

/* ── Controls: the one search box, then the chips ────────────────────── */

.sjp-listing__controls {
    margin-bottom: 26px;
}

/* The label is for screen readers and for anyone who has zoomed past the
   placeholder; the placeholder says the same thing to everyone else. Not
   display:none, which some screen readers skip. */
.sjp-listing__search-label {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* The field is the box; the input inside it is transparent and borderless, so
   the magnifying glass can sit inside the border without the input's own
   padding fighting it. */
.sjp-listing__search-field {
    display: flex;
    align-items: center;
    gap: 8px;
    /* ⚠️ SMALLER, as David asked — 420px rather than 680px wide and 9px of
       vertical padding rather than 13px. The width is the bigger part of
       "smaller": at 680px it was the widest thing on the page and read as the
       primary control, which the chips now are. */
    max-width: 420px;
    padding: 9px 14px;
    background: #fff;
    border: 1px solid var(--sjp-field);
    border-radius: 4px;
}

/* The focus ring moves to the FIELD, since that is the visible box now. */
.sjp-listing__search-field:focus-within {
    outline: 2px solid var(--sjp-purple);
    outline-offset: 1px;
    border-color: var(--sjp-purple);
}

.sjp-listing__search-icon {
    flex: none;
    color: var(--sjp-muted);
}

.sjp-listing__search {
    flex: 1 1 auto;
    min-width: 0;
    padding: 0;
    font-family: inherit;
    color: var(--sjp-title-dark);
    background: none;
    border: 0;
    /* ⚠️ 16px, AND NOT SMALLER EVEN THOUGH "a little smaller" WAS THE REQUEST.
       iOS Safari zooms the whole page when a focused input's text is under
       16px, and the reader then has to pinch back out to see the results they
       just filtered. The box got smaller instead — narrower and shorter — which
       is what reads as "smaller" anyway. */
    font-size: 16px;
}

.sjp-listing__search:focus {
    outline: none;
}

/* The chips come FIRST now, so they carry no top margin and the search below
   them does. */
.sjp-listing__chips {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 16px;
}

.sjp-listing__chip {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    /* 44px tall counting the border, which is the minimum comfortable touch
       target; the chips are the main control on a phone. */
    min-height: 44px;
    padding: 8px 15px;
    font-family: inherit;
    font-size: 14px;
    line-height: 1.2;
    color: var(--sjp-chip-ink);
    background: var(--sjp-chip-bg);
    /* ⚠️ #7448FF, the same purple the SELECTED chip is filled with, so the row
       reads as one family — and the border matters MORE now than it did. At
       #F4F1FF the fill is 1.1:1 against the white page: nearly invisible on
       its own, so the edge is the only thing giving an unselected chip a
       shape. It is 5.11:1, comfortably past the 3:1 WCAG 1.4.11 asks for the
       boundary of a control. ⛔ Do not drop it to match the fill. */
    /* ⚠️ 2px, NOT the 1px this had before 2026-10-30. The tinted fill is
       David's (see the token block); the 2px edge is the lead's call, kept
       from the outline version when the fill was reverted. ⚠️ HE HAS NOT SEEN
       tint + 2px TOGETHER — his screenshot was tint + 1px — so if he says the
       edge looks heavy, this single line is the reason and 1px is the fix. */
    border: 2px solid var(--sjp-chip-edge);
    /* ⚠️ 4px, THE "Join Us" BUTTON'S RADIUS, measured on the live site. 999px
       made these lozenges; the site's own call-to-action is a soft rectangle,
       and the chips should belong to the same family.

       ⛔ ONLY THE RADIUS IS TAKEN FROM THAT BUTTON. Its padding is 5px 30px,
       which is right for one call to action and wrong for seven chips that
       each carry a count — 30px a side would not fit a phone row. The padding
       below is unchanged and still gives the 44px touch target. */
    border-radius: 4px;
    cursor: pointer;
}

/* ⚠️ BOTH CHIP STATES CONVERGE ON THE SAME DARK PURPLE, which is what makes
   this feel like the rest of the site rather than like a listing with its own
   ideas. Measured on /article/Hogland-26014/: .sjp-btn-primary and
   .sjp-btn-outline look completely different at rest — one filled, one
   outlined — and BOTH go to #2D2163 with white text on hover and on
   :focus-visible. The outlined one fills in; the filled one darkens. The chips
   mirror that structure already, so they now mirror the behaviour.

   White on #2D2163 is a measured 13.91:1 — AAA — and the count badge inherits
   the chip's colour, so it turns white with the label rather than staying a
   grey that would vanish on the dark fill.

   0.18s, matching the article buttons' 0.2s closely enough that the two never
   appear side by side out of step. */
.sjp-listing__chip,
.sjp-listing__chip.is-active {
    transition: background-color 0.18s, color 0.18s, border-color 0.18s;
}

/* ⚠️ HOVER IS BEHIND A POINTER QUERY AND THAT IS NOT PEDANTRY. The chips are
   the main control on a phone, and a touch browser applies :hover on tap and
   LEAVES IT APPLIED until something else is tapped — so without this, tapping
   "Ecology" would leave a chip sitting dark and looking selected when the
   selected one is a different chip entirely. A pointer query costs nothing on
   a mouse and removes the whole class of problem on a screen. */
@media (hover: hover) {
    .sjp-listing__chip:hover,
    .sjp-listing__chip.is-active:hover {
        background: var(--sjp-purple-dark);
        border-color: var(--sjp-purple-dark);
        color: #FFFFFF;
        text-decoration: none;
    }
}

/* ⚠️ FOCUS IS OUTSIDE THAT QUERY — a keyboard reader has no pointer, so
   hiding this behind `hover: hover` would leave them with the outline alone
   where a mouse user gets the full state change. The outline STAYS on top of
   the fill: it is drawn outside the border, so the two do not fight, and it is
   the non-colour half of the signal. */
/* ⚠️ `.is-active` IS LISTED EXPLICITLY AND THAT IS NOT REDUNDANT.
   `.sjp-listing__chip:focus-visible` and `.sjp-listing__chip.is-active` have
   the SAME specificity (0,2,0), and `.is-active` appears later in this file —
   so without the second selector here the selected chip kept its #7448FF fill
   on focus while every other chip went dark. Caught by tabbing onto it; the
   hover rule above already names both for the same reason. */
.sjp-listing__chip:focus-visible,
.sjp-listing__chip.is-active:focus-visible {
    background: var(--sjp-purple-dark);
    border-color: var(--sjp-purple-dark);
    color: #FFFFFF;
    text-decoration: none;
    outline: 2px solid var(--sjp-purple);
    outline-offset: 2px;
}

/* ⚠️ The selected chip differs in LIGHTNESS as well as hue — filled purple
   against white outlines — so it is still obviously the selected one to a
   reader who cannot separate the two colours. A coloured border alone would
   not be. */
/* The selected chip, filled with the brand's primary purple.

   ⚠️ WHITE ON #7448FF IS 5.11:1 — AA, NOT AAA, and that is a real trade David
   made knowingly when he picked these two. The previous pairing (#513CA3 with
   white) was 8.32:1 and AAA; this is the brand's primary purple and the colour
   the rest of the page already uses for every action, which is the consistency
   he wanted. It passes AA at this size. ⛔ Do not "improve" it back to #513CA3
   — that was tried and he chose this.

   The state distinction is the strongest it has been: the two fills are
   4.59:1 apart with luminances 0.894 against 0.156, and the text inverts with
   them. */
.sjp-listing__chip.is-active {
    color: var(--sjp-chip-on-ink);
    background: var(--sjp-chip-on-bg);
    border-color: var(--sjp-chip-on-bg);
    font-weight: 600;
}

.sjp-listing__chip-icon {
    flex: none;
    /* currentColor in the SVG, so the icon follows the chip's state with no
       second rule to keep in step. */
}

/* ⚠️ THE COUNT IS EXACTLY THE LABEL'S COLOUR — no dimming. It inherits, and
   there is deliberately no `opacity` here: it was 0.75, and David asked for
   the number to match the stack name, white when the chip is selected and
   near-black when it is not. Dimming it also cost contrast on the selected
   chip, where white at 0.75 on #7448FF is about 3.9:1 and fails for 14px. */
.sjp-listing__chip-count {
    color: inherit;
    font-variant-numeric: tabular-nums;
}

/* The selected chip's count needs no rule of its own: .sjp-listing__chip-count
   already inherits its chip's colour at 0.75 opacity, which on the dark fill
   is white dimmed — the same treatment as the unselected chip, one rule fewer,
   and no second place for the two states to drift apart. */

/* ── What is being shown ─────────────────────────────────────────────── */

.sjp-listing__status {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 4px 16px;
    padding-bottom: 10px;
}

.sjp-listing__count,
.sjp-listing__sort {
    margin: 0;
    font-size: 14px;
    /* ⚠️ AN EXPLICIT WEIGHT, BECAUSE <strong> IS RELATIVE. Its default is
       `font-weight: bolder`, which resolves against the PARENT, not to a fixed
       700 — so "34 Articles" and "Sorted newest first" came out at different
       weights from identical markup. The count line inherited 300 from the
       theme, where bolder lands on 400; the sort control sits inside a
       <button>, which the UA sets to 400, where bolder lands on 700. The two
       lines sit side by side and looked like different kinds of text.

       ⚠️ It is also why this cannot be left to the theme: Divi on the live site
       sets a different body weight again, so `bolder` would resolve to a third
       value there. Both ends are pinned below. */
    font-weight: 400;
    color: var(--sjp-muted);
}

.sjp-listing__count strong,
.sjp-listing__sort strong,
.sjp-listing__sort-button strong {
    font-weight: 700;
}

/* ⚠️ THE WHOLE COUNT IS BOLD — "34 Articles", not just the 34. Set on the
   SPAN rather than on its parts, which is what makes it survive filtering:
   the script empties this element and rebuilds a <strong> plus a text node
   inside it, so a rule targeting the text would have to be reapplied in
   JavaScript. A rule on the container cannot be forgotten there.

   Everything after it on the line — "· Open Access · CC BY 4.0" — sits
   OUTSIDE this span and stays at 400, which is the point: the count is the
   fact, the rest is the footnote. */
.sjp-listing__count [data-sjp-count] {
    font-weight: 700;
    /* ⚠️ THE COLOUR BELONGS ON THE SPAN FOR THE SAME REASON THE WEIGHT DOES.
       Only the <strong> was being darkened, so "34" came out #1A1A19 and
       "Articles" beside it #626162 — one phrase in two colours, which is what
       David saw the moment the word went bold and stopped being background.
       Both now sit on the container, so the number and the noun cannot drift
       apart again, and the script cannot lose it when it rebuilds the span. */
    color: var(--sjp-title-dark);
}

/* The sort toggle. Styled as a quiet control rather than a button-looking
   button: it sits on the status line opposite the count, and a filled button
   there would outweigh the chips, which are the primary control. The border
   appears on hover and focus, which is what distinguishes it from the sentence
   it replaces — it has to LOOK clickable, since looking clickable without
   being clickable is the bug this fixes. */
.sjp-listing__sort-button {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    /* 44px of touch target without 44px of visible chrome: the padding does the
       work and the border only shows on interaction. */
    min-height: 44px;
    padding: 6px 10px;
    margin-right: -10px;
    font-family: inherit;
    font-size: 14px;
    color: var(--sjp-muted);
    background: none;
    border: 1px solid transparent;
    border-radius: 6px;
    cursor: pointer;
}

.sjp-listing__sort-button strong {
    color: var(--sjp-title-dark);
}

.sjp-listing__sort-button:hover {
    border-color: var(--sjp-field);
    color: var(--sjp-body);
}

.sjp-listing__sort-button:focus-visible {
    outline: 2px solid var(--sjp-purple);
    outline-offset: 1px;
}

.sjp-listing__sort-icon {
    flex: none;
    color: var(--sjp-purple);
}

.sjp-listing__count strong,
.sjp-listing__sort strong {
    color: var(--sjp-title-dark);
}

/* The licence link. ⚠️ NO UNDERLINE AT REST — David asked for it off, and at
   rest this is now visually plain text in the muted grey of the line it sits
   in.

   ⚠️ AND NO UNDERLINE ON HOVER EITHER. A pointer gets the colour darkening and
   the cursor; that is the entire affordance. A reader who never hovers has no
   way to know "CC BY 4.0" is clickable at all. That is not an oversight — it is
   what David asked for twice, for a quiet metadata line — and ⛔ the answer is
   NOT to give it the cards' purple, which is the loudness he was avoiding.
   :focus-visible keeps a real outline, so keyboard access is untouched. */
.sjp-listing__licence {
    color: inherit;
    text-decoration: none;
}

/* ⛔ THERE IS DELIBERATELY NO :hover RULE FOR THE LICENCE LINK. David asked for
   the underline off at rest, then off on hover, and now for the darkening off
   too — three steps to the same place, so this is settled rather than drifting.
   To a pointer, "CC BY 4.0" is now indistinguishable from the grey text around
   it until you click it; the CURSOR is the only cue left.

   ⚠️ That is a real cost and it is his call, not an oversight. The link still
   works, still says rel="license", and :focus-visible still draws a full
   outline — so a keyboard reader is unaffected by any of this. ⛔ If it is ever
   revisited, the answer is still not the cards' purple: that is the loudness he
   was avoiding from the start. */

.sjp-listing__licence:focus-visible {
    outline: 2px solid var(--sjp-purple);
    outline-offset: 2px;
    border-radius: 2px;
}

/* ── The list ────────────────────────────────────────────────────────── */

.sjp-listing__list {
    /* The ordered list carries the browse order semantically, but its numbers
       would read as volume or issue numbers, which these are not. */
    list-style: none;
    margin: 0;
    padding: 0;
}

/* The grey rules between cards: on the top of each, so the row of rules starts
   under the status line and there is no doubled line at the end of the list. */
/* ⚠️ 28px EACH SIDE OF THE RULE, matching the article pages. `.sjp-divider`
   there carries margin 28px top and bottom with padding 0 (measured on
   /article/davis-25012/); here the same 28px is PADDING on the card, because
   the rule is the card's own border rather than a separate element — margin
   would collapse between neighbours and give 28px total instead of 56px
   between two cards. Same gap on screen, different property, for that reason. */
.sjp-listing__item {
    /* ⚠️ 37px, a third more than the 28px that matches the article pages'
       .sjp-divider. David asked for more air between the rule and the text;
       the article pages' own value is still what 28 came from, so the two
       surfaces now differ here ON PURPOSE. */
    padding: 37px 0;
    border-top: 1px solid var(--sjp-rule);
}

/* ⚠️ GRID, AND ONLY WHEN THERE IS A PICTURE. A card with a graphical abstract
   is two columns; a card without one is a single column of text at the full
   width, NOT a column with an empty hole where the image would be. 5 of the 34
   published articles have no graphical abstract, so this is a real state, not a
   fallback nobody hits — see the template for why no placeholder is drawn.

   The thumbnail column is fixed at 150px and the text takes the rest. minmax(0,
   1fr) rather than 1fr: a grid track is auto-sized to its content by default,
   so a long unbroken title (a DOI, a species name with no spaces) would push
   the column wider than the card instead of wrapping. */
.sjp-listing__item--with-thumb {
    display: grid;
    /* ⚠️ ONE THIRD IMAGE, TWO THIRDS TEXT, as asked — and as FRACTIONS, not a
       pixel width. The old 150px column was about a sixth of the card and
       looked undersized once the type went up to the live page's scale.
       1fr/2fr keeps the ratio at every width the reading column takes, so it
       does not need revisiting if the max-width ever changes.

       minmax(0, …) on BOTH tracks: a grid track is auto-sized to its content
       by default, so a long unbroken title or a wide image would push its
       track past its share and quietly break the ratio. */
    grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
    /* 32px, a third more than 24px — David asked for more space between the
       photo and the text. ⚠️ The gap is INSIDE the 1/3 : 2/3 split, so widening
       it narrows both tracks equally and the ratio holds. */
    gap: 32px;
    align-items: start;
}

.sjp-listing__thumb {
    display: block;
    overflow: hidden;
    border-radius: 6px;
    /* The rule's grey, so an image that is slow or missing leaves a quiet
       rectangle rather than a bright one. */
    background: var(--sjp-rule);
}

.sjp-listing__thumb-img {
    display: block;
    width: 100%;
    /* ⚠️ A FIXED 4:3 BOX WITH object-fit: cover. The graphical abstracts are
       every shape there is — wide infographics, tall portraits, near-squares —
       and letting each set its own height makes the rules between cards march
       up and down the page. Cropping to one ratio keeps the list a list. The
       whole image is always on the article's own page. */
    aspect-ratio: 4 / 3;
    object-fit: cover;
}

/* No hover treatment on the thumbnail: it is aria-hidden and not in the tab
   order (see the template), so it is not somewhere a reader is ever "on". */

/* ⚠️ THE RULE UNDER THE LAST CARD, AND IT NEEDS BOTH SELECTORS.
   Every card draws its own border-TOP, so the line between any two cards and
   the line above the first are free. Only the bottom of the list needs
   closing, and that is where filtering breaks it:

   `:last-child` is the last card in the DOM, NOT the last one on screen. A
   filtered-out card still IS a child — it is only `hidden` — so when the
   oldest article does not match the chip, the bottom rule stays attached to a
   display:none element and the list ends with no line under it. David found
   this on 2026-10-30 filtered to Medicine & Immunology.

   ⚠️ IT LOOKED INTERMITTENT, WHICH IS WHY IT SURVIVED THIS LONG. Reproduced
   and measured on the preview:
       unfiltered              last visible IS the DOM last   1px  ✓
       Medicine & Immunology   last visible is NOT            0px  ✗
       Ecology                 last visible IS (it has the
                               oldest article)                1px  ✓
   So it fails only when the last match is not the oldest article, which is
   why "sometimes".

   `.is-last-visible` is set by the script, which is the only thing that knows
   what is on screen — CSS has no "last sibling that is not display:none".
   ⛔ KEEP `:last-child` TOO. It is the no-JavaScript path: nothing is hidden
   there, so the DOM last IS the last on screen, and a reader without the
   script still gets a closed list. When both match the same card they set the
   same property to the same value, so there is nothing to collide.
   ⚠️ And when NOTHING matches, no card carries the class and no card is
   visible, so the list closes with no stray line above the "no matches"
   message — which a border on the list CONTAINER would have drawn. */
.sjp-listing__item:last-child,
.sjp-listing__item.is-last-visible {
    border-bottom: 1px solid var(--sjp-rule);
}

/* ── The eyebrow: type · date ────────────────────────────────────────── */

.sjp-listing__eyebrow {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 2px 8px;
    margin: 0 0 5px;
    /* 14px, not the live page's 20px section heading — see note 2 above. */
    font-size: 14px;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    /* ⚠️ TWO PURPLES ON A CARD, ON PURPOSE, and they are a reading order:
         #513CA3  this line — the quieter label, what the article IS
         #7448FF  Full text / PDF / Peer review, and the title hover — what
                  a reader can DO
       Keep them distinct. Collapsing them to one purple would make the
       label and the actions compete, which is what this fixed. 8.32:1 on
       white, so it is the stronger colour carrying the quieter job — which
       is why it can be small and still read. */
    color: var(--sjp-eyebrow);
}

/* The Big Stack. ⚠️ It takes the TYPE's treatment, not the date's — weight 700,
   inherited from the eyebrow — because it is the same kind of thing: a label
   saying what the article IS. The date is the one value on this line, and it
   stays weight 400 so the line reads "label label value" rather than three
   equal words. No separate colour: one eyebrow, one purple. */
.sjp-listing__stack {
    font-weight: 700;
}

/* The date and the separator INHERIT the eyebrow's purple, so "RESEARCH
   ARTICLE · 29 OCTOBER 2026" is one line in one colour. They were each a
   separate grey before; splitting a six-word line across two colours made
   the date look like a different kind of thing from the label beside it. */
.sjp-listing__date,
.sjp-listing__dot {
    font-weight: 400;
    letter-spacing: 0.02em;
}

/* ⚠️ A DATE WITH NO LABEL BESIDE IT GOES BACK TO GREY. An article with no
   article type renders the date alone, and a lone purple date is a label
   labelling nothing — it reads as emphasis for no reason. :only-child is
   exactly that case: the template emits the separator only when BOTH the
   type and the date exist, so the date is an only child precisely when it
   stands alone. No PHP change, no extra class. */
.sjp-listing__eyebrow .sjp-listing__date:only-child {
    color: var(--sjp-muted);
    font-weight: 400;
}

/* ── The gold topic pills ────────────────────────────────────────────── */

/* Deliberately not links. See sjp_listing_topics(): these are each article's
   own topics as published, not Stacks, and nothing filters by them yet. */
/* The gap below is the gap before the LINKS. ⚠️ Slightly larger than the two
   above it because the tags are a row of filled blocks rather than a line of
   text — their boxes already touch the gap, where text leaves half-leading
   inside its own line box. If the tags move back above the title, this wants
   to be ~9px again. */
.sjp-listing__topics {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin: 0 0 9px;
}

.sjp-listing__topic {
    padding: 4px 12px;
    border-radius: 4px;
    background: var(--sjp-tag-bg);
    /* ⚠️ 16px, AND THE BRIEF ASKED FOR 18px. Read this before "correcting" it.
       David asked for the topic tags "slightly smaller — maybe the same size
       font as the authors?". They were ALREADY 18px/500, byte for byte what
       the author line is; matching the authors would have changed nothing and
       he would have seen no difference.

       What makes them look bigger than the authors is not the type, it is the
       gold fill and the padding around each one — five filled pills carry more
       weight than a line of grey text at the same size. So the instruction
       that does something is "slightly smaller", and this is one step down.
       The live page's pills are 18px; this is the one place the scale
       deliberately departs from it. */
    font-size: 16px;
    font-weight: 500;
    line-height: 1.3;
    /* ⚠️ --sjp-tag-ink (#2E1900), the yellow ramp's own darkest step, at
       12.87:1 on the gold. The body colour would pass too, but the ramp pairs
       its own ends and the article pages use the same gold. */
    color: var(--sjp-tag-ink);
}

/* ── Title, authors, links ───────────────────────────────────────────── */

/* ⚠️ THE GAPS INSIDE A CARD GROW WITH THE TYPE, they are not one value
   repeated. David said the eyebrow-to-title gap was right and the three below
   it were tight — and MEASURED, the ones he disliked were already as large or
   larger in box terms (eyebrow->title 10px, title->authors 8px, authors->tags
   10px, tags->links 12px). Matching the number would have changed almost
   nothing.

   The reason it reads tight is the type size: 10px under a 14px uppercase
   eyebrow is generous, and the same 10px under a 25px bold title is cramped.
   So each gap is now set against the block ABOVE it. If the type scale moves
   again, these want revisiting with it. */
.sjp-listing__item-title {
    margin: 0 0 7px;
    font-size: 25px;
    font-weight: 700;
    /* ⚠️ 1.3, NOT the live page's 25px-on-25px. See note 1 above. */
    line-height: 1.3;
}

.sjp-listing__item-title a {
    color: var(--sjp-title-dark);
    text-decoration: none;
}

/* ⛔ NOTHING HAPPENS ON HOVER. No colour, no underline — David asked for both
   to go, in that order, over two rounds. The title is a link because the whole
   card is about that link and the three actions beneath it say so in purple;
   it does not need to flinch when a pointer crosses it. A browse list of 34
   titles that each light up under the cursor is noisy, and he was right twice.

   ⚠️ There is deliberately no `:hover` rule here at all. Adding one back is
   the change to question, not the absence. */

/* ⚠️ THE KEYBOARD STATE IS NOT OPTIONAL, AND IT IS NOW THE ONLY STATE THIS
   LINK HAS. With hover doing nothing, a reader tabbing the list would have
   nothing whatever to go on without this. An OUTLINE — a shape drawn outside
   the text, not a colour change and not an underline, so it is unmistakably
   the keyboard indicator and not the hover styling David removed. */
.sjp-listing__item-title a:focus-visible {
    outline: 2px solid var(--sjp-purple);
    outline-offset: 3px;
    border-radius: 2px;
}

.sjp-listing__authors {
    margin: 0 0 7px;
    font-size: 18px;
    font-weight: 500;
    /* ⚠️ 1.45, NOT the live page's 20px-on-18px — zaragoza-26013 has 16
       authors and wraps to three lines. */
    line-height: 1.45;
    color: var(--sjp-muted);
}

.sjp-listing__links {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 22px;
    margin: 0;
    font-size: 18px;
    line-height: 1.7;
}

/* ⚠️ UNDERLINED AT REST, NOT ONLY ON HOVER. These three are the only things on
   a card a reader can act on, and on a page where the title is deliberately not
   underlined, colour alone was carrying the whole signal — which is exactly the
   case where someone who cannot separate purple from near-black sees no link at
   all. The underline is the non-colour cue.

   text-underline-offset keeps the rule off the descenders in "Peer" and "PDF"
   so the words stay readable with the underline always present. */
.sjp-listing__link {
    color: var(--sjp-purple);
    font-weight: 700;
    text-decoration-line: underline !important;
    text-underline-offset: 3px;
    text-decoration-thickness: 1px !important;
}

.sjp-listing__link:hover,
.sjp-listing__link:focus-visible {
    /* Already underlined, so hover thickens rather than adds — the state still
       changes, visibly, without the text reflowing. */
    text-decoration-thickness: 2px !important;
}

.sjp-listing__link:focus-visible {
    outline: 2px solid var(--sjp-purple);
    outline-offset: 2px;
}

/* ── States ──────────────────────────────────────────────────────────── */

.sjp-listing__empty,
.sjp-listing__truncated,
.sjp-listing__no-match {
    margin: 32px 0 0;
    font-size: 15px;
    color: var(--sjp-muted);
}

/* A button, because it does something; styled as a link, because next to a
   sentence that is what it reads as. */
.sjp-listing__clear {
    padding: 0;
    font-family: inherit;
    font-size: inherit;
    font-weight: 600;
    color: var(--sjp-purple);
    background: none;
    border: 0;
    text-decoration-line: underline !important;
    cursor: pointer;
}

/* ── Phone ───────────────────────────────────────────────────────────── */

@media (max-width: 600px) {
    .sjp-listing {
        /* Same 72px header as desktop — measured at 291px wide, it does
           not shrink. 72 + the 28px gap this breakpoint wants. */
        padding: 100px 16px 56px;
    }

    /* ⚠️ A STEP DOWN FROM THE DESKTOP SCALE — A JUDGEMENT, AND A SMALL ONE.
       MEASURED at 375x812 with all 34 real articles, both ways:

           full live scale   median card 648px   0.80 screens   list 21,735px
           this step         median card 551px   0.68 screens   list 18,261px
                             18% shorter cards, 19% shorter list

       So the un-stepped scale is NOT unusable — roughly one card per screen
       rather than one and a bit. The step buys the reader the top of the next
       card, which is what lets them compare two articles without scrolling,
       on the page whose job is browsing. It keeps the live page's
       proportions: title loudest, byline and links one step below.

       ⛔ Do not "fix" this by assuming the big scale was unreadable on a
       phone. It was not. Reverting this block is a defensible choice and
       costs 19% more scrolling, nothing else. */
    .sjp-listing__title {
        font-size: 30px;
    }

    .sjp-listing__standfirst {
        font-size: 16px;
    }

    .sjp-listing__item-title {
        font-size: 21px;
    }

    .sjp-listing__authors,
    .sjp-listing__links {
        font-size: 16px;
    }

    .sjp-listing__topic {
        font-size: 14px;
    }

    .sjp-listing__eyebrow {
        font-size: 13px;
    }

    /* ⚠️ ONE COLUMN ON A PHONE, image on top. A 150px thumbnail beside text in
       375px leaves about 190px for a title, which breaks three- and four-word
       titles onto six lines. Measured at 375px: the image band is 180px tall,
       which is enough to see what it is without pushing the title off the
       first screen. */
    .sjp-listing__item--with-thumb {
        grid-template-columns: minmax(0, 1fr);
        gap: 12px;
    }

    /* ⚠️ NO THIRDS ON A PHONE. A third of 375px is a 110px image beside a
       230px column — too small to see what it is, and too narrow for a
       25px-scale title beside it, which would wrap every other word. The
       image goes full width above the text instead, which is what the single
       column below already does; this block only has to keep the ratio rule
       from applying. Stated rather than left implicit, because "1/3 image"
       was the instruction and this is where it stops being true. */
    .sjp-listing__thumb-img {
        aspect-ratio: 16 / 9;
        max-height: 180px;
    }

    /* The status line stacks rather than squeezing "31 articles · open access ·
       CC BY 4.0" and "Sorted newest first" onto one 375px row. */
    .sjp-listing__status {
        flex-direction: column;
        align-items: flex-start;
    }

    /* ⚠️ THE CHIPS WRAP ON A PHONE, and this block is where they STOPPED
       scrolling sideways. David chose wrapping on 2026-10-30, having seen the
       scroller on a phone: four of the seven chips were off-screen with
       nothing on screen saying so, so a reader saw three and had no reason to
       believe there were more. He traded compactness for discoverability
       knowing the cost, which is measured below.

       ⛔ NOTHING OVERRIDES THE BASE RULE HERE ANY MORE, and that is deliberate.
       .sjp-listing__chips already says flex-wrap: wrap; the sideways scroller
       was THIS block overriding it, so deleting the override IS the change.
       Do not re-add `flex-wrap: wrap` here "to be explicit" — a duplicate that
       merely agrees with its base is what goes stale when the base changes.

       ⚠️ THE PREVIOUS COMMENT'S NUMBER WAS WRONG, which is why these are
       measured rather than repeated. It claimed seven wrapped chips were
       "~500px tall". They are 252px. Measured at 375x812 on the real listing,
       the 44px touch target intact in every row:

         sideways scroller     48px,  1 row,   first card at 575px
         wrapping (SHIPPED)   252px,  5 rows,  first card at 763px
         wrapping, icon hidden 200px, 4 rows,  first card at 711px

       The fold is 812px, so the first card's top is on screen either way.

       ⚠️ THE ICON IS THE ONLY LEVER THAT MOVES THE ROW COUNT. Both of the
       obvious alternatives were measured and changed NOTHING — still 5 rows,
       still 252px: padding 15px -> 10px, and label 14px -> 13px. So the
       padding here is left alone ON PURPOSE; shrinking it costs legibility and
       buys no rows. Hiding the 18px icon is the whole difference between 5
       rows and 4, and it is one rule:
           .sjp-listing__chip-icon { display: none; }
       ⛔ Not applied, and not an oversight. David picked these icons one at a
       time (he rejected a syringe and asked for the heart-and-pulse), they are
       what the desktop row shows, and 52px does not change whether the first
       card is on screen. That is his trade to make, not ours to take quietly.

       ⚠️⚠️ WRAPPING DOES NOT SURVIVE THE STACK LIST, and this is the line to
       re-read before adding subjects. Measured by cloning the row to 22 chips,
       the number the Stack list is heading for:

         22 chips, icon shown   824px, 16 rows, first card at 1335px
         22 chips, icon hidden  668px, 13 rows, first card at 1179px

       Both are unusable: the chips become the page. This block is right for
       seven and MUST be redesigned before 22 ship — a capped number of rows
       with a way to reach the rest, or a different control on a phone
       altogether. ⛔ It is not a tuning problem. No padding, font size or icon
       change fixes 13 rows; the three levers above are worth 52px between
       them. */

    /* A chip must not squeeze to fit: a shrunken "Geospatial Technology" with
       its count wrapped is worse than one that takes its own row. ⚠️ Now that
       the chips wrap, this also protects the touch target — a flex item
       allowed to shrink gets narrower AND shorter, and 44px is deliberate. */
    .sjp-listing__chip {
        flex: none;
    }

    /* ⚠️ THE CARD'S THREE LINKS ARE THE SMALLEST TARGETS ON THE PAGE, and they
       are the ones a reader on a phone actually taps. Measured at 375px before
       this rule: 27px tall, against the 44px the chips were deliberately given.
       The chips got the attention because they look like controls; "Full Text",
       "PDF" and "Peer Review" are plain links and were missed.

       ⚠️ WHY IT MATTERS MORE THAN THE NUMBER SUGGESTS: the three sit side by
       side, so a near-miss does not do nothing — it hits the neighbour, and a
       reader aiming for PDF opens the peer review instead. A wrong destination
       is worse than a dead tap, which is why this is a correctness fix and not
       a polish item.

       Height only. ⛔ The 22px horizontal gap in .sjp-listing__links is NOT
       widened: it already separates them, and growing it would push "Peer
       Review" onto its own row on a narrow card, costing a line on every card
       to solve a problem the height fix already solves.

       inline-flex + align-items rather than padding, so the text stays on its
       own baseline and only the HIT BOX grows — padding would have moved the
       underline away from the words and changed how the card reads. */
    .sjp-listing__link {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
    }
}
