/* ---------------------------------------------------------------
 * Ishmael website — base stylesheet.
 *
 * Two-theme model mirroring the editor app: Catppuccin Latte (light)
 * and Mocha (dark). data-theme on <html> drives the swap; pre-paint
 * script in base.njk sets it before first frame.
 * ------------------------------------------------------------- */

/* ---- Fonts ---------------------------------------------------- */
@font-face {
  font-family: "Inter";
  src: url("/assets/fonts/InterVariable.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Inter";
  src: url("/assets/fonts/InterVariable-Italic.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: "Literata";
  src: url("/assets/fonts/Literata.woff2") format("woff2-variations");
  font-weight: 200 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Literata";
  src: url("/assets/fonts/Literata-Italic.woff2") format("woff2-variations");
  font-weight: 200 900;
  font-style: italic;
  font-display: swap;
}

/* ---- Theme tokens -------------------------------------------- */
:root {
  /* Catppuccin Latte — full named palette. Use the named tokens
     (--ctp-mauve etc.) when picking an accent for a one-off rule;
     reach for the role tokens (--accent, --text…) for anything
     that should track the brand globally. */
  --ctp-rosewater: #dc8a78;
  --ctp-flamingo:  #dd7878;
  --ctp-pink:      #ea76cb;
  --ctp-mauve:     #8839ef;
  --ctp-red:       #d20f39;
  --ctp-maroon:    #e64553;
  --ctp-peach:     #fe640b;
  --ctp-yellow:    #df8e1d;
  --ctp-green:     #40a02b;
  --ctp-teal:      #179299;
  --ctp-sky:       #04a5e5;
  --ctp-sapphire:  #209fb5;
  --ctp-blue:      #1e66f5;
  --ctp-lavender:  #7287fd;

  --bg: #eff1f5;
  --bg-elevated: #e6e9ef;
  --surface: #ccd0da;
  --rule: #bcc0cc;
  --text: #4c4f69;
  --text-dim: #6c6f85;
  --accent: var(--ctp-mauve);          /* matches the app's brand */
  --accent-hover: #7a2ee0;
  --link: var(--ctp-mauve);
  --link-hover: var(--ctp-blue);

  --font-ui: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-serif: "Literata", Georgia, "Times New Roman", serif;
  --max-measure: 38rem;

  color-scheme: light;
}

/* Mocha palette. Reached two ways:
   1. Explicit pick — data-theme="dark" on <html>, set by the toggle
      or restored by the pre-paint script from localStorage.
   2. JS-disabled OS-prefers-dark — :root:not([data-theme]) inside
      the prefers-color-scheme media query below.
   Token names mirror the Latte block so anything reaching for
   var(--ctp-mauve) etc. swaps automatically. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]):not([data-theme="dark"]) {
    --ctp-rosewater: #f5e0dc;
    --ctp-flamingo:  #f2cdcd;
    --ctp-pink:      #f5c2e7;
    --ctp-mauve:     #cba6f7;
    --ctp-red:       #f38ba8;
    --ctp-maroon:    #eba0ac;
    --ctp-peach:     #fab387;
    --ctp-yellow:    #f9e2af;
    --ctp-green:     #a6e3a1;
    --ctp-teal:      #94e2d5;
    --ctp-sky:       #89dceb;
    --ctp-sapphire:  #74c7ec;
    --ctp-blue:      #89b4fa;
    --ctp-lavender:  #b4befc;

    --bg: #1e1e2e;
    --bg-elevated: #181825;
    --surface: #313244;
    --rule: #45475a;
    --text: #cdd6f4;
    --text-dim: #bac2de;
    --accent-hover: #d4b8ff;

    color-scheme: dark;
  }
}
:root[data-theme="dark"] {
  --ctp-rosewater: #f5e0dc;
  --ctp-flamingo:  #f2cdcd;
  --ctp-pink:      #f5c2e7;
  --ctp-mauve:     #cba6f7;
  --ctp-red:       #f38ba8;
  --ctp-maroon:    #eba0ac;
  --ctp-peach:     #fab387;
  --ctp-yellow:    #f9e2af;
  --ctp-green:     #a6e3a1;
  --ctp-teal:      #94e2d5;
  --ctp-sky:       #89dceb;
  --ctp-sapphire:  #74c7ec;
  --ctp-blue:      #89b4fa;
  --ctp-lavender:  #b4befc;

  --bg: #1e1e2e;
  --bg-elevated: #181825;
  --surface: #313244;
  --rule: #45475a;
  --text: #cdd6f4;
  --text-dim: #bac2de;
  --accent-hover: #d4b8ff;

  color-scheme: dark;
}

/* ---- Base elements ------------------------------------------- */
*,
*::before,
*::after {
  box-sizing: border-box;
}

html, body {
  margin: 0;
  padding: 0;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-serif);
  font-size: 1.05rem;
  line-height: 1.65;
  font-optical-sizing: auto;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

a {
  color: var(--link);
  text-decoration: underline;
  text-underline-offset: 0.18em;
  text-decoration-thickness: 1px;
}
a:hover {
  color: var(--link-hover);
}

h1, h2, h3, h4 {
  font-family: var(--font-ui);
  color: var(--text);
  line-height: 1.25;
  letter-spacing: -0.01em;
}
h1 { font-size: 2.4rem; margin: 0 0 0.75rem; }
h2 { font-size: 1.6rem; margin: 2rem 0 0.75rem; }
h3 { font-size: 1.2rem; margin: 1.5rem 0 0.5rem; }
p  { margin: 0 0 1rem; }

code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.92em;
  background: var(--bg-elevated);
  border: 1px solid var(--rule);
  border-radius: 3px;
  padding: 0.05em 0.35em;
}

/* ---- Theme toggle ------------------------------------------- */
/* Floating top-right pill. The sun/moon glyphs swap via CSS keyed
   off the data-theme attribute, so the only thing the JS does is
   flip that attribute. */
.theme-toggle {
  position: fixed;
  top: 1.25rem;
  right: 1.25rem;
  z-index: 10;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border: 1px solid var(--rule);
  background: var(--bg-elevated);
  color: var(--text-dim);
  border-radius: 999px;
  cursor: pointer;
}
.theme-toggle:hover {
  color: var(--text);
  border-color: var(--accent);
}
.theme-toggle-icon {
  width: 1rem;
  height: 1rem;
  display: none;
}
:root[data-theme="dark"] .theme-toggle-icon-sun  { display: block; }
:root[data-theme="light"] .theme-toggle-icon-moon { display: block; }
/* Pre-JS fallback if data-theme hasn't been set: follow the OS. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme]) .theme-toggle-icon-moon { display: block; }
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .theme-toggle-icon-sun  { display: block; }
}

main {
  flex: 1 1 auto;
  width: 100%;
  padding: 0 1.5rem;
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* Prose blocks stay at the reading measure; full-bleed sections
   (e.g. .hero) opt into a wider container via their own rule. */
main > * {
  width: 100%;
  max-width: var(--max-measure);
}

/* ---- Hero / index ------------------------------------------- */
/* Two-column above-the-fold: book art on the left, copy + CTA on
   the right. Breaks out past the body reading measure to give the
   art room to breathe. Stacks vertically on narrow viewports. */
.hero {
  max-width: 64rem;
  min-height: 100vh;
  display: flex;
  align-items: center;
  gap: 3rem;
  padding: 2rem 0;
}
.hero-art {
  flex: 1 1 0;
  min-width: 0;
  width: 100%;
  display: flex;
  justify-content: center;
}
/* Two cover JPGs ship — one composited onto Latte, one onto Mocha
   — and theme.js picks the matching src at load (and re-picks on
   toggle). The element is always visible; the src is what swaps.
   Starts at opacity:0 so the slide-in animation (gated on
   .is-loaded by the rule further down) runs only after the chosen
   src is actually decoded — no slide-in of an empty box. */
.hero-cover {
  width: 100%;
  max-width: 28rem;
  height: auto;
  display: block;
  opacity: 0;
  transition: opacity 500ms ease;
}
.hero-cover.is-loaded {
  opacity: 1;
}

/* Slide the cover in from the left once its src has actually
   loaded — gating the animation on .is-loaded means slow
   connections see the slide-in WITH the cover, not before it.
   Reduced-motion users skip the animation entirely; they get the
   plain opacity transition from the rule above. */
@media (prefers-reduced-motion: no-preference) {
  .hero-cover.is-loaded {
    animation: hero-slide-in 1400ms cubic-bezier(0.2, 0, 0.05, 1) both;
  }
}
@keyframes hero-slide-in {
  from {
    opacity: 0;
    transform: translateX(-7rem);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}
.hero-copy {
  flex: 1 1 0;
  min-width: 0;
}
.hero-copy h1 {
  font-family: var(--font-serif);
  font-weight: 400;
  font-size: 5rem;
  margin: 0 0 0.5rem;
}

/* One-line subtitle under the Ishmael wordmark. Set as a tracked,
   uppercase Inter descriptor — the publisher-spine treatment —
   so the mauve accent is reserved for the Download CTA. */
.tagline {
  font-family: var(--font-ui);
  font-style: normal;
  font-weight: 500;
  font-size: 0.85rem;
  line-height: 1.4;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--text-dim);
  margin: 0 0 1.75rem;
}
/* Set the hero copy as a page from a novel: justified, hyphenated,
   with an oversized drop cap on the opening letter. The site is the
   product demoing itself. */
.hero-tagline {
  font-size: 1.05rem;
  line-height: 1.6;
  color: var(--text);
  margin: 0 0 1.75rem;
  text-align: justify;
  hyphens: auto;
  -webkit-hyphens: auto;
  text-wrap: pretty;
}
/* Focus-mode homage: hovering the tagline dims the paragraph and
   keeps "You are a writer." at full strength — the same active-line
   highlight the editor does. The focus sentence carries its own
   constant colour so only the surrounding paragraph transitions;
   otherwise, on mouse-out, the span would briefly inherit the
   paragraph's still-dim colour and flicker. */
.hero-tagline {
  transition: color 200ms ease;
}
.hero-tagline:hover {
  color: color-mix(in srgb, var(--text) 28%, var(--bg));
}
.hero-focus {
  color: var(--text);
}
.hero-tagline::first-letter {
  font-family: var(--font-serif);
  font-weight: 500;
  float: left;
  font-size: 2.8em;
  line-height: 0.9;
  padding: 0.08em 0.12em 0 0;
  /* No own colour/transition: the drop cap belongs to "Call me
     Ishmael.", so it inherits the paragraph's animating colour and
     dims in perfect step with the rest of the text on hover. */
}
.cta {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem 1.1rem;
  background: var(--accent);
  color: var(--bg);
  border-radius: 5px;
  font-family: var(--font-ui);
  font-weight: 600;
  text-decoration: none;
}
.cta:hover {
  background: var(--accent-hover);
  color: var(--bg);
}
.cta-icon {
  height: 1.1em;
  width: auto;             /* viewBox isn't square; let aspect-ratio drive width */
  display: block;
  flex: 0 0 auto;
  /* Optical nudge — the Apple glyph's leaf sits above its bounding
     box, so a hair of lift centres it with the cap height. */
  margin-top: -0.08em;
}
.cta-help {
  font-family: var(--font-ui);
  font-size: 0.82rem;
  color: var(--text-dim);
  margin: 0.55rem 0 0;
}
/* Pre-launch: the download isn't available yet, so the button is a
   muted, inert placeholder and the helper leads with "Coming soon". */
.cta--disabled {
  background: var(--surface);
  color: var(--text-dim);
  cursor: not-allowed;
  pointer-events: none;
}
.cta-soon {
  color: var(--text);
  font-weight: 700;
}

/* ---- Coming soon placeholder --------------------------------- */
/* Standalone pre-launch page (no footer/nav). Centres the Ishmael
   cover beside a short holding message. */
.coming-soon {
  min-height: 100vh;
  max-width: 56rem;
  margin: 0 auto;
  padding: 4rem 1.5rem;
  display: flex;
  flex-wrap: wrap;
  gap: 3rem;
  align-items: center;
  justify-content: center;
}
.coming-soon-art {
  width: 100%;
  max-width: 20rem;
}
.coming-soon-copy {
  flex: 1 1 16rem;
  min-width: 0;
}
.coming-soon-copy h1 {
  font-family: var(--font-serif);
  font-size: 4rem;
  line-height: 1;
  margin: 0;
  color: var(--text);
}
.coming-soon-lead {
  font-family: var(--font-serif);
  font-size: 1.25rem;
  color: var(--text-dim);
  margin: 1.5rem 0 0;
}
.coming-soon-badge {
  display: inline-block;
  margin: 1.5rem 0 0;
  padding: 0.4rem 0.9rem;
  border-radius: 5px;
  background: var(--surface);
  color: var(--text);
  font-family: var(--font-ui);
  font-weight: 600;
  letter-spacing: 0.02em;
}
.coming-soon-help {
  margin: 2rem 0 0;
  font-family: var(--font-ui);
  font-size: 0.9rem;
  color: var(--text-dim);
}
.coming-soon-help a { color: var(--accent); }
@media (max-width: 34rem) {
  .coming-soon { text-align: center; flex-direction: column; }
  .coming-soon-copy h1 { font-size: 3rem; }
}

/* Secondary CTA — quiet outline variant so it doesn't compete with
   the filled accent Download button. */
.cta--subtle {
  margin-top: 1rem;
  background: transparent;
  color: var(--text);
  border: 1px solid var(--surface);
  font-weight: 500;
}
.cta--subtle:hover {
  background: var(--bg-elevated);
  color: var(--text);
  border-color: var(--text-dim);
}
/* The .cta-icon top nudge is tuned for the Apple glyph; the book-open
   mark is already centred, so cancel it here. */
.cta--subtle .cta-icon {
  margin-top: 0;
}

@media (max-width: 42rem) {
  .hero {
    flex-direction: column;
    gap: 2rem;
    text-align: center;
  }
}

/* ---- Dinkus -------------------------------------------------- */
/* Ornate scene-break ornament between sections. The PNG ships as
   a white-on-alpha shape and is used as a CSS mask, so the
   visible colour comes from background-color: var(--text-dim) and
   tracks the theme automatically. */
.dinkus {
  width: 100%;
  max-width: 20rem;
  aspect-ratio: 1536 / 59;
  margin: 0 auto;
  background-color: var(--text-dim);
  -webkit-mask: url(/assets/img/dinkus.png) center / contain no-repeat;
          mask: url(/assets/img/dinkus.png) center / contain no-repeat;
}

/* ---- Feature sections --------------------------------------- */
/* Each feature is roughly a window-tall slab so the page reads
   like a deck — one idea per scroll. Headings carry the visual
   weight; body copy stays at the prose measure. */
.feature {
  min-height: 90vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 4rem 0;
}

/* Same vocabulary as the hero: a tracked uppercase Inter eyebrow,
   a serif Literata display heading, and Literata body copy with a
   touch more leading. The eyebrow is opt-in (markup only where it
   makes sense) so sections without one degrade cleanly. */
.feature-eyebrow {
  font-family: var(--font-ui);
  font-weight: 500;
  font-size: 0.85rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--text-dim);
  margin: 0 0 1rem;
}
.feature h2 {
  font-family: var(--font-serif);
  font-weight: 400;
  font-size: 3.2rem;
  line-height: 1.1;
  letter-spacing: -0.005em;
  color: var(--text);
  margin: 0 0 1.5rem;
}
.feature p {
  font-size: 1.08rem;
  line-height: 1.65;
  color: var(--text);
  margin: 0;
}
/* Justify the body prose (not the eyebrow, which is a single
   tracked uppercase line) so each section reads like a page from
   a typeset book — same recipe as the hero's hero-tagline. */
