/*
 * ericville26 — component primitives
 * ==================================
 * Copied from design_handoff_ericville26/design/v26.css with exact values
 * preserved (13.5px, -.028em, .42s, 14%/62%, 46% — none rounded).
 *
 * Every rule is scoped under `.ev26`.
 * ----------------------------------
 * The prototype's class names are generic and six of them already mean
 * something else in this theme: `.tag` appears 89 times on the Lab page,
 * `.inverse` is a wp-block-group modifier, and the bare `nav` / `nav a` rules
 * — written for the new header, which does not exist until Stage 3 — were
 * collapsing the current header's nav tiles from 439px to 101px.
 *
 * So a component only takes effect inside an element carrying `.ev26`. As each
 * stage rebuilds a surface it opts that subtree in; everything else keeps
 * rendering as ericville25 until its turn. Stage 11 removes the scope and this
 * file merges back into v26.css.
 *
 * `:where(.ev26)` contributes zero specificity, so these behave exactly as the
 * unscoped prototype rules do once a subtree opts in.
 */
/* Real specificity, not :where(). basetags.css underlines every <a> at
   (0,0,1); a zero-specificity scope loses to it and every button, card and
   section row renders underlined. `.ev26 a` is (0,1,1) and clears it. */
.ev26 a{color:inherit;text-decoration:none}
:where(.ev26) .wrap{max-width:var(--col);margin:0 auto;padding:0 var(--gut)}
/* #1500 — Eric: Home/Lab cards read narrow and misaligned on phone; UX did
 * not have the problem. Cause: `.wrap` (used by Home, Lab, Art, Shop, footer,
 * and every single-post template) never shrank its gutter below the desktop
 * 44px, while v26-ux.css's own container carries this exact 600px/22px
 * override for `.content.ev26.ux-landing`. 44px a side is ~23% of a 375px
 * viewport gone to padding before any content starts — that's the "too
 * narrow" cards. Mirroring UX's number here fixes every `.wrap` page at once
 * rather than patching Home and Lab separately, and keeps the two gutter
 * systems the site actually has (`.wrap` and UX's own) in visual agreement. */
@media (max-width:600px){
  :where(.ev26) .wrap{padding-left:22px;padding-right:22px}
}
/* ── header: warm glass ─────────────────────────────── */
/* at rest: full-bleed, barely there. scrolled: a decisive floating pill. */
/* The fill is --frost, not --bg, and it is mixed in srgb like every other
   surface in the elevation system (--paper / --sunken). Mixing the GROUND into
   the ground is what made this a flat band; --frost is a material of its own.
   See the header-glass note in v26.css. */
/* #1464: .bar itself is now BARE at rest — no fill, no filter, no shadow, no
   edge. It is a positioning context and a layout box and nothing else; the
   material lives in ::before / ::after (the band) and comes back onto .bar
   only when the header shrinks into a pill. Note there is deliberately no
   `backdrop-filter` here any more: declaring it at blur(0) would still promote
   the bar to a backdrop root on every page at every scroll position, which is
   most of the cost #1432 measured. */
:where(.ev26) .bar{position:sticky;top:0;z-index:200;background:transparent;border-bottom:var(--hdr-line) solid color-mix(in oklab,var(--line) 70%,transparent);box-shadow:var(--hdr-shadow);border-radius:var(--hdr-radius);width:100%;margin:0 auto;transition:background .42s var(--ease),box-shadow .46s var(--spring),border-radius .46s var(--spring),width .46s var(--spring),max-width .46s var(--spring),margin .46s var(--spring),top .46s var(--spring)}
/* backdrop-filter is deliberately NOT transitioned: animating a blur
   re-samples everything behind the bar every frame, which is the single most
   expensive thing a sticky header can do on scroll. It snaps between 14px and
   26px instead; against the .42s move of everything else the change reads as
   part of the same gesture — and raising the RESTING radius from 5px narrows
   that snap from 4x to under 2x, so the one thing that cannot be animated is
   also the thing that now moves least. Per-frame cost was measured before the
   radius was raised: see the header-glass note in v26.css. */
/* z-index 200, not the prototype's 20: blocks/intro/style.css sets z-index:100
   on the section banner, which painted straight over the sticky header on every
   non-home page. The chrome has to out-rank page content, so the bar sits above
   the highest z-index any block claims. */
/* The full-bleed bar carries no top-highlight line: its top edge IS the top of
   the window, so a specular line there is a line on the screen bezel, not on a
   pane. (The original rejection was also about the grain: the noise layer was
   a FIXED html::before painting over the chrome. #1421 moved it to body::after
   at z-index 9, and the bar is z-index 200 — the header is now above the grain,
   so that half of the objection is gone. The SHRUNK pill, which is a floating
   object with a real top edge in the middle of the page, does take a highlight;
   see the big move below.) */

/* ── the frost band (#1464) ─────────────────────────────────────────────────
 * AT REST THERE IS NO BAR. .bar paints no fill, runs no filter, casts no
 * shadow and draws no edge. What replaces it is a BAND that overhangs the
 * bar's own bottom edge by --hdr-fade and dies out inside that overhang.
 *
 * WHY IT CANNOT BE ONE LAYER ON .bar. Two hard constraints:
 *   - it has to extend BELOW the element, so it cannot be .bar's own
 *     background/filter, which are clipped to .bar's border box;
 *   - mask-image on .bar itself would mask .bar's DESCENDANTS too, i.e. it
 *     would fade out the wordmark and the nav row. Non-starter.
 * So the band is two absolutely-positioned pseudo-elements at z-index:-1.
 * Negative-z children of a stacking context paint ABOVE the element's own
 * background (paint step 3) and BELOW its in-flow content (steps 4-7), which
 * is exactly the slot a material needs: over the glass, under the type.
 *
 * WHY THE BLUR AND THE TINT CANNOT FADE SEPARATELY. The failure this project
 * has shipped and reverted four times (the hero wash, the footer band, the Lab
 * background, the paper-grain cutoff) is a fill that fades over an effect that
 * does not: the tint reaches zero, the blur is still at full radius, and the
 * eye reads the blur's hard cutoff as a line exactly where the fill said there
 * was none. The fix is structural rather than a matter of matching two
 * gradients by hand: the tint is `background` ON THE SAME ELEMENT whose
 * `backdrop-filter` is being masked, and one `mask-image` composites the whole
 * element — filtered backdrop and fill together. They cannot come apart,
 * because there is only one gradient and it is applied once, at composite.
 *
 * ONE FILTER, NOT TWO, AND CERTAINLY NOT EIGHT. taamannae.dev builds its
 * gradient blur from EIGHT stacked backdrop-filters (.5 -> 64px) with
 * overlapping masks, in a container at 130% of the navbar's height. That is
 * the right shape and the wrong bill for us. Measured here, /lab/ at 1440@2x
 * under a 6x CPU throttle, 179 rAF frames confined to the 0-38px at-rest
 * band: no filter 317ms of main-thread task time · #1432's single unmasked
 * bar filter 1054ms · this band with ONE masked filter 1305ms · with a second
 * masked filter layered on 2167ms. The second layer costs +46% and buys a
 * radius ramp that is only legible over detailed ground — and at rest the
 * ground on this site is ALWAYS a smooth section wash, because every template
 * puts a wash above its banner image. So the radius ramp was cut and the
 * SECOND layer (::before) keeps only the parts that are free: the material
 * and the grain, no filter, reaching further down than the blur does. The
 * blur's own box is also cropped to --hdr-blur-reach of the overhang rather
 * than all of it; that alone was 1559ms -> 1305ms, since a filter is billed
 * by area. Zero frames over 20ms in any configuration.
 *
 * THE ICE TOOTH IS IN THE MASK, NOT ON TOP OF IT. An overlaid grain does not
 * work here and cannot be made to: var(--grain) is an opaque JPEG, so as a
 * background-image it makes whatever carries it opaque, which is the one thing
 * a no-background header may not be; and as a mix-blend-mode layer it would
 * blend inside .bar's isolated stacking context (z-index:200) against pixels
 * that are transparent at rest, so multiply degrades to a flat grey veil.
 * Instead the noise goes into the MASK as a luminance layer, multiplied by the
 * fade and by a peak alpha:
 *     alpha = fade x luminance(noise) x --hdr-ice
 * so the frost is DEPOSITED UNEVENLY — patchy, granular, thicker here than
 * there — and its MEAN is the tint. There is no separate fill to keep in step
 * with the grain, because the tint is nothing more than the average of the
 * grain. Measured on /art/: horizontal SD through the band 3.0-4.4 levels
 * against the page's own paper grain at 2.8-3.8 immediately below it, so the
 * texture crosses the header boundary without a step.
 *
 * rgb(0 0 0/x) in the masks is alpha, not colour — a stencil has no hue. */
