/* The page shell: nav, mobile drawer, back to top, footer.
 *
 * One file for all 32 app pages. It deploys to /assets/shell.css at the origin
 * and every page links it from <head>, the way a11y.css and share.css already
 * work. Change the fleet's chrome here; there is nowhere else to change it.
 *
 * Why this exists: on 2026-09-14 the fleet carried THIRTEEN different nav
 * shapes and TWENTY-FIVE different footer shapes, because every page owned a
 * private copy of both and single-app polish passes edited their copy alone.
 * GifMint had drifted furthest: its own logo mark class, its own download
 * button class, a sticky bottom download bar and a back-to-top button that
 * existed on no other page in the fleet.
 *
 * This file is linked from <head> AFTER each page's inline <style> (the
 * injector puts the tag just before </head>, where the a11y tags already sit),
 * so at equal specificity these rules win. The few page rules that are more
 * specific are matched here at the same weight on purpose: search this file for
 * "specificity" to find them.
 *
 * COLOR IS NOT DECIDED HERE. Each page owns its palette and its app mark, and
 * every color below is a token read from that page, with a fallback chain for
 * the pages that never defined one: measured 2026-09-14, --acc and --acc2 are
 * missing on grabbr, morphr and stapl, --acc-btn on chatproof, --sep on datr,
 * --acclt on six and --font on three. What this file decides is GEOMETRY: the
 * sizes, the spacing, the radii, the breakpoints and the behavior.
 *
 * All 32 pages are dark today, which is the fleet rule, but nothing here
 * assumes it: the hairline and the two button washes are mixed from the page's
 * own --t1, so a light page would get a dark hairline rather than an invisible
 * one. (An older docstring in inject_comparisons.py calls Blinkr the fleet's
 * light page. It is not, and has not been: --bg is #14120D.)
 */