.feature p:not(.feature-eyebrow) {
  text-align: justify;
  hyphens: auto;
  -webkit-hyphens: auto;
  text-wrap: pretty;
}
/* Chapter-opening drop cap on the FIRST body paragraph only — the
   one that immediately follows the section heading. Subsequent
   paragraphs run as continuing prose: no drop cap, first-line
   indent, the way a printed novel sets paragraphs after the first
   under a chapter title. */
.feature h2 + p::first-letter {
  font-family: var(--font-serif);
  font-weight: 500;
  float: left;
  font-size: 2.8em;
  line-height: 0.9;
  padding: 0.08em 0.12em 0 0;
  /* No own colour: inherit the paragraph's colour so the drop cap
     dims in step with the focus-mode hover instead of staying bright
     and out-competing the highlighted sentence. */
}

/* Focus-mode hover for the feature sections (mirrors the hero
   tagline): hovering a section's text dims its body prose while the
   highlighted sentence holds at the normal body colour, so it stands
   out. The focus sentence keeps a constant colour so it never
   flickers on mouse-out. */
.feature-text p:not(.feature-eyebrow) {
  transition: color 200ms ease;
}
.feature-text:hover p:not(.feature-eyebrow) {
  color: color-mix(in srgb, var(--text) 28%, var(--bg));
}
.focus-line {
  color: var(--text);
}
.feature h2 + p ~ p {
  text-indent: 2em;
}

