/*
Theme Name: Nuastro
Theme URI: https://nuastro.com
Author: Nuastro
Author URI: https://nuastro.com
Description: The Nuastro brand block theme — one sky, in ink, bone, and a single thread of gold. Cormorant Garamond, EB Garamond, Inter. Full-site editing; the interactive tools embed via the Nuastro Widgets plugin ([nuastro tool="..."]).
Version: 0.9.35
Requires at least: 6.4
Tested up to: 6.8
Requires PHP: 7.4
License: Copyright Nuastro — all rights reserved
License URI: https://nuastro.com
Text Domain: nuastro
Tags: full-site-editing, block-styles, custom-colors, editor-style, blog, one-column, wide-blocks
*/

/* ⚠️ THE HEADER'S RULE AND THE FIRST LINE OF THE PAGE ARE 1.25rem APART, AND
   NOTHING ELSE IS ALLOWED IN BETWEEN (Elle, 8 Sep 2026: "halfen the distance
   between the header and the first title on all pages. it's still too big").

   WordPress puts `--wp--style--block-gap` (24px) between every pair of
   top-level blocks, which included the header part and `main` — so the real
   distance was always the template's own padding PLUS 24px that nothing in the
   templates mentioned. Every attempt to tune this by editing padding alone was
   therefore tuning the smaller half. The gap above `main` is zeroed here, and
   the padding on `main` (or `--nu-page-top`, in the widgets) is now the whole
   distance and says so.

   ⚠️ SCOPED TO `main`. The same gap separates every other top-level block on
   the page, the footer included, and those are correct. */
.wp-site-blocks > main { margin-block-start: 0; }

/* theme.json handles the design system; these are the few brand touches it
   can't express (small-caps eyebrow, letterspaced nav, the gold-underline link
   motion). Kept minimal on purpose. */

.nu-eyebrow {
  font-family: var(--wp--preset--font-family--ui);
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--wp--preset--color--gold);
}

/* Gold italic emphasis inside display headings — mirrors the React `.display em`
   (the coloured words, e.g. "The sky moved. Your astrology didn't."). */
h1 em,
h2 em,
h3 em,
.has-display-font-family em {
  font-style: italic;
  color: var(--wp--preset--color--gold-bright);
}