/* ── Tokens this file needs, with a fallback for every page that lacks one ── */
:root {
  /* A hairline mixed from the page's own text color works on a light page and
     a dark one without this file knowing which it is. A literal cannot. */
  --shell-sep: var(--sep, color-mix(in srgb, var(--t1) 14%, transparent));
  --shell-acc: var(--acc-btn, var(--acc, #0A84FF));
  --shell-acc-hover: var(--acc2, var(--acc, #0A84FF));
  --shell-font: var(--font, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
  /* The display face, for the wordmark and the app name. Read through a
     fallback like every other token here: a bare var(--font2) is how the
     twoplus11 wordmark came to be set in a serif on the two pages that
     defined that token as one. /assets/type.css now fixes the value for the
     whole fleet; this keeps the chrome legible if it fails to load. */
  --shell-font2: var(--font2, var(--shell-font));
  --shell-ink-on-acc: #fff;
  --shell-r: 8px;
  --shell-r-lg: 14px;
}

/* A page must never scroll sideways. This belongs on html, NOT on body:
   overflow-x on body silently kills position:sticky on the nav, which is how
   GifMint and Zoomr lost their sticky nav in September. */
html { overflow-x: hidden; }

/* ── Nav ─────────────────────────────────────────────────────────────────── */
nav {
  position: sticky; top: 0; z-index: 100;
  display: flex; align-items: center; gap: 20px;
  padding: 0 32px; height: 56px;
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
  border-bottom: 1px solid var(--shell-sep);
}
@supports not (backdrop-filter: blur(1px)) { nav { background: var(--bg); } }

.nav-twop11 { display: flex; flex-direction: column; gap: 1px; text-decoration: none; white-space: nowrap; flex: none; }
.nav-twop11-name { font-family: var(--shell-font2); font-size: 13px; font-weight: 700; color: var(--t2); letter-spacing: -.2px; line-height: 1.1; }
/* The wordmark blue is fixed fleet-wide and is not an app accent. */
.nav-twop11-name em { font-style: normal; color: #0A84FF; }
.nav-twop11-more { font-size: 10px; font-weight: 600; color: var(--t4); transition: color .15s; }
.nav-twop11:hover .nav-twop11-more { color: var(--acclt, var(--shell-acc)); }

.nav-logo { display: flex; align-items: center; gap: 10px; text-decoration: none; margin-right: auto; min-width: 0; }
/* The app's own mark keeps its own class and its own fill: .nav-icon, .tile and
   .logo-ring are each styled by the page that uses them, with that page's
   gradient tokens. All the shell owns is the box it sits in. */
nav .nav-icon, nav .tile, nav .logo-ring { width: 30px; height: 30px; flex: none; }
/* And the mark has to fit the box. Scrubbr's is not an inline SVG that scales
   with its container but a 512px base64 <img>, which drew at its natural size,
   hung 61px off the right edge of a 375px phone and put the whole page into a
   sideways scroll. Sizing the container alone does not constrain a raster. */
nav .nav-icon > *, nav .tile > *, nav .logo-ring > *,
footer .nav-icon > *, footer .tile > *, footer .logo-ring > * {
  max-width: 100%; max-height: 100%;
}
.nav-name { font-family: var(--shell-font2); font-size: 16px; font-weight: 700; color: var(--t1); white-space: nowrap; }

.nav-right { display: flex; align-items: center; justify-content: flex-end; gap: 12px; }
.nav-links { display: flex; align-items: center; gap: 24px; }
.nav-links a { color: var(--t3); text-decoration: none; font-size: 14px; transition: color .15s; white-space: nowrap; }
.nav-links a:hover { color: var(--t1); }

.nav-review { display: inline-flex; align-items: center; gap: 5px; }

/* Matched at .nav-links a.x because that is the weight the pages already use
   for these two; a bare .nav-donate would lose to the page's own rule. */
.nav-links a.nav-donate, .nav-donate {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--shell-acc); color: var(--shell-ink-on-acc);
  border: none; border-radius: var(--shell-r); padding: 6px 14px;
  font-size: 13px; font-weight: 600; font-family: var(--shell-font);
  text-decoration: none; white-space: nowrap; transition: transform .12s;
}
.nav-donate:hover { transform: translateY(-1px); }

.nav-license {
  background: color-mix(in srgb, var(--t1) 7%, transparent);
  color: var(--t1);
  border: 1px solid color-mix(in srgb, var(--t1) 16%, transparent);
  border-radius: var(--shell-r); padding: 6px 14px;
  font-size: 13px; font-weight: 600; font-family: var(--shell-font);
  cursor: pointer; white-space: nowrap; transition: background .12s;
}
.nav-license:hover { background: color-mix(in srgb, var(--t1) 14%, transparent); }

.nav-links a.nav-buy, .nav-buy, .nav-buy-mobile {
  background: var(--shell-acc); color: var(--shell-ink-on-acc);
  border: none; border-radius: var(--shell-r); padding: 7px 16px;
  font-size: 13px; font-weight: 600; font-family: var(--shell-font);
  text-decoration: none; white-space: nowrap; cursor: pointer;
  transition: background .12s;
}
.nav-buy:hover, .nav-buy-mobile:hover { background: var(--shell-acc-hover); }
.nav-buy-mobile { display: none; padding: 7px 14px; }

.nav-burger {
  display: none; align-items: center; justify-content: center;
  width: 38px; height: 38px; flex: none;
  background: none; border: 0; padding: 0; cursor: pointer; color: var(--t1);
  border-radius: var(--shell-r);
}
.nav-burger:hover { background: color-mix(in srgb, var(--t1) 8%, transparent); }

/* ── Mobile drawer ───────────────────────────────────────────────────────── */
/* The drawer is a layer that covers everything below the nav, and the panel
 * inside it caps its height at 100% OF THAT LAYER. Not at a slice of the
 * viewport: `max-height: calc(100vh - 56px)` is the mistake
 * test_overlays_do_not_size_in_viewport_units.py exists to catch, because vh
 * measures the unzoomed viewport and is then scaled by the site's own "Larger
 * text" zoom, and because on iOS vh is the height with the toolbars hidden.
 * 100% of a fixed, inset layer is what is actually on screen in both cases.
 *
 * The layer itself takes no clicks, so the page behind it still scrolls and
 * the outside-click close in shell.js still sees its own clicks; only the
 * panel is interactive. Known and accepted: .nmob is a child of <body>, which
 * "Larger text" zooms while it exempts <nav>, so in that one mode the layer's
 * 56px top offset scales to ~64px and a thin sliver of page shows under the
 * nav. Moving the drawer inside <nav> would remove it at the cost of nesting a
 * second navigation landmark. */
.nmob {
  position: fixed; inset: 56px 0 0 0; z-index: 99;
  pointer-events: none;
  opacity: 0; visibility: hidden;
  /* Visibility flips instantly when opening and waits for the fade when
   * closing. It must NOT be interpolated: a transitioned `visibility` is still
   * computed `hidden` at progress 0, which is exactly when shell.js flushes
   * style to focus the first link, and .focus() on a hidden element does
   * nothing at all. That is how the menu opened with focus left behind on the
   * burger. */
  transition: opacity .16s ease, visibility 0s linear .16s;
}
.nmob.open {
  opacity: 1; visibility: visible;
  transition: opacity .16s ease, visibility 0s linear 0s;
}
.nmob-inner {
  pointer-events: auto;
  display: flex; flex-direction: column; gap: 2px; padding: 12px 20px 20px;
  background: var(--bg); border-bottom: 1px solid var(--shell-sep);
  max-height: 100%; overflow-y: auto; -webkit-overflow-scrolling: touch;
  transform: translateY(-12px); transition: transform .16s ease;
}
.nmob.open .nmob-inner { transform: none; }
.nmob-inner a, .nmob-inner button {
  display: block; width: 100%; text-align: left;
  padding: 13px 4px; font-size: 16px; color: var(--t2);
  text-decoration: none; background: none; border: 0;
  border-bottom: 1px solid var(--shell-sep);
  font-family: var(--shell-font); cursor: pointer;
}
.nmob-inner a:last-child, .nmob-inner button:last-child { border-bottom: 0; }
.nmob-inner .nmob-buy {
  margin-top: 14px; text-align: center; border-bottom: 0;
  background: var(--shell-acc); color: var(--shell-ink-on-acc);
  border-radius: var(--shell-r); font-weight: 600;
}

/* ── Back to top ─────────────────────────────────────────────────────────── */
.btt {
  position: fixed; right: 20px; bottom: 20px; z-index: 90;
  width: 42px; height: 42px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--card); color: var(--t1);
  border: 1px solid var(--shell-sep); cursor: pointer;
  font-size: 17px; line-height: 1;
  box-shadow: 0 4px 16px rgba(0, 0, 0, .28);
  opacity: 0; visibility: hidden; transform: translateY(8px);
  transition: opacity .18s, transform .18s, visibility .18s;
}
.btt.vis { opacity: 1; visibility: visible; transform: none; }
.btt:hover { border-color: var(--shell-acc); }

/* ── Footer ──────────────────────────────────────────────────────────────── */
footer {
  border-top: 1px solid var(--shell-sep);
  padding: 40px 32px; max-width: 1000px; margin: 0 auto;
  display: flex; justify-content: space-between; align-items: center;
  flex-wrap: wrap; gap: 16px;
}
.foot-left { display: flex; align-items: center; gap: 10px; max-width: 900px; }
footer .nav-icon, footer .tile, footer .logo-ring { width: 24px; height: 24px; flex: none; }
footer .logo-ring .logo-dot { width: 7px; height: 7px; }
.foot-copy { font-size: 13px; color: var(--t3); line-height: 1.6; }
.foot-copy a { color: inherit; }
.foot-version { font-size: 12px; color: var(--t4); white-space: nowrap; }
.foot-links { display: flex; gap: 20px; flex-wrap: wrap; }
.foot-links a { color: var(--t3); text-decoration: none; font-size: 13px; transition: color .15s; }
.foot-links a:hover { color: var(--t1); }

/* ── Focus, for every interactive thing the shell owns ───────────────────── */
nav a:focus-visible, nav button:focus-visible,
.nmob-inner a:focus-visible, .nmob-inner button:focus-visible,
.btt:focus-visible, .foot-links a:focus-visible {
  outline: 2px solid var(--shell-acc); outline-offset: 3px; border-radius: 4px;
}

/* ── Crowded ─────────────────────────────────────────────────────────────── */
/* The nav is one 56px row that must never scroll sideways, and the pages do
 * not carry the same number of links: Datr and Monthr put ten items in the row
 * where most pages put seven. At 1024px, a 13" laptop, six of the 32 pages
 * overflowed and four of them pushed the whole page into a sideways scroll.
 * Tightening the gaps buys back ~115px, which is more than the worst page
 * needed (Datr, 113px). Measured, not guessed: every page fits at 1024 with
 * this, and a page that adds enough links to break it again fails
 * test_pages_share_one_shell.py's nav budget. */
@media (max-width: 1200px) {
  nav { gap: 12px; padding: 0 20px; }
  .nav-links { gap: 14px; }
  .nav-links a { font-size: 13.5px; }
  .nav-license, .nav-donate, .nav-links a.nav-donate { padding: 6px 11px; }
  .nav-links a.nav-buy, .nav-buy { padding: 7px 13px; }
}

/* ── Narrow ──────────────────────────────────────────────────────────────── */
@media (max-width: 860px) {
  nav { padding: 0 16px; gap: 12px; }
  .nav-links { display: none; }
  .nav-buy-mobile { display: inline-block; }
  .nav-burger { display: flex; }
  .nav-twop11-more { display: none; }
  footer { flex-direction: column; text-align: center; padding: 32px 16px; }
  .foot-left { flex-direction: column; text-align: center; }
  .foot-links { justify-content: center; }
  .btt { right: 14px; bottom: 14px; }
}
@media (max-width: 420px) {
  .nav-name { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .nmob, .nmob-inner, .btt, .nav-donate, .nav-buy, .nav-buy-mobile, .nav-license { transition: none; }
  .nav-donate:hover { transform: none; }
}