/* Showcase variant — pairs the copy with a large product
   screenshot side-by-side so the whole slab fits in one viewport.
   Breaks the slab out to the wider hero measure (64rem); on narrow
   viewports it stacks. */
.feature--showcase {
  max-width: 64rem;
  display: grid;
  grid-template-columns: 1fr 1.2fr;
  gap: 3rem;
  align-items: center;
}
.feature--showcase .feature-text {
  max-width: none;
}
.feature-shot {
  margin: 0;
}
/* Reverse variant — screenshot on the left, copy on the right.
   Same proportions as the default, columns mirrored. Useful when
   you want to alternate the visual rhythm down the page. */
.feature--showcase-reverse {
  grid-template-columns: 1.2fr 1fr;
}

/* Export formats image — a single 2x2 PNG that ships the four
   format thumbnails (EPUB, HTML, MD, Word). Theme-neutral by
   design, so no light/dark variants are needed. */
.format-figure {
  margin: 0;
  display: flex;
  justify-content: center;
}
.format-image {
  width: 100%;
  max-width: 22rem;
  height: auto;
  display: block;
}
@media (max-width: 48rem) {
  .feature--showcase,
  .feature--showcase-reverse {
    grid-template-columns: 1fr;
  }
}
/* The screenshot/video element is always visible; theme.js sets
   src to the cross-shaded variant (light page previews the dark
   editor and vice versa). Reader's data-src-on-* both point at
   the same light spread, so it stays light in both themes.
   Starts at opacity:0 and fades in once its src is decoded —
   slow connections see a soft reveal instead of a pop. The
   width/height attributes in the markup reserve aspect-ratio
   space so the layout doesn't shift when the bitmap arrives. */
