/* ==========================================================================
   URSY ESTATE BRAND — the one ground for every property (4 Sep 2026).
   Source of truth: ursy.dev (tokens.css + aurora.css). Light is the default on
   every site; a site is dark only when it says so itself (html[data-theme="dark"],
   body.ursy-dark) or is dark by design (body.ueb-dark, e.g. ursy.games).
   Washes only, behind a veil, whole page. Never orb spheres.
   ========================================================================== */
:root {
  --ueb-bg: #fbfbfa; --ueb-ink: #131518; --ueb-ink2: #5c6167; --ueb-ink3: #686c72;
  --ueb-line: rgba(18, 20, 24, .08); --ueb-line2: rgba(18, 20, 24, .15);
  --ueb-glass: rgba(255, 255, 255, .72); --ueb-card: rgba(255, 255, 255, .82); --ueb-topline: rgba(255, 255, 255, .75);
  /* 1.3.4: the network strip's own ground. Near-white glass let it dissolve into the white header
     underneath; this is ursy.food's band tint, which reads as a separate row. */
  --ueb-band: rgba(244, 246, 249, .9);
  --ueb-scrim: rgba(250, 250, 249, .3); --ueb-neon: #15E7F4;
  --ueb-o1: oklch(.76 .22 208); --ueb-o2: oklch(.66 .30 272); --ueb-o3: oklch(.86 .21 92); --ueb-o4: oklch(.66 .30 312);
  --ueb-vig: radial-gradient(130% 90% at 50% -22%, rgba(255, 255, 255, .6), rgba(255, 255, 255, 0) 56%),
             radial-gradient(130% 100% at 50% 120%, rgba(18, 20, 24, .075), rgba(18, 20, 24, 0) 52%);
  --ueb-field-op: .45;
  --ueb-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --ueb-ui: "Instrument Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
}
/* 1.3.60 - 11 September 2026: data-ursy-theme joins the list. ursy.fm is the only
   property that signals dark with THIS attribute, and until now this block could
   not see it - so fm flipped its own tokens while the estate kept handing out the
   LIGHT ones. That mattered more than it sounds, because the estate owns the page
   ground on every property:

       html      { background: var(--ueb-bg) !important }
       body.ueb  { background: transparent  !important }

   fm's own `body { background: var(--bg) }` is overridden by that second rule, so
   with --ueb-bg stuck at #fbfbfa the page stayed WHITE no matter what fm's theme
   did - which is the white page under pale grey body text that was reported. */
html[data-theme="dark"],
html[data-ursy-theme="dark"],
html:not([data-theme="light"]):has(body.ueb-dark),
html:not([data-theme="light"]):has(body.ursy-dark) {
  --ueb-bg: #050506; --ueb-ink: #f6f4f1; --ueb-ink2: #a5abb2; --ueb-ink3: #8e959d;
  --ueb-line: rgba(255, 255, 255, .085); --ueb-line2: rgba(255, 255, 255, .17);
  --ueb-glass: rgba(17, 17, 20, .7); --ueb-card: rgba(17, 17, 20, .8); --ueb-topline: rgba(255, 255, 255, .07);
  --ueb-band: rgba(12, 12, 15, .92);
  --ueb-scrim: rgba(5, 5, 6, .26);
  --ueb-o1: oklch(.72 .25 205); --ueb-o2: oklch(.62 .33 274); --ueb-o3: oklch(.82 .24 90); --ueb-o4: oklch(.64 .33 314);
  --ueb-vig: radial-gradient(130% 90% at 50% -22%, rgba(255, 255, 255, .05), rgba(255, 255, 255, 0) 56%),
             radial-gradient(130% 100% at 50% 120%, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0) 55%);
  --ueb-field-op: .38;
}

/* The ground: the html paints the estate ground, the body lets the field through. */
html { background: var(--ueb-bg) !important; }
body.ueb { background: transparent !important; isolation: isolate; }

.ueb-field { position: fixed; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; contain: strict; }
.ueb-field__hue { position: absolute; inset: 0; animation: uebHue 90s linear infinite; }
.ueb-field__washes { position: absolute; inset: -25%; filter: blur(110px) saturate(110%); opacity: var(--ueb-field-op); }
.ueb-field__washes i { position: absolute; display: block; border-radius: 50%; }
.ueb-field__washes i:nth-child(1) { inset-inline-start: -5%; top: -8%; width: 64%; height: 46%; opacity: .6;
  background: radial-gradient(circle at 45% 40%, var(--ueb-o1) 0%, var(--ueb-o2) 52%, transparent 74%); animation: uebA 46s ease-in-out infinite; }
.ueb-field__washes i:nth-child(2) { inset-inline-end: -8%; top: 12%; width: 58%; height: 40%; opacity: .5;
  background: radial-gradient(circle at 55% 55%, var(--ueb-o2) 0%, var(--ueb-o1) 50%, transparent 72%); animation: uebB 54s ease-in-out infinite; }
.ueb-field__washes i:nth-child(3) { inset-inline-start: 20%; top: 52%; width: 60%; height: 34%; opacity: .42;
  background: radial-gradient(circle at 50% 50%, var(--ueb-o3) 0%, var(--ueb-o4) 48%, transparent 74%); animation: uebA 68s ease-in-out infinite reverse; }
.ueb-field__washes i:nth-child(4) { inset-inline-end: 2%; bottom: -6%; width: 54%; height: 30%; opacity: .4;
  background: radial-gradient(circle at 45% 50%, var(--ueb-o4) 0%, var(--ueb-o2) 52%, transparent 74%); animation: uebB 76s ease-in-out infinite; }
