/* blatt-site/public/css/ui.css
 *
 * The interaction layer added 2026-09-14. Paired with /js/ui.js.
 *
 * Momentum rules hold throughout, and a chrome layer is where they are
 * under the most pressure, because every off-the-shelf component for
 * these jobs ships fully rounded. Radii here are 6px to 10px. Nothing is
 * a pill. Nothing is italic.
 */

/* ════════════════════════════════════════════════════════════════
   LIGHT MODE
   Opt-in, never automatic. See the boot script in the document head
   for why prefers-color-scheme is not consulted.

   Only the tokens are redefined. Not one component below knows which
   theme it is in, which is what stops a light mode from becoming a
   second stylesheet that drifts.

   The gold darkens rather than staying #fabd59: gold on white fails
   contrast badly, and a brand colour that cannot be read is not
   serving the brand.
   ════════════════════════════════════════════════════════════════ */

:root[data-theme="light"] {
  --mv-gold:               #8a5f12;
  --mv-gold-dark:          #6d4a0c;
  --mv-gold-border:        #b8893e;
  --mv-gold-glow:          rgba(138, 95, 18, 0.22);
  --mv-gold-subtle:        rgba(138, 95, 18, 0.07);
  --mv-gold-border-subtle: rgba(138, 95, 18, 0.26);
  --mv-border-card:        rgba(120, 90, 40, 0.24);

  --mv-bg-primary:         #f7f5f1;
  --mv-bg-card:            #ffffff;
  --mv-bg-elevated:        rgba(255, 255, 255, 0.97);
  --mv-bg-glass:           rgba(255, 255, 255, 0.82);

  --mv-text-primary:       #14120e;
  --mv-text-secondary:     #3b372f;
  --mv-text-muted:         #5d574c;   /* 4.5:1 on the page ground */
  --mv-text-dim:           #6d675b;

  --mv-border:             #ddd6c9;

  --mv-text:               #14120e;
  --mv-line:               rgba(120, 90, 40, 0.2);
  --b-hairline:            rgba(120, 90, 40, 0.16);
  --b-hairline-firm:       rgba(120, 90, 40, 0.3);

  color-scheme: light;
}

/* THE SECOND AND THIRD TOKEN FAMILIES.
 *
 * The first version of this light mode overrode only the --mv-* tokens
 * in momentum.css and shipped. Anric found it in a minute: "the header
 * goes black when you scroll, you've got white writing on white
 * backgrounds, and some of the blocks in the Start Here section are
 * still black."
 *
 * He was right on all three, and my own contrast check had passed
 * because it sampled three elements. A browser audit of every element on
 * four pages found 173 failures.
 *
 * There are THREE token families on this estate, not one. blatt.css
 * carries --b-*, lustre.css carries --lu-*, and both were still dark.
 * --b-text-ivory alone is why every lead paragraph and section heading
 * came out near-white on near-white.
 *
 * Tokens first, then the hardcoded surfaces below, because nineteen
 * declarations across five files paint black without asking a token at
 * all. */
:root[data-theme="light"] {
  /* blatt.css */
  --b-ink:            #f7f5f1;
  --b-panel:          linear-gradient(158deg, rgba(138,95,18,0.05) 0%, rgba(255,255,255,0.96) 62%);
  --b-hairline:       rgba(120,90,40,0.18);
  --b-hairline-firm:  rgba(120,90,40,0.34);
  --b-text-ivory:     #211d16;
  --b-temp-wash:      rgba(138,95,18,0.05);
  --b-temp-edge:      rgba(138,95,18,0.3);

  /* lustre.css. The metal keeps its shape, on a pale ground. */
  --lu-lit:           #a8761f;
  --lu-mid:           #8a5f12;
  --lu-shade:         #6d4a0c;
  --lu-deep:          #513608;
  --lu-ground:        #f2efe8;
  --lu-surface:       #ffffff;
  --lu-ambient:       0 24px 60px -30px rgba(80,60,20,0.28);
  --lu-contact:       0 8px 22px -12px rgba(80,60,20,0.22);
  --lu-lip:           inset 0 1px 0 rgba(255,255,255,0.9);
  --lu-lip-warm:      inset 0 1px 0 rgba(255,244,222,0.9);
  --lu-underlip:      inset 0 -1px 0 rgba(120,90,40,0.16);
}

:root[data-theme="light"] body { background: var(--mv-bg-primary); color: var(--mv-text-primary); }

/* ── The hardcoded dark surfaces ────────────────────────────────
 *
 * Every one of these paints black with a literal rgba, so no token
 * override can reach it. Listed by the selector each one belongs to,
 * resolved from the source rather than guessed, so a reader can check
 * any line against the file it overrides.
 *
 * The stuck header is first because it is the one Anric named, and
 * because it was the single largest cause: dark text is CORRECT on this
 * page, and it was landing on a black bar, which is why the audit
 * reported the brand and all six nav links as unreadable on all four
 * pages at once. */