.feature-shot-img {
  display: block;
  width: 100%;
  height: auto;
  opacity: 0;
  transition: opacity 300ms ease;
}
.feature-shot-img.is-loaded {
  opacity: 1;
}

/* Smart Editing follows the cross-shade pattern used by the other
   showcase sections — a light page previews the dark editor and
   vice versa. (No ID override here; the default cross-shade rules
   above already do this.) The GIFs ship as flat rectangles
   without the macOS window shadow margin the screenshots have
   baked in, so we add a rounded corner, a soft drop shadow, and
   breathing room around the figure to mirror that look. */
#smart-editing .feature-shot {
  padding: 1.75rem 2rem;
}
#smart-editing .feature-shot-img {
  border-radius: 7px;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.45);
}

/* ---- Page (typographic article surface) ----------------------- */
/* For simple Markdown-authored pages (about, privacy, terms…).
   A single column of serif prose at the body reading measure,
   with a display heading, an optional italic lede, a drop cap on
   the opening paragraph, and quiet hierarchy below. */
.page {
  max-width: var(--max-measure);
  margin: 0 auto;
  padding: 5rem 0 6rem;
}
.page-header {
  margin-bottom: 3rem;
}
.page-eyebrow {
  font-family: var(--font-ui);
  font-weight: 500;
  font-size: 0.78rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-dim);
  margin: 0 0 0.75rem;
}
.page-title {
  font-family: var(--font-serif);
  font-weight: 400;
  font-size: 3.4rem;
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: var(--text);
  margin: 0 0 1rem;
}
.page-lede {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 1.2rem;
  line-height: 1.55;
  color: var(--text-dim);
  margin: 0;
}
.page-body {
  font-family: var(--font-serif);
  font-size: 1.1rem;
  line-height: 1.75;
  color: var(--text-dim);
}
.page-body p {
  margin: 0;
  text-align: justify;
  hyphens: auto;
  -webkit-hyphens: auto;
  text-wrap: pretty;
}
/* Novel-style body copy: the first paragraph of a run sits flush, and
   each paragraph that directly follows another is indented with no
   blank line between — the way a printed book sets prose. A paragraph
   that opens a new block (after a heading, list, quote, or break) is
   not preceded by a <p>, so it stays flush. */