:where(.ev26) .bar::before,
:where(.ev26) .bar::after{content:"";position:absolute;left:0;right:0;top:0;bottom:calc(-1 * var(--hdr-fade));z-index:-1;pointer-events:none}
/* far field: the frost TAIL. Deliberately carries NO backdrop-filter -- see the
   measured reason in the cost note above -- only the material, at a low alpha,
   through the same grain stencil, reaching further down than the blur does so
   the band's colour outlives its blur instead of the other way round. Zero
   filter cost; it is one more translucent rectangle. */
:where(.ev26) .bar::before{background-color:var(--frost-band);
  -webkit-mask-image:linear-gradient(to bottom,rgb(0 0 0/var(--hdr-ice)) 0,rgb(0 0 0/var(--hdr-ice)) 34%,rgb(0 0 0/0) 100%);
  mask-image:linear-gradient(to bottom,rgb(0 0 0/.44) 0,rgb(0 0 0/.44) 34%,rgb(0 0 0/0) 100%),
             var(--grain),
             linear-gradient(rgb(0 0 0/var(--hdr-ice)),rgb(0 0 0/var(--hdr-ice)));
  mask-mode:alpha,luminance,alpha;
  mask-size:auto,var(--hdr-grain-size),auto;
  mask-repeat:no-repeat,repeat,no-repeat;
  mask-composite:intersect,intersect,add}
/* near field: the deep step and the tint, laid down through the grain mask.
   The -webkit-mask-image line is the pre-15.4 Safari fallback and is
   deliberately the gradient ALONE — a smooth frost band, no tooth, rather
   than a broken one. Blink treats -webkit-mask-image as an alias, so the
   unprefixed block below overrides it there. */
:where(.ev26) .bar::after{bottom:calc(-1 * var(--hdr-fade) * var(--hdr-blur-reach));backdrop-filter:blur(var(--hdr-blur)) saturate(var(--hdr-band-sat));-webkit-backdrop-filter:blur(var(--hdr-blur)) saturate(var(--hdr-band-sat));
  background-color:var(--frost-band);
  -webkit-mask-image:linear-gradient(to bottom,rgb(0 0 0/var(--hdr-ice)) 0,rgb(0 0 0/var(--hdr-ice)) 26%,rgb(0 0 0/0) 100%);
  mask-image:linear-gradient(to bottom,rgb(0 0 0/1) 0,rgb(0 0 0/1) 24%,rgb(0 0 0/.38) 62%,rgb(0 0 0/0) 100%),
             var(--grain),
             linear-gradient(rgb(0 0 0/var(--hdr-ice)),rgb(0 0 0/var(--hdr-ice)));
  mask-mode:alpha,luminance,alpha;
  mask-size:auto,var(--hdr-grain-size),auto;
  mask-repeat:no-repeat,repeat,no-repeat;
  mask-composite:intersect,intersect,add}
:where(.ev26) .bar .wrap{display:flex;align-items:center;justify-content:space-between;padding-top:var(--hdr-pad);padding-bottom:var(--hdr-pad);gap:24px;transition:padding .46s var(--spring)}
:where(.ev26) .bar .logo{transition:width .42s var(--ease),opacity .28s var(--ease)}
:where(.ev26) .bar .lockup{position:relative;display:flex;align-items:center;height:34px}
:where(.ev26) .bar .lockup .mark{position:absolute;left:0;height:30px;width:auto;opacity:0;transform:scale(.9);transition:opacity .3s var(--ease),transform .42s var(--ease)}
/* ── the big move ─────────────────────────────────── */
/* Thicker glass when it floats. Higher alpha and a wider radius because this
   state has real content moving under it: at 62% of a near-neutral the pill
   let dark photography smear through as warm grey (#BEBCB7 measured over a
   black image, next to #E7E5E0 over the ground — one pane reading as two).
   72% of --frost attenuates the same image to a consistent pale ice.
   --hdr-shadow carries BOTH halves of "this is a lifted pane": the cast shadow
   (now token-derived from --shade, so it survives dark) and an inset specular
   line along the top edge. That highlight is a deliberate, scoped reversal of
   the "no highlight on the glass" spec rule -- argued in the .bar::before note
   above and in plan/design/1432-header-glass.md. The nav's own active pill has
   used exactly this device since #1418. */
/* #1480: the 72% above is the HISTORY of this value, not its current setting.
   Eric: "the scrolling version is now not glassy enough; make it more
   transparent and textured." The dial moved out to --hdr-pill-tint in v26.css
   (48% light / 56% dark) — it had been hard-coded here while the token note
   claimed it lived in :root, which is a trap the next tuner should not fall
   into twice. #1432's finding still stands and is why dark is cut less: at 62%
   of a NEAR-NEUTRAL the pane read as two panes. The fix for that was never the
   alpha alone, it was --frost's own hue; 48% of #F2FAFD stays one pane. */
/* #1483: every literal that used to sit in this rule — the 8px pad, the 26px
   blur, the 14px-a-side inset and the 1080px cap — is now a token in v26.css
   alongside --hdr-pill-tint, and every one of them moved. The pill is taller
   (50 -> 64px), wider (1080 -> the band minus 16px a side) and more
   transparent. The reasoning for each number, and the reason widening the
   glass does NOT move the type, is in the --hdr-pill-* note in v26.css. */
/* #1485 corrects the WIDTH half of #1483: --hdr-pill-max is now var(--col), so
   the pill's box IS .wrap's box and the inside gutter is exactly --gut at every
   viewport. #1483 capped the GLASS at 1440 while the CONTENT stayed at --col —
   two independently centred boxes, so the padding between them was an accident
   of the viewport (204px a side at 1512, 108 at 1280, and an outside gap of
   36/80/144/240 above 1472 that matched nothing). Reasoning + measured ladder:
   the --hdr-pill-max note in v26.css. */