/* Wordmark + primary nav */
.wp-block-navigation {
  font-family: var(--wp--preset--font-family--ui);
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.wp-block-navigation .wp-block-navigation-item__content { color: var(--wp--preset--color--paper-dim); }
.wp-block-navigation a:hover .wp-block-navigation-item__content,
.wp-block-navigation .current-menu-item .wp-block-navigation-item__content { color: var(--wp--preset--color--paper); }

/* Buttons: the "Begin" gold pill already comes from theme.json; ghost variant.
   The outline uses paper-faint (not the near-invisible 'line' hairline) so the
   clickable boundary meets WCAG 1.4.11 non-text contrast. */
.is-style-ghost .wp-block-button__link {
  background: transparent;
  color: var(--wp--preset--color--paper);
  box-shadow: inset 0 0 0 1px var(--wp--preset--color--paper-faint);
}
.is-style-ghost .wp-block-button__link:hover {
  box-shadow: inset 0 0 0 1px var(--wp--preset--color--gold);
  color: var(--wp--preset--color--gold-bright);
}

/* ── Footer — begun as a 1:1 port of the React site (.footer in global.css) ──
   A precise 5-column grid (not wp:columns flex), transparent over the
   starfield, gold micro-labels, serif-italic tagline, tight fit-content
   hover targets. Tokens map React→theme: --ui→ui, --serif→display,
   --reading→reading, --line-soft→line-soft, --gutter→the same clamp().

   ⚠️ THE TWO HAVE DRIFTED, AND THIS ONE IS THE LIVE FOOTER. The React site's
   has four blocks and different links (no Socials, its own routes); this has
   five. Do not "re-sync" them by copying either way — the WordPress theme is
   what visitors see, and global.css compiles into the widgets' bundle, so
   editing it there costs a plugin rebuild and upload for no visible gain. */
.nu-footer { border-top: 1px solid var(--wp--preset--color--line-soft); background: transparent; }
.nu-footer-inner {
  display: grid;
  grid-template-columns: 1.6fr 1fr 1fr 1fr 1.3fr;
  gap: 3rem;
  max-width: 1240px;
  margin: 0 auto;
  /* ⚠️ THE TOP PADDING IS HALF OF THE GAP ABOVE THE FOOTER, AND IT WAS THE
     HALF NOBODY LOOKED AT (Elle, 1 Sep 2026: "check the distance between
     content and footer, some pages have inadequate gaps and look awkward...
     they need to be smaller"). A page's last section already ends in up to
     5rem of its own padding; 4.5rem on top of that put ~150px of nothing
     between the last line and the first footer heading, on every page at once.
     It was also a FIXED value, so a phone got the whole 4.5rem under a section
     that had shrunk to 2.75rem, which is where it looked worst.

     Clamped now, and roughly half at the small end. Changing it here changes
     every page: the footer is one template part. Do not chase this gap per
     page. */
  padding: clamp(2rem, 4.5vw, 3.2rem) clamp(1.25rem, 4vw, 4rem) clamp(1.6rem, 3.5vw, 2.6rem);
}
.nu-footer-word {
  font-family: var(--wp--preset--font-family--ui);
  font-weight: 600; font-size: 0.8rem; letter-spacing: 0.42em;
  text-transform: uppercase; color: var(--wp--preset--color--paper); margin: 0;
}
.nu-footer-line {
  margin: 0.9rem 0 0;
  font-family: var(--wp--preset--font-family--display); font-style: italic;
  font-size: 1.15rem; color: var(--wp--preset--color--paper-dim);
}
.nu-footer-col { display: grid; gap: 0.7rem; align-content: start; }
.nu-footer-col h2 {
  font-family: var(--wp--preset--font-family--ui);
  font-weight: 500; font-size: 0.62rem; letter-spacing: 0.3em;
  text-transform: uppercase; color: var(--wp--preset--color--gold); margin: 0 0 0.5rem;
}
.nu-footer-col a {
  font-family: var(--wp--preset--font-family--reading);
  font-size: 0.95rem; color: var(--wp--preset--color--paper-dim);
  text-decoration: none; transition: color 0.4s; width: fit-content;
}
.nu-footer-col a:hover { color: var(--wp--preset--color--paper); }
/* Socials column — icon row, ready for real channel links (href set in footer.html). */
.nu-footer-social-row { display: flex; gap: 1.05rem; margin-top: 0.15rem; }
.nu-footer-social-row a {
  display: inline-flex; color: var(--wp--preset--color--paper-dim);
  transition: color 0.4s; width: auto;
}
.nu-footer-social-row a:hover { color: var(--wp--preset--color--gold); }
.nu-footer-social-row svg { display: block; }
.nu-footer-base {
  display: flex; justify-content: space-between; gap: 1rem;
  max-width: 1240px; margin: 0 auto;
  padding: 1.4rem clamp(1.25rem, 4vw, 4rem) 2.2rem;
  border-top: 1px solid var(--wp--preset--color--line-soft);
  font-family: var(--wp--preset--font-family--ui);
  font-size: 0.64rem; letter-spacing: 0.14em; color: var(--wp--preset--color--paper-faint);
}
@media (max-width: 880px) {
  .nu-footer-inner { grid-template-columns: 1fr 1fr; gap: 2.4rem; }
  .nu-footer-brand { grid-column: 1 / -1; }
}
/* ⚠️ THE PHONE KEEPS TWO COLUMNS (Elle, 9 Sep 2026: "adjust the mobile footer,
   it's long and looks bad").

   It used to collapse to ONE at 480px, and one column of four stacked blocks
   made the footer 976px tall on a 375px screen — 1.2 screens of links under
   every page on the site, so the last thing anyone saw was a list that would
   not end. Four blocks in a 2×2 under the brand is a shape you take in at a
   glance, and NOTHING WAS HIDDEN to get there: every link that was in the
   footer is still in it.

   The rest is weight rather than structure. The tagline is set in the display
   face at 1.15rem, a size drawn to be the last word on a wide page; on a phone
   it was simply the biggest thing down there. The link rhythm closes up to
   match a column half as wide. */
@media (max-width: 560px) {
  .nu-footer-inner {
    grid-template-columns: 1fr 1fr;
    gap: 1.9rem 1.1rem;
    padding-bottom: 1.2rem;
  }
  .nu-footer-brand { grid-column: 1 / -1; }
  .nu-footer-line { font-size: 1rem; margin-top: 0.55rem; }
  /* ⚠️ THE SPACE BETWEEN LINKS BECOMES PART OF THE LINKS. The rhythm is
     identical either way — the column is exactly as tall — but as `gap` the
     space belongs to nobody, and a footer link is then a 24px target on a
     touch screen. Moved into the anchor's own padding it is a ~34px one, for
     no extra height. `width: fit-content` keeps the hover box tight to the
     words, so this only ever grows the target downwards and up. */
  .nu-footer-col { gap: 0; }
  .nu-footer-col a { padding: 0.28rem 0; }
  .nu-footer-col h2 { margin-bottom: 0.3rem; }
  /* ⚠️ AND THE LINKS STAY ON ONE LINE EACH. "Terms & Conditions" is the widest
     thing in the footer and it has ~150px to live in now; at 0.95rem it wrapped
     and dragged its whole column a row taller than its neighbour, which is the
     ragged look this was meant to fix. */
  .nu-footer-col a { font-size: 0.86rem; }
  .nu-footer-base {
    flex-direction: column; gap: 0.5rem;
    padding-top: 1.1rem; padding-bottom: 1.7rem;
  }
}

/* Reading measure for post/prose content */
.nu-prose { font-family: var(--wp--preset--font-family--reading); }

/* ── Reading width + type ──────────────────────────────────────────────
   These ALSO live in theme.json, but WordPress compiles theme.json into a
   global-styles block that hosts like WordPress.com cache very aggressively —
   so a theme update can leave the OLD narrow width / small font in place.
   style.css cache-busts reliably by version, so declaring them here guarantees
   the readable size ships even when the theme.json cache is stale. */
/* `html body` so it beats WP's `body{}` global-styles rule regardless of which
   stylesheet the host prints last. Font FAMILY is pinned here too (not only in
   theme.json) so the body always renders in EB Garamond (the brand reading
   face) even when WP.com serves a stale theme.json — a delicate Cormorant
   fallback is the "hard to read" symptom we're guarding against. */
/* ⚠️ THE PAGE NEVER PANS SIDEWAYS (Elle, 3 Sep 2026: on a small phone "when
   the thumb touches the screen it shifts to the left or right"). One element a
   few pixels past the right edge makes the WHOLE document wider than the
   phone, and from then on every vertical scroll is also a horizontal drag. The
   site is a single column that fits, so the axis is closed at the root and the
   page stays put under the thumb.

   ON `html` AND NOT ON `body`: overflow on the root propagates to the viewport,
   which is what stops the pan. Adding it to `body` as well would turn the body
   into its own scroll container and quietly break every `position: sticky` on
   the site. Anything genuinely wide — a widget's ticker, a reading's table —
   still scrolls INSIDE its own box with `overflow-x: auto`, which is
   unaffected. The same rule is in the widgets' global.css; both surfaces need
   it, because a member meets this site through both. */
html { overflow-x: hidden; }

html body {
  font-size: 1.2rem;
  line-height: 1.72;
  font-family: var(--wp--preset--font-family--reading);
}
/* ── Headings below display size are set in the READING face ──────────────
   Same rule as the widgets' --title token (see the note at the top of
   src/embed/widget.css): Cormorant Garamond is a display Garamond, and below
   about 1.5rem its hairline strokes shimmer rather than read — which no amount
   of weight can fix, because weight thickens the thick strokes and leaves the
   thin ones exactly where they were. So h1/h2 keep Cormorant, and h3/h5/h6 —
   the sub-headings inside an article, which are read rather than looked at —
   take EB Garamond one weight up. h4 was already Inter (a small-caps label)
   and is untouched.

   Declared HERE as well as in theme.json for the reason given above: WP.com
   caches the compiled global-styles block hard, and style.css cache-busts by
   version. Keep the two in step.

   h2's floor is raised from 1.35rem to 1.5rem for the same reason: on a phone
   the clamp used to land it under the display floor, in the one face that
   cannot go there. */
html h3,
html h5,
html h6 {
  font-family: var(--wp--preset--font-family--reading);
  font-weight: 500;
}
html h3 { line-height: 1.3; }
html h5 { line-height: 1.3; }
html h6 { line-height: 1.35; }
html h2 { font-size: clamp(1.5rem, 2.6vw, 2rem); }
/* Roomier reading column on articles, blog listings and content pages — NOT the
   full-bleed homepage (excluded via :not(.home)). Overrides the constrained
   content-size var for those page types, AND caps the constrained children
   directly (higher specificity than WP's layout rule) so it wins even if the
   host doesn't drive width through the var. Widened 820→980px so the column
   fills a wide screen (less side whitespace) while staying readable. */
/* Reading-column pages ONLY — posts, blog listings, archives, search. NOT the
   tool pages (Birth Chart etc., which use the wide template and must stay wide),
   and NOT generic pages. Those keep their template's own width. */
.single main.wp-block-group,
.blog main.wp-block-group,
.archive main.wp-block-group,
.search main.wp-block-group { --wp--style--global--content-size: 980px; }
.single main.wp-block-group.is-layout-constrained > :where(:not(.alignwide):not(.alignfull):not(.alignleft):not(.alignright)),
.blog main.wp-block-group.is-layout-constrained > :where(:not(.alignwide):not(.alignfull):not(.alignleft):not(.alignright)),
.archive main.wp-block-group.is-layout-constrained > :where(:not(.alignwide):not(.alignfull):not(.alignleft):not(.alignright)),
.single .wp-block-post-content.is-layout-constrained > :where(:not(.alignwide):not(.alignfull):not(.alignleft):not(.alignright)) {
  max-width: 980px;
}
/* Body copy on reading pages: pin EB Garamond + a touch more size/contrast so
   long-form articles read cleanly at the wider measure. */
.single .wp-block-post-content p,
.single .wp-block-post-content li,
.blog .wp-block-post-content p,
.archive .wp-block-post-content p {
  font-family: var(--wp--preset--font-family--reading);
  font-size: 1.25rem;
  line-height: 1.75;
}

/* ── Tool pages: the [nuastro] widget runs full-bleed, like the homepage
   horoscope ──────────────────────────────────────────────────────────────
   Each tool page (Birth Chart, Love Chart, Transits, Moon) embeds ONE widget
   that renders its own full layout and centres its content at 1240px inside
   its shadow root. It's meant to span the page like the homepage daily-sky.
   The `page-wide` template does this via post-content align:full — but pages
   assigned the DEFAULT template box the widget into the 780px reading column
   (that's the "tool too narrow" symptom). Break the widget wrapper out to the
   full viewport regardless of template; its own inner max-width re-centres
   everything. Verified at 1280px + 375px: full-bleed, content centred, no
   horizontal scroll.
   - `main ` prefix + !important beats WP's constrained rule, whose child
     margins are `auto !important`.
   - the :has() guard clips the 100vw-vs-scrollbar overflow on tool pages only
     (overflow:clip, not hidden — it neither scrolls nor breaks the fixed
     header, and shadow-DOM sticky is unaffected). */
main .wp-block-post-content > .nuastro-rg,
main .entry-content > .nuastro-rg {
  width: 100vw;
  max-width: 100vw !important;
  margin-left: calc(50% - 50vw) !important;
  margin-right: calc(50% - 50vw) !important;
}
main:has(> .wp-block-post-content > .nuastro-rg),
main:has(> .entry-content > .nuastro-rg) { overflow-x: clip; }
/* A page that OPENS with a tool needs no template pad of its own — the widget
   brings its own opening space, and the two together read as a hole between the
   header and the tool's title. Only when the widget is the first thing on the
   page, so a page with prose above a tool keeps its breathing room. */
main:has(> .wp-block-post-content > .nuastro-rg:first-child),
main:has(> .entry-content > .nuastro-rg:first-child) { padding-top: 0 !important; }

/* ═══════════════════════════════════════════════════════════════════════
   The quiet sky (starfield). A fixed canvas of faint breathing stars behind
   all content (assets/js/starfield.js). Content sits above it; the full-width
   section bands go transparent so it shows through, while the header/footer
   and raised ink-2 cards keep their surfaces — the same layering as the React
   site. The body keeps its ink (from theme.json), behind the canvas.
   ═══════════════════════════════════════════════════════════════════════ */
.nu-starfield { position: fixed; inset: 0; width: 100%; height: 100%; z-index: 0; pointer-events: none; }
.wp-site-blocks { position: relative; z-index: 1; background: transparent; }
.wp-block-group.alignfull.has-ink-background-color,
.wp-block-group.alignfull.has-ink-3-background-color { background-color: transparent !important; }


/* ═══════════════════════════════════════════════════════════════════════
   Runtime light/dark toggle (the site's ☉/☽)

   The whole theme paints from --wp--preset--color--* custom properties, so
   redefining those on the <html> element retunes every block at once — no
   per-component rules needed. [data-theme] set by the toggle wins over
   theme.json. Both states are declared explicitly so the switch works even if
   the light "Atelier" style variation is the active base. Mirrors
   nuastro-site global.css.

   ⚠️ LIGHT IS THE GROUND EVERY VISITOR MEETS (9 Aug 2026) — see the long note
   in functions.php. theme.json still ships the dark palette as its base, which
   is what paints when nothing has set [data-theme]: the block editor, and a
   browser with JavaScript off. The `:not([data-theme])` rule below is what
   makes those two cases daylight as well, so no reader ever lands on a ground
   the house no longer defaults to. It is a base, not an override: the moment
   the head script stamps [data-theme] (before first paint) the two explicit
   blocks take it.
   ═══════════════════════════════════════════════════════════════════════ */
:root[data-theme="dark"] {
  --wp--preset--color--ink: #0a0c11;
  --wp--preset--color--ink-2: #0d1017;
  --wp--preset--color--ink-3: #12161f;
  --wp--preset--color--paper: #e9e4d8;
  --wp--preset--color--paper-dim: #b7b1a4;
  --wp--preset--color--paper-faint: #837e73;
  --wp--preset--color--gold: #c7a468;
  --wp--preset--color--gold-bright: #e0c088;
  --wp--preset--color--gold-dim: rgba(199, 164, 104, 0.4);
  --wp--preset--color--crimson: #b76a60;
  --wp--preset--color--line: rgba(233, 228, 216, 0.13);
  --wp--preset--color--line-soft: rgba(233, 228, 216, 0.07);
  color-scheme: dark;
}
:root:not([data-theme]),
:root[data-theme="light"] {
  --wp--preset--color--ink: #efeadd;               /* warm bone paper — the ground */
  --wp--preset--color--ink-2: #eae4d6;             /* raised surface */
  --wp--preset--color--ink-3: #e6dfd0;             /* deepest recess */
  --wp--preset--color--paper: #171a21;             /* the figure: near-black ink */
  --wp--preset--color--paper-dim: #4c4940;         /* body copy */
  --wp--preset--color--paper-faint: #686358;       /* captions, meta */
  --wp--preset--color--gold: #7b5f26;              /* gold deepens in daylight */
  --wp--preset--color--gold-bright: #5e4819;
  --wp--preset--color--gold-dim: rgba(123,95,38,0.5);
  --wp--preset--color--crimson: #9a4a3f;
  --wp--preset--color--line: rgba(23, 26, 33, 0.16);
  --wp--preset--color--line-soft: rgba(23, 26, 33, 0.085);
  color-scheme: light;
}

/* Brief cross-fade when the theme flips; the script removes it after 500ms. */
html.theme-fade,
html.theme-fade *,
html.theme-fade *::before,
html.theme-fade *::after {
  transition: background-color 0.45s cubic-bezier(0.16, 1, 0.3, 1),
    border-color 0.45s cubic-bezier(0.16, 1, 0.3, 1),
    color 0.45s cubic-bezier(0.16, 1, 0.3, 1),
    fill 0.45s cubic-bezier(0.16, 1, 0.3, 1),
    box-shadow 0.45s cubic-bezier(0.16, 1, 0.3, 1) !important;
}

/* ⚠️ ONE FAMILY: every round control in the header is a GOLD RING with a GOLD
   glyph and nothing inside it (Elle, 4 Aug 2026, after the account medallion).
   `.nu-icon-btn` (the bell AND the Store), `.nu-account-mark` and `.nu-nav-toggle`
   share the ring, the size and the hover; they were
   previously a mix of near-invisible `--line` hairlines and paper-dim glyphs.

   The Store was the last holdout — a filled gold pill — and Elle asked for it
   to join the set as a basket glyph, so the header now has NO filled control
   at all. That is the intended look; see `.nu-store-btn` below. */

/* ⚠️ BORDER-BOX ON ALL OF THEM, OR THEY ARE NOT THE SAME SIZE. `<button>` picks up
   border-box from the UA/theme reset but `<a>` and `<span>` do not, so the
   account mark and the Store basket measured 32px against the button rings'
   30px — a 2px mismatch, invisible in code and obvious in a row of
   circles. Measured in the static replica; do not drop this. */
.nu-icon-btn,
.nu-account-mark,
.nu-nav-toggle { box-sizing: border-box; position: relative; }
/* ⚠️ NO ROTATION HERE ANY MORE (10 Aug 2026). The 40° tilt was written for a
   ☉/☽ glyph, where a circle rotating inside a circle reads as the sky turning.
   The mark is a bulb now, and a bulb at 40° reads as a broken one.
   The swell that replaced it has in turn been replaced by the shared 2px lift
   every ring in this bar now takes (28 Aug 2026); the bulb's own gesture is
   the glow. Both live in "The rings answer the hand" further down. */
@media (prefers-reduced-motion: reduce) {
  html.theme-fade, html.theme-fade *, html.theme-fade *::before, html.theme-fade *::after { transition: none !important; }
}

/* ⚠️ THE "THERE IS A NIGHT SKY TOO" CARD AND ITS RULES ARE GONE (9 Sep 2026).
   It introduced the night theme to a visitor meeting a daylight house; the
   house ground is night, it stopped being enqueued on 9 Sep, and the control it
   pointed at — the header's theme ring — has now been removed as well, so there
   is nothing left for it to point AT. assets/js/theme-hint.js is deleted with
   it rather than left as a file that cannot run. */

/* ═══════════════════════════════════════════════════════════════════════
   Header parity with the React site (Nav.jsx): the orbit wordmark, the
   Sign-in medallion, and a sticky bar that settles on scroll.
   ═══════════════════════════════════════════════════════════════════════ */
/* Fixed (not sticky) so it ALWAYS follows on scroll — sticky silently fails in
   WP when any ancestor clips overflow / with the admin bar. Fixed is immune to
   both. A spacer (--nu-header-h, JS-measured; CSS fallback) keeps content clear. */
.nu-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  transition: background-color 0.4s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
/* Push the page content below the fixed header. The header lives inside
   .wp-site-blocks (out of flow), so padding the container clears its siblings. */
.wp-site-blocks { padding-top: var(--nu-header-h, 68px); }
/* Anchored links land below the bar, not under it. */
:root { scroll-padding-top: var(--nu-header-h, 68px); }
/* Logged-in: sit below the WordPress admin bar instead of under it. */
body.admin-bar .nu-header { top: 32px; }
@media (max-width: 782px) { body.admin-bar .nu-header { top: 46px; } }
@media (max-width: 782px) { body.admin-bar .nu-bell-panel { top: calc(var(--nu-header-h, 68px) + 52px); } }
/* On scroll: a faint translucent settle (JS toggles .is-scrolled). */
.nu-header.is-scrolled {
  background-color: color-mix(in srgb, var(--wp--preset--color--ink) 88%, transparent);
  backdrop-filter: saturate(1.2) blur(10px);
  box-shadow: 0 1px 0 var(--wp--preset--color--line);
}

/* The header bar — brand · nav · actions, authored HTML for full control. */
/* No padding-inline here on purpose. The gutter belongs to .nu-header (see
   parts/header.html), which pads the full-bleed band and constrains this bar to
   the canonical 1240px column — the same rail as the widgets, the pricing band
   and .nu-footer-inner. It used to carry clamp(1rem, 4vw, 2rem) INSIDE a
   1180px cap, which put the wordmark 30-62px right of every content column
   (the exact figure depended on whether this raw div computed border-box or
   content-box, and the theme ships no reset). */
.nu-headbar { display: flex; align-items: center; justify-content: space-between; gap: 1.5rem; }
.nu-brand { display: inline-flex; align-items: center; gap: 0.6rem; text-decoration: none; flex: 0 0 auto; }
.nu-word {
  font-family: var(--wp--preset--font-family--display);
  font-weight: 500; font-size: clamp(1.2rem, 1rem + 1.2vw, 1.35rem); letter-spacing: 0.02em;
  color: var(--wp--preset--color--paper); line-height: 1;
}

/* Orbit mark — the ☉ ring + dot to the left of the wordmark. */
.nu-mark {
  display: inline-flex;
  align-items: center;
  color: var(--wp--preset--color--gold);
  line-height: 0;
}
.nu-mark svg { display: block; }

/* Primary nav (desktop: inline centered; mobile: dropdown, see @media). */
/* The menu sits to the RIGHT, beside the controls — the React Nav's shape
   (.nav-links { margin-left: auto }), not a centred bar. */
.nu-nav { flex: 1 1 auto; display: flex; justify-content: flex-end; margin-right: 1.8rem; }
.nu-nav-list {
  display: flex; align-items: center; gap: 1.6rem;
  list-style: none; margin: 0; padding: 0;
}
.nu-nav-list a {
  font-family: var(--wp--preset--font-family--ui);
  font-size: 0.72rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--wp--preset--color--paper-dim); text-decoration: none;
  white-space: nowrap;
  transition: color 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}