:root[data-theme="light"] .b-nav[data-stuck="true"] {
  background: rgba(255, 255, 255, 0.88);
  border-bottom-color: var(--b-hairline);
}

:root[data-theme="light"] .b-panel--wash {
  background: linear-gradient(158deg, var(--b-temp-wash) 0%, rgba(255, 255, 255, 0.95) 62%);
}
:root[data-theme="light"] .b-card {
  background: linear-gradient(160deg, var(--b-temp-wash) 0%, rgba(255, 255, 255, 0.96) 60%);
}
:root[data-theme="light"] .b-card--featured {
  background: linear-gradient(160deg, rgba(138, 95, 18, 0.08) 0%, rgba(255, 255, 255, 0.97) 62%);
}
:root[data-theme="light"] .b-faq details {
  background: linear-gradient(160deg, var(--b-temp-wash) 0%, rgba(255, 255, 255, 0.94) 60%);
}
:root[data-theme="light"] .b-gallery figure,
:root[data-theme="light"] .b-pillar {
  background: rgba(255, 255, 255, 0.7);
}
:root[data-theme="light"] .b-stat {
  background: rgba(255, 255, 255, 0.92);
}
:root[data-theme="light"] .b-footer {
  background: rgba(255, 255, 255, 0.72);
}

/* The registry. These are the "blocks in the Start Here section" that
   were still black. */
:root[data-theme="light"] .b-tier {
  background: linear-gradient(168deg, #ffffff 0%, #f4f1ea 100%);
}
:root[data-theme="light"] .b-fgroup {
  background: linear-gradient(180deg, rgba(138, 95, 18, 0.03), rgba(0, 0, 0, 0.035));
}
:root[data-theme="light"] .b-claim input,
:root[data-theme="light"] .b-claim select,
:root[data-theme="light"] .b-claim textarea {
  background: #ffffff;
  color: var(--mv-text-primary);
}
:root[data-theme="light"] .b-claim-result {
  background: linear-gradient(180deg, rgba(138, 95, 18, 0.06), rgba(0, 0, 0, 0.03));
}

:root[data-theme="light"] .b-book-nocover {
  background: linear-gradient(148deg, rgba(0,0,0,0.04), rgba(0,0,0,0.1));
}
:root[data-theme="light"] .b-kit-copy {
  background: linear-gradient(90deg, rgba(138, 95, 18, 0.05), rgba(0, 0, 0, 0.035));
}

:root[data-theme="light"] .mv-top-bar { background: rgba(255, 255, 255, 0.85); }
:root[data-theme="light"] .mv-section {
  background: linear-gradient(135deg, rgba(138, 95, 18, 0.04) 0%, rgba(255, 255, 255, 0.95) 100%);
}
:root[data-theme="light"] select,
:root[data-theme="light"] textarea,
:root[data-theme="light"] input[type="text"],
:root[data-theme="light"] input[type="email"],
:root[data-theme="light"] input[type="tel"],
:root[data-theme="light"] input[type="search"] {
  background: #ffffff;
  color: var(--mv-text-primary);
}

/* Anything sitting ON the gold now sits on a DARK gold, so its text has
   to invert. #8a5f12 needs white over it, not near-black, which is what
   the dark theme puts there. */
:root[data-theme="light"] .b-fab,
:root[data-theme="light"] .b-skip,
:root[data-theme="light"] .b-btn--gold {
  color: #ffffff;
}
:root[data-theme="light"] .b-fab svg,
:root[data-theme="light"] .b-btn--gold svg { color: #ffffff; }

/* The atmospheric washes are built for black. On white they turn into
   grey smears, so they come off rather than being re-tinted. */
:root[data-theme="light"] body::before,
:root[data-theme="light"] body::after,
:root[data-theme="light"] .b-hero-canvas,
:root[data-theme="light"] .b-dial { display: none; }

/* Shimmer is a light-on-dark effect. In light mode it becomes solid ink,
   because a near-white gradient on white is invisible. */
:root[data-theme="light"] .b-shimmer {
  -webkit-text-fill-color: var(--mv-text-primary);
  color: var(--mv-text-primary);
  background: none;
}

:root[data-theme="light"] .b-orb { opacity: 0.55; }

/* ── Screen reader only ─────────────────────────────────────────── */
.b-sr {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ════════════════════════════════════════════════════════════════
   FOCUS
   One visible ring everywhere, on :focus-visible only, so a mouse user
   never sees it and a keyboard user always does.
   ════════════════════════════════════════════════════════════════ */

a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--mv-gold);
  outline-offset: 2px;
  border-radius: 4px;
}

/* ── Buttons shared by the chrome ───────────────────────────────── */

.b-iconbtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px; height: 40px;
  padding: 0;
  border: 1px solid var(--b-hairline, rgba(250, 189, 89, 0.2));
  border-radius: 8px;
  background: transparent;
  color: var(--mv-text-secondary);
  cursor: pointer;
  transition: border-color 0.18s ease, color 0.18s ease, background 0.18s ease;
}
.b-iconbtn svg { width: 19px; height: 19px; }
@media (hover: hover) {
  .b-iconbtn:hover { border-color: var(--mv-gold); color: var(--mv-gold); background: var(--mv-gold-subtle); }
}