:where(.ev26) .bar.big.shrunk{--hdr-pad:var(--hdr-pill-pad);--hdr-tint:var(--hdr-pill-tint);--hdr-blur:var(--hdr-pill-blur);--hdr-sat:1.7;--hdr-radius:999px;--hdr-line:0px;
  --hdr-shadow:0 10px 28px -14px color-mix(in srgb,var(--shade) 34%,transparent),
               inset 0 1px 0 color-mix(in srgb,var(--lift) var(--hdr-hi),transparent);
  background:color-mix(in srgb,var(--frost) var(--hdr-tint),transparent);
  backdrop-filter:blur(var(--hdr-blur)) saturate(var(--hdr-sat));-webkit-backdrop-filter:blur(var(--hdr-blur)) saturate(var(--hdr-sat));
  width:calc(100% - var(--hdr-pill-inset)*2);max-width:min(calc(100% - var(--hdr-pill-inset)*2),var(--hdr-pill-max));margin:0 auto;top:12px}
/* #1464, the second half of Eric's note: "once scrolling, that heavy noise
   texture is MAINTAINED within the floating block". The band is gone — a
   gradiated overhang on an object that is now 1080px wide with a 999px radius
   and 12px of air above it would be a smear, not a material — so the frost
   goes back onto .bar, and ::after stops being a mask and becomes the tooth.
   THE PILL IS THE ONE PLACE A BLENDED GRAIN ACTUALLY WORKS. mix-blend-mode
   inside .bar's isolated stacking context needs opaque pixels beneath it to
   blend WITH; at rest there are none, but the pill paints a 72% frost fill
   plus its blurred backdrop into that same group first. So here the grain can
   be the page's own paper grain doing exactly what it does on body::after —
   multiply in light, overlay in dark — and the header stops being the one
   surface on the site with no texture on it. Same asset, same blend, ~3x the
   amplitude (.34 / .20 against the page's .12 / .06) and a 200px tile instead
   of 380px, because at pill scale a 380px tile shows one soft blotch rather
   than grain. border-radius:inherit clips it to the pill; no filter on this
   layer, so `opacity` is a safe amplitude control here. */
/* #1485. TWO CORRECTIONS TO THE PARAGRAPH ABOVE, both measured:
   (1) `opacity` here is NOT a pure amplitude control. This layer is bright
   (noise1.jpg through --hdr-grain-tone means ~225) and it blends over a pane
   that is only partly opaque; CSS composites a blend as (1-ab)*Cs + ab*B(),
   so at a 34% pane 66% of that pale layer lands as a FLAT VEIL. --hdr-grain
   was therefore half the pill's opacity, and lowering it made the pill darker
   over dark grounds and dropped /essay/ light to 3.19:1. #1485 moves that
   coverage into --hdr-pill-tint and cuts the noise 61%. The full argument is
   in the #1485 block above --hdr-pill-tint in v26.css — read it before
   touching --hdr-grain again.
   (2) background-size is --hdr-pill-grain-size (620px), NOT --hdr-grain-size
   (200px). The latter is the at-rest band's tile and is deliberately left
   alone; the pill needs a tile larger than the 431px asset so the texture
   upscales into slow undulation ("wavy glass") instead of fine sand. */
:where(.ev26) .bar.big.shrunk::before{content:none}
:where(.ev26) .bar.big.shrunk::after{inset:0;bottom:0;border-radius:inherit;
  backdrop-filter:none;-webkit-backdrop-filter:none;background-color:transparent;
  background-image:var(--grain);background-size:var(--hdr-pill-grain-size);background-repeat:repeat;
  mix-blend-mode:var(--hdr-grain-blend);filter:var(--hdr-grain-tone);opacity:var(--hdr-grain);
  -webkit-mask-image:none;mask-image:none}
/* #1483. This rule USED to read `padding-left:22px;padding-right:22px;
   max-width:none` — the pill took over the column, so the wordmark's x was a
   function of the pill's width and every width tune dragged the type sideways.
   It is now deleted rather than retuned: .wrap keeps var(--col) and var(--gut)
   in the scrolled state exactly as it has them at rest, so the pill can be any
   width at all and the content does not move. That is what makes the widened
   pill legal, and it is also "more padded" — the inside gutter goes 22px ->
   44px on desktop — without a single new number.
   Below the column's binding width the content shifts inward by exactly
   --hdr-pill-inset, which is the whole visible cost of the pill being a
   floating object; the 640px rule below already sets both states to the same
   16px, so the shift there is the inset alone, and smaller than the 16px the
   old 14+14 arrangement produced. */
:where(.ev26) .bar.big.shrunk .logo{width:0;opacity:0}
:where(.ev26) .bar.big.shrunk .lockup .mark{opacity:1;transform:scale(1)}
:where(.ev26) .bar.big.shrunk nav{font-size:var(--t-meta)}
:where(.ev26) .bar.float{width:calc(100% - var(--hdr-inset)*2);max-width:var(--col);margin:var(--hdr-inset) auto 0}
:where(.ev26) .bar.float .wrap{padding-left:24px;padding-right:24px}
:where(.ev26) .bar .logo{height:32px;width:auto}
/* ── the identity in dark ───────────────────────────────────────────────────
 * #1456. There are THREE places the identity renders and only one of them had
 * a dark rule:
 *   .bar .logo      the wordmark, at rest above 640px       — had this rule
 *   .bar .mark      the logomark, shown when the bar shrinks past 40px AND at
 *                   all times below 640px                   — had NOTHING, so
 *                   on a phone in dark the site's identity was a near-black E
 *                   on a near-black ground from the first paint
 *   .foot-mark img  the footer wordmark, every page         — had NOTHING
 *
 * ONE mechanism for all three, and the eyeballed terms are gone. The old
 * declaration was invert(1) brightness(1.6) saturate(0): both extra terms are
 * no-ops on this artwork (inverting fill="black" already clamps to white, and
 * there is no colour to strip) and both would misbehave on any future coloured
 * mark. Plain invert(1) is the whole intent.
 *
 * Considered and rejected: swapping the source to assets/logo-light.svg, which
 * does exist and IS the real light-on-dark wordmark (fill #fcfbf8, same 489x90
 * viewBox). It only solves ONE of the three — there is no light logomark asset
 * — so taking it would mean a source swap for the wordmark and a filter for the
 * mark: two mechanisms for three logos, which is how this drifted apart in the
 * first place. If a light logomark is ever drawn, swap all three together and
 * this rule retires as a unit. (assets/logo-light.svg stays unused for now.)
 *
 * The footer image carries no class of its own, so it is reached through its
 * link — no markup change, and footer.php is untouched. */
[data-theme="dark"] :where(.ev26) .bar .logo,
[data-theme="dark"] :where(.ev26) .bar .lockup .mark,
[data-theme="dark"] :where(.ev26) .foot-mark img{filter:invert(1)}
/* Scoped to the bar. Unscoped, this caught every <nav> on the page -- the
   article's contents rail and its breadcrumb are navs too, and both were being
   laid out as a flex row of header pills. */
/* gap:0 on purpose. The row's only two flex children are the <ul> and the
   switcher, and ALL of the space between them is now expressed once, in
   --util-mar, derived from the ink rhythm below. A flex gap here would be a
   second, invisible term in that arithmetic. */