.nu-nav-list a:hover, .nu-nav-list a[aria-current="page"] { color: var(--wp--preset--color--paper); }
/* ── The Horoscope item's unread dot (assets/js/horo-dot.js) ──────────────
   Gold, not crimson: an invitation to a page that is new today, while the
   bell keeps crimson for things addressed to the member. The halo trick is
   the bell dot's, for the same retina reason. */
.nu-nav-list a { position: relative; }
.nu-nav-dot {
  position: absolute;
  top: -1px;
  right: -9px;
  inline-size: 5px;
  block-size: 5px;
  border-radius: 50%;
  background: var(--wp--preset--color--gold-bright);
}

/* Right-side controls. */
.nu-actions { display: flex; align-items: center; gap: 1.1rem; flex: 0 0 auto; }
/* ⚠️ THE STORE IS A RING NOW, NOT A FILLED PILL (Elle, 4 Aug 2026). It was
   `.nu-begin`, a gold pill reading "Store"; it takes `.nu-icon-btn` and a
   basket glyph so the header is one family of rings end to end. `.nu-begin` is
   gone from the markup and its rules are deleted rather than left to rot —
   nothing else in the theme referenced it (checked parts/ and patterns/).

   ⚠️ An earlier note in this file said the pill was "deliberately excluded"
   from the ring family on Elle's instruction. She reversed that; do not
   restore the pill on the strength of a stale comment.

   The only thing `.nu-icon-btn` does not already cover is that this one is an
   <a>, so the underline has to go. */
.nu-store-btn { text-decoration: none; }
/* The Store's menu twin — phone only (see parts/header.html).
   ⚠️ IT WAS BRIEFLY IN THE DESKTOP BAR TOO (27 Aug 2026) and Elle took it
   straight back out the same day: the bar carries the basket ring and that is
   the whole of the Store up here. Do not re-add the written link on desktop. */
.nu-nav-store { display: none; }

/* Hamburger — hidden on desktop, shown on mobile. */
/* The menu button only appears on small screens, where it sits directly beside
   the bell, account and Store rings — so it takes the ring too, or the mobile
   header is one bare icon among three medallions. (It was written when the
   neighbours were the search and theme rings; both have since left the bar and
   the rule is unchanged, because what it is really saying is that nothing in
   this row is allowed to be the one control without a ring.) */
.nu-nav-toggle {
  display: none;
  inline-size: 30px; block-size: 30px; padding: 0;
  border: 1px solid var(--wp--preset--color--gold); border-radius: 50%;
  background: transparent; color: var(--wp--preset--color--gold);
  cursor: pointer; place-items: center;
  transition: color 0.35s cubic-bezier(0.16, 1, 0.3, 1), border-color 0.35s cubic-bezier(0.16, 1, 0.3, 1);
}
.nu-nav-toggle:hover { color: var(--wp--preset--color--gold-bright); border-color: var(--wp--preset--color--gold-bright); }
/* 18px was sized for a borderless 34px button; inside the 30px ring it leaves
   6px each side and reads as crowded. `currentColor` carries the gold down. */
.nu-nav-bars { display: grid; gap: 3.5px; width: 14px; }
.nu-nav-bars span { display: block; height: 1.5px; background: currentColor; border-radius: 2px; transition: transform 0.3s, opacity 0.2s; }
.nu-nav-toggle[aria-expanded="true"] .nu-nav-bars span:nth-child(1) { transform: translateY(5.5px) rotate(45deg); }
.nu-nav-toggle[aria-expanded="true"] .nu-nav-bars span:nth-child(2) { opacity: 0; }
.nu-nav-toggle[aria-expanded="true"] .nu-nav-bars span:nth-child(3) { transform: translateY(-5.5px) rotate(-45deg); }

/* Sign-in medallion + label (mirrors React's .nav-account). */
.nu-account {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  color: var(--wp--preset--color--paper-dim);
  text-decoration: none;
  transition: color 0.35s cubic-bezier(0.16, 1, 0.3, 1);
}
.nu-account:hover { color: var(--wp--preset--color--paper); }
/* ⚠️ OUTLINED, NOT FILLED (Elle, 4 Aug 2026). A gold ring and a gold
   silhouette, with nothing behind them: a filled gold coin was tried and read
   as too loud for a header.

   The ring is drawn from --wp--preset--color--gold rather than a literal, so
   it deepens correctly in daylight along with everything else. That is safe
   HERE, unlike on a filled medallion: an outline is a line like any other
   line on the page, so it should follow the theme.

   The original barely-there hairline used --line; the visibility this needed
   comes from the gold ring and the thicker stroke below, not from a fill. */
.nu-account-mark {
  inline-size: 30px;
  block-size: 30px;
  display: grid;
  place-items: center;
  border: 1px solid var(--wp--preset--color--gold);
  border-radius: 50%;
  background: transparent;
  transition: border-color 0.35s cubic-bezier(0.16, 1, 0.3, 1);
}
.nu-account:hover .nu-account-mark { border-color: var(--wp--preset--color--gold-bright); }
/* Both SVG shapes stroke with currentColor, so one colour on the <svg> lights
   head and shoulders together; set there rather than on the mark so the link's
   own :hover colour cannot drag it back to paper. */
.nu-account-mark svg { color: var(--wp--preset--color--gold); }
.nu-account:hover .nu-account-mark svg { color: var(--wp--preset--color--gold-bright); }
/* 1.2 was the original weight and is what made the silhouette hard to find.
   Thickened here rather than in the header markup. */
.nu-account-mark svg circle,
.nu-account-mark svg path { stroke-width: 1.6; }
/* Icon medallion — the header bell and Store buttons (match the account disc) */
.nu-icon-btn {
  inline-size: 30px;
  block-size: 30px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--wp--preset--color--gold);
  border-radius: 50%;
  background: transparent;
  color: var(--wp--preset--color--gold);
  cursor: pointer;
  transition: color 0.35s cubic-bezier(0.16, 1, 0.3, 1), border-color 0.35s cubic-bezier(0.16, 1, 0.3, 1);
}
.nu-icon-btn:hover { color: var(--wp--preset--color--gold-bright); border-color: var(--wp--preset--color--gold-bright); }
.nu-icon-btn:focus-visible { outline: 2px solid var(--wp--preset--color--gold); outline-offset: 2px; }
.nu-icon-btn svg { display: block; }

/* ── The rings answer the hand (Elle, 28 Aug 2026) ────────────────────────
   Motion for the header medallions: bell, profile, Store — and, when it was
   written, the search and bulb rings that have since left the bar.
   The brief was subtle and polished, not bouncing. So: ONE movement shared by
   every ring, so the bar still behaves as a single object, and one small
   gesture per glyph, so each control feels like the thing it is.

   Three states and no more. Hover lifts the ring 2px over a faint gold wash,
   press compresses it like a physical button, keyboard focus draws the gold
   outline. 180ms is the whole budget: long enough to read as movement, short
   enough that a pointer crossing the bar leaves no wake behind it.

   ⚠️ HOVER IS GUARDED BY `(hover: hover)`, THE PRESS IS NOT. A touch screen
   has no way to leave a hover, so unguarded, one tap would leave a ring lifted
   and washed until something else was touched. The press state stays outside
   the guard on purpose: it is the only feedback a finger ever gets.

   ⚠️ THE WASH AND THE SHADOW ARE MIXED FROM --gold, NEVER A LITERAL. Gold
   deepens in daylight, so a fixed rgba(128,97,31,…) would be a bright smear on
   ink and a muddy bruise on bone. Mixed from the token, one declaration is a
   soft glow at night and a warm shadow by day.

   ⚠️ THIS BLOCK SITS AFTER EVERY RING'S OWN RULES ON PURPOSE. `transition` is
   a shorthand: the new properties cannot be appended from up there, they have
   to be restated below where they win. Moving it earlier silently kills the
   lift and leaves the colour fade behind. */
.nu-icon-btn,
.nu-account-mark,
.nu-nav-toggle {
  transition:
    color 0.35s cubic-bezier(0.16, 1, 0.3, 1),
    border-color 0.35s cubic-bezier(0.16, 1, 0.3, 1),
    transform 180ms cubic-bezier(0.2, 0.8, 0.2, 1),
    background-color 180ms ease,
    box-shadow 180ms ease;
}
.nu-icon-btn svg,
.nu-account-mark svg {
  transition: transform 180ms cubic-bezier(0.2, 0.8, 0.2, 1);
}

@media (hover: hover) {
  .nu-icon-btn:hover,
  .nu-nav-toggle:hover,
  .nu-account:hover .nu-account-mark {
    transform: translateY(-2px);
    background-color: color-mix(in srgb, var(--wp--preset--color--gold) 8%, transparent);
    box-shadow: 0 5px 12px color-mix(in srgb, var(--wp--preset--color--gold) 20%, transparent);
  }

  /* One glyph, one gesture. Each is a single small motion that says what the
     control does, and each ends: nothing loops while a pointer rests on it,
     which is the difference between a header that responds and one that
     fidgets. */
  .nu-store-btn:hover svg { transform: translateY(-1.5px) scale(1.05); }  /* the basket comes off the shelf */
  .nu-account:hover .nu-account-mark svg { transform: scale(1.08); }
  /* A bell pivots where it hangs, not through its middle — hence the origin
     up at the crown. Swung from the centre it reads as a wobbling cup. */
  .nu-bell:hover svg {
    transform-origin: 50% 18%;
    animation: nu-bell-swing 450ms cubic-bezier(0.2, 0.8, 0.2, 1);
  }
  /* The bulb is the day/night switch, so it does the one thing a bulb does. */
}

@keyframes nu-bell-swing {
  0%, 100% { transform: rotate(0deg); }
  25% { transform: rotate(8deg); }
  50% { transform: rotate(-6deg); }
  75% { transform: rotate(3deg); }
}
@keyframes nu-bulb-glow {
  0%, 100% { transform: scale(1); filter: drop-shadow(0 0 0 transparent); }
  50% {
    transform: scale(1.1);
    filter: drop-shadow(0 0 4px color-mix(in srgb, var(--wp--preset--color--gold) 55%, transparent));
  }
}

/* Press. Shorter than the lift it interrupts, because a button that takes as
   long to go down as it took to rise does not read as pressed. */
.nu-icon-btn:active,
.nu-nav-toggle:active,
.nu-account:active .nu-account-mark {
  transform: translateY(0) scale(0.94);
  box-shadow: 0 2px 5px color-mix(in srgb, var(--wp--preset--color--gold) 14%, transparent);
  transition-duration: 0.35s, 0.35s, 90ms, 90ms, 90ms;
}
/* `animation: none` matters here: without it the bell keeps swinging under the
   finger that is pressing it still. */
