/* ============================================================
   NivaLogic site layer
   Import order: nivalogic-fonts.css → nivalogic-tokens.css
                 → nivalogic-brand.css → site.css

   This file adds ONLY what a multi-page site needs on top of the brand
   pack: page scaffold, header/nav, hero, section rhythm, grids, forms,
   footer. Every colour, face, space and motion value comes from the
   tokens, so nothing is hard-coded that the brand pack already owns.

   The visual language follows CurrentState/BrandStyling/Marketing/
   NivaLogic-Marketing-Direction.html: brass hairlines, mono eyebrows
   with a brass tick, squared buttons, the cyan signal used sparingly.
   ============================================================ */

/* ---------- Reset / page ---------- */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  min-height: 100vh;
  font-size: 17px;
  overflow-x: hidden;
  /* Long unbroken tokens (pasted URLs, German compounds, feed titles) must
     never widen the page. Belt-and-braces with the per-component rules. */
  overflow-wrap: break-word;
  /* Default grey tap flash reads as a bug on a navy ground. */
  -webkit-tap-highlight-color: rgba(0, 196, 204, 0.18);
}

/* Mobile browsers grow and shrink the viewport as the toolbar hides, so 100vh
   is taller than the screen on iOS/Android. `svh` is the smallest stable box. */
@supports (height: 100svh) {
  body { min-height: 100svh; }
}

/* The brand pack activates inside .niva-root; we put it on <body> so the
   navy ground and the ambient signal wash cover the whole page. */
body.niva-root {
  /* The brand pack paints the ground with gradients only, leaving
     background-color transparent. If a gradient layer fails to paint — or is
     mis-sized, as fixed attachment is on iOS — the page falls back to white
     and the cream-on-navy text becomes unreadable. Ink underneath is the
     floor: worst case the page is flat navy, never blank. */
  background-color: var(--niva-ink);
  background-attachment: fixed;
  display: flex;
  flex-direction: column;
}

/* iOS Safari does not honour `background-attachment: fixed`; it sizes the
   background to the whole document instead of the viewport, which stretches
   the gradient over the full page height and drags the ambient glow out of
   place. Touch devices get scroll attachment, which they render correctly and
   which also avoids the repaint cost of a fixed layer during momentum scroll. */
@media (hover: none) and (pointer: coarse) {
  body.niva-root { background-attachment: scroll; }
}

main { flex: 1 0 auto; }

img, svg { max-width: 100%; }

/* Skip link: first focusable element on every page. */
.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: 0.8em 1.2em;
  background: var(--niva-cyan);
  color: #04222B;
  font-family: var(--niva-font-mono);
  font-size: var(--niva-text-sm);
  text-decoration: none;
}
.skip:focus { left: 0; }

/* ---------- Shell ----------
   The measure is a variable so the TV / very-large-display tiers at the foot
   of this file can widen it without restating every rule that uses it.
   The safe-area padding is what keeps content clear of the notch and the
   rounded corners in landscape now that the pages ship viewport-fit=cover;
   it computes to 0 on every device that has no cutout. */
.shell {
  width: min(var(--shell-max, 1180px), 92vw);
  margin-inline: auto;
  padding-left: env(safe-area-inset-left, 0px);
  padding-right: env(safe-area-inset-right, 0px);
}
.shell--narrow { width: min(var(--shell-narrow, 760px), 92vw); }

/* ---------- Header ---------- */
.top {
  position: sticky;
  top: 0;
  z-index: 40;
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  background: linear-gradient(180deg, rgba(7, 20, 38, 0.82), rgba(7, 20, 38, 0.42));
  border-bottom: 1px solid var(--niva-border);
  /* Clears the status bar / notch under viewport-fit=cover. Zero elsewhere. */
  padding-top: env(safe-area-inset-top, 0px);
}

/* The header is only 42–82% opaque because the blur behind it does the real
   work of separating it from the page. Smart-TV browsers and older Android
   WebViews have no backdrop-filter, so there the headline text would scroll
   visibly through the bar. Where the filter is unsupported, go opaque. */