.page-body p + p {
  text-indent: 1.5em;
}
.page-body p:first-of-type::first-letter {
  font-family: var(--font-serif);
  font-weight: 500;
  float: left;
  font-size: 2.8em;
  line-height: 0.9;
  padding: 0.08em 0.12em 0 0;
  color: var(--text);
}
.page-body h2 {
  font-family: var(--font-serif);
  font-weight: 500;
  font-size: 1.7rem;
  line-height: 1.25;
  letter-spacing: -0.005em;
  color: var(--text);
  margin: 2.5rem 0 0.75rem;
}
.page-body h3 {
  font-family: var(--font-serif);
  font-weight: 500;
  font-size: 1.25rem;
  margin: 2rem 0 0.5rem;
}
.page-body a {
  color: var(--text);
  text-decoration-color: var(--rule);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
}
.page-body a:hover {
  color: var(--accent);
  text-decoration-color: var(--accent);
}
.page-body ul,
.page-body ol {
  padding-left: 1.5rem;
  margin: 1.25rem 0;
}
.page-body li { margin: 0 0 0.4rem; }
.page-body blockquote {
  margin: 1.75rem 0;
  padding: 0 0 0 1.25rem;
  border-left: 2px solid var(--rule);
  font-style: italic;
  color: var(--text-dim);
}
.page-body hr {
  border: 0;
  margin: 3rem 0;
  text-align: center;
  font-family: var(--font-serif);
  color: var(--text-dim);
  letter-spacing: 0.5em;
}
.page-body hr::after {
  content: "* * *";
}
.page-back {
  margin-top: 4rem;
  font-family: var(--font-ui);
  font-size: 0.85rem;
}
.page-back a {
  color: var(--text-dim);
  text-decoration: none;
}
.page-back a:hover { color: var(--accent); }