.nu-icon-btn:active svg,
.nu-account:active .nu-account-mark svg { transform: scale(0.96); animation: none; }

/* Focus. One offset for all five, pushed out to clear the 2px lift, and given
   to the account's RING rather than its link so the outline is a circle round
   a circle instead of a rectangle round the whole "Sign in". */
.nu-icon-btn:focus-visible,
.nu-nav-toggle:focus-visible,
.nu-account:focus-visible .nu-account-mark {
  outline: 2px solid var(--wp--preset--color--gold);
  outline-offset: 3px;
}
.nu-account:focus-visible { outline: none; }

/* The wash and the shadow stay: they are colour, not motion, and they are what
   tells a reader which ring they are on. Only the movement goes. */
@media (prefers-reduced-motion: reduce) {
  .nu-icon-btn,
  .nu-account-mark,
  .nu-nav-toggle {
    transition: color 0.2s, border-color 0.2s, background-color 0.2s, box-shadow 0.2s;
  }
  .nu-icon-btn:hover, .nu-nav-toggle:hover,
  .nu-account:hover .nu-account-mark,
  .nu-icon-btn:active, .nu-nav-toggle:active,
  .nu-account:active .nu-account-mark { transform: none; }
  .nu-icon-btn svg, .nu-account-mark svg { transition: none; }
  .nu-icon-btn:hover svg, .nu-account:hover .nu-account-mark svg,
  .nu-icon-btn:active svg,
  .nu-account:active .nu-account-mark svg { transform: none; animation: none; }
}

/* ── The notifications bell ───────────────────────────────────────────────
   An ordinary `.nu-icon-btn` ring carrying one extra thing: a dot.

   ⚠️ A DOT, NEVER A NUMBER (Elle, 28 Aug 2026). The badge says "there is
   something", not "there are four things". A count on a bell that legitimately
   sits empty for weeks turns a quiet instrument into a scoreboard, and the
   notices behind it are already capped at ten.

   ⚠️ CRIMSON, NOT GOLD. Every other control in this bar is a gold glyph in a
   gold ring, so a gold dot on the bell's own gold border is one gold shape
   touching another and reads as a smudge at 30px. Crimson is the only
   attention colour the palette defines, it is declared in BOTH themes
   (#b76a60 night / #9a4a3f day), and it is the convention every reader
   already knows. The 2px ring is the header's own ground, so the dot stays
   detached from the bell's border in either theme. */
/* ⚠️ THE BELL IS FOR EVERYONE (Elle, 28 Aug 2026). It was hidden from
   signed-out visitors on the grounds that nothing is addressed to someone with
   no record. That is still true of the CONTENTS, but the bell is also how a
   visitor learns the feature exists at all, so the ring stays and the panel
   invites them in.

   ⚠️ BUT NEVER THE DOT. A dot says "there is something waiting for you", and
   for a visitor with no account there is nothing — a badge that claims unread
   messages to someone who cannot have any is a lie told to earn a click. The
   rule below is deliberately belt-and-braces: `nu-has-unread` is only ever
   added for a signed-in member, and this makes it impossible for a stray class
   to dot a guest's bell anyway.

   Ring counts still fit, with room to spare since the search ring left
   (16 Sep 2026): four on a phone (bell, account, Store, hamburger) and four on
   desktop (bell, account, Store, toggle). See the measured width note in the
   mobile block below. */
body:not(.logged-in) .nu-bell-dot { display: none !important; }

/* The wrapper exists ONLY to be the panel's positioning context. It must not
   change the row: it is the size of the button inside it. */
.nu-bell-wrap { position: relative; display: flex; flex: 0 0 auto; }
.nu-bell { position: relative; }
.nu-bell-dot {
  position: absolute;
  /* ⚠️ OUTSIDE THE RING, NOT ON IT, AND THE REASON ONLY SHOWS ABOVE 1x.
     Sat at top:0/right:0 the dot's halo punched a bone gap clean through the
     gold arc. At 1x that is invisible and either placement looks the same, but
     every retina phone renders this at 2x, where the gap reads as a chipped
     ring rather than a badge — on a bar whose entire idea is one unbroken gold
     ring repeated five times. Pushed 2px out with a 1.5px halo, the circle
     stays whole at every scale and the dot is no less legible. Compared at
     1x/2x/4x in a static replica before choosing. */
  top: -2px;
  right: -2px;
  inline-size: 7px;
  block-size: 7px;
  border-radius: 50%;
  background: var(--wp--preset--color--crimson);
  box-shadow: 0 0 0 1.5px var(--wp--preset--color--ink);
  /* Hidden until the server says otherwise. `nu-has-unread` is added to <body>
     by notifications.php through the core `body_class` filter, so the dot is
     correct in the FIRST PAINT rather than appearing once a script has run.
     JavaScript removes the class when the panel is opened. */
  display: none;
}
body.nu-has-unread .nu-bell-dot { display: block; }

/* ── The notifications panel ──────────────────────────────────────────────
   A dropdown hung under the bell, never a centred modal. The bell is a glance:
   nothing is dimmed and nothing is blocked, so the page stays readable behind
   it. (The header's one modal was the search popup, which is gone — this is
   now the only panel in the bar, and it stays a dropdown.) */
.nu-bell-panel {
  position: absolute;
  top: calc(100% + 14px);
  right: 0;
  /* Only ever ranks the panel against its siblings INSIDE the header, which is
     itself a z-index:100 stacking context — so this number is local and cannot
     climb over anything at root level. That is the order we want: the bell is
     not modal and must never behave as though it were. */
  z-index: 5;
  inline-size: min(22rem, calc(100vw - 2.5rem));
  background: var(--wp--preset--color--ink-2);
  border: 1px solid var(--wp--preset--color--gold-dim);
  border-radius: 14px;
  box-shadow: 0 24px 70px -28px rgba(0, 0, 0, 0.6);
  overflow: hidden;
  opacity: 0;
  transform: translateY(-8px) scale(0.985);
  transition: opacity 0.24s cubic-bezier(0.16, 1, 0.3, 1),
    transform 0.28s cubic-bezier(0.16, 1, 0.3, 1);
}
/* CRITICAL: an `opacity:0` panel that is still laid out is an invisible sheet
   sitting over the page, and on this one it would cover the Store and the
   toggle. `hidden` has to win. (The deleted search overlay carried the same
   note about its own `display:grid`, and that is the trap to remember if
   anything full-screen is ever added to this bar again.) */
.nu-bell-panel[hidden] { display: none; }
.nu-bell-panel.is-open { opacity: 1; transform: none; }

.nu-bell-head {
  display: flex; align-items: center; justify-content: space-between; gap: 0.75rem;
  padding: 0.85rem 1rem;
  border-bottom: 1px solid var(--wp--preset--color--line);
}
.nu-bell-title {
  font-family: var(--wp--preset--font-family--ui);
  font-size: 0.62rem; font-weight: 600; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--wp--preset--color--gold);
}
/* Mark all as read. Sits beside the close button and shares its shape, but is
   gold rather than dim: closing is housekeeping, acknowledging is an action.
   `hidden` whenever there is nothing to acknowledge — see syncReadBtn(). */
.nu-bell-read {
  flex: 0 0 auto; display: grid; place-items: center;
  inline-size: 26px; block-size: 26px; padding: 0;
  border: 1px solid var(--wp--preset--color--line); border-radius: 50%;
  background: transparent; color: var(--wp--preset--color--gold); cursor: pointer;
  transition: color 0.3s, border-color 0.3s;
}
.nu-bell-read:hover { color: var(--wp--preset--color--gold-bright); border-color: var(--wp--preset--color--gold-dim); }
.nu-bell-read:focus-visible { outline: 2px solid var(--wp--preset--color--gold); outline-offset: 2px; }
.nu-bell-read[hidden] { display: none; }
/* The head holds a title and now two controls, so they need to sit together. */
.nu-bell-head-actions { display: flex; align-items: center; gap: 0.4rem; }
.nu-bell-x {
  flex: 0 0 auto; display: grid; place-items: center;
  inline-size: 26px; block-size: 26px; padding: 0;
  border: 1px solid var(--wp--preset--color--line); border-radius: 50%;
  background: transparent; color: var(--wp--preset--color--paper-dim); cursor: pointer;
  transition: color 0.3s, border-color 0.3s;
}
.nu-bell-x:hover { color: var(--wp--preset--color--paper); border-color: var(--wp--preset--color--gold-dim); }

.nu-bell-list {
  list-style: none; margin: 0; padding: 0;
  max-height: min(60vh, 26rem); overflow-y: auto;
}
.nu-bell-list li + li { border-top: 1px solid var(--wp--preset--color--line-soft); }
.nu-bell-item {
  display: block; padding: 0.8rem 1rem; text-decoration: none;
  transition: background 0.25s;
}
.nu-bell-item:hover, .nu-bell-item:focus-visible {
  background: color-mix(in srgb, var(--wp--preset--color--gold) 9%, transparent);
  outline: none;
}
/* An announcement from the house with nowhere to go. Rendered as a span rather
   than an anchor (see assets/js/notifications.js), so it keeps the row and
   loses every affordance of a link: no wash, no pointer. */
.nu-bell-item--flat { cursor: default; }
.nu-bell-item--flat:hover, .nu-bell-item--flat:focus-visible { background: none; }
/* ⚠️ EB Garamond, NOT Cormorant. Cormorant is display only, from about 1.5rem
   up; a notice is read at text size and a hairline serif at 0.9rem is a
   legibility problem no weight can fix. */
.nu-bell-text {
  display: block;
  font-family: var(--wp--preset--font-family--reading);
  font-size: 0.94rem; line-height: 1.45;
  color: var(--wp--preset--color--paper);
}
.nu-bell-meta {
  display: block; margin-top: 0.3rem;
  font-family: var(--wp--preset--font-family--ui);
  font-size: 0.62rem; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--wp--preset--color--paper-faint);
}
/* An unread notice is marked with the same crimson as the dot, as a hairline
   rather than a second badge: the bell already said there was something. */
.nu-bell-item.is-new { border-left: 2px solid var(--wp--preset--color--crimson); }
.nu-bell-empty {
  padding: 1rem;
  font-family: var(--wp--preset--font-family--reading);
  font-size: 0.92rem; line-height: 1.5;
  color: var(--wp--preset--color--paper-faint);
}
/* The visitor's way in, inside the invitation paragraph. */
.nu-bell-invite {
  display: inline-block;
  margin-top: 0.5rem;
  font-family: var(--wp--preset--font-family--ui);
  font-size: 0.66rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--wp--preset--color--gold); text-decoration: none;
}
.nu-bell-invite:hover { color: var(--wp--preset--color--gold-bright); }
.nu-bell-all {
  display: block; padding: 0.7rem 1rem;
  border-top: 1px solid var(--wp--preset--color--line);
  font-family: var(--wp--preset--font-family--ui);
  font-size: 0.66rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--wp--preset--color--gold); text-decoration: none;
}
.nu-bell-all:hover { color: var(--wp--preset--color--gold-bright); }
@media (prefers-reduced-motion: reduce) {
  .nu-bell-panel { transition: opacity 0.15s; transform: none; }
  .nu-bell-panel.is-open { transform: none; }
}