:where(.ev26) .bar nav{display:flex;gap:0;align-items:center}
/* ── the control row: nav items and the switcher are one family ──────────────
 *
 * THE BUG FIRST. basetags.css line 142 declares `h1,h2,h3,h4,nav{font-family:
 * var(--header-fonts)}` and the compat bridge points --header-fonts at
 * var(--serif). Nothing in this file ever set a family on the bar's nav, so the
 * primary navigation was rendering in Source Serif 4 -- the DISPLAY face -- at
 * 600, next to a serif wordmark. Half of "poorly styled" was that. basetags'
 * selector is (0,0,1); `.ev26 .bar nav` is (0,2,1) and settles it honestly,
 * without editing a file that dies in Stage 11.
 *
 * THE LOCALS. Four derived values, all color-mix of existing tokens -- no new
 * colour enters the system, and dark mode keeps working because every input is
 * a token that flips with [data-theme].
 *
 *   --nav-ink         resting label colour. Was --muted, which measures
 *                     4.76:1 against the home page's mint gradient as seen
 *                     THROUGH the glass -- a hair over AA and visibly faint,
 *                     which is the other half of Eric's complaint. --text
 *                     measures 9.34:1 light / 7.07:1 dark and still sits
 *                     clearly below the wordmark in the hierarchy.
 *
 *   --nav-accent-ink  hover / focus label colour. Bare --accent as text FAILS
 *                     AA on the light ground: jade 2.38:1, vermilion 2.96:1,
 *                     cyan 2.84:1 -- hovering a link made it LESS readable, and
 *                     the accent focus ring missed the 3:1 non-text minimum on
 *                     the same three sections. Pulling the accent 35% toward
 *                     --ink keeps the hue and clears AA on every section
 *                     (worst case jade 4.77:1). In dark mode the accents are
 *                     already bright (5.03-6.21:1) and mixing toward the light
 *                     --ink would only wash them, so dark keeps them pure.
 *
 *   --nav-fill        the active pill. --on-accent on bare --accent also FAILS
 *                     AA in light mode -- jade 3.26:1, cyan 3.88:1, vermilion
 *                     4.04:1; only UX passed. Deepening the fill 20% toward
 *                     --ink brings all four to 4.80-7.94:1 with no loss of
 *                     section identity. Dark mode already runs 7.7-9.5:1, so
 *                     it keeps the pure accent.
 *
 *   --nav-wash        the hover pill. Was --accent-soft, a near-white mint that
 *                     is effectively invisible over translucent glass on a mint
 *                     page. A 13% accent tint reads on any ground, light, dark
 *                     or glass, and needs no second token per section.
 *
 * One control height (--nav-ctl) governs the links AND the switcher, so the row
 * has a single optical rhythm instead of text-next-to-hardware. */
.ev26 .bar nav{
  font-family:var(--sans);
  font-size:var(--t-meta);font-weight:600;letter-spacing:var(--tr-ui);
  /* #1494 — 32px was a legible row but a small target: a two-letter pill
     ("UX") measured 32x32 at rest and 30x30 scrolled, i.e. below the 44px touch
     floor in both dimensions and the least confident thing in the bar. The
     control grows to 36 and the pill padding to 15, so the pills read as
     buttons rather than as text that happens to highlight. The rhythm below is
     untouched — every other number in this row derives from --nav-ctl and
     --nav-pad, so the ink spacing #1433 measured holds by construction. */
  --nav-ctl:36px;
  /* ── the row's rhythm, measured in INK, not in boxes ─────────────────────
   * #1433. Every number that set this row used to be a box measurement, and
   * the boxes are not where the ink is: a nav pill carries 13px of padding
   * inside its box and the switcher's sun sits 4.6px inside its 32px circle.
   * Measured on the rendered page at 1440, the optical gaps were
   *
   *     UX --28-- Lab --24-- | --13.6-- (sun)
   *
   * i.e. the hairline sat almost twice as far from "Lab" as from the disc, and
   * the whole utility group hung 38.6px off the nav — two tight pairs with a
   * hole in the middle, which is exactly what Eric saw. Equalising the DECLARED
   * values is how it got that way.
   *
   * So the row now declares the ink rhythm and derives the boxes from it:
   *
   *   --nav-pad   pill padding — the only number a breakpoint has to change
   *   --ul-gap    box gap between pills
   *   --util-ink  ink gap from the last label to the toggle's disc. Defined as
   *               2*--nav-pad + --ul-gap, which IS the ink gap between two nav
   *               labels — so the utility group is spaced identically to the
   *               navigation by construction, at every breakpoint and in both
   *               bar states, and can never drift again.
   *
   * #1494 removed the hairline separator that used to sit in that gap (and with
   * it --util-bias, which existed only to centre it optically). The ink rhythm
   * it was measured into SURVIVES it: --util-ink still spaces the toggle from
   * the last label exactly as two nav labels are spaced, so the row keeps
   * #1433's even cadence and simply stops drawing a rule across it. With three
   * items in the row the divider was labelling a distinction the type already
   * made — a disc is obviously not a word. */
  --nav-pad:15px;
  --ul-gap:2px;
  --util-ink:calc(2*var(--nav-pad) + var(--ul-gap));
  /* The switcher's true ink width: a 9.5px core plus eight rays out to a 10px
     radius with a 1.4px point, so 22.8px across inside its --nav-ctl circle. */
  --themer-ink:22.8px;
  --themer-inset:calc((var(--nav-ctl) - var(--themer-ink))/2);
  /* box margin that produces --util-ink of INK gap */
  --util-mar:calc(var(--nav-pad) + var(--ul-gap) - var(--themer-inset));
  --nav-ink:var(--text);
  --nav-accent-ink:color-mix(in oklab,var(--accent) 65%,var(--ink));
  --nav-fill:color-mix(in oklab,var(--accent) 80%,var(--ink));
  --nav-wash:color-mix(in oklab,var(--accent) 13%,transparent);
}
[data-theme="dark"] .ev26 .bar nav{
  --nav-accent-ink:var(--accent);
  --nav-fill:var(--accent);
}
/* ── the theme switcher ──────────────────────────────
 * It was a 32px hairline-outlined circle: at rest nearly invisible, and the
 * outline made it read as a piece of hardware bolted onto the row rather than
 * the last item in it. The border is gone. What is left is a control the exact
 * height of a nav pill, with the same grow-from-.82 wash on hover, the same
 * 1px lift, the same .95 press and the same focus ring -- so it is legible as a
 * sibling, not an appliance.
 *
 * #1494: it used to carry a hairline rule on its left (a ::before) saying "this
 * one is utility, not navigation". That is gone. The signal was redundant with
 * the shape — the toggle is a disc among words — and in the scrolled pill, where
 * the row is at its densest, a 1px vertical rule inside translucent glass read
 * as a rendering artefact rather than as punctuation. The ink gap it sat in
 * remains (--util-mar), so the toggle is still spaced apart from the nav. */
.ev26 .themer{
  position:relative;isolation:isolate;flex:0 0 auto;
  margin-left:var(--util-mar);
  width:var(--nav-ctl);height:var(--nav-ctl);padding:0;
  /* forms.css gives every <button> `min-height:50px` at (0,0,1). min-height
     out-ranks height regardless of specificity, so the switcher was rendering
     32 wide by 50 tall -- a vertical ellipse in a row of 32px controls. That,
     not the border alone, is why it read as bolted on. */
  min-height:0;min-width:0;line-height:1;font-size:var(--t-meta);
  display:inline-flex;align-items:center;justify-content:center;
  border:0;border-radius:999px;background:transparent;
  font-family:var(--sans);color:var(--nav-ink);
  cursor:pointer;-webkit-appearance:none;appearance:none;
  transition:color var(--fast) var(--ease),transform .28s var(--spring);
}
/* `:where(.ev26) .themer{display:flex}` was (0,1,0) and beat the UA sheet's
   [hidden] rule, so the button flashed visible on every load before theme.js
   ran. An author-level [hidden] at (0,3,0) is the honest fix. */