/* ---- Support form -------------------------------------------- */
/* Sits inside the serif .page-body, but the form itself is UI type.
   Fields adopt the elevated surface + hairline border used elsewhere;
   focus lifts the border to the accent with a soft ring. The submit
   button reuses the filled .cta pill. */
.support-form {
  margin-top: 2.5rem;
  font-family: var(--font-ui);
}
.support-form .form-row {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin-bottom: 1.1rem;
}
.support-form label {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text);
}
.support-form input,
.support-form textarea {
  font-family: var(--font-ui);
  font-size: 1rem;
  color: var(--text);
  background: var(--bg-elevated);
  border: 1px solid var(--surface);
  border-radius: 6px;
  padding: 0.6rem 0.7rem;
  width: 100%;
  box-sizing: border-box;
}
.support-form textarea {
  resize: vertical;
  line-height: 1.5;
}
.support-form input:focus,
.support-form textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 25%, transparent);
}
.support-form button[type="submit"] {
  border: 0;
  cursor: pointer;
  margin-top: 0.25rem;
}
.support-fallback {
  margin: 1.1rem 0 0;
  font-size: 0.9rem;
  color: var(--text-dim);
}

/* ---- Footer -------------------------------------------------- */
/* Restrained, four-column footer below the last feature slab.
   Brand + tagline on the left, three navigational columns on the
   right; stacks on narrow viewports. Visually separated from the
   page above by a hairline border and a slight elevation. */