/* ── Header: mobile nav (hamburger → full-width dropdown under the bar) ── */
@media (max-width: 900px) {
  .nu-nav { flex: 0 0 auto; margin-right: 0; }
  .nu-nav-toggle { display: grid; }
  /* The nav list is behind the hamburger here, so the unread dot climbs onto
     the toggle or a phone never sees it. Same halo as the bell dot, and it
     goes away while the menu is open: the real dot on the link is visible
     then, and two dots for one fact is one too many. */
  body.nu-horo-unread .nu-nav-toggle:not([aria-expanded="true"])::after {
    content: '';
    position: absolute;
    top: -2px;
    right: -2px;
    inline-size: 7px;
    block-size: 7px;
    border-radius: 50%;
    background: var(--wp--preset--color--gold-bright);
    box-shadow: 0 0 0 1.5px var(--wp--preset--color--ink);
  }
  .nu-nav-list {
    position: absolute; top: 100%; left: 0; right: 0;
    flex-direction: column; align-items: stretch; gap: 0;
    background: var(--wp--preset--color--ink);
    border-bottom: 1px solid var(--wp--preset--color--line);
    box-shadow: 0 14px 34px -14px rgba(0, 0, 0, 0.55);
    padding: 0.3rem 0;
    display: none;
  }
  .nu-nav.is-open .nu-nav-list { display: flex; }
  .nu-nav-list a {
    display: block; padding: 0.9rem clamp(1.25rem, 5vw, 2.2rem);
    font-size: 0.8rem; letter-spacing: 0.12em;
  }
  .nu-nav-list li + li a { border-top: 1px solid var(--wp--preset--color--line-soft); }
  /* Full-width rows: an absolutely-placed dot would hang off the word into
     the row's padding, so here it sits inline after the label instead. */
  .nu-nav-list a .nu-nav-dot { position: static; display: inline-block; margin-left: 8px; vertical-align: 2px; }
  /* ⚠️ FIVE 30px RINGS IS THE PHONE CEILING, AND IT IS MEASURED, NOT GUESSED.
     Six rings at 0.55rem is 224px of controls against 110px of wordmark inside
     a 335px bar, which put the hamburger at 372–402px on a 375px screen — two
     thirds of it off the edge, and the menu unreachable.

     The Store was the sixth and was dropped for it on 28 Aug 2026, when the
     bell took a slot. ⚠️ IT IS BACK, AND ONLY BECAUSE THE THEME RING LEFT
     (Elle, 9 Sep 2026: "replace the theme toggle button on header with store on
     mobile only").

     The row is DOWN TO FOUR since the search ring left (16 Sep 2026) — bell,
     account, Store, plus the hamburger — so the phone bar now sits comfortably
     inside the ceiling the measurement above describes. That ceiling is still
     five, not six, and it is still measured rather than guessed.

     Re-measure before going back past five. */
  .nu-actions { gap: 0.55rem; }
  /* ── The panel becomes a sheet ────────────────────────────────────────
     Hung off the bell, a 22rem dropdown would sit against the right bezel and
     read as a tooltip. Across the bar it is a sheet, which is the shape the
     nav menu already uses at this width — the header has one dropdown idiom,
     not two. It stays non-modal: nothing behind it is dimmed or blocked. */
  .nu-bell-panel {
    position: fixed;
    top: calc(var(--nu-header-h, 68px) + 6px);
    left: 0.9rem;
    right: 0.9rem;
    inline-size: auto;
  }
  /* Clear the WordPress admin bar, which pushes the header down by its height
     (see the .nu-header rules above). Only ever affects signed-in staff. */
  body.admin-bar .nu-bell-panel { top: calc(var(--nu-header-h, 68px) + 38px); }
  /* ⚠️ AND THE MENU'S STORE ROW GOES WITH IT. Its own note said it was the only
     way to the Store on a phone and not to remove it "without putting the ring
     back" — the ring is back, so the row is a second Store on one screen, one
     of them behind a tap. The rule that showed it is deleted rather than
     overridden; `.nu-nav-store { display: none }` up the sheet is the base. */
}


/* ═══════════════════════════════════════════════════════════════════════
   Homepage sections that mirror the React marketing page: the Monthly/Annual
   billing toggle, the Star/Moon/Sun plan cards, and the precision stat band.
   ═══════════════════════════════════════════════════════════════════════ */

/* Centered header — eyebrow, heading, billing toggle (mirrors React .section-head, centered).
   Flex column with an explicit 0.9rem gap so the eyebrow→title→toggle spacing is exact and
   immune to WP block-gap/margin-collapse; the title's own margins are zeroed in the pattern. */
/* ⚠️ EVERY NUMBER IN THIS HEAD WAS MEASURED FOR A HEADING THAT IS NO LONGER
   THERE (Elle, 8 Sep 2026: "still a very large gap. needs to be much smaller").
   "Choose your plan." came out this evening and left three separate pieces of
   air behind it — a 1.8rem push under the heading, a 2.5rem skirt under the
   head, and 2rem on the cells below — none of which had anything left to
   separate. Together with WordPress's own block gap above the band they put
   about 105px between the ticker and the word MONTHLY. */
.nu-plans-head { display: flex; flex-direction: column; align-items: center; gap: 0.9rem; text-align: center; margin-bottom: 0.9rem; }
.nu-plans-head .nu-eyebrow { margin: 0; font-size: 0.66rem; letter-spacing: 0.34em; }
.nu-plans-head h2 { font-weight: 300; letter-spacing: -0.005em; }        /* mirror React .display */
.nu-plans-head .nu-billing { margin-top: 0; }   /* it was 1.8rem under the heading; there is no heading */

/* Billing switch: the slide, as the tools draw it (Elle, 3 Sep 2026: "i hate
   pill buttons"). Two words either side of a rail with a gold bead at the chosen
   end; the chosen WORD lights, nothing fills. `.seg-mech` is the rail, and
   theme-toggle.js throws the switch when it is pressed; `.is-b` on the group
   means Annual is drawn; `.is-lit` is the word just chosen, for under a second,
   so the flare runs on a flip and never on load. Mirrors `.seg-toggle` in the
   React site's global.css: change one, change the other. */
.nu-billing { display: inline-flex; align-items: center; position: relative; }
.nu-billing button {
  display: inline-flex;
  align-items: center;
  border: 0;
  background: transparent;
  cursor: pointer;
  padding: 0.6em 0;
  color: var(--wp--preset--color--paper-faint);
  font-family: var(--wp--preset--font-family--ui);
  font-size: 0.62rem;
  font-weight: 500;
  line-height: 1;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  transition: color 0.45s cubic-bezier(0.16,1,0.3,1);
}
.nu-billing button:hover { color: var(--wp--preset--color--paper-dim); }
.nu-billing button.is-active, .nu-billing button.is-active:hover { color: var(--wp--preset--color--gold); }
.nu-billing button:focus-visible { outline: 2px solid var(--wp--preset--color--gold); outline-offset: 3px; border-radius: 2px; }
.nu-billing button.is-lit { animation: nu-word-on 0.9s cubic-bezier(0.16,1,0.3,1) both; }
@keyframes nu-word-on {
  0%   { color: var(--wp--preset--color--paper-faint); text-shadow: 0 0 0 rgba(0, 0, 0, 0); }
  30%  { color: var(--wp--preset--color--gold-bright); text-shadow: 0 0 14px var(--wp--preset--color--gold-dim); }
  100% { color: var(--wp--preset--color--gold); text-shadow: 0 0 0 rgba(0, 0, 0, 0); }
}
.nu-billing .seg-mech { position: relative; align-self: stretch; min-height: 22px; width: 2.1rem; flex: none; cursor: pointer; }
.nu-billing .seg-mech::before, .nu-billing .seg-mech::after { content: ''; position: absolute; }
.nu-billing .seg-mech::before { left: 2px; right: 2px; top: 50%; height: 1px; background: var(--wp--preset--color--paper-faint); opacity: 0.45; transform: translateY(-50%); }
.nu-billing .seg-mech::after { left: 2px; top: 50%; width: 7px; height: 7px; margin-top: -3.5px; border-radius: 50%; background: var(--wp--preset--color--gold); transition: transform 0.38s cubic-bezier(0.16,1,0.3,1); }
.nu-billing.is-b .seg-mech::after { transform: translateX(calc(2.1rem - 4px - 7px)); }
/* The saving is said beside the word, in the reading face, and never shouts. */
.nu-billing .save { font-family: var(--wp--preset--font-family--reading); font-style: italic; font-size: 0.92rem; font-weight: 400; letter-spacing: 0; text-transform: none; color: var(--wp--preset--color--paper-faint); margin-left: 0.55em; transition: color 0.45s cubic-bezier(0.16,1,0.3,1); }
.nu-billing button.is-active .save { color: var(--wp--preset--color--paper-dim); }
@media (pointer: coarse) { .nu-billing button { min-height: 34px; } }
@media (prefers-reduced-motion: reduce) { .nu-billing button, .nu-billing .seg-mech::after { transition: none; animation: none; } }

/* ── THE CELL: one closed line that opens (Elle's sketch, 8 Sep 2026) ──────
   "remove the choose your plan from the sub boxes, and make them compact ...
   symbol, name, price on one line with No card needed/billed monthly under and
   a plus next to it. when the plus is clicked the full sub boxes are shown."
   And, for the tools band under it: "the boxes for tools are the same as the
   boxes for the subscriptions."

   So this is ONE idiom used by two bands, and it must stay one: `.nu-cell` is
   a `<details>`, its `<summary>` is the closed line, and what used to be a tall
   plan card is now what the cell contains. The browser owns the open and close,
   the keyboard, and what a screen reader says about it — a scripted accordion
   would have to be given all three by hand and would get one of them wrong.

   ⚠️ CELLS, NOT CARDS, still (Elle, 3 Sep 2026: "the cells with second version
   of saying most loved (gold band all around)"). No ground, no shadow, no
   radius. Hairlines and the section's own ink. Standard keeps its gold frame
   and its legend set into the top edge; the glowing badge that preceded it was
   the answer to "too yellow", and a legend has no fill to be yellow with. */
.nu-cells {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.4rem;
  align-items: start;
}
@media (max-width: 820px) { .nu-cells { grid-template-columns: 1fr; } }

/* ═══════════════════════════════════════════════════════════════════════
   THE CAROUSEL — one card at a time, with its neighbours showing
   Elle's sketch, 9 Sep 2026: "the packages being listed like this are not UX.
   make them as the image shows, same for the tools."

   ⚠️ ONE IDIOM, TWO BANDS, exactly as `.nu-cell` already is. The plans and the
   tools are the same object on a phone — a short row of things you choose
   between — and they were both being answered by stacking, which turns three
   choices into a scroll. Here the choice is one card, held in the middle, with
   the ones either side of it visibly there.

   ⚠️ IT IS A SCROLL CONTAINER, NOT A SCRIPTED SLIDER. `scroll-snap` gives the
   swipe, the momentum, the keyboard and the accessibility for nothing, and the
   band still works with the script blocked — you can drag it. carousel.js only
   adds the two arrows Elle drew and keeps them honest at the ends. A JS slider
   would have to be handed all of that by hand and would get one of it wrong.

   ⚠️ THE PADDING AND THE CARD WIDTH ARE ONE SUM. 12 + 76 + 12 = 100, which is
   what centres the first and last card rather than leaving them against the
   edge — and what makes the neighbours peek by exactly the same amount on both
   sides. Change the width and change the padding with it.                */
.nu-carousel { position: relative; }
.nu-carousel-track { scrollbar-width: none; -ms-overflow-style: none; }
.nu-carousel-track::-webkit-scrollbar { display: none; }
/* The arrows are bare chevrons. NO PILL, no disc, no ground: the sketch draws
   two marks in the margin, and every other control in this house is a hairline
   or a square plate. They stand over the peeking neighbour, so a tap at the
   edge of the band means "the card on this side", which is what the edge of a
   carousel should mean anyway. */
.nu-carousel-arrow {
  position: absolute; top: 0; bottom: 0; width: 12%;
  display: none;                       /* desktop has all three cards */
  align-items: center; justify-content: center;
  background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--wp--preset--color--gold);
  transition: color 0.3s, opacity 0.3s;
  z-index: 2;
}
/* ⚠️ THE CHEVRON SITS AT THE OUTER EDGE OF ITS OWN BOX, and that is what keeps
   it off the neighbour. The box is deliberately wide — a thumb needs it — but
   centred inside it the mark landed on the peeking card's own `+`, two gold
   glyphs a few pixels apart, which read as one broken symbol. Pushed out, the
   hit area is unchanged and the mark is alone in the margin. */
.nu-carousel-prev { left: 0; justify-content: flex-start; padding-left: 2px; }
.nu-carousel-next { right: 0; justify-content: flex-end; padding-right: 2px; }
.nu-carousel-arrow::before {
  content: ''; width: 11px; height: 11px;
  border-top: 1.5px solid currentColor; border-right: 1.5px solid currentColor;
}
.nu-carousel-prev::before { transform: rotate(-135deg); margin-left: 3px; }
.nu-carousel-next::before { transform: rotate(45deg); margin-right: 3px; }
.nu-carousel-arrow:hover { color: var(--wp--preset--color--gold-bright); }
/* ⚠️ NEITHER ARROW IS EVER DISABLED, because the run wraps (9 Sep 2026):
   past the last card is the first one. They were dimmed at the ends for one
   build; there are no ends now. */