@supports not ((backdrop-filter: blur(2px)) or (-webkit-backdrop-filter: blur(2px))) {
  .top { background: var(--niva-abyss); }
}
.top .shell {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 70px;
  gap: var(--niva-space-5);
}
.brand {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  text-decoration: none;
  flex-shrink: 0;
}
.brand img { height: 30px; width: auto; display: block; }

.nav {
  display: flex;
  align-items: center;
  gap: 1.8rem;
  font-family: var(--niva-font-mono);
  font-size: 0.76rem;
  letter-spacing: 0.06em;
}
.nav a {
  color: var(--niva-fg-soft);
  text-decoration: none;
  transition: color var(--niva-transition);
  padding: 0.4em 0;
}
.nav a:hover { color: var(--niva-cyan-soft); }

/* Current page marker: a brass hairline, echoing the eyebrow tick. */
.nav a[aria-current="page"] {
  color: var(--niva-fg);
  border-bottom: 1px solid var(--niva-brass);
}

.avail {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  color: var(--niva-fg-muted);
  text-transform: uppercase;
  letter-spacing: 0.16em;
  font-size: 0.68rem;
  white-space: nowrap;
}

/* Mobile menu toggle: hidden on desktop, shown under 820px. */
.nav-toggle {
  display: none;
  background: none;
  border: 1px solid var(--niva-border-strong);
  border-radius: var(--niva-radius-sm);
  color: var(--niva-fg);
  font-family: var(--niva-font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 0.6em 0.9em;
  cursor: pointer;
  transition: border-color var(--niva-transition), color var(--niva-transition);
}
.nav-toggle:hover { border-color: var(--niva-cyan); color: var(--niva-cyan-soft); }

@media (max-width: 820px) {
  .nav-toggle { display: inline-flex; align-items: center; gap: 0.6em; }
  .nav {
    position: absolute;
    /* Was a hard 70px. The header now grows by the safe-area inset on notched
       phones, so anchor to the header's own bottom edge instead. */
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
    padding: var(--niva-space-4) max(4vw, env(safe-area-inset-left, 0px))
             calc(var(--niva-space-5) + env(safe-area-inset-bottom, 0px));
    background: var(--niva-abyss);
    border-bottom: 1px solid var(--niva-border);
    font-size: 0.92rem;
    /* A short landscape phone (e.g. 320px tall) must still be able to reach
       the last link, so the panel scrolls rather than running off-screen. */
    max-height: calc(100dvh - 100%);
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  /* The nav ships with `hidden` so it never flashes open before JS runs.
     Without JS, .no-js survives on <html> and re-opens it permanently, so a
     keyboard or screen-reader visitor is never left without navigation. */
  .nav[hidden] { display: none; }
  .no-js .nav[hidden] { display: flex; }
  .nav a, .nav .avail {
    width: 100%;
    padding: 0.95em 0;
    border-bottom: 1px solid var(--niva-border);
  }
  .nav a[aria-current="page"] { border-bottom-color: var(--niva-brass); }
  .nav .avail { border-bottom: 0; padding-top: 1.2em; }
}

/* ---------- Section rhythm ---------- */
.section { padding: var(--niva-section) 0; }
.section--tight { padding: clamp(3rem, 6vw, 5rem) 0; }
.section--edge { border-top: 1px solid var(--niva-border); }

.sec-head {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
  max-width: 44ch;
  margin-bottom: clamp(2.6rem, 5vw, 4rem);
}
.sec-head h2 { margin: 0; }
.sec-head > p {
  color: var(--niva-fg-muted);
  max-width: 52ch;
  margin: 0;
}

/* ---------- Hero ---------- */
.hero { padding: clamp(4rem, 10vw, 8.5rem) 0 clamp(3.5rem, 7vw, 6rem); }
.hero .niva-eyebrow { margin-bottom: 1.9rem; }
.hero .niva-display { margin: 0 0 1.6rem; max-width: 14ch; }
.hero .niva-lede { max-width: 40ch; }

.hero-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 2rem 3.4rem;
  align-items: flex-end;
  margin-top: 2.9rem;
}
.hero-aside {
  max-width: 32ch;
  border-left: 1px solid var(--niva-border);
  padding-left: var(--niva-space-5);
}
.hero-aside p { color: var(--niva-fg-soft); font-size: 0.98rem; }

/* Page hero for interior pages: smaller, no brass frame. */
.page-head { padding: clamp(3.2rem, 7vw, 5.5rem) 0 clamp(2rem, 4vw, 3rem); }
.page-head h1 { font-size: var(--niva-text-3xl); margin: 1.6rem 0 1.2rem; max-width: 18ch; }
.page-head .niva-lede { max-width: 52ch; }

.cta-row { display: flex; flex-wrap: wrap; gap: 0.9rem; }

/* ---------- Grids ---------- */
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--niva-space-5); }
.grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--niva-space-5); }
@media (max-width: 860px) { .grid-3 { grid-template-columns: 1fr; } }
@media (max-width: 720px) { .grid-2 { grid-template-columns: 1fr; } }