.site-footer {
  width: 100%;
  margin-top: 4rem;
  padding: 5rem 1.5rem 2.5rem;
  border-top: 1px solid var(--rule);
  background: var(--bg-elevated);
}
.site-footer-inner {
  max-width: 64rem;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1.6fr 1fr 1fr 1fr;
  gap: 3rem;
  align-items: start;
}
.site-footer-brand {
  max-width: 20rem;
}
.site-footer-name {
  font-family: var(--font-serif);
  font-weight: 400;
  font-size: 1.6rem;
  color: var(--text);
  margin: 0 0 0.5rem;
}
.site-footer-blurb {
  font-family: var(--font-ui);
  font-size: 0.88rem;
  line-height: 1.55;
  color: var(--text-dim);
  margin: 0;
}
.site-footer-col {
  font-family: var(--font-ui);
}
.site-footer-heading {
  font-family: var(--font-ui);
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text);
  margin: 0 0 1rem;
}
.site-footer-col ul {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}
.site-footer-col a {
  font-size: 0.88rem;
  color: var(--text-dim);
  text-decoration: none;
}
.site-footer-col a:hover {
  color: var(--accent);
}
.site-footer-bottom {
  max-width: 64rem;
  margin: 3rem auto 0;
  padding-top: 1.5rem;
  border-top: 1px solid var(--rule);
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.75rem;
}
.site-footer-copy,
.site-footer-built {
  font-family: var(--font-ui);
  font-size: 0.78rem;
  color: var(--text-dim);
  margin: 0;
}
@media (max-width: 48rem) {
  .site-footer-inner {
    grid-template-columns: 1fr 1fr;
    gap: 2.5rem 2rem;
  }
  .site-footer-brand {
    grid-column: 1 / -1;
  }
}
@media (max-width: 30rem) {
  .site-footer {
    padding: 3.5rem 1.5rem 2rem;
  }
  .site-footer-inner {
    grid-template-columns: 1fr;
    gap: 2rem;
  }
  .site-footer-bottom {
    flex-direction: column;
    text-align: center;
    margin-top: 2.25rem;
  }
}

/* ---- Click-to-zoom -------------------------------------------- */
/* Above the showcase stacking breakpoint, the screenshots become
   clickable lightboxes — driven by zoom.js + the View Transitions
   API. Below the breakpoint, leave them inert (the image already
   fills the main column). */
@media (min-width: 48rem) {
  .feature-shot-img { cursor: zoom-in; }
}
/* Avoid accidental selection/drag when clicking to zoom — a slow
   click or a small mouse drift would otherwise pick up the image
   as a draggable resource. */
.feature-shot-img {
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
}
.feature-shot-img.is-zoomed {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  /* Auto width/height with both maxes lets the image fit its
     largest in-viewport size while preserving its intrinsic
     aspect ratio. Critical for the morph: the OLD rect (width:100%
     in grid) and the NEW rect (this) both have the same aspect,
     so the View Transition group interpolates as a uniform scale
     instead of stretching the snapshot mid-flight. */
  width: auto;
  height: auto;
  max-width: 92vw;
  max-height: 92vh;
  margin: 0;
  z-index: 1000;
  cursor: zoom-out;
}
body.has-zoom { overflow: hidden; }

/* Invisible click shield while a zoom is open. Sits between the
   page chrome (theme toggle at z:10, etc.) and the zoomed image
   (z:1000), so any click on the page below the image hits the
   shield instead of the underlying control — the document click
   handler in zoom.js then closes the zoom. No visible effect. */
body.has-zoom::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 999;
  cursor: zoom-out;
}

/* Keep the root content (everything but the morphing image) static
   during the transition — no cross-fade of the rest of the page. */
::view-transition-old(root),
::view-transition-new(root) {
  animation: none;
  mix-blend-mode: normal;
}
::view-transition-old(root) { opacity: 0; }
::view-transition-new(root) { opacity: 1; }

/* View Transitions tune-up. The active screenshot carries the
   view-transition-name "feature-shot-active" while zoomed, so the
   browser morphs its size and position between in-grid and
   centred. The default cross-fade between old and new snapshots
   produces a doubled ghost (the same image at two sizes blending),
   so we suppress old's fade-out and keep new fully visible — only
   the group's size/position morph remains, over 500ms. */
::view-transition-group(feature-shot-active) {
  animation-duration: 500ms;
  animation-timing-function: cubic-bezier(0.2, 0, 0.05, 1);
}
::view-transition-old(feature-shot-active) {
  animation: none;
  opacity: 0;
}
::view-transition-new(feature-shot-active) {
  animation: none;
  opacity: 1;
}