.nu-carousel-arrow:focus-visible { outline: 2px solid var(--wp--preset--color--gold); outline-offset: -4px; }

@media (max-width: 900px) {
  .nu-carousel-arrow { display: flex; }
  /* ⚠️ TWO CLASSES, AND BOTH OF THEM ARE LOAD-BEARING. The track is also
     `.nu-cells` or `.nu-tools`, and each of those declares its own
     `grid-template-columns` further down this sheet — `.nu-tools` at 1fr below
     820px. A single-class selector here ties on specificity and LOSES on
     source order, which is not a small failure: the cards then go into the
     band's own explicit columns and `grid-auto-columns` is never consulted at
     all, so three cards share one screen's width and their contents print on
     top of each other. */
  .nu-carousel .nu-carousel-track {
    display: grid;
    /* ⚠️ AND THE EXPLICIT TRACKS HAVE TO BE UNSAID, not merely outnumbered.
       `grid-auto-columns` sizes IMPLICIT tracks only; while a template exists
       the three cards fill it and this is decoration. */
    grid-template-columns: none;
    grid-auto-flow: column;
    /* ⚠️ 100%, AND IT IS NOT THE WHOLE BAND. A percentage on a grid track
       resolves against the CONTENT box, which the padding below has already
       taken the two peeks out of — so `100%` is exactly "one card, centred,
       with a peek either side", and the two numbers can never disagree. It was
       written as 76% first, guessing at the same answer, and got 76% of the
       ALREADY-INSET box: a card 30px narrower than its slot, sitting 30px off
       the centre of a band whose arrows were placed on the true centre. */
    grid-auto-columns: 100%;
    gap: 0.9rem;
    align-items: start;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    /* ⚠️ THE PEEK SHOWS A CARD'S EDGE, NOT ITS CONTENTS. At 12% the sliver was
       wide enough to include the neighbour's own `+`, so the band carried three
       plus marks and only one of them could be pressed. 10% shows the frame and
       the first letter of the next name — enough to say "there is more this
       way", which is the whole job of a peek. */
    padding-inline: 10%;
    scroll-padding-inline: 10%;
    /* A card can carry a gold frame or a legend set into its top edge, and a
       scroll container clips its own overflow — so the band needs a little air
       above and below or the frame is shaved. */
    padding-block: 0.35rem;
  }
  .nu-carousel .nu-carousel-track > * { scroll-snap-align: center; min-width: 0; }
  /* The instrument is drawn at 260px for a third of a 1240px page. Given a
     whole phone screen to itself it becomes the band, and the name of the tool
     underneath it — which is the thing being chosen between — falls below the
     fold. */
  .nu-carousel--tools .nu-art { max-width: 190px; }
  .nu-carousel--tools .nu-tool { gap: 0.9rem; }
}

.nu-cell {
  position: relative;
  border: 1px solid var(--wp--preset--color--line);
  background: transparent;
}
.nu-cell.is-featured { border-color: var(--wp--preset--color--gold); }
/* The legend rides the top edge, on the band's own ground. */
.nu-cell-badge {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translate(-50%, -50%);
  background: var(--wp--preset--color--ink);
  color: var(--wp--preset--color--gold);
  font-family: var(--wp--preset--font-family--ui);
  font-size: 0.58rem;
  font-weight: 500;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  padding: 0 0.9em;
  white-space: nowrap;
  z-index: 1;
}
/* ⚠️ `list-item` IS WHAT PUTS THE ARROW THERE. Safari draws a disclosure
   triangle on a summary unless the display is changed; `list-style: none` alone
   does not remove it, and the ::-webkit-details-marker rule under it is the
   older half of the same fix. */
.nu-cell-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.2rem 1rem;
  padding: 1.15rem clamp(1rem, 2vw, 1.4rem);
  cursor: pointer;
  list-style: none;
}
.nu-cell-head::-webkit-details-marker { display: none; }
.nu-cell-head:focus-visible { outline: 2px solid var(--wp--preset--color--gold); outline-offset: -3px; }
.nu-cell-line { display: flex; align-items: baseline; gap: 0.55rem; min-width: 0; }
.nu-cell-glyph { font-family: var(--wp--preset--font-family--reading); font-size: 1.25rem; line-height: 1; color: var(--wp--preset--color--gold); }
.nu-cell-name { font-family: var(--wp--preset--font-family--display); font-size: 1.5rem; font-weight: 400; line-height: 1.1; color: var(--wp--preset--color--paper); }
.nu-cell-price { margin-left: auto; white-space: nowrap; }
.nu-cell-price .amount { font-family: var(--wp--preset--font-family--display); font-size: 1.5rem; line-height: 1; color: var(--wp--preset--color--paper); }
.nu-cell-price .per { font-family: var(--wp--preset--font-family--display); font-size: 0.85rem; color: var(--wp--preset--color--paper-faint); margin-left: 0.1em; }
/* The second line: what the price actually costs, or what the tool actually is. */
.nu-cell-note {
  grid-column: 1;
  font-family: var(--wp--preset--font-family--ui);
  font-size: 0.66rem;
  letter-spacing: 0.08em;
  color: var(--wp--preset--color--paper-faint);
}
.nu-cell-note b { color: var(--wp--preset--color--gold); font-weight: 500; }
/* ⚠️ THE PLUS IS DRAWN, NOT TYPED. A "+" in the reading face sits off the
   optical centre of its box and turns into a lopsided "x" when rotated. Two
   bars and one rotation are exact at every size. */
.nu-cell-plus {
  grid-row: 1 / span 2;
  grid-column: 2;
  position: relative;
  width: 15px;
  height: 15px;
  align-self: center;
  transition: transform 0.4s cubic-bezier(0.16,1,0.3,1);
}
.nu-cell-plus::before, .nu-cell-plus::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  background: var(--wp--preset--color--gold);
}
.nu-cell-plus::before { width: 15px; height: 1px; transform: translate(-50%, -50%); }
.nu-cell-plus::after { width: 1px; height: 15px; transform: translate(-50%, -50%); }
.nu-cell[open] .nu-cell-plus { transform: rotate(45deg); }
.nu-cell-head:hover .nu-cell-plus { transform: rotate(90deg); }
.nu-cell[open] .nu-cell-head:hover .nu-cell-plus { transform: rotate(135deg); }

.nu-cell-body { padding: 0 clamp(1rem, 2vw, 1.4rem) 1.3rem; }
/* No height animation: a `<details>` has no open state to interpolate across
   in every browser we serve, and a fake one that jumps on the last frame is
   worse than none. The content arrives instead. */
.nu-cell[open] .nu-cell-inner { animation: nu-cell-in 0.5s cubic-bezier(0.16,1,0.3,1) both; }
@keyframes nu-cell-in { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
.nu-cell-inner { display: grid; gap: 1rem; }
.nu-cell-lead { margin: 0; font-family: var(--wp--preset--font-family--ui); font-size: 0.8rem; color: var(--wp--preset--color--paper); }
.nu-cell-lead strong { font-weight: 500; }
/* A rule, not a plate: the homepage is allowed exactly one filled gold object
   and the closing call has it. See the buttons note in the site's global.css. */
.nu-cell-cta {
  justify-self: start;
  font-family: var(--wp--preset--font-family--ui);
  font-size: 0.68rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--wp--preset--color--gold);
  text-decoration: none;
  border-bottom: 1px solid var(--wp--preset--color--gold-dim, rgba(199,164,104,.4));
  padding-bottom: 0.35em;
}
.nu-cell-cta:hover { border-bottom-color: var(--wp--preset--color--gold); }
.nu-cell-cta .arr { transition: transform 0.3s cubic-bezier(0.16,1,0.3,1); display: inline-block; }
.nu-cell-cta:hover .arr { transform: translateX(3px); }

/* ── The free day, under the price ─────────────────────────────────────────
   ⚠️ QUIETER THAN THE CTA ABOVE IT, ON PURPOSE. Purchase is the gold rule;
   this is the same small-caps ui face without the rule, so the plate still has
   ONE primary action and this reads as a second door rather than a competing
   button. Square, like everything else here: no pill, no fill, no radius. */
.nu-cell-trial {
  margin: 0.85rem 0 0;
  display: flex; flex-direction: column; align-items: flex-start; gap: 0.3rem;
}
.nu-trial-go {
  font-family: var(--wp--preset--font-family--ui);
  font-size: 0.68rem; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--wp--preset--color--paper, #efeadd);
  background: none; border: 0; border-bottom: 1px solid transparent;
  border-radius: 0; padding: 0 0 0.35em; margin: 0;
  cursor: pointer; text-decoration: none; text-align: left;
}
.nu-trial-go:hover,
.nu-trial-go:focus-visible {
  color: var(--wp--preset--color--gold);
  border-bottom-color: var(--wp--preset--color--gold);
}
.nu-trial-go[disabled] { opacity: .55; cursor: default; }
.nu-trial-go .arr { transition: transform 0.3s cubic-bezier(0.16,1,0.3,1); display: inline-block; }
.nu-trial-go:hover .arr { transform: translateX(3px); }

/* The promise, and the reason anybody presses the line above it. Gold small
   caps, the same legend treatment the featured cell's badge uses, because it
   is the part Elle wanted to carry (9 Sep 2026: "highlight NO CREDIT CARD
   NEEDED AVAILABLE IMMEDIATELY"). */
.nu-trial-note {
  font-family: var(--wp--preset--font-family--ui);
  font-size: 0.58rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--wp--preset--color--gold);
  opacity: .95; line-height: 1.5;
}
/* The server's reason, printed where the promise was. Sentence case and a
   normal size, because it is a thing to READ and act on rather than a legend:
   the small caps that suit four words would make a sentence hard work. */
.nu-trial-note.is-refused {
  font-family: var(--wp--preset--font-family--reading);
  font-size: 0.85rem; letter-spacing: 0; text-transform: none;
  color: var(--wp--preset--color--crimson);
}

/* ⚠️ A PLATE, AND THE HOMEPAGE HAS EXACTLY THREE NOW (Elle, 8 Sep 2026: "add
   buttons in the tool boxes saying Personalize it / Compare it / Foresee it").
   The rule the plans use is the second rank, right for a cell whose job is to
   explain a price. These three are the page's calls to action, and the closing
   band that used to hold the one filled plate has gone — so the fill is free,
   and it belongs to them. Square by 2px, never a pill: see the buttons note in
   the site's global.css. */
/* ⚠️ A GROUND UNDER THE FRAME, AND ONE SIZE UP (Elle, 8 Sep 2026: "these are
   too minimalistic and hard to notice"). The hairline ring at 0.66rem is the
   quiet rank the plans use, and on a drawing-heavy band it disappeared. This is
   option 2 of nine put in front of her — a wash rather than a fill, because
   three solid plates in one row is the "too yellow" she sent back on 4 Sep.
   Swap to another option by changing this rule alone; the markup is one class. */
.nu-cell-btn {
  justify-self: start;
  display: inline-block;
  padding: 0.95em 1.7em;
  border: 1px solid var(--wp--preset--color--gold);
  border-radius: 2px;
  background: color-mix(in srgb, var(--wp--preset--color--gold) 12%, transparent);
  font-family: var(--wp--preset--font-family--ui);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--wp--preset--color--gold-bright);
  text-decoration: none;
  transition: background-color 0.4s cubic-bezier(0.16,1,0.3,1), color 0.4s cubic-bezier(0.16,1,0.3,1);
}
/* ⚠️ IN DAYLIGHT `--gold-bright` IS THE DARKER GOLD, not the lighter one (see
   the palette block above: #5e4819 against #7b5f26). Over a wash it goes muddy,
   so the light theme keeps the plain gold. */
:root:not([data-theme]) .nu-cell-btn,
:root[data-theme="light"] .nu-cell-btn { color: var(--wp--preset--color--gold); }
.nu-cell-btn:hover { background: color-mix(in srgb, var(--wp--preset--color--gold) 24%, transparent); }
.nu-cell-btn .arr { display: inline-block; transition: transform 0.3s cubic-bezier(0.16,1,0.3,1); }
.nu-cell-btn:hover .arr { transform: translateX(3px); }
@media (prefers-reduced-motion: reduce) { .nu-cell-btn, .nu-cell-btn .arr { transition: none; } }