.ueb-field__veil { position: absolute; inset: 0; background: var(--ueb-scrim); }
.ueb-field__vig { position: absolute; inset: 0; background: var(--ueb-vig); }
.ueb-field__grain { position: absolute; inset: -4%; opacity: .04; mix-blend-mode: overlay; animation: uebGrain 7s steps(4) infinite;
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='160'%20height='160'%3E%3Cfilter%20id='n'%3E%3CfeTurbulence%20type='fractalNoise'%20baseFrequency='0.82'%20numOctaves='3'%20stitchTiles='stitch'/%3E%3C/filter%3E%3Crect%20width='160'%20height='160'%20filter='url(%23n)'/%3E%3C/svg%3E"); }

@keyframes uebHue { 0% { filter: hue-rotate(0deg) saturate(1.15) } 25% { filter: hue-rotate(90deg) saturate(1.35) } 50% { filter: hue-rotate(180deg) saturate(1.15) } 75% { filter: hue-rotate(270deg) saturate(1.35) } 100% { filter: hue-rotate(360deg) saturate(1.15) } }
@keyframes uebA { 0%, 100% { transform: translate3d(-14%, -10%, 0) scale(1.1) rotate(0deg) } 50% { transform: translate3d(16%, 12%, 0) scale(1.38) rotate(14deg) } }
@keyframes uebB { 0%, 100% { transform: translate3d(12%, 14%, 0) scale(1.28) rotate(0deg) } 50% { transform: translate3d(-16%, -12%, 0) scale(1.04) rotate(-16deg) } }
@keyframes uebGrain { 0% { transform: translate(0, 0) } 25% { transform: translate(-2%, 1%) } 50% { transform: translate(1%, -2%) } 75% { transform: translate(2%, 2%) } 100% { transform: translate(0, 0) } }
@media (prefers-reduced-motion: reduce) { .ueb-field__hue, .ueb-field__washes i, .ueb-field__grain { animation: none; } }

/* The network bar: one slim row above every header, every property, the current one lit. Scrolls sideways on phones.
   1.3.4 polish (Lee, 10 Sep 2026 — "the top bar is not looking very good at all ... all ursy top nav bars should be
   a similar experience across the estate"): measured against ursy.food, which he gave as the reference. Food's band
   is 35px tall, 11px type, on a cool tint (rgba(244,246,249,.9)) with a hairline under it. Ours was 32px of 10.5px
   type on near-white glass, so it dissolved into the white header beneath it instead of reading as its own band.
   Matched to food: same height, same type size, same cool tint, hairline kept. */
.ueb-bar { position: relative; z-index: 4; display: flex; align-items: center; gap: 2px; height: 35px; padding: 0 clamp(14px, 3vw, 40px); box-sizing: border-box;
  overflow-x: auto; overflow-y: hidden; scrollbar-width: none; -webkit-overflow-scrolling: touch; background: var(--ueb-band); box-shadow: inset 0 -1px 0 var(--ueb-line2);
  backdrop-filter: blur(20px) saturate(140%); -webkit-backdrop-filter: blur(20px) saturate(140%); font-family: var(--ueb-mono); font-size: 11px; letter-spacing: .05em; color: var(--ueb-ink2); }
.ueb-bar::-webkit-scrollbar { display: none; }
.ueb-bar__label { flex: none; margin-right: 10px; font-size: 9.5px; letter-spacing: .16em; color: var(--ueb-ink3); }
.ueb-bar__link { flex: none; display: inline-flex; align-items: center; min-height: 24px; padding: 0 8px; border-radius: 999px; color: var(--ueb-ink2); text-decoration: none; white-space: nowrap; transition: color .15s ease, background .15s ease; }
.ueb-bar__link:hover { color: var(--ueb-ink); background: var(--ueb-card); text-decoration: none; }
.ueb-bar__link:focus-visible { outline: 2px solid var(--ueb-neon); outline-offset: -2px; }
.ueb-bar__link.is-here { color: var(--ueb-ink); background: var(--ueb-card); box-shadow: inset 0 0 0 1px var(--ueb-line2); }

/* The network footer: after the site's own footer, identical on every property. */
.ueb-foot { position: relative; z-index: 3; background: var(--ueb-glass); box-shadow: inset 0 1px 0 var(--ueb-line);
  backdrop-filter: blur(24px) saturate(140%); -webkit-backdrop-filter: blur(24px) saturate(140%); color: var(--ueb-ink); font-family: var(--ueb-ui); }
.ueb-foot__inner { width: min(1180px, 100%); margin-inline: auto; padding: clamp(22px, 3.4vw, 34px) clamp(18px, 4vw, 44px) clamp(18px, 3vw, 26px); box-sizing: border-box; }
.ueb-foot__head { margin: 0 0 14px; font-family: var(--ueb-mono); font-size: 10px; font-weight: 500; letter-spacing: .18em; color: var(--ueb-ink3); }
.ueb-net { display: flex; flex-wrap: wrap; gap: 8px; }
.ueb-pill { display: inline-flex; align-items: center; gap: 8px; min-height: 38px; padding: 0 15px; border-radius: 999px; font-size: 13.5px; font-weight: 500;
  letter-spacing: -.01em; color: var(--ueb-ink2); text-decoration: none; background: var(--ueb-card);
  box-shadow: inset 0 0 0 1px var(--ueb-line), inset 0 1px 0 var(--ueb-topline); transition: transform .18s ease, box-shadow .18s ease, color .18s ease; }
.ueb-pill:hover { color: var(--ueb-ink); transform: translateY(-1px); box-shadow: inset 0 0 0 1px var(--ueb-line2), inset 0 1px 0 var(--ueb-topline), 0 10px 24px -16px rgba(16, 18, 22, .5); text-decoration: none; }
.ueb-pill:focus-visible { outline: 2px solid var(--ueb-neon); outline-offset: 2px; }
.ueb-pill.is-here { color: var(--ueb-ink); box-shadow: inset 0 0 0 1px var(--ueb-line2), inset 0 1px 0 var(--ueb-topline); }
/* REVISED 11 September 2026 (1.3.47). This was color: var(--ueb-neon) - #15E7F4, which is NOT
   redefined in the dark register, so it is the same bright cyan whatever the page. On the footer's
   own near-white pill that measured 1.52:1 at 8px on ursy.io and ursy.domains: the marker telling a
   reader WHICH property they are on was the one word in the footer they could not read. (On
   ursy.games, whose footer is dark, the same cyan measures 12.43 - which is why it was never noticed.)
   --ueb-ink2 flips with the register, so it reads on both: 6.25 light, 8.14 dark. The pill's own
   ground still marks "here"; the label no longer has to do it in a colour nobody can see. */
.ueb-pill__here { font-family: var(--ueb-mono); font-size: 8px; letter-spacing: .14em; color: var(--ueb-ink2); }
.ueb-foot__bottom { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px 24px; margin-top: 18px; padding-top: 14px;
  box-shadow: inset 0 1px 0 var(--ueb-line); font-family: var(--ueb-mono); font-size: 9px; letter-spacing: .2em; color: var(--ueb-ink3); padding-bottom: env(safe-area-inset-bottom); }

/* Each property's OWN background field and OWN network strip give way to the estate ones (scoped by body class; no inline styles, ursy.fm's CSP forbids them). */
body.ueb-host-ursy-io :is(#ursy-aura, .ursy-field[aria-hidden="true"], .u-aurora[aria-hidden="true"], div.ursy-aurora[aria-hidden="true"]) { display: none !important; }
body.ueb-host-ursy-games :is(#ursy-aura, .ursy-field[aria-hidden="true"], .u-aurora[aria-hidden="true"], div.ursy-aurora[aria-hidden="true"]) { display: none !important; }
body.ueb-host-ursy-finance :is(#ursy-aura, .ursy-field[aria-hidden="true"], .u-aurora[aria-hidden="true"], div.ursy-aurora[aria-hidden="true"]) { display: none !important; }
body.ueb-host-ursy-news :is(#ursy-aura, .ursy-field[aria-hidden="true"], .u-aurora[aria-hidden="true"], div.ursy-aurora[aria-hidden="true"]) { display: none !important; }
body.ueb-host-ursy-domains :is(#ursy-aura, .ursy-field[aria-hidden="true"], .u-aurora[aria-hidden="true"], div.ursy-aurora[aria-hidden="true"]) { display: none !important; }
body.ueb-host-ursy-hosting :is(#ursy-aura, .ursy-field[aria-hidden="true"], .u-aurora[aria-hidden="true"], div.ursy-aurora[aria-hidden="true"], .ursy-foot .ursy-net) { display: none !important; }
body.ueb-host-ursy-email :is(#ursy-aura, .ursy-field[aria-hidden="true"], .u-aurora[aria-hidden="true"], div.ursy-aurora[aria-hidden="true"], .ursy-net__head, .ursy-net__list, .ursy-net__links, .ursy-net__bar) { display: none !important; }
body.ueb-host-ursy-photos :is(#ursy-aura, .ursy-field[aria-hidden="true"], .u-aurora[aria-hidden="true"], div.ursy-aurora[aria-hidden="true"], .ursy-net__head, .ursy-net__list, .ursy-net__links, .ursy-net__bar) { display: none !important; }
body.ueb-host-ursy-cloud :is(#ursy-aura, .ursy-field[aria-hidden="true"], .u-aurora[aria-hidden="true"], div.ursy-aurora[aria-hidden="true"]) { display: none !important; }
body.ueb-host-ursy-fm :is(#ursy-aura, .ursy-field[aria-hidden="true"], .u-aurora[aria-hidden="true"], div.ursy-aurora[aria-hidden="true"], .u-rule:has(+ .u-chips .u-chip__domain), .u-chips:has(.u-chip__domain)) { display: none !important; }
body.ueb-host-ursy-ad :is(#ursy-aura, .ursy-field[aria-hidden="true"], .u-aurora[aria-hidden="true"], div.ursy-aurora[aria-hidden="true"], .ursy-net, .ursy-net__links) { display: none !important; }
body.ueb-host-ursy-id :is(#ursy-aura, .ursy-field[aria-hidden="true"], .u-aurora[aria-hidden="true"], div.ursy-aurora[aria-hidden="true"], .ursy-field[aria-hidden="true"] + .ursy-aurora, .ursy-net) { display: none !important; }

/* The wordmark strap (Lee, 4 Sep 2026): every property's logo carries the same estate line beneath it,
   set exactly as ursy.dev sets it. Each theme keeps its own wordmark colours; only the strap is added. */
.ueb-strap-inline { display: inline-block !important; line-height: 1; }
.ueb-strap-grid { display: inline-grid !important; grid-template-columns: auto auto; grid-auto-flow: row; align-items: baseline; line-height: 1; }
body.ueb-host-ursy-news header .un-brand, body.ueb-host-ursy-email header .ursy-brand, body.ueb-host-ursy-photos header .ursy-brand,
body.ueb-host-ursy-fm header .u-brand, body.ueb-host-ursy-ad header .ursy-mark, body.ueb-host-ursy-id header .ursy-mark,
body.ueb-host-ursy-finance .u-wordmark, body.ueb-host-ursy-io .wordmark-name, body.ueb-host-ursy-games .wordmark-type { display: inline-block; line-height: 1; }
body.ueb-host-ursy-domains header .ursy-logo, body.ueb-host-ursy-hosting header .brand, body.ueb-host-ursy-cloud header .ursy-wordmark { display: inline-grid !important; grid-template-columns: auto auto; grid-auto-flow: row; justify-content: start; align-items: baseline; gap: 0 !important; line-height: 1; }
body.ueb-host-ursy-news header .un-brand::after, body.ueb-host-ursy-email header .ursy-brand::after, body.ueb-host-ursy-photos header .ursy-brand::after,
body.ueb-host-ursy-fm header .u-brand::after, body.ueb-host-ursy-ad header .ursy-mark::after, body.ueb-host-ursy-id header .ursy-mark::after,
body.ueb-host-ursy-finance .u-wordmark::after, body.ueb-host-ursy-io .wordmark-name::after, body.ueb-host-ursy-games .wordmark-type::after,
body.ueb-host-ursy-domains header .ursy-logo::after, body.ueb-host-ursy-hosting header .brand::after, body.ueb-host-ursy-cloud header .ursy-wordmark::after {
  content: "PART OF THE URSY NETWORK"; display: block; grid-column: 1 / -1; margin-top: 3px;
  font: 500 7.5px/1 var(--ueb-mono); letter-spacing: .2em; text-transform: uppercase; white-space: nowrap; text-align: left; color: var(--ueb-ink3);
}
@media (max-width: 640px) {
  body.ueb-host-ursy-news header .un-brand::after, body.ueb-host-ursy-email header .ursy-brand::after, body.ueb-host-ursy-photos header .ursy-brand::after,
  body.ueb-host-ursy-fm header .u-brand::after, body.ueb-host-ursy-ad header .ursy-mark::after, body.ueb-host-ursy-id header .ursy-mark::after,
  body.ueb-host-ursy-finance .u-wordmark::after, body.ueb-host-ursy-io .wordmark-name::after, body.ueb-host-ursy-games .wordmark-type::after,
  body.ueb-host-ursy-domains header .ursy-logo::after, body.ueb-host-ursy-hosting header .brand::after, body.ueb-host-ursy-cloud header .ursy-wordmark::after { font-size: 6.5px; letter-spacing: .16em; }
}


/* ==========================================================================
   BRAND CONSISTENCY (Lee, 4 Sep 2026 ~15:30): the ursy.games logo is the model for every property —
   a dark rounded tile with a U, then URSY in ink, then the domain in the property's own colour, then
   the estate strap. Each property keeps its own domain colour. One sign-in control everywhere.
   ========================================================================== */
:root { --ueb-accent: #15E7F4; }
body.ueb-host-ursy-finance { --ueb-accent: #0a84ff; }
body.ueb-host-ursy-news    { --ueb-accent: #0a84ff; }
body.ueb-host-ursy-games   { --ueb-accent: #2fe673; }
body.ueb-host-ursy-io      { --ueb-accent: #ff6a3d; }
body.ueb-host-ursy-domains { --ueb-accent: #06b6d4; }
body.ueb-host-ursy-hosting { --ueb-accent: #0891b2; }
body.ueb-host-ursy-email   { --ueb-accent: #0aa5c2; }
body.ueb-host-ursy-photos  { --ueb-accent: #0ea5e9; }
body.ueb-host-ursy-cloud   { --ueb-accent: #0e8f96; }
body.ueb-host-ursy-fm      { --ueb-accent: #0aa5c2; }
body.ueb-host-ursy-ad      { --ueb-accent: #00a3b4; }
body.ueb-host-ursy-id      { --ueb-accent: #0aa5c2; }

/* The wordmark elements (same list as the strap). Inline ones get the tile floated left so the strap
   lands under the name, to the right of the tile — exactly the games layout. */
body.ueb-host-ursy-news header .un-brand, body.ueb-host-ursy-email header .ursy-brand, body.ueb-host-ursy-photos header .ursy-brand,
body.ueb-host-ursy-fm header .u-brand, body.ueb-host-ursy-ad header .ursy-mark, body.ueb-host-ursy-id header .ursy-mark,
body.ueb-host-ursy-finance .u-wordmark, body.ueb-host-ursy-io .wordmark-name, body.ueb-host-ursy-games .wordmark-type {
  font-size: 17px !important; font-weight: 700 !important; letter-spacing: -.03em !important; color: var(--ueb-ink) !important; font-family: var(--ueb-ui) !important; text-decoration: none !important;
}
body.ueb-host-ursy-news header .un-brand::before, body.ueb-host-ursy-email header .ursy-brand::before, body.ueb-host-ursy-photos header .ursy-brand::before,
body.ueb-host-ursy-fm header .u-brand::before, body.ueb-host-ursy-ad header .ursy-mark::before, body.ueb-host-ursy-id header .ursy-mark::before,
body.ueb-host-ursy-finance .u-wordmark::before, body.ueb-host-ursy-io .wordmark-name::before, body.ueb-host-ursy-games .wordmark-type::before,
body.ueb-host-ursy-domains header .ursy-logo::before, body.ueb-host-ursy-hosting header .brand::before, body.ueb-host-ursy-cloud header .ursy-wordmark::before {
  content: "U"; display: flex; align-items: center; justify-content: center; width: 30px; height: 30px; margin-right: 9px; border-radius: 9px;
  background: var(--ueb-ink); color: var(--ueb-bg); font: 800 15px/1 var(--ueb-ui); letter-spacing: 0;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .06) inset, 0 6px 18px -8px var(--ueb-accent);
}
body.ueb-host-ursy-news header .un-brand::before, body.ueb-host-ursy-email header .ursy-brand::before, body.ueb-host-ursy-photos header .ursy-brand::before,
body.ueb-host-ursy-fm header .u-brand::before, body.ueb-host-ursy-ad header .ursy-mark::before, body.ueb-host-ursy-id header .ursy-mark::before,
body.ueb-host-ursy-finance .u-wordmark::before, body.ueb-host-ursy-io .wordmark-name::before, body.ueb-host-ursy-games .wordmark-type::before { float: left; }
/* grid variant: tile in column 1 spanning both rows; name in 2-3; strap under the name */
body.ueb-host-ursy-domains header .ursy-logo, body.ueb-host-ursy-hosting header .brand, body.ueb-host-ursy-cloud header .ursy-wordmark {
  grid-template-columns: auto auto auto; font-size: 17px !important; font-weight: 700 !important; letter-spacing: -.03em !important; font-family: var(--ueb-ui) !important; color: var(--ueb-ink) !important; text-decoration: none !important;
}
body.ueb-host-ursy-domains header .ursy-logo::before, body.ueb-host-ursy-hosting header .brand::before, body.ueb-host-ursy-cloud header .ursy-wordmark::before { grid-column: 1; grid-row: 1 / 3; align-self: center; }
body.ueb-host-ursy-domains header .ursy-logo::after, body.ueb-host-ursy-hosting header .brand::after, body.ueb-host-ursy-cloud header .ursy-wordmark::after { grid-column: 2 / -1; }
body.ueb-host-ursy-domains header .ursy-logo strong, body.ueb-host-ursy-hosting header .brand strong, body.ueb-host-ursy-cloud header .ursy-wordmark strong { font-weight: 700; color: var(--ueb-ink); }

/* The domain part, in the property's own colour, same weight and face as the name. */
body.ueb-host-ursy-finance .u-wordmark > span, body.ueb-host-ursy-news header .un-brand span, body.ueb-host-ursy-games .wordmark-dot,
body.ueb-host-ursy-io .wordmark-name span, body.ueb-host-ursy-domains header .ursy-logo .tld, body.ueb-host-ursy-hosting header .brand .tld,
body.ueb-host-ursy-email header .ursy-brand__product, body.ueb-host-ursy-photos header .ursy-brand__product, body.ueb-host-ursy-cloud header .ursy-wordmark .mono,
body.ueb-host-ursy-fm header .u-brand__tld, body.ueb-host-ursy-ad header .ursy-mark span, body.ueb-host-ursy-id header .ursy-mark span {
  color: var(--ueb-accent) !important; font: inherit !important; font-weight: 700 !important; letter-spacing: -.03em !important; text-transform: none !important; opacity: 1 !important; margin: 0 !important; padding: 0 !important;
}
/* Old marks give way to the one tile. */
body.ueb-host-ursy-io .wordmark-mark, body.ueb-host-ursy-finance .u-mark, body.ueb-host-ursy-games .wordmark-mark { display: none !important; }

/* One sign-in control: ink pill with the ID tile, in the network bar on every property and on the
   themes' own header buttons where they exist. */
.ueb-bar__auth { margin-left: auto; flex: none; display: inline-flex; align-items: center; gap: 8px; padding-left: 12px; }
.ueb-signin, .ueb header a[href*="/auth/ursy-id/start"], .ueb .u-head .u-signin, .ueb .un-top .un-signin,
body.ueb-host-ursy-cloud header a[href$="/login/"], body.ueb-host-ursy-id header a[href*="/account"] {
  display: inline-flex !important; align-items: center; gap: 7px; height: 30px; min-height: 30px; padding: 0 12px 0 5px !important; border-radius: 999px !important;
  background: var(--ueb-ink) !important; color: var(--ueb-bg) !important; border: 0 !important; box-shadow: none !important; text-decoration: none !important;
  font: 700 12.5px/1 var(--ueb-ui) !important; letter-spacing: -.005em; white-space: nowrap; text-transform: none !important; transition: transform .15s ease, opacity .15s ease;
}
.ueb-signin:hover, .ueb header a[href*="/auth/ursy-id/start"]:hover, .ueb .u-head .u-signin:hover, .ueb .un-top .un-signin:hover,
body.ueb-host-ursy-cloud header a[href$="/login/"]:hover, body.ueb-host-ursy-id header a[href*="/account"]:hover { transform: translateY(-1px); opacity: .92; color: var(--ueb-bg) !important; }
.ueb-signin:focus-visible, .ueb header a[href*="/auth/ursy-id/start"]:focus-visible { outline: 2px solid var(--ueb-accent); outline-offset: 2px; }
.ueb-signin__mark, .ueb header a[href*="/auth/ursy-id/start"]::before, body.ueb-host-ursy-cloud header a[href$="/login/"]::before, body.ueb-host-ursy-id header a[href*="/account"]::before {
  content: "ID"; display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border-radius: 7px; flex: none;
  /* REVISED 11 September 2026 (1.3.48): was color: var(--ueb-ink). The tile's GROUND is
     --ueb-accent, which is a per-property brand colour and is bright on every property - cyan,
     #0a84ff blue, #2fe673 green, #ff6a3d orange, #06b6d4, #0891b2, #0aa5c2. The INK flipped with the
     register and the ground did not, so on every dark-rendering property the two letters went light
     on bright. Measured in dark: ursy.games 1.51, ursy.photos 2.52, ursy.hosting 3.35 - all failing,
     all passing in light. The ink is pinned dark because the ground is always bright: that gives
     4.97 on the darkest accent (#0891b2) and 11.05 on the lightest (#2fe673).
     THE TILES WERE NEVER CHECKED BEFORE because nine of the twelve draw them with ::before, and a
     pseudo-element is not in querySelectorAll - every sweep walked straight past them. */
  background: var(--ueb-accent); color: #131518; font: 800 9px/1 var(--ueb-mono); letter-spacing: .08em;
}
.ueb .u-head .u-signin::before, .ueb .un-top .un-signin::before { content: none; }
/* Same pinning as above, for the themes that print their own mark element. */
.ueb .u-signin__mark, .ueb .un-signin__mark { width: 22px !important; height: 22px !important; border-radius: 7px !important; background: var(--ueb-accent) !important; color: #131518 !important; font: 800 9px/1 var(--ueb-mono) !important; }
.ueb-signin--on { background: var(--ueb-card) !important; color: var(--ueb-ink) !important; box-shadow: inset 0 0 0 1px var(--ueb-line2) !important; }
.ueb-bar__out { color: var(--ueb-ink3); text-decoration: none; font-size: 10.5px; }
.ueb-bar__out:hover { color: var(--ueb-ink); }
@media (max-width: 640px) { .ueb-bar__auth { padding-left: 8px; } .ueb-signin { height: 26px; min-height: 26px; padding-right: 9px !important; } }


/* 1.2.1 (4 Sep 2026): header review fixes. Wordmarks never clip their strap; games stacks tile+name in a
   row like everyone else; the sign-in control in the network bar stays pinned at the right while the
   domain links scroll beneath it. */
body.ueb-host-ursy-news header .un-brand, body.ueb-host-ursy-email header .ursy-brand, body.ueb-host-ursy-photos header .ursy-brand,
body.ueb-host-ursy-fm header .u-brand, body.ueb-host-ursy-ad header .ursy-mark, body.ueb-host-ursy-id header .ursy-mark,
body.ueb-host-ursy-finance .u-brand, body.ueb-host-ursy-finance .u-wordmark, body.ueb-host-ursy-io .wordmark, body.ueb-host-ursy-io .wordmark-name,
body.ueb-host-ursy-games .wordmark, body.ueb-host-ursy-games .wordmark-type,
body.ueb-host-ursy-domains header .ursy-logo, body.ueb-host-ursy-hosting header .brand, body.ueb-host-ursy-cloud header .ursy-wordmark {
  min-width: max-content !important; max-width: none !important; overflow: visible !important; flex: none !important; white-space: nowrap;
}
body.ueb-host-ursy-games .wordmark { display: inline-flex !important; align-items: center !important; }
body.ueb-host-ursy-games .wordmark-type, body.ueb-host-ursy-io .wordmark-name, body.ueb-host-ursy-finance .u-wordmark { display: inline-block !important; }
body.ueb-host-ursy-io .wordmark, body.ueb-host-ursy-finance .u-brand { display: inline-flex !important; align-items: center !important; gap: 0 !important; }

/* 1.3.4: both rules below existed ONLY to let the sticky sign-in sit flush against the right edge.
   The sign-in left the strip on 10 Sep 2026, so `padding-right: 0` had stopped being a fix and become
   a fault — the last domain ran to the very edge of the window with no air after it. The strip is
   symmetrical again. .ueb-bar__auth is left styled, harmlessly, so a page still cached with the old
   markup does not look broken before it refreshes. */
.ueb-bar__auth { margin-left: auto; padding-left: 18px; }


/* 1.2.2: in the grid wordmarks the strap (spanning the name columns) was widening the URSY column and opening a
   gap before the dot. Name column is max-content; the domain column takes the slack; the strap only stretches it. */
body.ueb-host-ursy-domains header .ursy-logo, body.ueb-host-ursy-hosting header .brand, body.ueb-host-ursy-cloud header .ursy-wordmark {
  grid-template-columns: auto max-content 1fr !important; justify-items: start;
}


/* 1.2.3 (4 Sep 2026): the cookie/consent bar took a quarter of a phone screen (188px on io, 165px on games,
   buttons wrapping to 59px). One compact estate-wide treatment on phones: full-width strip at the bottom,
   one line of copy, two equal buttons on one row. Desktop untouched. */
@media (max-width: 640px) {
  .ueb .consent-bar { left: 8px !important; right: 8px !important; bottom: 8px !important; width: auto !important; max-width: none !important; transform: none !important;
    display: flex !important; flex-direction: column !important; align-items: stretch !important; gap: 8px !important; padding: 10px 12px !important; border-radius: 14px !important; }
  .ueb .consent-bar p { margin: 0 !important; font-size: 12.5px !important; line-height: 1.4 !important; max-width: none !important; }
  .ueb .consent-bar .consent-actions { display: flex !important; flex-direction: row !important; gap: 8px !important; width: 100% !important; }
  .ueb .consent-bar .consent-actions button { flex: 1 1 0 !important; min-height: 38px !important; height: 38px !important; padding: 0 10px !important; font-size: 13px !important; white-space: nowrap !important; line-height: 1 !important; }
}


/* 1.2.4 (4 Sep 2026): phone headers on the ursy-top family (email, photos, cloud, hosting, ad, id) overflowed
   the screen once the wordmark grew — email and photos even scrolled sideways. Rules: nothing may widen the
   page; on phones the bar carries the one sign-in so the themes' own ghost sign-in hides, the palette pill
   hides, buttons tighten, and the strap steps aside under 480px. Footer wordmarks are left alone. */
html { overflow-x: clip; }
body.ueb-host-ursy-email footer .ursy-brand, body.ueb-host-ursy-photos footer .ursy-brand,
body.ueb-host-ursy-fm footer .u-brand, body.ueb-host-ursy-ad footer .ursy-mark, body.ueb-host-ursy-news footer .un-brand {
  min-width: 0 !important; font-size: inherit !important; }
body.ueb-host-ursy-email footer .ursy-brand::before, body.ueb-host-ursy-photos footer .ursy-brand::before,
body.ueb-host-ursy-fm footer .u-brand::before, body.ueb-host-ursy-ad footer .ursy-mark::before, body.ueb-host-ursy-news footer .un-brand::before,
body.ueb-host-ursy-email footer .ursy-brand::after, body.ueb-host-ursy-photos footer .ursy-brand::after,
body.ueb-host-ursy-fm footer .u-brand::after, body.ueb-host-ursy-ad footer .ursy-mark::after, body.ueb-host-ursy-news footer .un-brand::after { content: none !important; display: none !important; }
.ueb .ursy-foot__status { flex-wrap: wrap; white-space: normal; max-width: 100%; }
/* 1.3.42. max-width:100% is 100% OF THE COLUMN, and the column was the problem. .ursy-foot__brand is
   a grid with no explicit columns, so its single column is sized by its widest item at max-content -
   here the status line "DEMO + URSY.ID SIGN-IN LIVE - ACCOUNT UPLOAD IN TESTING", which measured
   423px on a 375px phone. The column became 423, and everything in it followed: the footer wordmark,
   whose own content is 110px wide, was stretched to 423 and clipped 66px past the viewport. Measured
   on ursy.photos at 375. minmax(0, 1fr) makes the column fit the footer instead of the other way
   round, and the items inside it wrap as they were already told to. */
.ueb .ursy-foot__brand { grid-template-columns: minmax(0, 1fr); }
@media (max-width: 640px) {
  /* REVISED 11 September 2026 (1.3.22). This line read
       .ueb header a.u-btn--ghost[href*="/auth/ursy-id/start"], ... { display: none !important; }
     and its reason, written above, was "on phones the bar carries the one sign-in". That stopped
     being true in 1.3.3, when Lee moved the sign-in OUT of the top strip and into the header: the
     strip is estate domains and nothing else. The hide outlived its premise, so ursy.photos,
     ursy.email, ursy.hosting and ursy.cloud spent that time with NO visible sign-in at all below
     640px - measured on ursy.photos at 375px: six sign-in links in the document, none of them
     drawn. The pill now collapses to its 30px ID tile below 560px and fits, so it stays shown. */
  .ueb header .ursy-palette, .ueb header .ursy-palette-btn, .ueb header .u-iconbtn.ursy-desk-only { display: none !important; }
  .ueb .ursy-top { gap: 6px !important; padding-inline: 10px !important; }
  .ueb .ursy-top__actions { gap: 6px !important; flex: none !important; }
  /* 1.3.23: 34px here and 36px there left the collapsed ID tile a different height from the
     buttons beside it. One phone size for the whole control row, 36px square. */
  .ueb .ursy-top .u-btn { padding: 0 10px !important; height: 36px !important; min-height: 36px !important; font-size: 12.5px !important; white-space: nowrap; }
  .ueb .ursy-top .u-iconbtn { width: 36px !important; height: 36px !important; }
}
@media (max-width: 480px) {
  body.ueb-host-ursy-news .un-brand::after, body.ueb-host-ursy-email header .ursy-brand::after, body.ueb-host-ursy-photos header .ursy-brand::after,
  body.ueb-host-ursy-fm .u-brand::after, body.ueb-host-ursy-ad .ursy-mark::after, body.ueb-host-ursy-id header .ursy-mark::after,
  body.ueb-host-ursy-finance .u-wordmark::after, body.ueb-host-ursy-io .wordmark-name::after, body.ueb-host-ursy-games .wordmark-type::after,
  body.ueb-host-ursy-domains header .ursy-logo::after, body.ueb-host-ursy-hosting header .brand::after, body.ueb-host-ursy-cloud header .ursy-wordmark::after { display: none !important; }
  body.ueb-host-ursy-news .un-brand::before, body.ueb-host-ursy-email header .ursy-brand::before, body.ueb-host-ursy-photos header .ursy-brand::before,
  body.ueb-host-ursy-fm .u-brand::before, body.ueb-host-ursy-ad .ursy-mark::before, body.ueb-host-ursy-id header .ursy-mark::before,
  body.ueb-host-ursy-finance .u-wordmark::before, body.ueb-host-ursy-io .wordmark-name::before, body.ueb-host-ursy-games .wordmark-type::before,
  body.ueb-host-ursy-domains header .ursy-logo::before, body.ueb-host-ursy-hosting header .brand::before, body.ueb-host-ursy-cloud header .ursy-wordmark::before { width: 26px; height: 26px; margin-right: 7px; font-size: 13px; }
  body.ueb-host-ursy-domains header .ursy-logo::before, body.ueb-host-ursy-hosting header .brand::before, body.ueb-host-ursy-cloud header .ursy-wordmark::before { grid-row: 1; }
}


/* 1.2.5 (4 Sep 2026): ursy.finance's inner pages (markets, money, currency, converter, London) use their own
   room header, so the estate wordmark never reached them. Two markup variants: the market rooms wrap the name
   in a span after an old three-bar mark; the money rooms put the name straight in the anchor. */
body.ueb-host-ursy-finance .u-room-brand { color: var(--ueb-ink) !important; font-size: 17px !important; font-weight: 700 !important; letter-spacing: -.03em !important; font-family: var(--ueb-ui) !important; text-decoration: none !important; gap: 0 !important; min-width: max-content; }
body.ueb-host-ursy-finance .u-room-brand .u-room-mark { display: none !important; }
/* market rooms: name lives in the last span */
body.ueb-host-ursy-finance .u-room-brand:has(> .u-room-mark) > span:last-child { display: inline-block; line-height: 1; }
body.ueb-host-ursy-finance .u-room-brand:has(> .u-room-mark) > span:last-child::before,
body.ueb-host-ursy-finance .u-room-brand:not(:has(> .u-room-mark))::before {
  content: "U"; float: left; display: flex; align-items: center; justify-content: center; width: 30px; height: 30px; margin-right: 9px; border-radius: 9px;
  background: var(--ueb-ink); color: var(--ueb-bg); font: 800 15px/1 var(--ueb-ui); letter-spacing: 0; box-shadow: 0 0 0 1px rgba(255, 255, 255, .06) inset, 0 6px 18px -8px var(--ueb-accent);
}
body.ueb-host-ursy-finance .u-room-brand:has(> .u-room-mark) > span:last-child::after,
body.ueb-host-ursy-finance .u-room-brand:not(:has(> .u-room-mark))::after {
  content: "PART OF THE URSY NETWORK"; display: block; margin-top: 3px; font: 500 7.5px/1 var(--ueb-mono); letter-spacing: .2em; text-transform: uppercase; white-space: nowrap; text-align: left; color: var(--ueb-ink3);
}
/* money rooms: the anchor itself is the wordmark */
body.ueb-host-ursy-finance .u-room-brand:not(:has(> .u-room-mark)) { display: inline-block !important; line-height: 1; }
/* the domain, in finance blue, same weight as the name */
body.ueb-host-ursy-finance .u-room-brand > span:last-child > span, body.ueb-host-ursy-finance .u-room-brand:not(:has(> .u-room-mark)) > span {
  color: var(--ueb-accent) !important; font: inherit !important; font-weight: 700 !important; letter-spacing: -.03em !important; margin: 0 !important; padding: 0 !important;
}
@media (max-width: 480px) {
  body.ueb-host-ursy-finance .u-room-brand:has(> .u-room-mark) > span:last-child::after, body.ueb-host-ursy-finance .u-room-brand:not(:has(> .u-room-mark))::after { display: none; }
  body.ueb-host-ursy-finance .u-room-brand:has(> .u-room-mark) > span:last-child::before, body.ueb-host-ursy-finance .u-room-brand:not(:has(> .u-room-mark))::before { width: 26px; height: 26px; margin-right: 7px; font-size: 13px; }
}


/* 1.2.6 (4 Sep 2026): ursy.domains' tool pages run the older skin (bundle.css): header.header > a.wordmark > span.tld,
   and their footer carries its own network grid. Same wordmark treatment, and the estate footer replaces the grid. */
body.ueb-host-ursy-domains header.header .wordmark { display: inline-block !important; line-height: 1; color: var(--ueb-ink) !important; font-size: 17px !important; font-weight: 700 !important; letter-spacing: -.03em !important; font-family: var(--ueb-ui) !important; text-decoration: none !important; min-width: max-content; }
body.ueb-host-ursy-domains header.header .wordmark::before { content: "U"; float: left; display: flex; align-items: center; justify-content: center; width: 30px; height: 30px; margin-right: 9px; border-radius: 9px; background: var(--ueb-ink); color: var(--ueb-bg); font: 800 15px/1 var(--ueb-ui); letter-spacing: 0; box-shadow: 0 0 0 1px rgba(255, 255, 255, .06) inset, 0 6px 18px -8px var(--ueb-accent); }
body.ueb-host-ursy-domains header.header .wordmark::after { content: "PART OF THE URSY NETWORK"; display: block; margin-top: 3px; font: 500 7.5px/1 var(--ueb-mono); letter-spacing: .2em; text-transform: uppercase; white-space: nowrap; text-align: left; color: var(--ueb-ink3); }
body.ueb-host-ursy-domains header.header .wordmark .tld { color: var(--ueb-accent) !important; font: inherit !important; font-weight: 700 !important; letter-spacing: -.03em !important; margin: 0 !important; padding: 0 !important; }
body.ueb-host-ursy-domains .footer__net { display: none !important; }
@media (max-width: 480px) {
  body.ueb-host-ursy-domains header.header .wordmark::after { display: none; }
  body.ueb-host-ursy-domains header.header .wordmark::before { width: 26px; height: 26px; margin-right: 7px; font-size: 13px; }
}
/* the older skin also paints its own orb field; the estate field replaces it */
body.ueb-host-ursy-domains div.field:has(.field__washes) { display: none !important; }

/* 1.2.7 (4 Sep 2026): the hidden attribute must win. ursy.fm showed its "Nothing matches" box beside real results because .u-empty{display:grid} outranked [hidden]. Estate-wide rule. */
.ueb [hidden]:not([hidden="until-found"]) { display: none !important; }


/* 1.2.8 (4 Sep 2026): ursy.id account routes (register, sign-in, recover) render outside the theme with their own
   orb background and no estate chrome; the layer now reaches them. Their orbs give way to the field, the one
   unstyled input (recover) takes the theme's field look, and the languages page hero, whose entrance animation
   never finished, is simply shown. */
body.ueb-host-ursy-id .orb, body.ueb-host-ursy-id.ursy-authpage .ursy-aurora, body.ueb-host-ursy-id.ursy-authpage .ursy-field { display: none !important; }
body.ueb-host-ursy-id.ursy-authpage form label { display: block; font: 500 10px/1 var(--ueb-mono); letter-spacing: .12em; text-transform: uppercase; color: var(--ueb-ink3); margin: 0 0 6px; }
body.ueb-host-ursy-id.ursy-authpage form input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="hidden"]):not(.u-field) {
  display: block; width: 100%; height: 44px; padding: 0 13px; margin: 0 0 14px; box-sizing: border-box; border: 1px solid var(--ueb-line2); border-radius: 10px;
  background: rgba(255, 255, 255, .78); color: var(--ueb-ink); font: 400 14px var(--ueb-ui); outline: none;
}
body.ueb-host-ursy-id.ursy-authpage form input:not(.u-field):focus-visible { border-color: var(--ueb-accent); box-shadow: 0 0 0 3px rgba(21, 231, 244, .25); }
body.ueb-host-ursy-id .ursy-lang__head h1, body.ueb-host-ursy-id .ursy-lang__head .ursy-lang__lead, body.ueb-host-ursy-id .ursy-lang__head .ursy-source { animation: none !important; opacity: 1 !important; transform: none !important; }

/* 5 Sep 2026 (1.3.1): the footer's link to this site's twelve-room truth site. */
.ueb-foot__link{color:inherit;text-decoration:underline;text-underline-offset:2px}
.ueb-foot__link:hover,.ueb-foot__link:focus-visible{text-decoration-thickness:2px}

/* ===== 1.3.2, 9 Sep 2026: the advert rides in the footer =====
   Lee, 5 Sep: the plate used to print between the last section and this footer as
   a dark band of its own on a light ground — "a new background space ... looks
   poo". The footer carries it now, as its first row, on the footer's own ground.
   This rule owns only the ROW; how the plate itself looks in here is
   estate-ad.css's business (.estate-ad-infoot). Nothing is printed at all when
   the page shows no advert, so there is no empty row to leave behind. */
/* 1.3.7 (Lee, 10 Sep 2026: "it needs to be combined at the bottom of the footer"): the plate moved from the
   footer's first row to its last, so the hairline that separated it from the rest has to move with it — it
   is now drawn ABOVE the advert, not below. Same centring, same footer ground. */
.ueb-foot__ad { display: flex; justify-content: center; margin: clamp(18px, 2.4vw, 26px) 0 0;
  padding-top: clamp(16px, 2.2vw, 22px); box-shadow: inset 0 1px 0 var(--ueb-line); }
.ueb-foot__ad > .estate-ad { margin: 0; }
@media (max-width: 560px) { .ueb-foot__ad { margin-top: 16px; padding-top: 14px; } }

/* ---- 1.3.8, 10 September 2026: the header row, balanced ---------------------------------------
   Lee: "all ursy top nav bars should be a similar experience across the estate but also look good
   and be nicely balanced", naming ursy.photos as the bad example — "its not spaced out very well".

   Measured at 1280 before touching anything. ursy.photos carried an explicit 358px spacer element
   between its nav and its actions, so three links sat jammed against the wordmark with a dead zone
   after them; ursy.domains left a 622px void in the same place. ursy.news (19px), ursy.email (15px)
   and ursy.food — Lee's reference — already fill the row, food by centring its nav.

   So the nav takes the middle on the properties that left it hanging, which is the shape food
   already had, and the themes' own fixed spacers stand down. Measured after: photos 358 -> 201,
   domains 622 -> 427; the remainder is the honest gap between a short nav and the actions cluster,
   not a dead zone. Nothing here moves a site that was already balanced by more than its spacer. */
/* ---- 1.3.16: one consent bar, estate-wide -----------------------------------------------------
   Lee, 10 September 2026: "a lot of the cookie warning pages are different, they need uniforming ...
   short and sweet and the box the same size and balanced nicely."

   Two bars were live with two shapes as well as two wordings — the estate's own .ueb-consent and the
   themes' .consent-bar on ursy.io, games, finance and news. The words are unified in PHP; this makes
   the box the same on both: one width, one radius, one padding, the sentence and the buttons on one
   balanced row, wrapping to two only when there is genuinely no room.

   The buttons are NOT restyled beyond their size — each theme's primary button colour is its own, and
   an accept button that matches its site reads as part of that site rather than as a stranger's banner. */
.ueb .ueb-consent,
.ueb .consent-bar {
  box-sizing: border-box !important;
  /* 1.3.35: this was max-width, so the box shrank to whatever each theme's type happened to measure
     and the estate showed four different bars - 640 on ursy.io and ursy.photos, 558 on ursy.games,
     546 on ursy.finance, all at 1280. Lee asked for "the box the same size and balanced nicely", so
     it is a WIDTH now, and the same one everywhere. The min() keeps it inside a phone. */
  width: min(640px, calc(100vw - 32px)) !important;
  max-width: min(640px, calc(100vw - 32px)) !important;
  margin-inline: auto !important;
  display: flex !important;
  flex-wrap: wrap !important;
  align-items: center !important;
  justify-content: center !important;
  gap: 10px 16px !important;
  padding: 12px 16px !important;
  border-radius: 14px !important;
}
.ueb .ueb-consent > p,
.ueb .consent-bar > p {
  margin: 0 !important;
  flex: 1 1 260px;
  min-width: 0;
  font-size: 13px !important;
  line-height: 1.45 !important;
}
.ueb .ueb-consent__actions,
.ueb .consent-actions {
  display: inline-flex !important;
  align-items: center !important;
  gap: 8px !important;
  flex: none !important;
}
.ueb .ueb-consent__actions button,
.ueb .consent-actions button {
  height: 34px !important;
  min-height: 34px !important;
  padding: 0 14px !important;
  border-radius: 10px !important;
  font-size: 13px !important;
  white-space: nowrap !important;
}
@media (max-width: 560px) {
  .ueb .ueb-consent,
  .ueb .consent-bar { padding: 11px 13px !important; gap: 9px 12px !important; }
  .ueb .ueb-consent > p,
  .ueb .consent-bar > p { flex-basis: 100%; }
}

/* Links the estate adds to a property's own nav (API, Under the hood). They sit among the theme's own
   links, so they take the theme's type and colour by inheritance rather than being told what to look
   like — that is what keeps them from reading as bolted on. Only the things that do not inherit are set. */
.ueb .ueb-nav-add {
  font: inherit;
  color: inherit;
  text-decoration: none;
  white-space: nowrap;
}
.ueb .ueb-nav-add:hover { text-decoration: none; opacity: .82; }

/* IN DARK, `inherit` is not enough. Measured on ursy.photos in its dark register, 11 September 2026: the
   injected links computed rgb(19,21,24) — near-black on a near-black header — while the theme's own nav
   links beside them were rgb(92,97,103). A theme rule for anchors inside its nav outranks `color:inherit`
   and does not flip with the theme, so the two estate links were the only illegible thing in the row.
   Light is left exactly as it was, where inherit demonstrably matches the theme's own links; only the dark
   register is given an explicit token, and --ueb-ink2 is the same one the network strip's links use, so
   the two rows agree. */
html[data-theme="dark"] .ueb .ueb-nav-add,
html:not([data-theme="light"]):has(body.ueb-dark) .ueb .ueb-nav-add,
html:not([data-theme="light"]):has(body.ursy-dark) .ueb .ueb-nav-add { color: var(--ueb-ink2) !important; }

/* The nav the estate gives a header that had none of its own (ursy.finance). It needs its own row rules,
   unlike the links added to a theme's existing nav, which inherit that nav's spacing: as a bare <nav> it
   defaulted to display:block and the two links ran together as "APIUnder the hood" on the live site. */
.ueb .ueb-hdr-nav {
  display: inline-flex;
  align-items: center;
  gap: clamp(12px, 1.4vw, 18px);
  flex: none;
  margin-right: clamp(10px, 1.2vw, 16px);
}

.ueb .ursy-top__spacer { display: none !important; }
.ueb .ursy-top__nav { margin-left: auto !important; margin-right: auto !important; }
body.ueb-host-ursy-domains .ursy-nav { margin-left: auto !important; margin-right: auto !important; }

/* ursy.domains at tablet width: the header actions ran 149px past the viewport and the page could be
   scrolled sideways. Measured at 768 on 11 September 2026 — viewport 753, nav ending at 706 (it fits),
   .ursy-header__actions ending at 902.
   This one is mine. That row held a theme toggle and a basket, about 94px, until 1.3.8 put the estate
   sign-in pill in it — ursy.domains was one of two properties with no sign-in of its own — which took it
   to 184px on a row that had no spare width. The nav is the flexible item, so it is the one that gives:
   min-width:0 lets a flex item shrink below its content (it will not, by default) and the row then fits
   at every width between the burger and the desktop layout. The nav scrolls rather than wraps, which is
   what the estate's other narrow navs already do. */
@media (max-width: 980px) {
  body.ueb-host-ursy-domains .ursy-nav {
    min-width: 0 !important;
    flex: 1 1 auto !important;
    overflow-x: auto !important;
    scrollbar-width: none;
    margin-left: 12px !important;
    margin-right: 12px !important;
  }
  body.ueb-host-ursy-domains .ursy-nav::-webkit-scrollbar { display: none; }
  body.ueb-host-ursy-domains .ursy-header__actions { flex: none !important; }
}

/* The theme controls, one shape estate-wide. Lee: "if there a theme colour change button it should be
   working and set the same for the rest of the websites."

   Working: EVERY theme control on all twelve was clicked on the live site on 10 September and every one
   of them changed the document — nothing was broken. (An earlier pass reported ursy.photos' palette chip
   as dead; that reading was wrong, it compared only class and data-theme while the chip writes --o1..--o4
   to the html style attribute and persists ursyPalette. It cycles AURORA / ICE / FOREST correctly.)

   The same: they were not. Measured at 1280 — palette chips 64x36 r999px on hosting, 61x44 r12px on
   cloud, 85x38 r10px on photos; icon toggles 44x44 on most, 40 on io, 31 on news. Three radii, four
   heights, in a row that sits beside an identical sign-in pill on every site. One height, one radius,
   one hit area now; each theme keeps its own colours and its own icon. Six properties carry a palette
   chip (hosting, email, photos, fm, ad, and ursy.food) and six do not — that is a difference in what
   the themes offer, not in how they look, and porting the palette to the other six is a separate job. */
.ueb header .ursy-palette,
.ueb header .ursy-palette-btn,
.ueb header .ursy-profile,
.ueb header .ursy-contrast,
.ueb header .u-iconbtn--theme,
.ueb header .ursy-theme-toggle,
.ueb header .theme-toggle,
.ueb header .un-theme,
.ueb header .u-seg {
  height: 40px !important;
  min-height: 40px !important;
  border-radius: 12px !important;
  box-sizing: border-box !important;
  flex: none !important;
  align-self: center;
}
/* The icon-only ones are square, so the row reads as evenly spaced controls rather than odd slivers.
   `flex: none` is not decoration: ursy.news gives its toggle `flex: 0 1 auto`, so in a tight row it
   SHRANK past the width set here and measured 34x40 while every other property sat at 40x40 — the
   height and the radius had applied and only the width had been squeezed out. A control that changes
   size with how full the row happens to be is exactly the unevenness this block exists to end.
   ursy.domains' basket wears the same .ursy-icon-btn class as its theme toggle, so it is squared off
   with them rather than left as a 44px circle beside a 40px square. */
.ueb header .ursy-contrast,
.ueb header .u-iconbtn--theme,
.ueb header .ursy-theme-toggle,
.ueb header .ursy-icon-btn,
.ueb header .theme-toggle,
.ueb header .un-theme {
  width: 40px !important;
  height: 40px !important;
  min-height: 40px !important;
  flex: none !important;
  border-radius: 12px !important;
  padding: 0 !important;
  align-items: center !important;
  justify-content: center !important;
}
/* `display` is set ONLY on the named theme controls, never on the generic .ursy-icon-btn. Forcing
   inline-flex on that class un-hid ursy.domains' mobile burger at desktop width — the theme keeps it
   at display:none there, and an !important display beat it. A rule that makes things the same size
   must not also decide what is on screen. */
.ueb header .ursy-contrast,
.ueb header .u-iconbtn--theme,
.ueb header .ursy-theme-toggle,
.ueb header .theme-toggle,
.ueb header .un-theme {
  display: inline-flex !important;
}
/* ursy.photos and ursy.email give their dark toggle the bare .u-iconbtn class, which their burger also
   wears — the burger is display:none at this width, so matching both costs nothing and misses neither. */
body.ueb-host-ursy-photos header .u-iconbtn,
body.ueb-host-ursy-email header .u-iconbtn {
  width: 40px !important; height: 40px !important; min-height: 40px !important;
  border-radius: 12px !important; padding: 0 !important;
}

/* ---- 1.3.3, 10 September 2026: the sign-in left the top strip ---------------------------------
   Lee, against ursy.food as the reference: the strip is "ursy estate domains" and nothing else, and
   the sign-in goes in "the actual nav bar". Nine properties already print their own sign-in in their
   own header and the .ueb-signin rules above already shape it. ursy.io and ursy.games printed none
   of their own — the strip held their only one — so the estate control is injected beside their
   theme toggle, and these rules seat it on that row. .ueb-bar__auth is left defined above so a page
   served from cache mid-deploy still looks right until it refreshes. */
.ueb-hdr-auth { display: inline-flex; align-items: center; gap: 8px; flex: none; margin-left: 10px; }
.ueb-hdr-auth .ueb-signin { height: 32px; min-height: 32px; }
/* The rule above that shapes a theme's OWN sign-in link supplies the ID tile with ::before, because those
   themes print a bare <a>. The injected control brings its own .ueb-signin__mark, so inside a <header> both
   fired and the pill read "ID ID Sign in" (seen live on ursy.io, 10 Sep 2026). Ours keeps the real element. */
.ueb-hdr-auth .ueb-signin::before, .ueb-hdr-auth a[href*="/auth/ursy-id/start"]::before { content: none !important; }
/* Both themes put the toggle at the end of a flex row; the pill must not be the item that shrinks. */
body.ueb-host-ursy-io .ueb-hdr-auth { order: 0; flex: none; }
/* 1.3.23: games anchors on the burger (see the PHP note), which is the FIRST control in the row,
   so the tile is ordered last to keep the desktop row reading wordmark -> nav -> sign-in. */
body.ueb-host-ursy-games .ueb-hdr-auth { order: 99; flex: none; margin-left: 8px; }
/* 1.3.41. Moving the control out of the nav (1.3.23) fixed the phone and broke the tablet, which the
   phone-and-desktop checks did not look at. Measured on ursy.games at 768: wordmark 136 + gap 28 +
   nav 560 + gap 28 + pill 86 = 838 inside a 728px row, so the pill sat at 772-858 against a 753px
   viewport - off the screen, and clipped rather than scrollable, so it could not be reached at all.
   The nav is flex:0 1 auto but min-width:auto, so it would not give way. Now it does, and scrolls. */
body.ueb-host-ursy-games .site-navigation {
  min-width: 0; overflow-x: auto; scrollbar-width: none; -ms-overflow-style: none;
}
body.ueb-host-ursy-games .site-navigation::-webkit-scrollbar { width: 0; height: 0; }
@media (max-width: 900px) { .ueb-hdr-auth { margin-left: 6px; } .ueb-hdr-auth .ueb-signin { height: 30px; min-height: 30px; } }

/* ---- 1.3.28, 11 September 2026: ursy.finance's pill overhung its own header on a tablet ---------
   Measured at 768: the masthead's content box is 671px wide and its row demanded 696 - brand 152,
   gaps 28, and a .u-head-right of 516 - so the row ran to x=740 against a header that ends at 729,
   and the sign-in hung 11px past the card's rounded edge. 234px of that row is the estate layer's
   own: the injected API / Under the hood nav (148) and the pill (86). Lee asked for the API to be
   advertised, so the nav stays and the pill collapses to the tile instead, recovering 50px and
   bringing the row to 646 inside 671. It is finance-only because no other masthead is this full at
   this width - every other property still shows the full pill here. Starts to bind near 785px;
   the existing 900px band is used rather than inventing a breakpoint 100px above the fault. */
@media (max-width: 900px) {
  body.ueb-host-ursy-finance .u-head-right .u-signin {
    width: 36px !important; height: 36px !important; min-height: 36px !important;
    padding: 0 !important; gap: 0 !important; font-size: 0 !important; justify-content: center;
  }
  body.ueb-host-ursy-finance .u-head-right .u-signin .u-signin__mark { font-size: 9px !important; }
}
@media (max-width: 640px) { .ueb-hdr-auth .ueb-signin { height: 28px; min-height: 28px; padding-right: 10px !important; } }

/* ---- 1.3.22, 11 September 2026: the header sign-in collapses to its ID tile on phones ----------
   Measured live on ursy.domains at 375px: wordmark 159 + row gap 12 + the actions group 230 came to
   401 inside a 343px content box, and the document scrolled sideways to 418. The pill is the estate
   layer's own injection, so the fault and the fix are the estate's, not the theme's. Below 560px the
   pill becomes the ID tile alone. The words are hidden with font-size:0, NOT display:none, so the
   link keeps its accessible name and its title text — a screen reader still reads "ID Sign in".
   The themes' own sign-in links collapse at the same width so every property behaves identically. */
@media (max-width: 560px) {
  .ueb-hdr-auth { margin-left: 0; gap: 6px; }
  /* ursy.domains carries four controls beside a 159px wordmark; at 36px each the row needs a
     4px gap to stay inside a 375px phone. Measured: 339 of the 343px content box. */
  .ueb .ursy-header__actions { gap: 4px !important; }

  /* 1.3.24. Two properties keep an unmediated 40px icon-button rule further up this file, which
     outranks the phone sizes in the 640px block and left them 40px tall beside a 36px tile. Restated
     here, later and at the same specificity, so the whole phone control row is one size. */
  body.ueb-host-ursy-photos header .u-iconbtn,
  body.ueb-host-ursy-email header .u-iconbtn { width: 36px !important; height: 36px !important; }

  /* ursy.photos alone could not fit its row: burger 40 + wordmark 146 + toggle 40 + tile 36 + an
     89px "Open demo" button + gaps came to 375.6 inside a 355px content box, so flex squeezed both
     icon buttons to 29.7px wide - measured live at 375px. That header button is a duplicate: the
     hero carries the same link as a 339x50 "Open the live demo" button 501px down the page, and the
     burger menu carries it too. On phones the duplicate goes and the real call to action stands. */
  body.ueb-host-ursy-photos header .u-btn--accent { display: none !important; }

  /* SPECIFICITY, 1.3.26. The collapse above is written as .ueb header a[href*=...], which scores
     (0,2,2). The .ursy-top family's own phone rule is .ueb .ursy-top .u-btn at (0,3,0) and carries
     !important, so it KEPT font-size:12.5px and the pill rendered as a 36px circle with the word
     "Sign" hanging out of it - seen live on ursy.photos at 375px. Restated at (0,3,1) so the
     collapse actually lands on those four properties. */
  .ueb .ursy-top .u-btn[href*="/auth/ursy-id/start"],
  .ueb .ursy-top .u-btn[href$="/login/"] {
    width: 36px !important; height: 36px !important; min-height: 36px !important;
    padding: 0 !important; gap: 0 !important; font-size: 0 !important; justify-content: center;
  }
  .ueb-hdr-auth .ueb-signin,
  .ueb header a[href*="/auth/ursy-id/start"],
  body.ueb-host-ursy-cloud header a[href$="/login/"],
  body.ueb-host-ursy-id header a[href*="/account"] {
    /* !important on the box too (1.3.27): ursy.news and ursy.finance reach their own sign-in
       through .ueb .un-top .un-signin / .ueb .u-head .u-signin, which score (0,3,0) against this
       rule's (0,2,2) and kept height:30px - measured live, 36x30 where every other property was
       36x36. Marking the box important settles it without another specificity race. */
    width: 36px !important; height: 36px !important; min-height: 36px !important;
    gap: 0; justify-content: center; padding: 0 !important; font-size: 0 !important;
  }
  .ueb-hdr-auth .ueb-signin .ueb-signin__mark,
  .ueb .u-head .u-signin .u-signin__mark,
  .ueb .un-top .un-signin .un-signin__mark,
  .ueb header a[href*="/auth/ursy-id/start"]::before,
  body.ueb-host-ursy-cloud header a[href$="/login/"]::before,
  body.ueb-host-ursy-id header a[href*="/account"]::before { font-size: 9px !important; }
}

/* ---- 1.3.29, 11 September 2026: a full header nav must shrink, never cut the button off ---------
   Measured at 1280 on ursy.hosting: wordmark 183 + nav 737 + actions 419 + gaps came to 1367 inside
   a 1182px content box, and the row ran to x=1411. The "See hosting plans" button was entirely off
   the screen, and because html and body carry overflow-x:clip the reader could not scroll to it -
   it was simply gone. ursy.email lost 79px of "See plans" the same way. The PHP crowding guard now
   adds only "API" to a nav that already carries seven links, which recovers most of it; these rules
   make the remainder non-destructive rather than relying on that arithmetic staying true.

   min-width:0 is the whole trick: a flex item defaults to min-width:auto and will NOT shrink below
   its content, so the nav pushed its siblings out of the viewport instead of giving way. With it,
   the nav yields first and the buttons stay put; overflow-x:auto keeps every link reachable rather
   than clipped. The scrollbar is hidden because it would sit under the nav on a desktop header. */
.ueb .ursy-top__nav { min-width: 0; overflow-x: auto; scrollbar-width: none; -ms-overflow-style: none; }
.ueb .ursy-top__nav::-webkit-scrollbar { width: 0; height: 0; }

/* 44px gutters on a 1270px viewport spend 88px on empty edges while a button falls off the other
   end. Lee had already called this family's spacing out ("not spaced out very well"). 24px above the
   phone band, where the 640px rule sets its own 10px. */
@media (min-width: 641px) { .ueb .ursy-top { padding-inline: 24px !important; } }

/* Our own injected links carried more side padding than the themes' own: "API" measured 51px wide
   for three characters against ursy.hosting's 58px for "Email". Brought into line. */
.ueb-nav-add { padding-inline: 10px !important; }

/* ---- 1.3.30, 11 September 2026: ursy.fm's header lost its button off the right-hand edge --------
   Measured at 1280: brand 144 + nav 653 + palette 74 + language 107 + an icon button 44 + sign-in 86
   + "Listen free" 99, with eight 14px gaps, came to 1319 inside a 1092px content box. "Listen free"
   sat at 1295-1394 against a 1270 viewport - entirely off the screen - and "Sign in" was clipped at
   the edge beside it. fm's nav is .u-pill, not the .ursy-top__nav the rule above covers, so it needs
   naming; and fm was over its own box by 37px even BEFORE the estate added anything, so the crowding
   guard alone would not have been enough. Same treatment: 24px gutters instead of 44, and a nav that
   shrinks and scrolls rather than shoving the buttons off the end. */
@media (min-width: 641px) { body.ueb-host-ursy-fm header.u-top { padding-inline: 24px !important; } }
body.ueb-host-ursy-fm header .u-pill {
  min-width: 0; overflow-x: auto; scrollbar-width: none; -ms-overflow-style: none;
}
body.ueb-host-ursy-fm header .u-pill::-webkit-scrollbar { width: 0; height: 0; }

/* ---- 1.3.31, 11 September 2026: a header for the runtime's own pages ---------------------------
   Every property's /under-the-hood/ rooms are drawn by the runtime's document shell, which never
   calls the theme's header template. Measured on ursy.hosting: the document went straight from the
   estate strip into <main class="uth2"> - no wordmark, no site nav, no sign-in, nothing but a
   breadcrumb to get back. Lee: "every page should have a tidy top nav like this". This is that nav,
   built from the estate's own tokens so it reads as the same furniture, not a second design. */
.ueb-hdr-runtime {
  display: flex; align-items: center; gap: 14px;
  padding: 12px 24px; min-height: 60px;
  background: var(--ueb-band); border-bottom: 1px solid var(--ueb-line);
  position: relative; z-index: 3;
}
.ueb-hdr-runtime__brand {
  display: inline-flex; align-items: center; gap: 10px; flex: none;
  text-decoration: none; color: var(--ueb-ink);
}
.ueb-hdr-runtime__mark {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 10px; flex: none;
  background: var(--ueb-ink); color: var(--ueb-bg);
  font: 800 15px/1 var(--ueb-ui);
}
.ueb-hdr-runtime__name { font: 800 17px/1 var(--ueb-ui); letter-spacing: -.03em; }
.ueb-hdr-runtime__dot { color: var(--ueb-accent); }
/* The nav takes the middle and yields first — the same min-width:0 that stopped the themes' headers
   pushing their buttons off the screen. */
.ueb-hdr-runtime .ueb-hdr-nav {
  display: inline-flex; align-items: center; gap: 2px;
  flex: 1 1 auto; min-width: 0; overflow-x: auto;
  scrollbar-width: none; -ms-overflow-style: none;
}
.ueb-hdr-runtime .ueb-hdr-nav::-webkit-scrollbar { width: 0; height: 0; }
.ueb-hdr-runtime .ueb-hdr-auth { margin-left: auto; flex: none; }
@media (max-width: 640px) {
  .ueb-hdr-runtime { padding: 10px 14px; gap: 10px; min-height: 54px; }
  .ueb-hdr-runtime__name { font-size: 15px; }
}
/* On a phone the three links plus a wordmark plus the tile do not fit on one line: measured on
   ursy.news at 375, the nav needed 78px more than it had and "Under the hood" was cut to "Unde"
   against the sign-in tile. Scrolling is there and nothing was lost, but a half-word reads as
   broken. The nav takes its own row instead - wordmark and sign-in on the first, links on the
   second - so every link is whole and nothing has to be scrolled to. */
@media (max-width: 560px) {
  .ueb-hdr-runtime { flex-wrap: wrap; row-gap: 8px; padding-bottom: 8px; }
  .ueb-hdr-runtime .ueb-hdr-auth { order: 2; }
  .ueb-hdr-runtime .ueb-hdr-nav { order: 3; flex: 1 0 100%; }
}

/* ---- 1.3.36: the injected nav yields first, wherever it is ------------------------------------
   ursy.finance's header now names its own sections (Markets, Money, London) as well as the estate's
   two, which is what Lee asked for - "at least 4/5 really". Five links need room at tablet widths
   that two did not, so the injected nav gets the same min-width:0 the themes' own navs got: a flex
   item defaults to min-width:auto and will not shrink below its content, which is how a header ends
   up pushing its buttons off the screen instead of giving way. */
.ueb .ueb-hdr-nav {
  min-width: 0; overflow-x: auto; scrollbar-width: none; -ms-overflow-style: none;
}
.ueb .ueb-hdr-nav::-webkit-scrollbar { width: 0; height: 0; }
/* min-width:0 on the nav is not enough on its own: the nav can only give way if everything BETWEEN it
   and the header can give way too. ursy.finance puts it inside .u-head-right, which is flex:0 0 auto,
   so the row simply grew and took the controls off the edge with it - measured at 768 immediately
   after adding the three section links: the row ran 269px past the header's content box, clipped
   rather than scrollable, so the sign-in was gone. The row shrinks now, and the nav scrolls inside it. */
body.ueb-host-ursy-finance .u-head-right { min-width: 0; flex: 1 1 auto; }
/* And the nav must be the item that gives, not the controls. Letting the row shrink without saying
   which child yields just moved the damage: at 768 the nav kept its natural width and squeezed the
   sign-in to 22px and off the edge. The nav flexes; everything beside it is pinned. */
.ueb .ueb-hdr-nav { flex: 1 1 auto; }
body.ueb-host-ursy-finance .u-head-right > *:not(.ueb-hdr-nav) { flex: none !important; }
/* Below 900 there is not enough width for five links AND the market controls on one line, and a nav
   that has to be scrolled 200px is not a nav anyone uses. It takes its own row instead - controls
   above, links below - so every link is whole and reachable. */
@media (max-width: 900px) {
  body.ueb-host-ursy-finance .u-head-right { flex-wrap: wrap; row-gap: 8px; }
  body.ueb-host-ursy-finance .u-head-right .ueb-hdr-nav { flex: 1 0 100%; order: 2; }
}
/* On a phone even a full row is not wide enough for five links: measured on ursy.finance at 375, the
   nav still needed 117px more and cut "Under the hood" to "U". It scrolled, so nothing was lost - but
   a half-word reads as broken, the same judgement made for the runtime header. It wraps instead. */
@media (max-width: 560px) {
  .ueb .ueb-hdr-nav { flex-wrap: wrap; overflow-x: visible; row-gap: 4px; }
}

/* ---- 1.3.43: ursy.hosting's billing-term control could not be used on a phone ------------------
   Found by the 375px element sweep, not by anything that looks at the page scroll. .ursy-term is a
   339px flex row holding 485px of buttons - Monthly 82, "1 year / 2 MONTHS SAVED" 171, and
   "2 years / 3 MONTHS SAVED - BEST" 222 - with flex-wrap:nowrap and overflow-x:visible. The third
   option ran from x=281 to x=503 against a 375px viewport: 128px of it off the screen, and because
   html and body carry overflow-x:clip it could not be scrolled to either. A reader on a phone could
   not choose the two-year price at all, which is the one the site marks BEST.

   Not something this layer caused - it is the theme's own control - but it is a commercial control a
   reader cannot reach, so it is worth the four lines. Wrapping beats scrolling here: with a price
   choice every option should be visible at once, not discovered by dragging. */
@media (max-width: 560px) {
  .ueb .ursy-term { flex-wrap: wrap; row-gap: 6px; }
}

/* ---- 1.3.44: ursy.fm's header CTA hung off the right of a phone -------------------------------
   Measured at 375: header content box 325px, row demanding burger 44 + brand 108 + toggle 44 +
   sign-in 36 + "Listen free" 99 with four 14px gaps = 387. The button ran to x=421 against a 375
   viewport - 46px off the screen, and clipped rather than scrollable.

   It is a DUPLICATE, and that was checked rather than assumed: the header button and the hero's
   "Start listening" carry the same data-ursy-play attribute and do the same thing, and the hero one
   is 170x48 against the header's 99x30. Same judgement as ursy.photos' "Open demo": on a phone the
   duplicate goes and the real call to action stands. Desktop keeps it (verified at 1280). */
@media (max-width: 560px) {
  body.ueb-host-ursy-fm header .u-btn--primary { display: none !important; }
}

/* ---- 1.3.45: ursy.fm's own name was invisible in its own dark mode --------------------------------
   Measured with the browser emulating a dark preference: the wordmark "URSY.FM" rendered
   rgb(19,21,24) on a rgba(17,17,20,.7) header - 1.03:1. Unreadable, and it is the site's name.

   IT IS OURS. This stylesheet sets color: var(--ueb-ink) !important on that wordmark. The estate's
   dark register keys on html[data-theme="dark"], body.ueb-dark and body.ursy-dark - and ursy.fm
   signals dark with a FOURTH thing, data-ursy-theme, which none of those match. So --ueb-ink stayed
   the light register's near-black and we painted it onto fm's dark header.

   WHY THIS IS SCOPED TO THE HEADER AND NOT THE ROOT. Adding html[data-ursy-theme="dark"] to the token
   register was tried in the browser first and makes things WORSE: --ueb-bg flips too, the page ground
   goes dark, and fm's own h1 - which its theme leaves at rgb(27,32,39) - disappears into it. fm's
   dark mode is half-built and that belongs in fm's theme, not here. So the tokens are redefined on
   the header element alone: everything the estate paints INSIDE that header reads correctly, and the
   page below is untouched.

   --ueb-bg is flipped here too, deliberately. The sign-in pill is background:var(--ueb-ink) with
   color:var(--ueb-bg); flipping only the ink would have made it light-on-light. */
html[data-ursy-theme="dark"] body.ueb-host-ursy-fm header.u-top {
  --ueb-ink: #f6f4f1; --ueb-ink2: #a5abb2; --ueb-ink3: #8e959d;
  --ueb-bg: #050506;
  --ueb-line: rgba(255, 255, 255, .085); --ueb-line2: rgba(255, 255, 255, .17);
  /* 1.3.49: and a SOLID ground, which is the cause rather than another symptom. fm's dark header is
     rgba(17,17,20,.7) over a page that stays LIGHT, so it composites to about rgb(87,87,89) - a mid
     grey where neither a light nor a dark muted ink works. That is why its 7.5px "PART OF THE URSY
     NETWORK" strap measured 2.37 and its 8.5px palette label 3.10 even after the inks were corrected.
     Opaque, the same inks read properly: strap 5.86, palette 7.66, wordmark ~17. */
  background: #111114 !important;
}
/* fm's own nav links are rgb(27,32,39) from its theme, not a token, so they stay dark-on-dark on
   their own. Same header, same reader, same fault - given the estate already owns this row, it is
   fixed here rather than left unreadable. */
html[data-ursy-theme="dark"] body.ueb-host-ursy-fm header.u-top .u-pill a { color: var(--ueb-ink2) !important; }

/* 1.3.46. Two more found by measuring every header link on all twelve with the alpha layers
   COMPOSITED - the earlier pass compared text against a raw rgba() string, which is not a colour a
   reader ever sees. Both are the same shape of fault: a small label on a translucent ground.

   ursy.fm's palette name ("AURORA") is 8.5px and was taking --ueb-ink2. fm's header is
   rgba(17,17,20,.7) over a LIGHT page, which composites to about rgb(87,88,90) - so ink2 measured
   3.10:1 after the 1.3.45 fix, up from roughly 1.5 before it, and still short of 4.5. At that size
   it needs the full ink. */
/* REVERTED 1.3.49: this forced the palette name to full ink to clear the mid-grey ground. With the
   ground now solid (above) --ueb-ink2 measures 7.66 there, so the label goes back to being quieter
   than the wordmark beside it, which is the hierarchy it was designed with. */
html[data-ursy-theme="dark"] body.ueb-host-ursy-fm header.u-top .u-top__paletteName {
  color: var(--ueb-ink2) !important;
}

/* ursy.ad marks the current page with its own rule - .ursy-nav a[aria-current="page"] gets
   background:var(--neon-soft) and color:var(--neon), which is cyan rgb(21,231,244) on 14% cyan over
   white: composited rgb(222,251,253), measured 1.41:1. Every sibling link in that nav reads 6.24 or
   18.26, so the one item a reader cannot make out is the marker telling them where they are. The
   tinted pill is kept - it is the affordance - and only the text is given the estate ink. */
body.ueb-host-ursy-ad header .ursy-nav a[aria-current="page"] { color: var(--ueb-ink) !important; }

/* ============================================================================
   1.3.50 - 11 September 2026. The LIGHT-register contrast sweep of every header
   and footer on all twelve properties.

   METHOD, and why the earlier passes missed these. This sweep reads direct text
   nodes AND ::before/::after, and it composites the text over its ground at the
   PRODUCT of every ancestor's opacity before computing the ratio. Every previous
   pass omitted one of those three: pseudo-elements are invisible to a plain
   querySelectorAll walk, and reading getComputedStyle(el).color while ignoring
   an opacity above it OVER-reports - ursy.news's footer line below measured 5.82
   that way and renders at 4.17.

   Eight faults, six causes. The eight wordmark tlds that also measure under 4.5
   are logotype and exempt under WCAG 1.4.3; they stay on Lee's list, unchanged.

   NOT changed, deliberately: --ueb-neon itself, and ursy.finance's --accent, and
   ursy.io's --mint. Each is correct where it is a ground or a dark-register ink;
   only the specific TEXT uses that land on white are moved. Changing the token
   would have fixed the label and broken the chip it sits on. */

/* (a) ursy.email, ursy.photos, ursy.ad - the footer badge, measured at 1.52:1.
   "LAUNCH PILOT", "EARLY ACCESS" and "START AT £39" are 10.5px in --neon
   (#15E7F4) on a WHITE footer band. 1.52 is not "low contrast", it is not
   readable at all, and on ursy.ad the unreadable line is the PRICE on the page
   whose job is selling advertising.

   The deep cyan keeps the accent - these are eyebrow labels and should read as
   accent, not as body ink - and measures 5.80 on white. The neon is kept for the
   dark register underneath, so a property gaining the theme toggle later does
   not inherit a too-dark label.

   SPECIFICITY, learned the hard way one hour ago: the host theme's rule is
   .ursy-foot__pilot span:first-child, which is (0,2,1). A first attempt using
   body :is(.ursy-foot__pilot > span, ...) computes (0,1,2) - :is() takes the
   specificity of its most specific ARGUMENT - and lost silently, reporting the
   same 1.52 after the override as before it. */
body .ursy-foot__pilot > span,
body .ursy-foot__pilot > b,
body .ursy-foot__sell > b { color: #0c7079 !important; }
html[data-ursy-theme="dark"] body .ursy-foot__pilot > span,
html[data-ursy-theme="dark"] body .ursy-foot__pilot > b,
html[data-ursy-theme="dark"] body .ursy-foot__sell > b { color: var(--ueb-neon) !important; }

/* (b) ursy.io's --dim (#647185) measured 4.21 on the footer, four times: the
   brand blurb, the copyright, the language label and the consent reopener.

   It reads 4.54 in the header and main - passing - and 4.21 in the footer, for
   one reason: the footer sits on --bg-deep (#e8edf5) and the page on --bg
   (#f2f5fa). Same ink, darker ground, and it drops under.

   Changed at the token rather than per-rule because --dim is used as `color` in
   all 37 of its rules and as a background in none - checked before changing it,
   because a darker ink is only ever safe on a light ground. 109 live instances
   were measured across two templates: none sits on a dark ground, none is made
   worse, and the worst goes 4.21 -> 4.95. */
/* superseded by the register-scoped 1.3.54 rule at the end of this file */

/* (c) ursy.io's consent bar links to "What we record" in --mint (#14b877) at
   13px on near-white: 2.56:1. That link is the only route from the bar to the
   page explaining what the site records, so it is the one word in the bar that
   has to be readable. Darkened to the same green family - 5.27 - and the
   underline the theme already draws is left alone. --mint itself is untouched:
   it also feeds --accent on three category chips, where it is a GROUND. */
/* superseded by the register-scoped 1.3.54 rule at the end of this file */

/* (d) ursy.games' --dim-2 (#6e6e7a) on the footer column headings, 9.1px at
   4.05:1. games' footer ground is rgb(5,5,6) - nearly black - so this one moves
   the opposite way from every other fix here: the ink is BRIGHTENED. A first
   pass that assumed "light register means light ground" had candidates at 3.19,
   3.00 and 2.77, all worse than the fault.

   Measured per element by identity, before and after: 6 elements take the token
   at body level, all six improve, none is made worse, worst 4.05 -> 4.94.

   NOT fixed here: .game-card-arrow ("Play ↗", 9.76px, 3.77:1) redeclares --dim-2
   below body, so this does not reach it - and it is main content, not the header
   or footer this pass covers. It is on the queue. */
/* superseded by the register-split 1.3.56 rules at the end of this file */

/* (e) ursy.finance's live-data chip. The 10px "DATA" is #1e9e43 on the chip's
   own 13% green tint (composited rgb(228,249,233)): 3.15:1.

   The held state was found by forcing .is-held, because the chip was live while
   the sweep ran and the state paints nothing until the market data goes stale.
   Its amber #a58200 on a 14% yellow tint measures 3.42 - so the badge that tells
   a reader the prices in front of them are NOT current was the less readable of
   the two. Both keep their hue: 3.15 -> 5.66 and 3.42 -> 5.32. */
body.ueb-host-ursy-finance .u-live b { color: #136f35; }
body.ueb-host-ursy-finance .u-live.is-held b { color: #7f6400; }

/* (f) ursy.news's footer line "© 2026 URSY · United Kingdom · ..." at 4.17.
   The colour is not the fault - --dim on that ground is 5.82. The fault is
   `opacity: .85` on the paragraph, which is why every sweep that read the
   computed colour alone called this line a pass. --dim is already the quiet ink;
   the opacity was quieting an ink that had been chosen to be quiet. Removed
   rather than recoloured, so the designed hierarchy is what survives. */
body.ueb-host-ursy-news .un-foot__small { opacity: 1; }

/* ============================================================================
   1.3.51 - 11 September 2026. One fault, found only after the sweep itself was
   fixed.

   The 1.3.50 sweep could not see this, for TWO reasons at once. Its colour
   parser was hand-written and returned null for oklch(), so the element was
   silently SKIPPED - never measured, never reported, and indistinguishable in
   the output from an element that passed. And its backdrop walker read
   background-color only, so any panel painted with a gradient was measured
   against whatever colour sat beneath it.

   Both are now gone: the sweep composites through a 1x1 canvas and lets the
   BROWSER parse every colour, so oklch/oklab/color(srgb)/hsl all resolve, and
   it composites each gradient colour-stop as a candidate ground. Re-run across
   all twelve it measured 42/45/37/31/27/35/37/35/31/32/43/27 items - between 5
   and 11 MORE per property than the version it replaced. Those extra items were
   not passing before; they were never looked at.

   ursy.fm's four footer column headings are 10.5px in per-column tints. Three
   pass on their own (PRODUCTS 4.70, FREE WITH URSY 4.71, LEGAL 5.26). The
   fourth, "URSY.FM" in --m-stations, measures 3.95.

   Only the lightness moves - 0.56 to 0.51, hue 205 and chroma 0.16 untouched -
   which lands it at 4.80, inside the band its three siblings already occupy.
   Scoped to the footer heading rather than the tint class: .u-tint-stations
   also paints copy elsewhere on fm, and those are main content, measured
   separately, not this pass's business. */
/* superseded by the widened 1.3.54 rule at the end of this file */

/* ============================================================================
   1.3.52 - 11 September 2026. The DARK register, measured properly for the
   first time, and a REGRESSION OF MY OWN from an hour earlier.

   (a) THE REGRESSION. 1.3.51 set ursy.fm's "URSY.FM" footer heading to
   oklch(0.51 0.16 205) with no register scope. fm's own theme already flips
   that tint for dark - its three siblings go to about 0.70-0.72 lightness and
   measure 5.85 to 7.05 - and stations flips to oklch(0.74 0.13 205), which is
   7.64 on the dark footer. My unscoped rule overrode that flip and pinned the
   LIGHT teal in both registers: 4.17 before I touched it, 3.44 after. I made a
   failing item worse while fixing it in the other register.

   The rule above now carries html:not([data-ursy-theme="dark"]), so fm's own
   dark tint is left to do its job. Light stays 4.80, dark returns to 7.64.

   I talked myself out of checking this at the time, on the reasoning that fm's
   footer stays light in dark mode because its PAGE ground does. The footer
   ground is rgba(13,13,16,.93) - it goes dark on its own. Reasoning is not
   measuring.

   (b) fm's dark footer links, 26 of them, at 1.02:1 - the same colour as the
   ground they sit on, which is not "low contrast", it is invisible.

   The cause is inheritance, not a footer rule: nothing sets a colour on .u-foot
   at all, and shell.css's `.ursy-mount a { color: inherit }` comes after
   components.css's `.u-foot__col a { color: var(--ink2) }` at equal
   specificity, so every footer link inherits from the column, which inherits
   rgb(27,32,39) from .ursy-mount.

   Fixed at the footer and ONLY at the footer. .ursy-mount must keep its dark ink
   in the dark register - fm's page ground deliberately stays light, and its own
   h1 is rgb(27,32,39), so flipping the ink higher up makes the h1 vanish. That
   was proved in the browser at 1.3.46 and is why the 1.3.49 header fix is scoped
   to header.u-top. The footer is the same shape of anomaly as the header was: a
   dark band on a light page, owned by the estate row. #a5abb2 is --ueb-ink2's
   dark value and measures 7.18 there. Footer text that sets its own colour
   (rgb(142,149,157), 5.49) is untouched - this only reaches what INHERITS. */
html[data-ursy-theme="dark"] body.ueb-host-ursy-fm .u-foot { color: #a5abb2; }

/* NOTE FOR THE NEXT PASS - ursy.fm cannot be trialled by style injection.
   It is the ONLY estate host sending `style-src 'self'` with no 'unsafe-inline'
   (email, cloud and id send 'unsafe-inline'; io, games, finance, news, domains,
   photos send no CSP at all; ad sends a CSP with no style-src). An injected
   <style> lands in the DOM with NO sheet attached - element.sheet is null - so a
   trial rule silently does nothing and reads back as "the fix did not work".
   CSSOM writes (element.style.setProperty) are NOT blocked, which is why an
   outline set that way took effect while a stylesheet on the same page did not.
   Trial fm's CSS by editing an ALREADY-LOADED sheet through the CSSOM, or just
   deploy and measure. Same fault class as the ursy.food CSP that discarded 42
   inline declarations on 10 September. */

/* ============================================================================
   1.3.53 - 11 September 2026. The 1.3.50 footer-badge fix had a dark variant
   that fired on ONE of the estate's FOUR dark signals.

   The estate signals dark four different ways, and this file already said so at
   1.3.45 - "signals dark with a FOURTH thing, data-ursy-theme, which none of
   those match". I then wrote the 1.3.50 escape hatch against data-ursy-theme
   alone, which is the one signal ursy.email, ursy.photos and ursy.ad do NOT
   use. They use html[data-theme="dark"].

   So on the three properties the badge fix was written FOR, the dark variant
   never fired: clicking their own "Toggle dark mode" turns the footer ground to
   rgb(30,30,33) and left the deep cyan sitting on it at 2.86:1 - worse than the
   1.52 it replaced was on white, and reachable by any reader who prefers dark.

   Listed out in full rather than deduped, because a selector list is the only
   place CSS cannot factor: one missing signal is one silently unstyled site.
   The neon is correct on a genuinely dark ground - #15E7F4 on rgb(30,30,33)
   measures about 14:1 - and the deep cyan stays for every light ground. */
html[data-theme="dark"]       body .ursy-foot__pilot > span,
html[data-theme="dark"]       body .ursy-foot__pilot > b,
html[data-theme="dark"]       body .ursy-foot__sell  > b,
html[data-ursy-theme="dark"]  body .ursy-foot__pilot > span,
html[data-ursy-theme="dark"]  body .ursy-foot__pilot > b,
html[data-ursy-theme="dark"]  body .ursy-foot__sell  > b,
html:not([data-theme="light"]) body.ueb-dark  .ursy-foot__pilot > span,
html:not([data-theme="light"]) body.ueb-dark  .ursy-foot__pilot > b,
html:not([data-theme="light"]) body.ueb-dark  .ursy-foot__sell  > b,
html:not([data-theme="light"]) body.ursy-dark .ursy-foot__pilot > span,
html:not([data-theme="light"]) body.ursy-dark .ursy-foot__pilot > b,
html:not([data-theme="light"]) body.ursy-dark .ursy-foot__sell  > b { color: var(--ueb-neon) !important; }

/* ============================================================================
   1.3.54 - 11 September 2026. THE SAME MISTAKE, A THIRD TIME. Read this before
   changing any colour in this file.

   1.3.50 changed two things on ursy.io - the --dim token and the consent bar's
   link - and scoped NEITHER to a register, because I had measured io in light
   and concluded it was light-only. It is not. io has a dark register on
   html[data-theme="dark"], and an earlier sweep missed it only because that
   sweep set data-ursy-theme, which is not io's signal.

   io's own dark theme was already right. Measured with my two rules switched
   off through the CSSOM:

       --dim in dark   #8792a1  -> footer blurb 6.47
       consent link    #3ddc97  -> 11.34

   And with my unscoped rules on:

       --dim #5a6678   -> 3.50 on rgb(3,5,9), three items. It was 4.13 before
                          1.3.50, so it was already failing and I made it worse.
       link  #0e7a4e   -> 3.73, DOWN FROM A PASSING 7.78. I broke a good item.

   THE RULE, now that it has cost three fixes in one morning (1.3.52 for fm's
   tint, 1.3.53 for the badge's dark variant, this one for io):

       A COLOUR MUST BE MEASURED IN BOTH REGISTERS BEFORE IT SHIPS.
       A fix written against one ground is a regression waiting on the other.

   The light values are kept, scoped so they cannot reach a dark ground by any
   of the estate's FOUR dark signals - so this stays correct even if io changes
   which one it uses. Nothing is added for dark: io's own values already pass,
   and the best repair for an override that should not have existed is to stop
   overriding. */
html:not([data-theme="dark"]):not([data-ursy-theme="dark"])
  body.ueb-host-ursy-io:not(.ueb-dark):not(.ursy-dark) { --dim: #5a6678; }

html:not([data-theme="dark"]):not([data-ursy-theme="dark"])
  body.ueb-host-ursy-io:not(.ueb-dark):not(.ursy-dark) .consent-bar a { color: #0e7a4e; }

/* And the same widening for the fm tint scoped at 1.3.51. fm signals with
   data-ursy-theme today; if it ever moves to one of the other three, a
   :not([data-ursy-theme="dark"]) alone would silently start painting the light
   teal on fm's dark footer again - which is exactly the 3.44:1 that 1.3.52 was
   written to undo. */
html:not([data-theme="dark"]):not([data-ursy-theme="dark"])
  body.ueb-host-ursy-fm:not(.ueb-dark):not(.ursy-dark)
  .u-foot__head.u-tint-stations { color: oklch(0.51 0.16 205); }

/* ============================================================================
   1.3.55 - 11 September 2026. ursy.games was UNREADABLE one click from a fresh
   browser, and this file was the cause.

   WHAT A READER SEES. Load ursy.games with no stored preference and press
   "Switch between light and dark mode" once. The header turns white, the estate
   strip turns white - and the "URSY" wordmark stays #f6f4f1. White on white,
   measured 1.01:1. The h1's second line goes dark-on-dark, the ".games" tld
   reads 1.36, the network strap 2.48, the footer copy 3.56. Thirteen items in
   the header and footer alone, and the page ground stays dark behind a light
   header. It is not subtle; a screenshot shows the wordmark simply missing.

   WHY. `ueb-dark` is not a reader's choice. It is a STATIC per-site design flag
   - ursy_estate_brand_body_class() adds it from $cfg['dark'], meaning "this
   property is dark by design", which for ursy.games it is. The reader's choice
   is a different thing entirely: games' own theme writes
   html[data-theme="light"] and repaints its surfaces.

   So after that one click both were true at once - the site is dark by design
   AND the reader asked for light - and every dark rule in this file still
   matched through `html:has(body.ueb-dark)`. The estate kept handing out
   dark-register tokens for a page that had just turned light, and the wordmark
   is painted by a rule in THIS file: the games branch of the header-wordmark
   rule takes var(--ueb-ink), which was still #f6f4f1.

   THE RULE. An explicit reader choice must outrank a design default. Every
   selector in this file that keys on the design flags now carries
   html:not([data-theme="light"]) - the token block, the added-nav link colour,
   and the 1.3.53 footer badge. Nothing is added for games' light register: with
   the design flag correctly suppressed, the light tokens at :root apply and the
   header paints itself.

   Left alone deliberately: the page GROUND staying dark under a light header is
   games' own theme, not the estate's row, and it is recorded for Lee rather than
   patched from here. This change fixes what the estate paints. */

/* ============================================================================
   1.3.56 - 11 September 2026. ursy.games' FOOTER in the light register: thirteen
   failures, and one of them was mine for the fourth time.

   1.3.55 fixed games' header and page ground when a reader picks light. Its
   FOOTER was still wrong, and the sweep had not caught it because games is the
   one property that is DARK BY DESIGN - so the clean-storage sweep measures its
   dark register, and its light register only exists after a reader clicks.

   (a) THE GROUND. games' own site.css says

           .site-footer { background: linear-gradient(180deg, #07060c, var(--bg-deep)) }

       --bg-deep flips with the theme; the hard-coded #07060c does NOT. In dark
       that is #07060c to black and correct. In light it is NEAR-BLACK to light
       grey, under footer inks that had flipped to their dark-on-light values.
       "Cookies & analytics" measured 1.85:1, the network strap 3.82, "Browse
       games" 3.96. Given the estate's own gradient tokens the light register
       reads var(--bg) to var(--bg-deep) - the same gentle vertical fade the
       dark one has, in the light palette.

   (b) --dim-2, AND THIS IS MINE. 1.3.50 set it to #7e7e8a for games with NO
       register scope. That value was measured against games' near-black footer,
       where it gives 4.94. On the corrected LIGHT footer it gives 3.29.

       Note what games' own theme declares, because it is worse, not better:
       :root has #6e6e7a (4.05 on the dark footer - the fault 1.3.50 fixed) and
       :root[data-theme="light"] has #8f8f9c, which is LIGHTER still. Neither
       register was right before today. So the token is now split explicitly:
       #7e7e8a where the ground is dark, #66666f where the reader chose light.

       That split also carries main content with it: .game-card-arrow ("Play ↗",
       9.76px, twelve of them) goes 4.01 -> 5.68 in light and stays 4.74 in dark.

   (c) The two inks the theme hard-codes rather than tokenises - the footer
       blurb and copyright at rgb(127,119,124), and the network strap on
       .wordmark-type::after at rgb(104,108,114), 4.33 - are given the footer's
       own light ink. --dim is set on the footer element itself rather than at
       the root, so nothing outside the footer moves.

   NOT TOUCHED, and deliberately:
     the "●" before "57 rooms ready to play" is aria-hidden and the sentence
     beside it carries the meaning, so it is decoration, not text at 2.61;
     the ".games" tld is logotype, exempt, and still Lee's ruling.

   THE FOURTH TIME. 1.3.52 (fm), 1.3.53 (badge), 1.3.54 (io) and now this were
   all one mistake: a colour measured against one ground and shipped unscoped.
   A DARK-BY-DESIGN SITE STILL HAS A LIGHT REGISTER, and it is one click away. */
html:not([data-theme="light"]) body.ueb-host-ursy-games { --dim-2: #7e7e8a; }
html[data-theme="light"]       body.ueb-host-ursy-games { --dim-2: #66666f; }

html[data-theme="light"] body.ueb-host-ursy-games footer.site-footer {
  background: linear-gradient(180deg, var(--bg), var(--bg-deep)) !important;
  --dim: #5f5f68;
  --dim-2: #66666f;
}
html[data-theme="light"] body.ueb-host-ursy-games footer.site-footer .footer-inner > div > p { color: #5f5f68; }
html[data-theme="light"] body.ueb-host-ursy-games footer.site-footer .wordmark-type::after { color: #5f5f68; }

/* 1.3.57 - 11 September 2026. The last non-logotype item in ursy.games' light
   register: the header's "Local play" status pill.

   games' site.css hard-codes `.header-status { color: rgb(154,219,182) }` with
   no register variant - a pale mint that reads correctly on its dark header and
   measures 1.56:1 once the reader picks light, on the pill's own
   rgba(119,230,167,.055) tint over white. Same shape as the 1.3.50 footer badges:
   an accent chosen against a dark ground and never re-measured against a light
   one. The hue is kept and only taken dark enough to read: 5.28 at 9.9px/650.
   Dark is untouched, where the pale mint is right. */
html[data-theme="light"] body.ueb-host-ursy-games .header-status { color: #0f7a45; }

/* ============================================================================
   1.3.58 - 11 September 2026. THE SAME FAULT ON A NEW AXIS: not light-vs-dark,
   but VIEWPORT. My own 1.3.55 and 1.3.57 broke ursy.games on a phone.

   games' own site.css carries

       @media (max-width: 760px) { .site-header { background: rgb(9,8,17) } }

   That rule is register-blind, exactly like the footer gradient's hard-coded
   stop in 1.3.56. So at 760px and below the header stays NEAR-BLACK in BOTH
   registers - while 1.3.55 had just given the wordmark the light register's dark
   ink and 1.3.57 had given the status pill a dark green.

   Measured at 375 before this fix, data-theme=light:
       wordmark  rgb(19,21,24) on rgb(9,8,17)   1.09:1   - invisible
       "Local play"                              3.17:1

   Both were CORRECT at 1280, where the header turns white. Both were wrong on a
   phone. Every colour in this file had been measured in two registers and one
   width.

       A COLOUR IS ONLY PROVEN AT THE WIDTHS ITS GROUND IS PROVEN AT.
       A media query can change the ground under ink that never moved.

   The repair is the ground, not the ink - the same conclusion as 1.3.49 (fm's
   header) and 1.3.56 (games' footer gradient). The mobile header takes the light
   palette in the light register, matching what the desktop rule already
   computes: color-mix(in srgb, var(--pane) 96%, transparent). Dark is untouched,
   where rgb(9,8,17) is right.

   Measured after: 375px light, 29 painted items in header and footer, the ONLY
   failures the two .games tlds, which are logotype. wordmark 1.09 -> 18.29. */
@media (max-width: 760px) {
  html[data-theme="light"] body.ueb-host-ursy-games header.site-header {
    background: color-mix(in srgb, var(--pane) 96%, transparent) !important;
  }
}

/* ============================================================================
   1.3.59 - 11 September 2026. The sliver after "Network" on ursy.fm.
   Reported by Lee from the live site; the cause was this file.

   fm's header nav is a rounded pill, `flex: 0 1 auto` with `border-radius:
   999px`. An earlier release gave it `min-width: 0; overflow-x: auto` so it
   could shrink rather than push the row wide. That is right for a nav that has
   room to lose. It is wrong once something is ADDED to it: the estate's own
   "API" link.

   MEASURED at 1280 before this fix: the pill's content is 526px in a 492px box,
   so 34px is hidden; the API link runs 706-749 while the pill ends at 719. About
   13px of it survived, cut off by the 999px radius - the "weird half mark".

   The row was exactly full: its flex spacer sat at 0px, so nothing could give.
   Of the places to find 34px, the header's own 14px gap is the least intrusive -
   eight gaps, so 5px each returns 40px. At 9px the pill's scrollWidth equals its
   clientWidth exactly (0 hidden, the API link fully inside) and the header's
   rightmost element does not move: 1201px, unchanged.

   NOT fixed here, because it is fm's own and predates this link: between 961px
   and about 1200px the pill clips badly - 198px hidden at 1014px, with "World
   atlas" and "Network" unreachable and the burger not appearing until 960px.
   The API link adds 42px to that; the other 156px was already there. Recorded
   for Lee rather than patched, because the repair is fm's breakpoint, not a
   colour or a gap. */
body.ueb-host-ursy-fm header.u-top { gap: 9px; }

/* ============================================================================
   1.3.61 - 11 September 2026. 320px: the menu button was unreachable on two
   properties. Lee's call, taken after he said to stop asking and do the work.

   MEASURED LIVE at 320 (iPhone SE 1st gen, older Android), element right edge
   against clientWidth - never documentElement.scrollWidth, which overflow-x:clip
   makes useless on this estate:

     ursy.domains  the header row needs 339px inside a 288px content box.
                   wordmark 159 + gap 12 + actions 168. The burger sat at
                   315-355 against a 320 viewport: FIVE PIXELS of it on screen.
                   A reader on that phone cannot open the menu at all.
     ursy.hosting  actions ran to 340, over by 20; burger clipped.

   WHY THE WORDMARK. 51px had to come from somewhere on domains. The estate's own
   sign-in tile is 36px and its gaps are small change; the 159px wordmark is the
   only item large enough to matter, and Lee's note on this was that shrinking it
   is a decision about the brand rather than a bug fix. He has taken it.

   Roughly 14% off, and only below 360px: 159 -> 137 on domains, 154 -> 136 on
   hosting. Above 360px nothing here applies, so 375, 768 and 1280 are untouched
   by construction.

   TAP TARGETS. A first working combination took the icon buttons to 34px. That
   was rejected: 34px is a worse thumb target than the 40px it replaced, and
   fixing a control a reader cannot reach by making the neighbouring controls
   harder to hit is not a fix. domains keeps 36px buttons and pays for it with
   two more pixels off the wordmark instead. 36px clears WCAG 2.5.8 (24px) and is
   what the estate's own sign-in tile already uses at this width.

   These carry !important because the rules they correct are this file's own, and
   they are important: the 17px wordmark size and the 36px tile are both set
   !important higher up. Without it these rules match, apply and change nothing -
   which is exactly what the first trial did, silently.

   PROVEN at 320 after deploy: domains burger right edge 309 of 320 (was 355),
   hosting 316 of 320 (was 340). Nothing in either header overflows. */
@media (max-width: 360px) {
  body.ueb-host-ursy-domains header .ursy-logo,
  body.ueb-host-ursy-domains header .ursy-logo .tld { font-size: 14px !important; }
  body.ueb-host-ursy-domains .ueb-hdr-auth .ueb-signin {
    width: 30px !important; min-width: 30px !important; height: 30px !important;
  }
  body.ueb-host-ursy-domains .ursy-header__actions { gap: 2px !important; }
  body.ueb-host-ursy-domains .ursy-header__actions .ursy-icon-btn {
    width: 36px !important; min-width: 36px !important; height: 36px !important;
  }

  body.ueb-host-ursy-hosting header .brand,
  body.ueb-host-ursy-hosting header .brand .tld { font-size: 13px !important; }
  body.ueb-host-ursy-hosting .ursy-top__actions { gap: 4px !important; }
}