/* Card body copy plus the "who it's for" footer line. */
.niva-card p { color: var(--niva-fg-soft); font-size: 0.96rem; line-height: 1.58; }
.card-for {
  margin-top: 1.3rem;
  font-family: var(--niva-font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.05em;
  color: var(--niva-cyan-soft);
  text-transform: uppercase;
}

/* ---------- Identity words ---------- */
.idwords { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(1.5rem, 4vw, 3rem); }
@media (max-width: 800px) { .idwords { grid-template-columns: 1fr; gap: 2.2rem; } }
.idword h3 {
  font-size: clamp(1.7rem, 3.4vw, 2.5rem);
  color: var(--niva-cream);
  margin: 0 0 0.5rem;
}
.idword .n {
  font-family: var(--niva-font-mono);
  font-size: 0.7rem;
  color: var(--niva-brass-soft);
  letter-spacing: 0.16em;
  display: block;
  margin-bottom: 1rem;
}
.idword p { color: var(--niva-fg-muted); font-size: 0.95rem; max-width: 34ch; }

/* ---------- Prose (interior long-form) ---------- */
.prose { max-width: 68ch; }
.prose h2 { font-size: var(--niva-text-2xl); margin: var(--niva-space-8) 0 var(--niva-space-4); }
.prose h2:first-child { margin-top: 0; }
.prose h3 { font-size: var(--niva-text-xl); margin: var(--niva-space-6) 0 var(--niva-space-3); }
.prose p, .prose li { color: var(--niva-fg-soft); }
.prose strong { color: var(--niva-fg); font-weight: 600; }
.prose ul { padding-left: 1.2rem; margin: 0 0 var(--niva-space-4); }
.prose li { margin-bottom: 0.7rem; }
.prose li::marker { color: var(--niva-brass); }

/* Pull-quote: Fraunces italic on a brass hairline. */
.pull {
  border-left: 2px solid var(--niva-brass);
  padding: 0.2rem 0 0.2rem var(--niva-space-5);
  margin: var(--niva-space-6) 0;
  font-family: var(--niva-font-display);
  font-style: italic;
  font-size: var(--niva-text-xl);
  color: var(--niva-cream);
  line-height: 1.4;
}

/* ---------- Step list (how it works) ---------- */
.steps { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; }
.steps li {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--niva-space-5);
  padding: var(--niva-space-5) 0;
  border-bottom: 1px solid var(--niva-border);
  align-items: start;
}
.steps li:first-child { border-top: 1px solid var(--niva-border); }
.steps .step-n {
  font-family: var(--niva-font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  color: var(--niva-brass-soft);
  padding-top: 0.35em;
}
.steps h3 { font-size: var(--niva-text-lg); margin: 0 0 0.5rem; color: var(--niva-fg); }
.steps p { color: var(--niva-fg-soft); font-size: 0.96rem; margin: 0; }

/* ---------- Translation Line stack (home) ----------
   The rows themselves are styled by the brand pack (.niva-tl-*). This is
   only the column that holds them. */
.tl { display: flex; flex-direction: column; }

/* ---------- Themes list (writing page) ---------- */
.themes { display: flex; flex-direction: column; }
.theme-item { padding: var(--niva-space-6) 0; border-bottom: 1px solid var(--niva-border); }
.theme-item:first-child { border-top: 1px solid var(--niva-border); }
/* Post titles arrive from the Medium feed, so their content is not ours to
   control: a pasted URL or a long hyphenated compound is a single unbreakable
   token that runs straight off a phone screen. `anywhere` lets the browser
   break mid-token as a last resort, and only as a last resort — ordinary
   titles still wrap on spaces exactly as before. */
.theme-item h3 {
  margin: 0.9rem 0 0.6rem;
  font-size: var(--niva-text-2xl);
  color: var(--niva-cream);
  overflow-wrap: anywhere;
}
.theme-item p { color: var(--niva-fg-soft); max-width: 62ch; margin: 0; overflow-wrap: anywhere; }

/* Post titles are links (Writing page, filled from the Medium feed). A cyan
   headline would shout, so the link inherits the cream and shifts on hover.
   Specificity has to beat `.niva-root a:not(.niva-btn)` from the brand pack. */
.niva-root .theme-item h3 a {
  color: inherit;
  text-decoration: none;
  transition: color var(--niva-transition);
}
.niva-root .theme-item h3 a:hover { color: var(--niva-cyan-soft); }

/* ---------- Small layout helpers ----------
   These exist because the site's CSP is `style-src 'self'` with no
   'unsafe-inline'. A `style="..."` attribute is inline CSS: the browser blocks
   it outright and the rule simply never applies, silently. Anything that would
   have been a style attribute has to live here instead. */

/* Contact: form column and the aside beside it, tops aligned. */
.grid-2--top { align-items: start; gap: clamp(2rem, 5vw, 4rem); }

/* The cyan interpuncts in "Independent · Rigorous · Translational". */
.sep-dot { color: var(--niva-cyan); }

/* Writing: the standing note shown while the Medium feed has nothing to list.
   Narrower than the shell so it reads as a note rather than a section. */
.feed-note { max-width: 46rem; }
.feed-note p + p { margin-top: 1rem; }

/* The "// published at blog.nivalogic.com" line the Function appends under the
   post list. It is emitted by functions/writing.js, so it is page markup and is
   bound by the same CSP — it cannot carry a style attribute either. */
.feed-source { margin-top: 2rem; }

/* ---------- Form ---------- */
.form { max-width: 34rem; margin-top: var(--niva-space-6); }
.field { margin-bottom: var(--niva-space-5); }
.field label {
  display: block;
  font-family: var(--niva-font-mono);
  font-size: var(--niva-text-xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--niva-fg-muted);
  margin-bottom: 0.6em;
}
.field .req { color: var(--niva-brass-soft); }
.field input,
.field textarea,
.field select {
  width: 100%;
  font-family: var(--niva-font-body);
  font-size: var(--niva-text-base);
  color: var(--niva-fg);
  background: rgba(16, 38, 74, 0.35);
  border: 1px solid var(--niva-border-strong);
  border-radius: var(--niva-radius-sm);
  padding: 0.85em 1em;
  transition: border-color var(--niva-transition), background var(--niva-transition);
}
.field textarea { min-height: 9rem; resize: vertical; line-height: 1.6; }
.field input::placeholder, .field textarea::placeholder { color: var(--niva-fg-muted); opacity: 0.75; }
.field input:focus, .field textarea:focus, .field select:focus {
  border-color: var(--niva-cyan);
  background: rgba(16, 38, 74, 0.55);
}
.field .hint { font-size: var(--niva-text-sm); color: var(--niva-fg-muted); margin: 0.5em 0 0; }

/* Honeypot: visually gone, still reachable to a bot filling every input. */
.hp {
  position: absolute !important;
  width: 1px; height: 1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap;
}

.form-note {
  font-family: var(--niva-font-mono);
  font-size: var(--niva-text-xs);
  color: var(--niva-fg-muted);
  letter-spacing: 0.04em;
  margin-top: var(--niva-space-4);
}

/* Status messages in a polite live region. */
.form-status {
  margin-top: var(--niva-space-5);
  padding: var(--niva-space-4) var(--niva-space-5);
  border-radius: var(--niva-radius-sm);
  border: 1px solid var(--niva-border-strong);
  font-size: var(--niva-text-sm);
}
.form-status[hidden] { display: none; }
.form-status--ok { border-color: rgba(0, 196, 204, 0.45); background: rgba(0, 196, 204, 0.08); color: var(--niva-fg); }
.form-status--err { border-color: rgba(255, 107, 107, 0.5); background: rgba(255, 107, 107, 0.08); color: var(--niva-fg); }

button.niva-btn:disabled { opacity: 0.55; cursor: not-allowed; transform: none; }

/* ---------- Direct contact fallback ---------- */
.direct {
  margin-top: var(--niva-space-8);
  padding-top: var(--niva-space-6);
  border-top: 1px solid var(--niva-border);
}
.direct dl { display: grid; grid-template-columns: auto 1fr; gap: 0.8rem var(--niva-space-5); margin: 0; }
.direct dt {
  font-family: var(--niva-font-mono);
  font-size: var(--niva-text-xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--niva-fg-muted);
  padding-top: 0.15em;
}
.direct dd { margin: 0; color: var(--niva-fg-soft); overflow-wrap: anywhere; }

/* Below ~420px the label column steals too much of the line from the email
   address, so stack the pairs instead of tabulating them. */
@media (max-width: 420px) {
  .direct dl { grid-template-columns: 1fr; gap: 0.3rem; }
  .direct dd { margin-bottom: 1rem; }
  .direct dd:last-child { margin-bottom: 0; }
}

/* ---------- Closing band ---------- */
.close {
  border-top: 1px solid var(--niva-border);
  text-align: center;
  padding-block: clamp(5rem, 10vw, 8rem);
}
.close .niva-eyebrow { justify-content: center; }
.close .niva-display { margin: 1.4rem auto 2.2rem; max-width: 16ch; }
.close .free {
  font-family: var(--niva-font-mono);
  font-size: 0.8rem;
  color: var(--niva-brass-soft);
  letter-spacing: 0.06em;
  margin: 0;
}
.close .cta-row { justify-content: center; margin-top: var(--niva-space-6); }

/* ---------- Footer ---------- */
.foot {
  border-top: 1px solid var(--niva-border);
  /* Bottom padding clears the iOS home indicator; zero on everything else. */
  padding: var(--niva-space-8) 0 calc(var(--niva-space-6) + env(safe-area-inset-bottom, 0px));
  background: rgba(7, 20, 38, 0.5);
}
.foot-grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--niva-space-6);
  justify-content: space-between;
  align-items: flex-start;
  margin-bottom: var(--niva-space-6);
}
.foot img { height: 26px; width: auto; opacity: 0.9; display: block; }
.foot-links {
  display: flex;
  flex-wrap: wrap;
  /* Column gap carries the visual spacing that the links' own padding used to
     have to fake, so the targets can grow without the row looking loose. */
  gap: 0.2rem 1.6rem;
  font-family: var(--niva-font-mono);
  font-size: 0.76rem;
  /* Pull the block back level with the logo now the links are taller. */
  margin-block: -0.55rem;
}
.foot-links a {
  color: var(--niva-fg-soft);
  text-decoration: none;
  /* These sit in a nav list, not in a sentence, so WCAG 2.5.8's inline
     exception does not apply: they need a real 24px target. Padding gets
     them to 24px+ without moving the text. */
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  padding-block: 0.55rem;
}
.foot-links a:hover { color: var(--niva-cyan-soft); }
.foot-base {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem var(--niva-space-5);
  justify-content: space-between;
  padding-top: var(--niva-space-5);
  border-top: 1px solid var(--niva-border);
}
.fmeta {
  font-family: var(--niva-font-mono);
  font-size: 0.72rem;
  color: var(--niva-fg-muted);
  letter-spacing: 0.04em;
  margin: 0;
}