.ev26 .themer[hidden]{display:none}
.ev26 .themer::after{
  content:"";position:absolute;inset:0;z-index:-1;border-radius:999px;
  background:var(--nav-wash);transform:scale(.82);opacity:0;
  transition:transform .34s var(--spring),opacity .2s var(--ease);
}
.ev26 .themer:hover{color:var(--nav-accent-ink);transform:translateY(-1px)}
.ev26 .themer:hover::after{transform:scale(1);opacity:1}
.ev26 .themer:active{transform:translateY(0) scale(.95);transition-duration:.08s}
.ev26 .themer:focus-visible{outline:2px solid var(--nav-accent-ink);outline-offset:3px;color:var(--nav-accent-ink)}
.ev26 .themer:focus-visible::after{transform:scale(1);opacity:1}
/* The icon: one shape that MORPHS rather than two pictures that swap.
 *
 * The half-filled disc it replaces flipped 180deg on toggle, which turns a
 * left-filled half disc into a right-filled half disc -- the two states were
 * almost indistinguishable, so the control could not tell you what it had done.
 * Here the sun's core grows while its eight rays retract into it, and an inset
 * shadow eats a crescent out of the grown disc: sun -> moon, one continuous
 * gesture on the spring curve, no glyph font, no SVG, no second asset.
 *
 * .themer-icon is a bare 20px positioning box; ::before is the disc and ::after
 * is the ray set, so the disc can scale without dragging the rays with it. */
.ev26 .themer-icon{position:relative;display:block;width:20px;height:20px}
.ev26 .themer-icon::before,
.ev26 .themer-icon::after{
  content:"";position:absolute;left:50%;top:50%;
  width:16px;height:16px;margin:-8px 0 0 -8px;border-radius:999px;
}
.ev26 .themer-icon::before{
  background:currentColor;box-shadow:inset 0 0 0 0 currentColor;
  transform:scale(.594) translateX(0);
  transition:transform .5s var(--spring),background .3s var(--ease),box-shadow .42s var(--ease);
}
/* Eight rays as 2.8px points at a 10px radius. Deliberately much smaller than
   the 9.5px core: earlier, heavier dots read as a loading spinner rather than
   as light. Total extent 22.8px, inside the 32px control with clearance. */
.ev26 .themer-icon::after{
  box-shadow:
    0 -10px 0 -6.6px currentColor,      0 10px 0 -6.6px currentColor,
    -10px 0 0 -6.6px currentColor,      10px 0 0 -6.6px currentColor,
    7.1px -7.1px 0 -6.8px currentColor, -7.1px 7.1px 0 -6.8px currentColor,
    7.1px 7.1px 0 -6.8px currentColor,  -7.1px -7.1px 0 -6.8px currentColor;
  transform:scale(1);opacity:1;
  transition:transform .5s var(--spring),opacity .26s var(--ease);
}
/* The crescent's mass sits to the right of the disc's geometric centre, so it
   is nudged 1.5px left to look centred in the control. Same transform function
   list as the sun state, so the morph interpolates instead of snapping. */