/* ⚠️ THE CADENCE SWAP HAS TO REACH INSIDE THE CELL, and the rules below it are
   only two classes deep. theme-toggle.js still just sets `data-bill` on
   `.nu-plans`; these say what that means for a line of type rather than for the
   old price block, which was `display: block` and would break the row. */
.nu-cells .nu-price-monthly { display: inline; }
.nu-cells .nu-price-annual { display: none; }
.nu-plans[data-bill="annual"] .nu-cells .nu-price-monthly { display: none; }
.nu-plans[data-bill="annual"] .nu-cells .nu-price-annual { display: inline; }

.nu-price-annual { display: none; }
.nu-plans[data-bill="annual"] .nu-price-annual { display: block; }
.nu-plans[data-bill="annual"] .nu-price-monthly { display: none; }

/* Perks — gold four-point-star bullets (mirror React .plan-perks) */
.nu-perks { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.7rem; font-family: var(--wp--preset--font-family--reading); color: var(--wp--preset--color--paper-dim); }
.nu-perks li { position: relative; padding-left: 1.6rem; font-size: 0.95rem; line-height: 1.4; }
.nu-perks li::before { content: "\2726"; position: absolute; left: 0; top: 0.05em; font-size: 0.8em; color: var(--wp--preset--color--gold); }

/* ⚠️ `.nu-pick` IS GONE (Elle, 9 Sep 2026: "the packages being listed like this
   are not UX"). It was the phone's own pricing layout — three rows you selected
   between, the chosen one open under it, one button at the foot — and below
   900px it replaced the cells entirely. The cells now serve both widths through
   `.nu-carousel` above, which also ends the thing that had bitten this pattern
   on every pass: the prices and perk leads existed TWICE, once per layout, and
   an edit to one silently disagreed with the other. If a phone-only pricing
   layout is ever wanted again, it starts from the carousel, not from here.

   (⚠️ A stray `}` stood here until 9 Sep 2026 — the closing brace of the
   `@media (max-width: 900px)` block those rules lived in, left behind when the
   rules went. Browsers discard an unmatched brace at the top level, so it cost
   nothing and showed nothing; it is gone now.) */

/* Precision stat band (mirrors React .stats) */
.nu-stats { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(1.6rem, 3vw, 2.6rem) clamp(2rem, 4vw, 3rem); }
.nu-stat b { display: block; font-family: var(--wp--preset--font-family--display); font-weight: 300; font-size: clamp(1.9rem, 3vw, 2.6rem); line-height: 1; color: var(--wp--preset--color--paper); }
.nu-stat b sup { font-family: var(--wp--preset--font-family--ui); font-size: 0.34em; letter-spacing: 0.05em; margin-left: 0.12em; color: var(--wp--preset--color--paper-faint); }
.nu-stat span { display: block; margin-top: 0.5rem; font-family: var(--wp--preset--font-family--reading); font-size: 0.92rem; line-height: 1.45; color: var(--wp--preset--color--paper-dim); }

@media (max-width: 600px) {
  .nu-stats { grid-template-columns: 1fr; }
}

/* ── The running sky line opens the page ───────────────────────────────────
   Elle, 30 Aug 2026: "move up the ticker again closer to the wheel … it's too
   low. Move it up." Then, 7 Sep 2026, the end of that road: "i want the ticker
   to always be visible on the homepage upon opening it without having to scroll
   down". Tightening the margins only ever got it to about 900px down the page;
   it is the FIRST block in front-page.html now, directly under the header.

   The rule stays and does a second job: WordPress puts its default 24px block
   gap above every block, and the strip is meant to sit flush under the header
   rather than floating below it.

   ⚠️ IT USED TO BE ONE HALF OF A PAIR with `.nu-app .daily { padding-bottom }`
   in the widget stylesheet, which closed the gap BETWEEN the day and the strip
   when the strip came second. That one is still right — it is now the foot of
   the whole bulletin — but the two are no longer a pair, and neither depends on
   the other. */
.nu-ephemeris-wrap { margin-top: 0 !important; }

/* ═══════════════════════════════════════════════════════════════════════
   The two icons that answer back (Elle, 30 Aug 2026, with sketches)
   ═══════════════════════════════════════════════════════════════════════ */

/* ── The store: a cart, and it pops a wheelie ────────────────────────────
   "replace the shop icon to a shopping cart and make the animation lift the
   cart's front two wheels when hovered and clicked."

   ⚠️ THE ORIGIN IS THE REAR WHEEL, which is what makes it a wheelie rather
   than a wobble: the cart pivots on the wheel it keeps on the ground, so the
   front lifts and the handle stays put. In this 24-unit box the rear wheel is
   at (10, 18.6), hence 42% / 78%. Move the artwork and this must move with it.

   ⚠️ ROTATION IS NEGATIVE. Positive is clockwise, which would bury the front
   wheel in the floor — the exact opposite of the sketch. */
.nu-store-btn .nu-cart {
  transform-origin: 42% 78%;
  transition: transform 0.32s cubic-bezier(0.34, 1.4, 0.5, 1);
}
.nu-store-btn:hover .nu-cart,
.nu-store-btn:focus-visible .nu-cart { transform: rotate(-15deg); }
/* Pressed, it rears a little further — the press should feel like the push. */
.nu-store-btn:active .nu-cart { transform: rotate(-24deg); }

/* ── The profile: a small wave ───────────────────────────────────────────
   "make the profile icon wave when hovered and clicked."

   The arm is absent at rest, so the mark stays the plain figure it has always
   been and nothing in the bar looks broken or busy. It appears only while the
   icon is being addressed, and waves twice rather than forever — a greeting,
   not a loop. */
.nu-account .nu-wave {
  opacity: 0;
  transform-origin: 73% 61%;   /* the shoulder end of the arm */
  transition: opacity 0.18s ease;
}
.nu-account:hover .nu-wave,
.nu-account:focus-visible .nu-wave,
.nu-account:active .nu-wave {
  opacity: 1;
  animation: nu-wave 0.62s ease-in-out 2;
}
@keyframes nu-wave {
  0%, 100% { transform: rotate(0deg); }
  25%      { transform: rotate(-20deg); }
  75%      { transform: rotate(20deg); }
}

/* ⚠️ BOTH ARE MOTION AND BOTH MUST BE OPT-OUTABLE. The cart still tilts (a
   state change the reader asked for by pointing at it); what stops is the
   repeated waving, which is decorative and is what a motion-sensitive reader
   is opting out of. */
@media (prefers-reduced-motion: reduce) {
  .nu-store-btn .nu-cart { transition: none; }
  .nu-account:hover .nu-wave,
  .nu-account:focus-visible .nu-wave,
  .nu-account:active .nu-wave { animation: none; }
}

/* ── The site carries no pictures ──────────────────────────────────────────
   Elle, 7 Sep 2026, of articles; widened to the whole site 9 Sep 2026 ("NO
   IMAGES anywhere. on any site"). Every raster image is hidden wherever
   WordPress can place one — featured image, post body, page body, archive card,
   editor image / gallery / cover / media-text block, and a bare <img> — past and
   future alike, so nothing has to be edited out of old posts.

   ⚠️ `img`, NEVER `svg`. Everything Nuastro actually draws is an SVG: the chart
   wheels, the sign and planet glyphs, the tool drawings on the homepage, the
   header's account and cart marks, the moon disc. A blanket rule that took both
   would empty the site of its own instruments. What goes is PHOTOGRAPHY, which
   is the thing Elle does not want on the page.

   ⚠️ AND IT DOES NOT REACH THE ARTICLE ONCE THE WIDGET HAS IT. `.nu-article-prose`
   is MOVED into the Community widget's shadow root at mount (see Marginalia's
   adopt effect), and a document stylesheet does not cross a shadow boundary — so
   this rule paints the server-rendered article and then stops applying the moment
   React adopts it. The other half lives in nuastro-site/src/styles/global.css,
   on `.community-prose`. THE TWO ARE ONE RULE. Change one, change the other, or
   an article's pictures come back the moment the bundle mounts.               */
.wp-block-post-content img,
.wp-block-post-content .wp-block-image,
.wp-block-post-content .wp-block-gallery,
.wp-block-post-content .wp-block-cover,
.wp-block-post-content .wp-block-media-text__media,
.wp-block-post-featured-image,
.entry-content img,
.nu-article-prose img,
.nu-article-prose .wp-block-image,
.nu-article-prose .wp-block-gallery,
.nu-article-prose .wp-block-cover { display: none !important; }

/* ⚠️ THE BAND UNDER THE TICKER TAKES NO BLOCK GAP. WordPress puts 24px between
   every pair of top-level blocks, and here that is 24px between a hairline
   strip and the band it introduces — the same trap the header's own gap was,
   two rounds ago. `.nu-ephemeris-wrap` already sets its own top to 0; this is
   the other side of it. Scoped to the strip's next sibling, so every other pair
   on the page keeps the gap. */
.nu-ephemeris-wrap + * { margin-block-start: 0; }

/* ═══════════════════════════════════════════════════════════════════════
   THE TOOLS BAND — three drawn instruments over three cells
   Elle's sketch, 8 Sep 2026: "under both of the texts we need to add tool
   description boxes for birth chart, love chart, and horoscope with again
   expandable boxes ... there has to be cool animation for the tool images."
   ═══════════════════════════════════════════════════════════════════════ */
.nu-tools {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.4rem;
  /* ⚠️ `start`, NOT `end`. The three drawings are the same square, so the cells
     under them already begin on one line — and `end` only mattered while a cell
     could grow: opening one then dragged its own drawing UP the page while its
     neighbours' stayed put. Started, an open cell grows downward and the three
     instruments never move. */
  align-items: start;
}
@media (max-width: 820px) { .nu-tools { grid-template-columns: 1fr; gap: 2rem; } }
/* ⚠️ THE DRAWING AND THE CELL ARE ONE COLUMN, not two rows of three. Laid out
   as separate rows, a tool whose name wraps on a narrow screen pushes its own
   drawing out of line with the other two; nested like this the pair travels
   together and the phone gets three whole objects rather than six halves. */
.nu-tool { display: grid; align-content: start; gap: 1.2rem; }
.nu-art { width: 100%; max-width: 260px; margin: 0 auto; }
.nu-art svg { display: block; width: 100%; height: auto; overflow: visible; }

/* The house hand: hairlines and gold, the same weights the real wheel is drawn
   in. Nothing here is filled except the bodies. */
/* ⚠️ `--line` IS TOO FAINT TO DRAW WITH. It is a 0.13 alpha hairline meant to
   separate two blocks of type, and at 260px across it left the instruments as
   ghosts on both grounds. These are drawings, so they are struck in the
   mid-tone — `--paper-faint`, which is a real colour in daylight and at night
   — and held back with opacity instead. */
.nu-art .ring { stroke: var(--wp--preset--color--paper-faint); stroke-width: 1; opacity: 0.5; }
.nu-art .ring--soft { stroke: var(--wp--preset--color--paper-faint); stroke-width: 1; opacity: 0.28; }
.nu-art .tick { stroke: var(--wp--preset--color--paper-faint); stroke-width: 1; opacity: 0.45; }
.nu-art .spoke { stroke: var(--wp--preset--color--paper-faint); stroke-width: 1; opacity: 0.22; }
.nu-art .chord { stroke: var(--wp--preset--color--gold); stroke-width: 1; opacity: 0.4; stroke-dasharray: 120; }
.nu-art .vesica { fill: var(--wp--preset--color--gold); opacity: 0.06; }
.nu-art .hand { stroke: var(--wp--preset--color--gold); stroke-width: 1; opacity: 0.75; }
.nu-art .dot { fill: var(--wp--preset--color--paper-faint); opacity: 0.85; }
.nu-art .dot--lit { fill: var(--wp--preset--color--gold); }
.nu-art .hub { fill: var(--wp--preset--color--gold); }
.nu-art .art-threads line { stroke: var(--wp--preset--color--gold); stroke-width: 1; opacity: 0.5; stroke-dasharray: 3 6; }
.nu-art .art-trail { stroke: var(--wp--preset--color--gold); stroke-width: 1; opacity: 0.5; stroke-dasharray: 232 465; }
.nu-art .art-dim { opacity: 0.7; }