/* ---------- Entrance animation ---------- */
.rise { opacity: 0; transform: translateY(18px); animation: niva-rise 0.9s var(--niva-ease) forwards; }
.d1 { animation-delay: 0.05s; }
.d2 { animation-delay: 0.16s; }
.d3 { animation-delay: 0.28s; }
.d4 { animation-delay: 0.4s; }
.d5 { animation-delay: 0.52s; }
@keyframes niva-rise { to { opacity: 1; transform: none; } }

/* No-JS: reveal elements must not stay invisible. */
.no-js .niva-reveal,
.no-js .niva-tl-room { opacity: 1 !important; transform: none !important; }
.no-js .niva-tl-node::before { opacity: 1; transform: scale(1); }
.no-js .niva-tl-node::after { opacity: 0.55; }

@media (prefers-reduced-motion: reduce) {
  .rise { opacity: 1 !important; transform: none !important; animation: none !important; }
}

/* ---------- 404 ---------- */
.oops { text-align: center; padding: clamp(5rem, 12vw, 9rem) 0; }
.oops .code {
  font-family: var(--niva-font-mono);
  font-size: 0.8rem;
  letter-spacing: 0.24em;
  color: var(--niva-brass-soft);
  text-transform: uppercase;
}
.oops h1 { margin: var(--niva-space-5) auto var(--niva-space-4); max-width: 16ch; }
.oops p { color: var(--niva-fg-soft); max-width: 44ch; margin-inline: auto; }
.oops .cta-row { justify-content: center; margin-top: var(--niva-space-6); }