.ev26 .themer[aria-pressed="true"] .themer-icon::before{
  transform:scale(1) translateX(-1.5px);background:transparent;
  box-shadow:inset -4.5px -2px 0 -.5px currentColor;
}
.ev26 .themer[aria-pressed="true"] .themer-icon::after{transform:scale(.3);opacity:0}
/* Visually-hidden label that stays available to screen readers. */
.ev26 .screen-reader-text{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
/* ── primary nav ────────────────────────────────────
 * The most-clicked control on the site, so it should behave like one rather
 * than like text with a faint wash behind it.
 *
 * Written at real specificity on purpose. `.ev26 a{color:inherit}` is (0,1,1)
 * and out-ranks the prototype's `nav a` at (0,0,2), so the resting --muted
 * colour never actually applied — the links were simply inheriting.
 *
 * The pill is a ::before rather than a background so it can scale: it grows
 * from 82% on the spring curve instead of fading in flat, which is what makes
 * it feel like it responds to you. The active item is a filled pill in the
 * current section's accent, so the nav changes colour as you move through the
 * site and you can always see where you are. */
.ev26 .bar nav a{
  position:relative;isolation:isolate;
  display:inline-flex;align-items:center;justify-content:center;gap:7px;
  height:var(--nav-ctl);padding:2px var(--nav-pad) 0;border-radius:999px;
  font-family:var(--sans);line-height:1;
  color:var(--nav-ink);font-weight:600;
  transition:color var(--fast) var(--ease),transform .28s var(--spring);
}
.ev26 .bar nav a::before{
  content:"";position:absolute;inset:0;z-index:-1;border-radius:999px;
  background:var(--nav-wash);
  transform:scale(.82);opacity:0;
  transition:transform .34s var(--spring),opacity .2s var(--ease),background var(--fast) var(--ease),box-shadow var(--fast) var(--ease);
}
.ev26 .bar nav a:hover{color:var(--nav-accent-ink);transform:translateY(-1px)}
.ev26 .bar nav a:hover::before{transform:scale(1);opacity:1}
/* Presses register. Snappier than the release so it feels mechanical. */
.ev26 .bar nav a:active{transform:translateY(0) scale(.95);transition-duration:.08s}
/* There was no focus ring at all before this. */
.ev26 .bar nav a:focus-visible{outline:2px solid var(--nav-accent-ink);outline-offset:3px;color:var(--nav-accent-ink)}
.ev26 .bar nav a:focus-visible::before{transform:scale(1);opacity:1}
/* Current section: filled, in that section's accent.
 *
 * #1497 removes the outer glow. The pill used to carry
 * `0 4px 12px -5px accent/60%` — a coloured drop-shadow that bled the accent
 * onto the glass behind it. Inside the scrolled pill (which is itself a
 * translucent, blurred, grained surface) that halo read as a smudge rather
 * than as lift, and on the two hardest grounds it fought the label scrim
 * directly above it. It also broke the one rule the rest of this chrome keeps:
 * every edge in the bar is a hairline or a fill, never a bloom.
 *
 * What replaces it is FORM, not light: the fill stays, and the pill gets a
 * two-stop inset ring — one pixel of specular highlight along the top and one
 * pixel of shade along the bottom. That is the same information the drop
 * shadow was carrying (this object is raised and physical) delivered entirely
 * INSIDE the pill's own footprint, so nothing paints onto the glass and the
 * treatment costs zero blur radius.
 *
 * Alternatives considered and rejected:
 *   - underline / border-only: loses the accent fill, and the fill is what
 *     makes the whole nav change colour by section. That signal is the point.
 *   - flat fill, no ring at all: legible, but reads printed-on — it is the
 *     exact failure the original inset highlight was added to fix.
 *   - colour-only (accent text, no pill): fails contrast on /lab/ dark and
 *     /art/ light, the same two grounds that forced --hdr-label-scrim.
 *
 * Both stops are mixed off --on-accent, so they invert with the fill and hold
 * across all four section accents in both themes without a per-theme override. */
.ev26 .bar nav li.current-menu-item>a,
.ev26 .bar nav a.on{color:var(--on-accent);font-weight:700}
.ev26 .bar nav li.current-menu-item>a::before,
.ev26 .bar nav a.on::before{
  background:var(--nav-fill);transform:scale(1);opacity:1;
  box-shadow:inset 0 1px 0 color-mix(in oklab,var(--on-accent) 26%,transparent),
             inset 0 -1px 0 color-mix(in oklab,var(--ink) 16%,transparent);
}
.ev26 .bar nav li.current-menu-item>a:hover,
.ev26 .bar nav a.on:hover{color:var(--on-accent)}
.ev26 .bar nav li.current-menu-item>a:hover::before,
.ev26 .bar nav a.on:hover::before{transform:scale(1.05)}
/* On a filled pill the count bubble would be accent-on-accent. Invert it. */
.ev26 .bar nav li.current-menu-item>a .cart-count{background:var(--on-accent);color:var(--nav-fill)}
/* Condensed header: same behaviour, tighter. Two numbers only — the control
   height and the pill padding. The pill height, the switcher's size and the
   switcher's margin all derive, so the shrunk row keeps the same even ink
   rhythm the resting row has instead of needing hand-set overrides.
   #1494 moves this pair with the resting pair (30/11 -> 34/13): the scrolled
   pill stays 2px shorter and 2px tighter than at rest, which is the whole
   point of the shrunk state, while both states clear a real target size. */
.ev26 .bar.big.shrunk nav{--nav-ctl:34px;--nav-pad:13px}
/* #1486. The scrolled pill went 52% -> 44% tint (light) and 50% -> 42% (dark),
   which is past the point where the GLASS can carry the labels' legibility on
   the two hardest grounds (/lab/ dark, a bright photograph under white 13.5px
   labels; /art/ light, the linocuts under dark ones). So the labels get their
   own ground, shaped like the labels — see the --hdr-label-scrim block in
   v26.css for why this is a text-shadow and not a second fill on the pill, and
   why one --bg-coloured token covers both themes and stays invisible on the
   calm section washes.
   Scoped to .shrunk on purpose: the at-rest band is out of scope since #1464
   and its labels measure 9:1+, so at rest this must not paint at all. */
.ev26 .bar.big.shrunk nav a{text-shadow:var(--hdr-label-scrim)}
/* The active item is an opaque accent lozenge already; a halo inside a filled
   pill is a visible smudge, not a scrim. */
.ev26 .bar.big.shrunk nav li.current-menu-item>a,
.ev26 .bar.big.shrunk nav a.on{text-shadow:none}
@media (prefers-reduced-motion:reduce){
  /* v26-base.css already kills every transition globally here; this drops the
     motion itself. Every colour and fill change survives -- the wash, the
     accent ink, the filled pill, the sun/moon fill -- only the lift, the press
     and the grow are removed. The disc's scale is state, not motion: it is what
     distinguishes sun from moon, and it now happens instantly. */
  .ev26 .bar nav a:hover,
  .ev26 .bar nav a:active,
  .ev26 .themer:hover,
  .ev26 .themer:active{transform:none}
  .ev26 .bar nav a:hover::before,
  .ev26 .bar nav a:focus-visible::before,
  .ev26 .bar nav li.current-menu-item>a:hover::before,
  .ev26 .bar nav a.on:hover::before,
  .ev26 .themer:hover::after,
  .ev26 .themer:focus-visible::after{transform:scale(1)}
  .ev26 .themer[aria-pressed="true"] .themer-icon::after{transform:none;opacity:0}
}
/* ── the row below 640px ─────────────────────────────
 * #1414: the bar did not collapse on a phone. lockup(174) + gutters(88) +
 * gap(24) + two pills + the switcher exceeded 375, so EVERY page forced a
 * horizontal scroll -- measured at a document width of 387px on Home, UX and
 * Art before this change.
 *
 * The answer is the one the bar already knows: past 40px of scroll the wordmark
 * collapses to the logomark. Below 640px it simply starts there and stays
 * there, in BOTH bar states, because on a phone the mark is the identity and
 * the wordmark is a luxury. Gutters, gaps and pill padding tighten with it.
 * Nothing is hidden behind a menu button -- with two to four destinations, a
 * hamburger would add a tap to reach every one of them. */
@media (max-width:640px){
  .ev26 .bar .wrap{gap:10px;padding-left:16px;padding-right:16px}
  /* #1483: the shrunk override here (14px) is gone with its desktop twin. Both
     states now run the same 16px, so on a phone the ONLY horizontal difference
     between them is --hdr-pill-inset, which is cut to 10px below 640 — the
     content lands 10px in instead of the 16px the old 14+14 produced, and the
     pill is 355px wide against the band's 375 instead of 347. */
  .ev26 .bar.big.shrunk{--hdr-pill-inset:10px;--hdr-pill-pad:13px}
  .ev26 .bar .logo{width:0;opacity:0}
  .ev26 .bar .lockup{width:30px;flex:0 0 30px}
  /* #1500 — Eric: the mark sits too close to the pill's rounded left corner
     once scrolled. Measured: the mark-to-pill-edge inset is 44px at desktop
     (var(--gut), plenty of room against a gentler curve) but only 12px here
     — --hdr-pill-inset drops to 10px and .wrap's own padding to 16px on a
     phone, and a 999px radius corner eats into a 12px gap much more visibly
     than it does a 44px one. margin-left on .lockup itself (the anchor,
     per `.lockup{text-decoration:none}` below), not a left offset on .mark
     alone — .lockup is a fixed 30px box exactly the icon's own width, so
     shifting only the icon would let 6px of the visible glyph render outside
     its own clickable area. Moving the whole tap target keeps the icon and
     its hit box aligned, and — because .wrap's row uses
     justify-content:space-between with the nav independently right-aligned
     — this has no effect on the nav's position. */
  .ev26 .bar.big.shrunk .lockup{margin-left:6px}
  .ev26 .bar .lockup .mark{opacity:1;transform:scale(1)}
  /* Both selectors, because .ev26 .bar.big.shrunk nav is (0,4,1) and would
     otherwise out-rank a (0,2,1) override from inside a media query —
     specificity is the dominant failure mode in this file. */
  /* #1494: the HEIGHT grows here (30 -> 34) but --nav-pad deliberately does
     NOT. Below 640 the pill is 355px wide against a 375px band and the row's
     width is the constraint that produced #1414's horizontal scroll; padding
     is horizontal, height is free. So the phone gets the taller target without
     spending a pixel of the width budget.
     #1500 — Eric: "the links are too tight and barely tappable," then, after
     a first pass, "spacing is still too narrow, and now the height is funky."
     The first pass under-shot for a reason worth recording: the ≤400px tier
     below (originally written to squeeze the 5-item COMMERCE nav onto a
     phone) had no scoping and was silently tightening the ordinary 3-item
     nav too — and 375-390px is exactly where the two most common iPhone
     widths live, so most visitors were hitting the tighter tier without it
     ever being meant for them. Re-measured properly, the 3-item nav has far
     more slack than that shared tier assumed: pad:16/gap:14 still fits at
     320px (the narrowest phone worth supporting) with room to spare, and
     only pad:16/gap:16 finally overflows by a single pixel. --nav-pad:12 /
     --ul-gap:12 below is comfortably inside that ceiling, not pressed
     against it.
     HEIGHT stays 40px — free either way, per the paragraph above, and
     unrelated to the gap problem.
     THE SWITCHER ("height is funky"). --util-mar's formula
     (--nav-pad + --ul-gap - --themer-inset) makes the icon's ink sit the
     same visual distance from Lab's pill as Lab sits from UX. --themer-inset
     grew when --nav-ctl did (a bigger button around the same 22.8px icon
     means a bigger inset), and the previous pass grew the button without
     growing pad/gap enough to compensate — margin collapsed to ~1px, reading
     as the switcher glued to the pill with its icon suddenly small inside a
     bigger circle. The formula was never wrong; it just needs pad+gap large
     enough for its own inset, and 12+12 covers that with room left over.
     Both selectors again, same reason as the original comment above: the
     desktop-authored `.ev26 .bar.big.shrunk nav{--nav-ctl:34px;--nav-pad:13px}`
     (specificity 0,4,1) otherwise out-ranks a plain `.ev26 .bar nav` mobile
     override (0,2,1) even inside this media query, since a media block adds
     no specificity of its own — dropping the second selector here would have
     left the OLD desktop numbers winning on a scrolled phone header. */
  .ev26 .bar nav,
  .ev26 .bar.big.shrunk nav{font-size:var(--t-meta);--nav-ctl:40px;--nav-pad:12px;--ul-gap:12px}
}
/* The commerce nav carries two more items (Shop + the cart) and genuinely
 * cannot afford the spine's roomier numbers above — scoped to it specifically
 * (:has(.woocommerce-nav)) rather than to every nav below 400px, which is the
 * mis-scoping #1500 traced the "still too narrow" report back to.
 * Swept twice, because the two bar states have different width budgets: the
 * SHRUNK pill loses an extra 2×--hdr-pill-inset (20px at this width) to its
 * own floating inset that the at-rest full-bleed band never pays. At rest,
 * pad:7/gap:6 is the ceiling (pad:8/gap:7 overflows); shrunk, that same pair
 * already overflows and pad:6/gap:6 is the true ceiling. pad:6/gap:5 clears
 * both states with a pixel of margin rather than sitting exactly on the
 * shrunk ceiling. */
@media (max-width:400px){
  .ev26 .bar .wrap{padding-left:12px;padding-right:12px}
  .ev26 .bar nav:has(.woocommerce-nav),
  .ev26 .bar.big.shrunk nav:has(.woocommerce-nav){--nav-pad:6px;--ul-gap:5px}
}
/* ── section rule with hairline draw ───────────────── */
/* Section head. No top rule and no hover wipe: Eric asked for the animated
   lines above sections to go, and a heading that already sits in its own space
   does not need a line drawn over it to say a section started. */
:where(.ev26) .shead{display:flex;align-items:center;gap:18px;margin:0 0 44px;position:relative;padding-top:0}
/* The accent line that fills the rest of the row, right of the heading --
   the same short-dash-before-a-label language the .lab-kind/.ux-kind micro
   marker already uses, stretched to flex:1 instead of a fixed 28px. It is the
   row's own ::after, not a child of the heading, so it works identically on
   template-authored .shead (an <i> sibling already existed here, dormant --
   left alone) and on the Gutenberg-authored .is-style-ev26-shead groups on
   /ux/, which have no such element to hook into. */
:where(.ev26) :is(.shead,.is-style-ev26-shead,.art-thread)::after{
  content:"";flex:1 1 auto;height:1px;background:var(--accent);
  align-self:center;min-width:24px;
}
/* THE GROUP-HEADER RUNG — declared once, for every section header in the theme.
 *
 * #1450. This used to be `.shead h3` at a hardcoded 14.5/700, and four landing
 * pages each restated the identical numbers to promote their own h2 into it:
 * v26-lab.css, v26-shop.css, v26-art.css and v26-ux.css. Five copies of one
 * declaration, which is why nobody noticed the number was smaller than body
 * text. The numbers now live in css/v26.css as --shead-*; this is their only
 * consumer, so there is exactly one place left to change them.
 *
 * The selector list is the set of section-header CONTAINERS this theme has —
 * they differ only because of where the markup comes from, not because they
 * are different things:
 *   .shead                  theme templates (front-page, lab, art, shop)
 *   .is-style-ev26-shead    a core Gutenberg group block style (/ux/)
 *   .art-thread             the timeline group on /art/
 * Descendant, not child: a core group block puts a
 * `.wp-block-group__inner-container` between the group and its heading on the
 * two block-authored surfaces, so `>` silently matches neither.
 *
 * The tag is whatever the document outline demands — h2 under an h1 page
 * title, h3 on the homepage — because the rung is a fact about the COMPONENT,
 * and the tag is a fact about the outline. They are allowed to disagree.
 *
 * Kept at (0,1,1) so a page layer can still override with one class, and
 * `margin:0` is stated because basetags.css loads later and puts --space-m
 * above and below every bare h2 at (0,0,1). */
:where(.ev26) :is(.shead,.is-style-ev26-shead,.art-thread) :is(h2,h3){
  font-family:var(--serif);font-size:var(--shead-size);font-weight:var(--shead-weight);
  letter-spacing:var(--shead-track);line-height:var(--shead-lh);color:var(--ink);margin:0;
}
/* THE SUB-GROUP RUNG. One rung below the header above it, for a label that
   divides a group without starting a new one (the four kinds inside Projects).
   Size and weight only — 1.58x smaller and 600 against 700 — because colour is
   not a hierarchy lever here: both rungs stay var(--ink) so the level survives
   dark mode, the accent wash and a greyscale print. */
/* :is(h2,h3), not a bare h3 -- the rung one up (.shead's own :is(h2,h3), just
   above) is tag-agnostic for exactly this reason: the rung is a fact about
   the COMPONENT, the tag is a fact about the OUTLINE, and they may disagree.
   #1495 needed this for the home bio's greeting, now the first heading in the
   document and h2 for that reason -- an h3 there would have opened the
   outline at level three from nothing. */
:where(.ev26) .shead-sub :is(h2,h3){
  font-family:var(--sans);font-size:var(--shead-sub-size);font-weight:var(--shead-sub-weight);
  letter-spacing:var(--shead-sub-track);line-height:var(--shead-sub-lh);color:var(--ink);margin:0;
}
:where(.ev26) .shead i{display:none}
:where(.ev26) .shead b{font-size:var(--t-meta);font-weight:500;color:var(--faint)}
:where(.ev26) .shead .go{font-size:var(--t-meta);font-weight:600;letter-spacing:var(--tr-ui);color:var(--accent)}
/* ── grid of hairline cells ────────────────────────── */
/* Borders, deliberately sparse.
 * The hairline box around every grid cell read as scaffolding over the
 * gradient ground -- four rules per card, none of them carrying information.
 * Separation is space now; the only rules left are the ones doing a job:
 * a section's top rule, a row divider, and the accent bar that appears on
 * hover to show which cell is live. */
:where(.ev26) .grid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}
:where(.ev26) .grid.two{grid-template-columns:repeat(2,1fr)}
:where(.ev26) .grid.four{grid-template-columns:repeat(4,1fr)}
:where(.ev26) .cell{padding:22px 22px 26px;border-radius:0 0 var(--r-m) var(--r-m);display:flex;flex-direction:column;position:relative;transition:background var(--med) var(--ease),box-shadow var(--med) var(--ease),transform .28s var(--spring)}
/* #1500 — a press state. Every other control on the site registers a click
   (nav pill, chip, button); the grid cards, the thing you click most, did
   not. Same gesture as the rest: scale down, snappier than the release. */