/* ⚠️ TRANSFORMS ONLY, AND ALL OF THEM AROUND THE SVG'S OWN CENTRE. `transform-
   box: fill-box` would take the origin from each element's own bounding box,
   which sends a spoke orbiting its own midpoint instead of the hub. */
.nu-art .art-turn,
.nu-art .art-sweep { transform-origin: 100px 100px; }
@keyframes nu-art-turn { to { transform: rotate(360deg); } }
@keyframes nu-art-breathe { 0%, 100% { opacity: 0.55; } 50% { opacity: 1; } }
@keyframes nu-art-drift-a { from { transform: translateX(0); } to { transform: translateX(6px); } }
@keyframes nu-art-drift-b { from { transform: translateX(0); } to { transform: translateX(-6px); } }
@keyframes nu-art-thread { from { stroke-dashoffset: 90; } to { stroke-dashoffset: 0; } }
@keyframes nu-art-trail { to { stroke-dashoffset: -697; } }

/* Slow on purpose: this is an instrument turning, not a spinner. A minute and
   a half for a full turn reads as motion without ever asking to be watched. */
.nu-art--birth .art-turn { animation: nu-art-turn 96s linear infinite; }
.nu-art--birth .art-turn--back { animation: nu-art-turn 132s linear infinite reverse; }
.nu-art--birth .dot { animation: nu-art-breathe 7s ease-in-out infinite; animation-delay: calc(var(--i, 0) * 0.6s); }
/* The two chords are the aspects the wheel would draw: they arrive, hold, and
   go, which is the one thing a still drawing of a chart cannot say. */
.nu-art--birth .chord { animation: nu-art-chord 11s ease-in-out infinite; animation-delay: calc(var(--i, 0) * 5.5s); }
@keyframes nu-art-chord {
  0%   { stroke-dashoffset: 120; opacity: 0; }
  18%  { opacity: 0.4; }
  45%  { stroke-dashoffset: 0; opacity: 0.4; }
  70%  { stroke-dashoffset: 0; opacity: 0.4; }
  100% { stroke-dashoffset: -120; opacity: 0; }
}
/* ⚠️ THE VESICA NEEDS ITS OWN KEYFRAME, and `nu-art-breathe` is the trap: that
   one swings opacity between 0.55 and 1, which is right for a 2px body and
   turned this 90px lens into a solid gold leaf. An animation overrides the
   static `opacity` on the rule, so a shared keyframe is not a shortcut here. */
@keyframes nu-art-glow { 0%, 100% { opacity: 0.05; } 50% { opacity: 0.12; } }
.nu-art--love .vesica { animation: nu-art-glow 9s ease-in-out infinite; }
.nu-art--love .art-pair-a { animation: nu-art-drift-a 9s ease-in-out infinite alternate; }
.nu-art--love .art-pair-b { animation: nu-art-drift-b 9s ease-in-out infinite alternate; }
.nu-art--love .art-threads line { animation: nu-art-thread 6s linear infinite; animation-delay: calc(var(--i, 0) * 1.2s); }
.nu-art--horoscope .art-sweep { animation: nu-art-turn 24s linear infinite; }
.nu-art--horoscope .art-trail { animation: nu-art-trail 24s linear infinite; }

/* The drawing answers its own cell: hovering the words lights the instrument
   they name. `:has` is what lets the column speak to a sibling above it. */
.nu-tool:hover .nu-art .ring,
.nu-tool:has(.nu-cell[open]) .nu-art .ring { stroke: var(--wp--preset--color--paper-faint); }
.nu-tool:hover .nu-art .dot--lit,
.nu-tool:has(.nu-cell[open]) .nu-art .dot--lit { filter: drop-shadow(0 0 6px var(--wp--preset--color--gold)); }
.nu-art .ring, .nu-art .dot--lit { transition: stroke 0.6s cubic-bezier(0.16,1,0.3,1), filter 0.6s cubic-bezier(0.16,1,0.3,1); }

/* ── The instrument answers the hand (Elle, 9 Sep 2026: "we need to add hover
   animation to the tools icons on homepage") ──────────────────────────────
   Each drawing does the thing it is a drawing OF: the wheel leans in and strikes
   its aspects faster, the two charts' overlap brightens, the dial's ring turns a
   notch under the sweep. Slow, and none of it is a spinner.

   ⚠️ THE HOVER STATES GO ON ELEMENTS THAT ARE NOT ALREADY ANIMATING, or they
   fight. An `animation` on a transform owns that property outright — a
   `transition` on the same element does nothing, and a second animation
   replaces the first mid-turn with a visible jump. So: the svg scales (no
   animation on it), the tick ring turns (its parent is the animated one), and
   where a hover must touch something already moving, the animation is switched
   OFF first and a transition takes over — `.vesica` below is the one case. */
.nu-art svg { transition: transform 0.75s cubic-bezier(0.16,1,0.3,1); }
.nu-tool:hover .nu-art svg { transform: scale(1.035); }
.nu-art .hub { transition: filter 0.6s cubic-bezier(0.16,1,0.3,1); }
.nu-tool:hover .nu-art .hub { filter: drop-shadow(0 0 7px var(--wp--preset--color--gold)); }

/* The wheel: the aspects strike about twice as often. A dashed line fading in
   and out is the one place a duration can change mid-flight without showing. */
.nu-tool:hover .nu-art--birth .chord { animation-duration: 5.5s; }

/* The pair: the overlap warms. `animation: none` first, because the lens is
   already breathing on a keyframe and a keyframed opacity cannot be
   transitioned past. */
.nu-tool:hover .nu-art--love .vesica { animation: none; opacity: 0.17; }
.nu-art .vesica { transition: opacity 0.6s cubic-bezier(0.16,1,0.3,1); }
.nu-art .art-threads line { transition: opacity 0.5s cubic-bezier(0.16,1,0.3,1); }
.nu-tool:hover .nu-art--love .art-threads line { opacity: 0.85; }

/* The dial: the ring of hours turns a notch, under a sweep that keeps its own
   pace, and the wake it leaves comes up. */
.nu-art--horoscope .art-dim { transition: transform 0.9s cubic-bezier(0.16,1,0.3,1); transform-origin: 100px 100px; }
.nu-tool:hover .nu-art--horoscope .art-dim { transform: rotate(15deg); }
.nu-art .art-trail { transition: opacity 0.6s cubic-bezier(0.16,1,0.3,1); }
.nu-tool:hover .nu-art--horoscope .art-trail { opacity: 0.85; }

/* ⚠️ EVERY ONE OF THESE STOPS ON REQUEST. Four of them run forever, which is
   exactly the kind of motion the setting exists for. */
@media (prefers-reduced-motion: reduce) {
  .nu-art * { animation: none !important; }
  /* ⚠️ THE HOVER STATES GO TOO, not just the loops. A reader who has asked for
     stillness has asked for all of it; a drawing that jumps to 1.035 under the
     pointer is motion, however short. The colour changes stay — they are not
     movement, and they are what says the drawing is answering. */
  .nu-art svg, .nu-art .art-dim { transition: none !important; transform: none !important; }
  .nu-cell[open] .nu-cell-inner { animation: none; }
  .nu-cell-plus { transition: none; }
}

/* ── The signup band (.nu-join) ────────────────────────────────────────────
   Printed BY THE SHORTCODE, around the quick signup box, and only when that box
   is printed at all — see nuastro_widgets_shortcode(). It used to be written
   into front-page.html, which left a signed-in reader looking at an empty
   tinted stripe above the footer: the box is for visitors only, and a template
   cannot ask who is reading it.

   ⚠️ THE RULE IS GREY, NOT GOLD, of the two Elle offered on 8 Sep. The band is
   full-bleed, so in gold that hairline would be the largest single mark on the
   homepage — and the last gold object that big came back as "the site yellow is
   too much". `--line` is what the ticker and the footer are drawn with.

   ⚠️ AND THE TINT IS `ink-2`, THE HOUSE'S RAISED SURFACE (Elle, 9 Sep 2026: "a
   slight tint so the registration stands out just a little bit"). One step off
   the page's ground and no more. It is the BAND that is tinted, full width, not
   a panel around the fields — a panel is the card that was sent back on 8 Sep. */
/* ⚠️ THE TINT RUNS ALL THE WAY TO THE FOOTER, AND THE BOX SITS AT ITS MIDDLE
   (Elle, 9 Sep 2026: "bring down the registration box exactly halfway down to
   the footer and color it to the footer"). It used to stop on the last line of
   the form, leaving an untinted strip between the band and the footer's rule —
   the page's own closing space, which lives on `main`.

   ⚠️ TWO PIECES OF SPACE STAND BETWEEN THIS BAND AND THE FOOTER, and both are
   OUTSIDE it: `main`'s own closing padding, and WordPress's 24px block gap
   above the footer template part. A negative bottom margin was tried first and
   only ate the first of them — the band is two boxes deep inside `main`, so
   what looked like a cancelled padding on a flat harness left a strip of
   untinted page on the real one.

   So the page gives the space UP instead, and only on a page that carries this
   band: `:has()` asks whether the band is there at all. A signed-in reader gets
   no band, keeps `main`'s padding, and the arcminute text still clears the
   footer's rule — which is the whole reason that padding exists.

   ⚠️ AND THE FALLBACK IS THE OLD LOOK, NOT A BROKEN ONE. Where `:has()` is not
   understood, `main` simply keeps its padding and the tint stops short of the
   footer, exactly as it did before this change.

   Top and bottom are the same figure, which is what "halfway" means here: the
   form is centred between the rule above it and the footer below. */
.nu-join {
  border-top: 1px solid var(--wp--preset--color--line);
  background: var(--wp--preset--color--ink-2);
  margin-top: clamp(2.4rem, 5vw, 4rem);
  padding: clamp(3rem, 7vw, 6rem) clamp(1.25rem, 4vw, 4rem);
}
main:has(.nu-join) { padding-bottom: 0 !important; }
/* ⚠️ AND IF NEITHER BAND DRAWS, THE HOMEPAGE STILL CLOSES TIGHTER (Elle, 9 Sep
   2026: "when logged in the homepage looks really empty at the bottom after
   registration disappears"). That was written when a member's page ended on the
   arcminute band and 136px of nothing; a member now gets the composer in the
   same seat, so this is the case where NEITHER is drawn — a member the forum
   will not let write, most of all. The footer's own 4.5rem of top padding is
   what keeps it from reading as cramped.

   ⚠️ SCOPED TO `body.home`. `main:not(:has(.nu-join))` on its own matches every
   page on the site, none of which carry that band, and would quietly re-space
   the whole house on the way past. */
body.home main:not(:has(.nu-join)) { padding-bottom: var(--wp--preset--spacing--40); }
.wp-site-blocks:has(.nu-join) > footer,
.wp-site-blocks:has(.nu-join) > .wp-block-template-part { margin-block-start: 0; }
.nu-join-inner { max-width: 1240px; margin: 0 auto; }

/* ── A tool page that ends in the visitor's registration band ─────────────
   Elle, 10 Sep 2026: "on all visitor tool pages tighten the gap between the
   colored background of registration and the footer, so the color is fully
   connected to the footer, like on homepage."

   The homepage's `.nu-join` above does exactly this and could be keyed on a
   class in the page's own markup. A tool cannot: it renders inside a shadow
   root, so nothing about its band is visible to a selector out here. The
   WIDGET raises the flag instead — `ChartSkeleton` adds `.nu-tool-flush` to
   its own host element, which IS light DOM — and only for a VISITOR, because
   only a visitor's page ends in that band. A member's tool page keeps the
   normal gap.

   ⚠️ THE HOST, NOT THE PAGE. `body`-level classes would have needed PHP to
   know which reader is looking, which the shortcode does not; the widget knows
   at render time and nothing has to be told twice. */
.wp-site-blocks:has(.nuastro-rg.nu-tool-flush) > footer,
.wp-site-blocks:has(.nuastro-rg.nu-tool-flush) > .wp-block-template-part { margin-block-start: 0; }
main:has(.nuastro-rg.nu-tool-flush) { padding-bottom: 0 !important; }
main:has(.nuastro-rg.nu-tool-flush) > .wp-block-post-content,
main:has(.nuastro-rg.nu-tool-flush) > .entry-content { margin-block-end: 0; }