/* ============================================================
   FORM FACTORS
   Everything above lays the site out by width. These blocks handle the
   things width alone cannot tell you: whether there is a pointer, how far
   away the screen is, and which way the device is being held.
   ============================================================ */

/* ---------- Touch: no hover, bigger targets ----------
   The brand pack lifts buttons and cards on :hover. On a touchscreen there is
   no hover to leave, so the lift fires on tap and then sticks until the next
   tap elsewhere — the card sits raised with its brass rule run out full width,
   looking selected when it is not. Gate the whole effect on a real pointer. */
@media (hover: none) {
  .niva-btn:hover,
  .niva-card:hover { transform: none; }
  .niva-card:hover::before { width: 34px; }
  .niva-btn:hover .niva-arw { transform: none; }
  .niva-card:hover { border-color: var(--niva-border); }
}

/* Coarse pointers are fingers, and a finger is about 9mm across whatever the
   screen width says. This is deliberately keyed to the pointer and not to a
   breakpoint, so an 11" tablet on the desktop nav still gets finger-sized
   targets. 44px is the Apple/Android guidance; WCAG 2.5.8 AA asks only 24. */
@media (pointer: coarse) {
  .nav-toggle { min-height: 44px; }
  .niva-btn { min-height: 44px; }
  .foot-links a { min-height: 44px; padding-block: 0.85rem; }
  .foot-links { margin-block: -0.85rem; gap: 0 1.6rem; }
}