:where(.ev26) .cell:active{transform:scale(.98);transition-duration:.08s}
/* Hover raise. --paper is a translucent lift now, so the ground tints it: on
   the home wash the hovered cell goes warm off-white, on a section page cool
   off-white, in dark a shade lighter than black. The cast shadow is what says
   "above" — without it a translucent fill reads as a wash painted ON the
   ground rather than a card lifted off it. Still no border. */
:where(.ev26) .cell:hover{background:var(--paper);box-shadow:var(--raise)}
:where(.ev26) .cell::after{content:"";position:absolute;left:0;right:0;top:0;height:2px;background:var(--accent);transform:scaleX(0);transform-origin:left;transition:transform var(--med) var(--ease)}
:where(.ev26) .cell:hover::after{transform:scaleX(1)}
:where(.ev26) .cell figure{margin:14px 0 16px;overflow:hidden;border-radius:var(--r-s);aspect-ratio:4/3;background:var(--sunken)}
:where(.ev26) .cell figure img{width:100%;height:100%;object-fit:cover;filter:grayscale(1) contrast(1.06);transition:filter var(--slow) var(--ease),transform var(--slow) var(--ease)}
:where(.ev26) .cell:hover figure img{filter:grayscale(0) contrast(1);transform:scale(1.03)}
:where(.ev26) .cell h4{margin-bottom:8px;transition:color var(--fast) var(--ease)}
:where(.ev26) .cell:hover h4{color:var(--accent)}
:where(.ev26) .cell p{margin:0;font-size:var(--t-meta);line-height:1.55;color:var(--muted)}
:where(.ev26) .cell .foot{margin-top:auto;padding-top:16px;display:flex;gap:10px;align-items:center}
/* Date badge. This was the same bug seen from the other side: --sunken used to
   be an opaque grey (a sticker on the home wash), so it was hand-patched to a
   one-off `color-mix(--ink 7%)` — which then read as a grey lozenge on the
   opaque paper card. Both symptoms were "a surface that does not know what it
   is over". --sunken IS that recess relationship now, so the badge simply uses
   it and rejoins the system: warm on the wash, cool on a section page, and a
   shade below black in dark. No local colour of its own. */