/* One button, two icons, swapped by state rather than by rewriting
   innerHTML. Nothing to re-parse and nothing to get out of sync. */
.b-ico-sun { display: none; }
:root[data-theme="light"] .b-ico-sun { display: block; }
:root[data-theme="light"] .b-ico-moon { display: none; }

.b-linkbtn {
  padding: 0;
  border: 0;
  border-bottom: 1px solid var(--b-hairline-firm, rgba(250, 189, 89, 0.34));
  background: none;
  color: var(--mv-text-muted);
  font: inherit;
  cursor: pointer;
}
@media (hover: hover) { .b-linkbtn:hover { color: var(--mv-gold); } }
.b-linkbtn[disabled] { opacity: 0.55; cursor: default; }

.b-btn--warn {
  border-color: var(--mv-red);
  background: var(--mv-red-subtle);
  color: #ff5f7e;
}
@media (hover: hover) { .b-btn--warn:hover { background: var(--mv-red); color: #fff; } }

/* ════════════════════════════════════════════════════════════════
   HEADER, MENU
   ════════════════════════════════════════════════════════════════ */

.b-nav-tools {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
}

.b-burger {
  display: none;
  width: 40px; height: 40px;
  padding: 0;
  border: 1px solid var(--b-hairline, rgba(250, 189, 89, 0.2));
  border-radius: 8px;
  background: transparent;
  cursor: pointer;
}
.b-burger-box { display: block; width: 18px; margin: 0 auto; }
.b-burger-box span {
  display: block;
  height: 1.5px;
  background: var(--mv-text-secondary);
  border-radius: 1px;
  transition: transform 0.22s ease, opacity 0.18s ease;
}
.b-burger-box span + span { margin-top: 4px; }

/* The X. Transform only, so it animates on the compositor. */
.b-burger[aria-expanded="true"] .b-burger-box span:nth-child(1) { transform: translateY(5.5px) rotate(45deg); }
.b-burger[aria-expanded="true"] .b-burger-box span:nth-child(2) { opacity: 0; }
.b-burger[aria-expanded="true"] .b-burger-box span:nth-child(3) { transform: translateY(-5.5px) rotate(-45deg); }

.b-menu {
  display: none;
  position: absolute;
  top: 100%; left: 0; right: 0;
  padding: 10px 20px 18px;
  border-top: 1px solid var(--b-hairline, rgba(250, 189, 89, 0.18));
  background: var(--mv-bg-elevated);
  backdrop-filter: blur(14px);
}
.b-menu-links { margin: 0; padding: 0; list-style: none; }
.b-menu-links li + li { border-top: 1px solid var(--b-hairline, rgba(250, 189, 89, 0.12)); }
.b-menu-links a {
  display: block;
  padding: 14px 4px;          /* 46px tall, clears the 44px tap floor */
  font-size: 1rem;
  text-decoration: none;
  color: var(--mv-text-primary);
}
@media (hover: hover) { .b-menu-links a:hover { color: var(--mv-gold); } }

/* THE BREAKPOINT. Below it the wide nav goes and the menu is available.
   861px rather than a round 860 so it cannot collide with the
   min-width: 861px query in ui.js: one of them has to own the boundary
   pixel and they must agree on which. */
@media (max-width: 860px) {
  .b-nav-wide { display: none; }
  .b-burger { display: inline-flex; align-items: center; justify-content: center; }
  .b-menu { display: block; }
  .b-menu[hidden] { display: none; }
}

/* The menu is position:absolute against the header. */
.b-nav { position: sticky; }

/* Scrolling under an open menu leaves the panel floating over content
   the visitor cannot reach. Lock the page instead. */
.b-menu-open, .b-menu-open body { overflow: hidden; }

/* ════════════════════════════════════════════════════════════════
   SEARCH
   ════════════════════════════════════════════════════════════════ */

.b-search {
  width: min(560px, calc(100vw - 32px));
  max-height: min(70vh, 640px);
  padding: 0;
  border: 1px solid var(--mv-gold-border-subtle);
  border-radius: 10px;
  background: var(--mv-bg-card);
  color: var(--mv-text-primary);
  overflow: hidden;
}
/* margin-block-start rather than a transform, so the dialog's own
   centring is not fought. Sitting it high is deliberate: results grow
   downward and a centred dialog jumps as they arrive. */
.b-search[open] { display: flex; flex-direction: column; margin-block-start: 10vh; }
.b-search::backdrop { background: rgba(0, 0, 0, 0.66); backdrop-filter: blur(3px); }

.b-search-form {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--b-hairline, rgba(250, 189, 89, 0.16));
}
.b-search-ico { width: 18px; height: 18px; flex: 0 0 auto; color: var(--mv-gold); }
.b-search-q {
  flex: 1 1 auto;
  min-width: 0;
  padding: 6px 0;
  border: 0;
  background: none;
  color: var(--mv-text-primary);
  font: inherit;
  font-size: 1rem;
}
.b-search-q:focus { outline: none; }   /* the dialog border carries focus */
.b-search-q::-webkit-search-cancel-button { display: none; }

.b-search-x {
  flex: 0 0 auto;
  padding: 4px 8px;
  border: 1px solid var(--b-hairline, rgba(250, 189, 89, 0.22));
  border-radius: 6px;
  background: none;
  color: var(--mv-text-muted);
  font-size: 0.7rem;
  letter-spacing: 0.07em;
  cursor: pointer;
}

.b-search-out { margin: 0; padding: 6px; list-style: none; overflow-y: auto; }
.b-search-row a {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 2px 10px;
  padding: 9px 10px;
  border-radius: 6px;
  text-decoration: none;
  color: inherit;
}
.b-search-kind {
  grid-row: 1 / span 2;
  align-self: center;
  padding: 2px 7px;
  border: 1px solid var(--mv-gold-border-subtle);
  border-radius: 5px;
  font-size: 0.62rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--mv-gold);
}
.b-search-t { font-weight: 600; line-height: 1.3; }
.b-search-b {
  font-size: 0.8rem;
  line-height: 1.4;
  color: var(--mv-text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Hover and the keyboard cursor paint identically, so a mouse user and a
   keyboard user are looking at the same thing. */
.b-search-row[aria-selected="true"] a,
.b-search-row a:hover { background: var(--mv-gold-subtle); }

.b-search-empty { margin: 0; padding: 18px 20px 22px; font-size: 0.9rem; color: var(--mv-text-muted); }

/* ════════════════════════════════════════════════════════════════
   BACK TO TOP, CONTACT
   Both bottom-right, stacked, so they never overlap. The contact button
   sits lower because it is the more important of the two.
   ════════════════════════════════════════════════════════════════ */

.b-totop, .b-fab {
  position: fixed;
  right: max(16px, env(safe-area-inset-right));
  z-index: 60;
  border-radius: 8px;
  cursor: pointer;
  transition: transform 0.18s ease, border-color 0.18s ease, background 0.18s ease;
}

.b-totop {
  bottom: calc(max(16px, env(safe-area-inset-bottom)) + 58px);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px; height: 44px;
  padding: 0;
  border: 1px solid var(--mv-gold-border-subtle);
  background: var(--mv-bg-elevated);
  color: var(--mv-gold);
  backdrop-filter: blur(10px);
}
.b-totop svg { width: 19px; height: 19px; }
.b-totop[hidden] { display: none; }

.b-fab {
  bottom: max(16px, env(safe-area-inset-bottom));
  display: inline-flex;
  align-items: center;
  gap: 9px;
  min-height: 46px;
  padding: 0 16px;
  border: 1px solid var(--mv-gold-border);
  background: var(--mv-gold);
  color: #12100b;
  font-weight: 600;
  font-size: 0.92rem;
  text-decoration: none;
  box-shadow: 0 8px 26px rgba(0, 0, 0, 0.4);
}
.b-fab[hidden] { display: none; }
.b-fab svg { width: 17px; height: 17px; }

@media (hover: hover) {
  .b-totop:hover { border-color: var(--mv-gold); transform: translateY(-2px); }
  .b-fab:hover { transform: translateY(-2px); }
}

/* On a phone the label costs width that the page needs more than the
   button does. The icon plus the aria-label still says what it is. */
@media (max-width: 560px) {
  .b-fab { padding: 0; width: 46px; justify-content: center; }
  .b-fab-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
}

/* ════════════════════════════════════════════════════════════════
   CONSENT
   ════════════════════════════════════════════════════════════════ */

.b-consent {
  position: fixed;
  left: 0; right: 0;
  bottom: 0;
  z-index: 80;
  padding: 14px max(16px, env(safe-area-inset-left)) calc(14px + env(safe-area-inset-bottom));
  border-top: 1px solid var(--mv-gold-border-subtle);
  background: var(--mv-bg-elevated);
  backdrop-filter: blur(14px);
}
.b-consent[hidden] { display: none; }
.b-consent-in {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px 20px;
  max-width: 1100px;
  margin: 0 auto;
}
.b-consent-copy {
  flex: 1 1 380px;
  margin: 0;
  font-size: 0.86rem;
  line-height: 1.5;
  color: var(--mv-text-secondary);
}
.b-consent-copy strong { color: var(--mv-text-primary); }
.b-consent-acts { display: flex; gap: 10px; flex: 0 0 auto; }
.b-consent-acts .b-btn { min-height: 42px; }

/* The banner sits above the floating buttons, so lift them clear while
   it is on screen rather than letting them overlap it. */
.b-consent:not([hidden]) ~ .b-fab { bottom: 96px; }
.b-consent:not([hidden]) ~ .b-totop { bottom: 154px; }

/* ════════════════════════════════════════════════════════════════
   CONFIRM
   ════════════════════════════════════════════════════════════════ */

.b-confirm {
  width: min(420px, calc(100vw - 32px));
  padding: 24px;
  border: 1px solid var(--mv-gold-border-subtle);
  border-radius: 10px;
  background: var(--mv-bg-card);
  color: var(--mv-text-primary);
}
.b-confirm::backdrop { background: rgba(0, 0, 0, 0.66); }
.b-confirm-t {
  margin: 0 0 10px;
  font-family: 'Russo One', sans-serif;
  font-weight: 400;
  font-size: 1.1rem;
}
.b-confirm-d { margin: 0 0 20px; font-size: 0.9rem; line-height: 1.55; color: var(--mv-text-secondary); }
.b-confirm-acts { display: flex; gap: 10px; justify-content: flex-end; }

/* ════════════════════════════════════════════════════════════════
   COPY TO CLIPBOARD
   ════════════════════════════════════════════════════════════════ */

[data-copy] { position: relative; }
.b-copy {
  position: absolute;
  top: 8px; right: 8px;
  padding: 4px 9px;
  border: 1px solid var(--b-hairline-firm, rgba(250, 189, 89, 0.3));
  border-radius: 6px;
  background: var(--mv-bg-elevated);
  color: var(--mv-text-muted);
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color 0.16s ease, border-color 0.16s ease;
}
@media (hover: hover) { .b-copy:hover { color: var(--mv-gold); border-color: var(--mv-gold); } }
.b-copy.is-done { color: var(--mv-green-dot); border-color: var(--mv-green-dot); }

/* ════════════════════════════════════════════════════════════════
   LOADING
   ════════════════════════════════════════════════════════════════ */

.is-loading {
  position: relative;
  background: linear-gradient(100deg,
    rgba(255, 255, 255, 0.035) 32%,
    rgba(255, 255, 255, 0.09) 50%,
    rgba(255, 255, 255, 0.035) 68%);
  background-size: 220% 100%;
  animation: bShimmer 1.25s linear infinite;
  border-radius: 6px;
}
@keyframes bShimmer { from { background-position: 180% 0; } to { background-position: -80% 0; } }
:root[data-theme="light"] .is-loading {
  background: linear-gradient(100deg, rgba(0,0,0,0.045) 32%, rgba(0,0,0,0.09) 50%, rgba(0,0,0,0.045) 68%);
  background-size: 220% 100%;
}

/* ── Reduced motion ─────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .b-totop, .b-fab, .b-iconbtn, .b-copy, .b-burger-box span { transition: none; }
  .b-totop:hover, .b-fab:hover { transform: none; }
  .is-loading { animation: none; }
}

/* ════════════════════════════════════════════════════════════════
   PRINT
   A founder page IS printed: a booker prints the bio, an assistant
   prints the press kit for a green room. So this is a real sheet, not
   three display:none rules.

   Everything below assumes the page is going onto white paper, whatever
   theme the screen was in.
   ════════════════════════════════════════════════════════════════ */

@media print {
  /* Every piece of chrome. None of it means anything on paper, and a
     floating Book a call button prints as a gold rectangle over the
     text. */
  .b-nav, .b-menu, .b-progress, .b-skip, .b-totop, .b-fab,
  .b-consent, .b-search, .b-confirm, .b-copy, .b-nav-tools,
  .b-hero-canvas, .b-dial, .b-orb, .b-gallery, .b-lk-cross {
    display: none !important;
  }

  :root, :root[data-theme="light"], :root[data-theme="dark"] {
    --mv-bg-primary: #fff;
    --mv-bg-card: #fff;
    --mv-text-primary: #000;
    --mv-text-secondary: #222;
    --mv-text-muted: #444;
    --mv-gold: #000;
    --mv-gold-dark: #000;
  }

  body {
    background: #fff !important;
    color: #000 !important;
    font-size: 11pt;
  }
  body::before, body::after { display: none !important; }

  .b-shimmer { -webkit-text-fill-color: #000 !important; color: #000 !important; background: none !important; }

  /* Reveal animations leave content at opacity 0 if the observer has not
     fired. Printing an unscrolled page would print blank sections. */
  .b-reveal, [class*="b-reveal"] { opacity: 1 !important; transform: none !important; }

  a { color: #000; text-decoration: underline; }

  /* The destination of every link, once, in the printed text. A printed
     page of underlined words that go nowhere is the standard failure of
     a print stylesheet nobody tested. Confined to content: putting the
     href after every navigation item would bury the page in URLs. */
  main a[href^="http"]::after {
    content: " (" attr(href) ")";
    font-size: 8.5pt;
    color: #444;
    word-break: break-all;
  }

  h1, h2, h3 { page-break-after: avoid; break-after: avoid; }
  .b-section, .b-index-row, .b-lk-item, .b-kit, figure, blockquote {
    page-break-inside: avoid;
    break-inside: avoid;
  }
  .b-section { border-top: 1px solid #999; }

  details { display: block; }
  details > summary { list-style: none; font-weight: 700; }
  details:not([open]) > *:not(summary) { display: revert; }

  @page { margin: 16mm 14mm; }
}

/* ════════════════════════════════════════════════════════════════
   404
   ════════════════════════════════════════════════════════════════ */

.b-404 {
  max-width: 44rem;
  margin: 0 auto;
  padding-block: clamp(48px, 12vw, 110px);
  padding-inline: 20px;
}
.b-404-code {
  margin: 0 0 6px;
  font-family: 'Russo One', sans-serif;
  font-size: clamp(3rem, 14vw, 5.5rem);
  line-height: 1;
  color: var(--mv-gold);
  opacity: 0.32;
}
.b-404-head {
  margin: 0 0 12px;
  font-family: 'Russo One', sans-serif;
  font-weight: 400;
  font-size: clamp(1.5rem, 5.5vw, 2.2rem);
  line-height: 1.15;
  color: var(--mv-text-primary);
}
.b-404-say { margin: 0 0 32px; font-size: 1rem; line-height: 1.6; color: var(--mv-text-secondary); }

.b-404-ways { margin: 0; padding: 0; list-style: none; display: grid; gap: 10px; }
.b-404-ways a {
  display: flex;
  align-items: center;
  gap: 14px;
  min-height: 60px;
  padding: 13px 16px;
  border: 1px solid var(--b-hairline, rgba(250, 189, 89, 0.19));
  border-radius: 8px;
  text-decoration: none;
  color: inherit;
  transition: border-color 0.18s ease, background 0.18s ease;
}
.b-404-w { flex: 1 1 auto; font-weight: 600; color: var(--mv-text-primary); }
.b-404-s { flex: 0 1 auto; font-size: 0.82rem; color: var(--mv-text-muted); text-align: right; }
.b-404-ways svg { width: 22px; height: 8px; color: var(--mv-gold); flex: 0 0 auto; }
@media (hover: hover) {
  .b-404-ways a:hover { border-color: rgba(250, 189, 89, 0.5); background: var(--mv-gold-subtle); }
}
@media (max-width: 560px) {
  .b-404-ways a { flex-wrap: wrap; }
  .b-404-s { flex-basis: 100%; text-align: left; }
}

.b-404-tip { margin: 28px 0 0; font-size: 0.86rem; color: var(--mv-text-muted); }
.b-404-tip kbd {
  padding: 2px 7px;
  border: 1px solid var(--b-hairline-firm, rgba(250, 189, 89, 0.3));
  border-radius: 5px;
  font-family: inherit;
  font-size: 0.82em;
  color: var(--mv-gold);
}

/* ── Tap targets on a phone ─────────────────────────────────────
 *
 * Sixteen links in the footer measured under 24px tall at 390px wide.
 * At desktop reading distance a 20px text link is fine; under a thumb
 * it is a miss waiting to happen, and the footer is where the contact
 * and legal links live, so a miss there costs something.
 *
 * Padding rather than a taller font, so the type stays the size it was
 * designed at and only the hit area grows. The negative inline margin
 * keeps the text visually aligned with the column above it, so nothing
 * moves; only the target grows.
 *
 * Measured with getBoundingClientRect in a real browser rather than
 * assumed from the CSS, because line-height, not font-size, is what
 * decides the height of a text link. */
@media (max-width: 700px) {
  .b-footer a,
  .b-footer .b-linkbtn {
    display: inline-block;
    padding-block: 7px;
    padding-inline: 2px;
    margin-inline: -2px;
  }
  .b-footer li { line-height: 1.25; }
}

/* ── The real 320px overflow ────────────────────────────────────
 *
 * At 320px the page scrolled sideways by 37px. Measuring every element's
 * bounding box against the viewport named two culprits, both of them
 * chrome added in this change and neither of them page content:
 *
 *   .b-nav-tools    right edge at 357px in a 320px viewport
 *   .b-consent-acts 322px of buttons inside a 288px column
 *
 * The first guess was the hub's price flag, on the reasoning that it is
 * the widest unbreakable string on the page. That was wrong, the fix
 * changed the measurement by zero pixels, and the only reason it did not
 * ship is that the number was re-measured rather than assumed.
 *
 * iPhone SE and the smaller Androids are still 320px CSS pixels. This is
 * not a hypothetical width. */
@media (max-width: 400px) {
  /* Three 40px buttons plus gaps did not fit beside the brand mark.
     Shrinking the buttons was necessary and not sufficient: the header
     is a flex row and .b-mark defaults to min-width:auto, so the brand
     refuses to give up a pixel and pushes the tools past the edge no
     matter how small they get. min-width:0 is what actually lets the
     row fit, and the ellipsis keeps the brand readable while it does. */
  .b-nav { gap: 10px; padding-inline: 14px; }
  .b-mark { min-width: 0; overflow: hidden; }
  .b-mark > span, .b-mark .b-mark-sub {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .b-nav-tools { gap: 4px; }
  .b-iconbtn, .b-burger { width: 36px; height: 36px; }
  .b-iconbtn svg { width: 17px; height: 17px; }

  /* Side by side the two consent buttons exceeded the column. Stacked
     full width they also become easier to hit, which is the right
     shape for a decision on a phone. */
  .b-consent-acts { flex-direction: column; width: 100%; }
  .b-consent-acts .b-btn { width: 100%; justify-content: center; }
}

/* A last-resort guard, not a substitute for the rules above.
   The header is fixed-width chrome with no reason ever to paint outside
   the viewport, and this makes a future regression clip instead of
   scrolling the whole document sideways. */
.b-nav, .b-consent { max-width: 100vw; }
.b-nav-tools { flex-shrink: 0; }

/* ── The registry's own text colours ────────────────────────────
 *
 * registry.css hardcodes nine near-white text colours (#f4ead9, #e8dcc6,
 * #d8cfbf and friends) rather than asking a token. On a pale tier card
 * every one of them is invisible, which is why the Start Here section
 * still failed after the surfaces were fixed: the blocks were no longer
 * black, and the writing on them was still white.
 *
 * Mapped to the ink scale rather than flattened to one colour, so the
 * card keeps its hierarchy: the tier name is the darkest thing on it,
 * the feature leads sit back, the notes sit further back again. A light
 * mode that collapses three levels of emphasis into one is readable and
 * wrong.
 *
 * Also the gold buttons. blatt.css puts #0a0a06 on them, which is right
 * over #fabd59 and unreadable over the darkened #8a5f12 that light mode
 * uses. They invert to white. */
:root[data-theme="light"] {
  --b-reg-ink:   #1b1810;   /* headings, names, the loudest text */
  --b-reg-body:  #332d22;   /* leads and body */
  --b-reg-quiet: #554c3b;   /* notes, badges, per-unit labels. 4.5:1 on white */
}

:root[data-theme="light"] .b-tier-name,
:root[data-theme="light"] .b-tier-title,
:root[data-theme="light"] .b-claim-result h3,
:root[data-theme="light"] .b-claim-result dd {
  color: var(--b-reg-ink);
}

:root[data-theme="light"] .b-fgroup-name,
:root[data-theme="light"] .b-claim-label,
:root[data-theme="light"] .b-claim input,
:root[data-theme="light"] .b-tier-brand,
:root[data-theme="light"] .b-tier-id,
:root[data-theme="light"] .b-counter-left {
  color: var(--b-reg-body);
}

:root[data-theme="light"] .b-fgroup-lead,
:root[data-theme="light"] .b-fgroup-note,
:root[data-theme="light"] .b-fgroup-badge,
:root[data-theme="light"] .b-tier-per,
:root[data-theme="light"] .b-tier-digits,
:root[data-theme="light"] .b-micro {
  color: var(--b-reg-quiet);
}

/* The error is red on both themes, but the dark theme's pink washes out
   on white. Darkened to carry the same urgency at 4.5:1. */
:root[data-theme="light"] .b-claim-error { color: #a01029; }

/* Gold buttons. #0a0a06 is correct over #fabd59 and invisible over the
   #8a5f12 that light mode darkens the gold to. */
:root[data-theme="light"] .b-btn--gold,
:root[data-theme="light"] .b-btn--gold:visited,
:root[data-theme="light"] button.b-btn--gold {
  color: #ffffff;
}

/* The claim form's last two, and the memoriam line.
 *
 * .b-claim-n is translucent gold at 62% alpha, which reads as a quiet
 * step numeral on black and vanishes on white. .b-claim-hint and the
 * memoriam line are mid greys chosen against a dark ground.
 *
 * All three are DELIBERATELY quiet, so they are darkened to the least
 * that clears 4.5:1 rather than promoted to body colour. A hint that
 * shouts is a different bug from a hint that cannot be read. */
:root[data-theme="light"] .b-claim-n { color: rgba(109, 74, 12, 0.85); }
:root[data-theme="light"] .b-claim-hint { color: #5d5648; }
:root[data-theme="light"] .b-quote-memoriam { color: #5a544a; }

/* ── Contrast floor in DARK mode ────────────────────────────────
 *
 * --mv-text-dim is #666666, which is 3.66:1 on black and fails WCAG AA
 * for body text. It is used estate-wide, so it is not changed at the
 * token level here: other sites read the same momentum.css and a global
 * change is not this session's to make.
 *
 * What IS fixed is the places on THIS site where it carries real
 * sentences rather than decoration. The footer legal line is the clearest
 * case: a copyright notice nobody can read is not a copyright notice.
 *
 * Found by auditing both themes rather than only the new one. The light
 * mode was the reported bug; dark had been failing here all along. */
.b-footer small,
.b-footer .b-legal,
.b-footer span {
  color: var(--mv-text-muted);
}

/* ── The stacked dark surfaces ──────────────────────────────────
 *
 * Five selectors in blatt.css paint a translucent gold gradient OVER an
 * opaque near-black one:
 *
 *   background:
 *     linear-gradient(158deg, rgba(255,242,214,0.055) ..., rgba(8,8,8,0.6));
 *     linear-gradient(#0b0b0b, #070707);
 *
 * The second layer is the surface. The first is a tint on top of it.
 * Overriding the tint alone changes nothing, which is why these came
 * through the first two passes of the light theme untouched and why the
 * house page still showed three black cards on a cream ground.
 *
 * My own audit called them clean, twice, for two separate reasons. It
 * skipped <a> elements so as not to flag a gold button, and these cards
 * ARE links. And it took the lightest gradient stop across every layer,
 * so rgba(255,242,214,0.055), a five percent tint, scored the card as a
 * light surface. Both are fixed in test/theme-contrast.mjs; the lesson
 * is that an audit which exempts things by tag rather than by measured
 * size will exempt the thing you are looking for.
 *
 * Replaced whole, both layers, because a partial override leaves the
 * opaque black underneath. */
:root[data-theme="light"] .b-door {
  background:
    linear-gradient(158deg, rgba(138, 95, 18, 0.06) 0%, rgba(138, 95, 18, 0.02) 34%, rgba(0, 0, 0, 0.035) 100%),
    linear-gradient(#ffffff, #f7f4ee);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.9),
    0 18px 38px -30px rgba(80, 60, 20, 0.45);
}
:root[data-theme="light"] .b-door--registry {
  background:
    linear-gradient(158deg, rgba(138, 95, 18, 0.14) 0%, rgba(138, 95, 18, 0.05) 40%, rgba(0, 0, 0, 0.04) 100%),
    linear-gradient(#fffdf8, #f6f1e6);
  border-color: rgba(138, 95, 18, 0.4);
}
:root[data-theme="light"] .b-card--lit,
:root[data-theme="light"] .b-panel--lit {
  background:
    linear-gradient(158deg, rgba(138, 95, 18, 0.05) 0%, rgba(0, 0, 0, 0.03) 100%),
    linear-gradient(#ffffff, #f8f5ef);
}
:root[data-theme="light"] .b-quote {
  background:
    linear-gradient(158deg, rgba(138, 95, 18, 0.04) 0%, rgba(0, 0, 0, 0.028) 100%),
    linear-gradient(#ffffff, #f8f6f1);
}

/* The heading inside a door was --b-text-ivory, which the token block
   already darkens, but the hover glow and the inset lip were still built
   for black. */
:root[data-theme="light"] .b-door:hover,
:root[data-theme="light"] .b-door:focus-visible {
  border-color: rgba(138, 95, 18, 0.55);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 1),
    0 28px 52px -32px rgba(80, 60, 20, 0.5),
    0 0 40px -22px rgba(138, 95, 18, 0.35);
}