/* Above the 820px breakpoint the nav is a horizontal row, so its height has to
   come from padding rather than from the stacked-link rule. This is the case
   that bites 11" tablets: wide enough for the desktop nav, still driven by a
   finger. Kept as a flat query — CSS nesting is not safe on TV browsers. */
@media (pointer: coarse) and (min-width: 821px) {
  .nav a { min-height: 44px; display: inline-flex; align-items: center; }
}

/* ---------- Landscape phones ----------
   A 5"–6" phone on its side is only ~320–430px tall. The vertical rhythm is
   tuned for a portrait page and eats the whole screen here, so a visitor sees
   padding and one line of type. Compress the rhythm; leave the type alone. */
@media (orientation: landscape) and (max-height: 500px) {
  .hero { padding: clamp(2rem, 6vh, 3.5rem) 0 clamp(1.75rem, 5vh, 3rem); }
  .page-head { padding: clamp(1.75rem, 5vh, 3rem) 0 clamp(1.25rem, 3vh, 2rem); }
  .section { padding: clamp(2.25rem, 7vh, 4rem) 0; }
  .section--tight { padding: clamp(1.75rem, 5vh, 3rem) 0; }
  .close { padding-block: clamp(2.5rem, 8vh, 4.5rem); }
  .hero-meta { margin-top: 1.5rem; gap: 1.25rem 3.4rem; }
  .foot { padding-top: var(--niva-space-6); }
}

/* ---------- Narrow phones ----------
   The Translation Line reveals by sliding each room in from its own side.
   Once the shell is within ~36px of the screen width that slide starts
   off-canvas, and `overflow-x: hidden` clips it rather than scrolling — so on
   the smallest phones the text appears already cut in half, then snaps whole.
   Below 430px the reveal drops the travel and fades in place instead. */
@media (max-width: 430px) {
  .niva-tl-q, .niva-tl-a { --niva-from: 0; }
}