:where(.ev26) .tag{font-family:var(--sans);font-size:var(--t-label);font-weight:700;letter-spacing:var(--tr-label);text-transform:uppercase;color:var(--muted);background:var(--sunken);border-radius:999px;padding:5px 11px}
/* ── buttons ───────────────────────────────────────── */
:where(.ev26) .btn{display:inline-flex;align-items:center;gap:9px;font-family:var(--sans);font-size:var(--t-meta);font-weight:700;letter-spacing:var(--tr-ui);padding:12px 22px;border-radius:999px;background:var(--ink);color:var(--bg);border:1px solid var(--ink);transition:transform var(--fast) var(--ease),background var(--fast) var(--ease)}
:where(.ev26) .btn:hover{transform:translateY(-1px)}
:where(.ev26) .btn.ghost{background:transparent;color:var(--ink);border-color:var(--line-strong)}
:where(.ev26) .btn.ghost:hover{border-color:var(--ink)}
:where(.ev26) .btn.accent{background:var(--accent);border-color:var(--accent);color:var(--on-accent)}
:where(.ev26) .more{display:inline-flex;align-items:center;gap:9px;font-size:var(--t-meta);font-weight:700;letter-spacing:var(--tr-ui);color:var(--ink)}
:where(.ev26) .more i{width:18px;height:1.5px;background:currentColor;transition:width var(--med) var(--ease)}
:where(.ev26) a.more:hover i,:where(.ev26) *:hover>.more i{width:32px}
/* ── slab: a lifted surface, not a coloured panel ──── */
/* Translucent lift + cast shadow, same as a hovered cell. Over the home wash
   the slab warms; over a section ground it stays cool; in dark it lightens. */
:where(.ev26) .slab{background:var(--paper);box-shadow:var(--raise);border-radius:var(--r-m);padding:34px 38px}
/* .inverse is the one surface that is deliberately OPAQUE: it is a figure/
   ground flip, not a raised card, and it is already relational — --ink over
   --bg, which swaps automatically in dark. Left alone on purpose. */
:where(.ev26) .inverse{background:var(--ink);color:var(--bg);border-radius:var(--r-l);padding:64px 48px;background-image:var(--grain);background-size:380px;background-blend-mode:soft-light}
:where(.ev26) .inverse h2,:where(.ev26) .inverse h1{color:var(--bg)}
:where(.ev26) .inverse p{color:color-mix(in oklab,var(--bg) 62%,transparent)}
:where(.ev26) .inverse .btn{background:var(--bg);color:var(--ink);border-color:var(--bg)}
:where(.ev26) .rise{opacity:0;transform:translateY(14px);animation:rise .6s var(--ease) forwards}
@keyframes rise{to{opacity:1;transform:none}}
/* ── additions beyond the prototype ─────────────────── */
/* Suppress the .42s transition for one frame when the page loads already
   scrolled, so the bar does not animate from full-bleed to pill on arrival.
   js/header.js adds and immediately removes this. */
:where(.ev26) .bar.no-anim,
:where(.ev26) .bar.no-anim *{transition:none!important}
/* The nav is a real wp_nav_menu(), so it is a <ul> of <li>. The prototype used
   bare <a> children. Flatten the list so the pills lay out identically. */
:where(.ev26) .bar nav ul{display:flex;gap:var(--ul-gap);align-items:center;list-style:none;margin:0;padding:0}
:where(.ev26) .bar nav li{margin:0;padding:0}
:where(.ev26) .bar nav li a{display:inline-flex;align-items:center;gap:7px}
/* WordPress marks the active item; the prototype called it nav a.on. */
/* Cart count bubble sits on the cart icon in the Woo nav. */
:where(.ev26) .bar nav .cart-count{font-size:var(--t-label);font-weight:700;line-height:1;padding:3px 6px;border-radius:999px;background:var(--accent);color:var(--on-accent)}
:where(.ev26) .bar nav svg{width:18px;height:18px;fill:currentColor}
/* Links inside the bar must not carry basetags' underline. Written with real
   specificity rather than :where(), which contributes none and loses to the
   plain `a` rule in basetags.css. */
.ev26 .bar nav a,
.ev26 .bar .lockup{text-decoration:none}
/* Home hero -- layout half only; Stage 4 adds the deck, buttons and plotter.
 *
 * Deliberately NOT :where(). basetags.css carries `h1:not(.show){display:none}`
 * — a real rule at specificity (0,1,1) that hid the old dummy heading — so a
 * zero-specificity selector loses and the headline renders invisible. .ev26
 * plus .home-hero clears it honestly, without touching the legacy file or
 * bolting `.show` onto the markup. The rule dies with basetags in Stage 11. */
.ev26.home-hero{padding:70px 0 84px}
.ev26.home-hero h1{display:block;max-width:13ch;margin:0;color:var(--ink);font-size:var(--t-d-page);line-height:.98}
@media (max-width:900px){
  .ev26.home-hero{padding:44px 0 56px}
}