/* Galaxy Fold closed (280px) and other sub-320px screens: the display face is
   sized for a 14-character measure that no longer fits, and the button row
   runs out of room for two buttons side by side. */
@media (max-width: 340px) {
  .niva-display { font-size: clamp(2.3rem, 11vw, 2.9rem); }
  .cta-row { gap: 0.7rem; }
  .cta-row .niva-btn { width: 100%; justify-content: center; }
  .hero-meta { gap: 1.5rem; }
}

/* ---------- TV and 10-foot displays ----------
   There is no dependable way to tell a television from a desktop monitor in
   CSS. A 1080p set reports exactly 1920x1080, the same as the monitor on a
   desk, and most 4K sets report 1920 CSS px as well. Input type does not
   settle it either: an LG Magic Remote reports `pointer: fine, hover: hover`
   like a mouse, while a Tizen or Android TV D-pad reports coarse or none.

   So the strategy is not detection. It is to make the default experience
   survive a TV — which is what the focus ring, the hover gating, the
   backdrop-filter fallback and the overscan padding below all do, on every
   device — and then to take the one clear win where a set does identify
   itself by reporting no pointer at all. When this block fires it is right;
   when it does not, nothing is lost. */
@media (hover: none) and (pointer: none) and (min-width: 1200px) {
  html { font-size: 22px; }
  .shell { --shell-max: 1700px; width: min(var(--shell-max), 86vw); }
  .shell--narrow { --shell-narrow: 1100px; }
  /* A D-pad has no cursor, so the focus ring is the only thing telling the
     viewer where they are. It has to read from three metres away. */
  .niva-root :focus-visible {
    outline: 4px solid var(--niva-focus);
    outline-offset: 5px;
    background-color: rgba(0, 196, 204, 0.10);
  }
  /* Overscan: older sets crop the outermost few percent of the panel. Keep
     the sticky header and the footer inside the title-safe area. */
  .top, .foot { padding-inline: 2vw; }
}

/* Very large displays — 4K monitors and the 4K sets that do report their true
   width. The 1180px measure is a ribbon down the middle of a 3840px panel;
   widen it and lift the base size, but keep the line length readable rather
   than simply filling the glass. This is also the tier that carries a 4K TV,
   which is why it scales type harder than a desktop alone would need. */
@media (min-width: 2200px) {
  html { font-size: 19px; }
  .shell { --shell-max: 1500px; }
  .shell--narrow { --shell-narrow: 900px; }
}
@media (min-width: 3000px) {
  html { font-size: 23px; }
  .shell { --shell-max: 1800px; }
  .shell--narrow { --shell-narrow: 1050px; }
}

/* Overscan. Older sets crop a few percent off every edge of the panel, and a
   set is the most likely reason to be looking at a viewport this wide. The
   body copy is centred and nowhere near the edge, but the sticky header and
   the footer run the full width, so their contents are what would be lost.
   Applied by width rather than by input type, because the width is the part
   that can actually be measured. */
@media (min-width: 1920px) {
  .top > .shell, .foot > .shell { padding-inline: 2vw; }
}

/* ---------- Focus ----------
   Strengthened over the brand pack's 2px. A visible focus ring is what makes
   the site navigable by keyboard, by switch, and by a television D-pad, and
   it is the single thing that most often decides whether a TV browser is
   usable at all. :focus-visible means a mouse click still never shows it, so
   this costs the pointer experience nothing. */
.niva-root :focus-visible {
  outline: 3px solid var(--niva-focus);
  outline-offset: 3px;
}

/* ---------- Focus fallback ----------
   Smart-TV and older WebView browsers that ship :focus but not :focus-visible
   would otherwise get no focus ring at all from the brand pack, leaving
   remote and keyboard users with no idea where they are on the page. */
@supports not selector(:focus-visible) {
  .niva-root a:focus,
  .niva-root button:focus,
  .niva-root input:focus,
  .niva-root textarea:focus,
  .niva-root select:focus {
    outline: 2px solid var(--niva-focus);
    outline-offset: 3px;
  }
}

/* ---------- Print ---------- */
@media print {
  .top, .foot, .close, .skip, .nav-toggle { display: none !important; }
  body.niva-root { background: #fff !important; color: #000; }
  .prose p, .prose li, .niva-lede { color: #222; }
}
