/* =====================================================================
 * MTV Zine Meltdown — Day-Glo punk / collage theme for Bumbag.
 * Visual-only overhaul matching the approved zine mock (two-column hero,
 * tilted cards with colored head bands, striped save bar, marker accents).
 * Class hooks relied on by the page JS are intact (banner/err/ok .show,
 * nav .open, .hovering, .sortable, .icon-btn, ...).
 * CSP-safe: no inline styles, fonts are self-hosted (font-src 'self').
 * ===================================================================== */

@font-face {
  font-family: "Rubik Mono";
  src: url("/static/fonts-rubik-mono-one.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Typewriter";
  src: url("/static/fonts-special-elite.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Marker";
  src: url("/static/fonts-permanent-marker.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

:root {
  /* keep existing semantic var names so components stay coherent */
  --bg: #fffef0;
  --bg-alt: #fffbdc;
  --panel: #ffffff;
  --panel-2: #fff4b8;
  --border: #101010;
  --text: #101010;
  --muted: #444444;
  --accent: #fc1cfc;
  --accent-2: #18b7ff;
  --ok: #0a8f00;
  --warn: #b37500;
  --danger: #d60022;
  --radius: 0px;

  --zine-y: #fcfc1c;
  --zine-m: #fc1cfc;
  --zine-c: #1cfcfc;
  --zine-l: #7cfc1c;
  --zine-o: #ff8c1a;
  --blk: #000000;
  --ink-solid: #000000; /* always-black ink: day-glo tile text + hover fills */
  --dots: rgba(0, 0, 0, 0.6);   /* halftone dots; flips to light in dark mode */
  --shadow: 8px 8px 0 var(--border);

  --font-chunk: "Rubik Mono", "Arial Black", Impact, sans-serif;
  --font-typo: "Typewriter", "Courier New", monospace;
  --font-marker: "Marker", "Comic Sans MS", cursive;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Skip link: the first Tab stop on every page, so the nav does not have to be
   tabbed through to reach the content. Hidden with the same clip technique as
   .sr-only rather than an off-screen left offset, because an element parked at
   left:-9999px can extend the scrollable area in an engine without
   `overflow-x: hidden` on <body> -- which stoic does not set.
   Duplicated in both stylesheets on purpose: a skip link left unstyled in one
   theme sits permanently visible at the top of every page, which is worse than
   not having one. */
.skip-link {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.skip-link:focus {
  position: static;
  /* inline-block is load-bearing, not decoration: an <a> is display:inline by
     default, and on an inline box the vertical padding and border overflow the
     line box instead of growing it. The link measured 8px *above* the document
     top and lost its top edge off-screen. It also makes margin-top apply at
     all, which inline boxes ignore. */
  display: inline-block;
  width: auto;
  height: auto;
  overflow: visible;
  clip: auto;
  white-space: normal;
  margin: 10px 0 0 20px;
  padding: 10px 18px;
  background: var(--zine-m);
  color: var(--blk);
  border: 2px solid var(--blk);
  box-shadow: 4px 4px 0 var(--blk);
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  text-decoration: none;
}
.skip-link:focus-visible { outline: 3px solid var(--zine-c); outline-offset: 2px; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-typo);
  line-height: 1.55;
  font-size: 1rem;
  min-height: 100vh;
  overflow-x: hidden;
}

/* Day-Glo halftone overlay across the whole site. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  opacity: 0.4;
  background-image: radial-gradient(var(--dots) 1px, transparent 1.1px);
  background-size: 13px 13px;
}

a { color: var(--accent-2); text-decoration: none; font-weight: bold; }
a:hover { text-decoration: underline; }

.container { max-width: 1000px; margin: 0 auto; padding: 0 20px 64px; position: relative; z-index: 1; }
.container.wide { max-width: 1000px; }

/* Anchored jumps into a deep page section still need a little headroom. */
#conflicts { scroll-margin-top: 20px; }

/* ---------------------------------------------------------------------
 * Top bar — sticker brand + tag-cloud nav (not sticky, like the mock).
 * ------------------------------------------------------------------- */
header.top {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  max-width: 1000px;
  margin: 0 auto;
  padding: 24px 20px 6px;
  flex-wrap: wrap;
}

.brand a {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-chunk);
  font-weight: 400;
  font-size: 1.375rem;
  letter-spacing: 1px;
  color: var(--ink-solid);
  text-decoration: none;
  background: var(--zine-y);
  border: 3px solid var(--border);
  box-shadow: 7px 7px 0 var(--border);
  padding: 8px 16px 8px 10px;
  transform: rotate(-2deg);
  transition: transform 0.1s;
  text-transform: uppercase;
}
.brand a:hover { transform: rotate(0deg) translateY(-1px); text-decoration: none; }
.brand .dot { color: var(--zine-m); }
.brand-icon { width: 30px; height: 30px; fill: var(--ink-solid); flex: none; }
.brand-text { display: inline-flex; gap: 0; }

nav.links { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
nav.links a {
  font-family: var(--font-typo);
  font-size: 0.9375rem;
  font-weight: 700;
  letter-spacing: 0.5px;
  color: var(--ink-solid);
  background: var(--zine-c);
  border: 2px solid var(--border);
  padding: 6px 12px;
  transform: rotate(var(--r, 0deg));
  text-decoration: none;
  text-transform: uppercase;
}
nav.links a[href="/login"] { background: var(--zine-m); --r: -2deg; }
nav.links a[href="/register"] { background: var(--zine-l); --r: 1.5deg; }
nav.links a[href="/app"] { background: var(--zine-c); --r: -1deg; }
nav.links a[href="/download"] { background: var(--zine-y); --r: 0.8deg; }
nav.links a[href="/pair"] { background: var(--zine-m); --r: -1.5deg; }
nav.links a#logout-link { background: var(--zine-l); --r: 1deg; }
nav.links a:hover { background: var(--ink-solid); color: #fff; transform: rotate(0); text-decoration: none; }
/* The other nav links pick their colour with an attribute selector, which ties
   with the rule above and so loses on source order. This one is an ID, which
   outranks the hover rule outright, so the sign-out button would keep its
   colour on hover while every other link went black. Match the ID again to win. */
nav.links a#logout-link:hover { background: var(--ink-solid); color: #fff; transform: rotate(0); text-decoration: none; }
nav.links .whoami,
nav.links #whoami {
  font-family: var(--font-marker);
  font-size: 1.125rem;
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  color: var(--blk);
  transform: rotate(-2deg);
  align-self: center;
  margin-right: 4px;
  /* #whoami is now the settings link: it must not pick up the nav-pill
     background/border/padding that nav.links a paints every <a> with, but it
     still needs to read as clickable. */
  background: none;
  border: 0;
  padding: 0;
  text-decoration: none;
}
nav.links a#whoami:hover {
  /* The ID outranks the generic hover rule, so restate the neutral look and
     swap the underline in. */
  background: none;
  color: var(--blk);
  transform: rotate(0deg);
  text-decoration: underline;
}
nav.links .muted { color: var(--muted); }

/* ---------------------------------------------------------------------
 * Buttons — chunky Rubik Mono blocks, hard offset shadow.
 * ------------------------------------------------------------------- */
.btn {
  display: inline-block;
  font-family: var(--font-chunk);
  font-size: 0.8125rem;
  letter-spacing: 0.6px;
  color: var(--ink-solid);
  background: var(--zine-o);
  border: 2px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 4px 4px 0 var(--border);
  padding: 12px 16px;
  cursor: pointer;
  text-transform: uppercase;
  transition: transform 0.05s, box-shadow 0.05s, background 0.15s, color 0.15s;
}
.btn:hover { transform: translate(-1px, -1px); background: var(--ink-solid); color: #fff; box-shadow: none; text-decoration: none; }
.btn:active { transform: translate(1px, 1px); }
.btn.btn-primary { background: var(--zine-m); }
.btn.btn-primary:hover { background: var(--ink-solid); color: #fff; }
.btn.btn-soft { background: var(--zine-y); }
.btn.btn-soft:hover { background: var(--ink-solid); color: #fff; }
.btn.ghost { background: var(--panel); color: var(--blk); }
.btn.ghost:hover { background: var(--ink-solid); color: #fff; }
.btn.danger { background: var(--danger); color: #fff; box-shadow: 4px 4px 0 #8a0015; }
.btn.danger:hover { background: var(--ink-solid); color: #fff; box-shadow: none; }
.btn.small { font-size: 0.75rem; padding: 6px 10px; }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }

/* Insurance against the icon-button JS path that happens to keep .btn. */
.btn.small.icon-btn, .btn.icon-btn { padding: 0; }

.actions { display: flex; gap: 6px; justify-content: flex-end; align-items: center; }
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  background: var(--panel);
  color: var(--blk);
  border: 2px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 3px 3px 0 var(--border);
  cursor: pointer;
  transition: transform 0.05s, box-shadow 0.05s, border-color 0.15s, color 0.15s;
}
.icon-btn:hover { transform: translate(-1px, -1px); box-shadow: none; border-color: var(--zine-m); color: var(--zine-m); }
.icon-btn:active { transform: translate(1px, 1px); }
.icon-btn.danger { color: var(--danger); }
.icon-btn.danger:hover { border-color: var(--danger); color: var(--danger); background: #ffe3ec; box-shadow: none; }
.icon-btn svg {
  width: 15px;
  height: 15px;
  fill: none;
  stroke: currentColor;
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* The sheet close X and the folder editor's save/cancel icons deliberately
 * match the hamburger menu's close X — one glyph for every close/cancel in
 * the chrome, at the nav button's 23px size. */
#detail-close svg,
.dev-form .icon-btn svg {
  width: 23px;
  height: 23px;
}
/* The wand's tip sparkle is the one filled glyph in the chrome. At 23px with the
 * 3px stroke the rest of these icons use, a stroked 4-point star fills in and
 * reads as a dot, so this one shape opts out of the stroke and takes the fill. */
.icon-sparkle { fill: currentColor; stroke: none; }

/* ---------------------------------------------------------------------
 * Cards — white paper "sticker" cards with colored head bands + tilt.
 * ------------------------------------------------------------------- */
.card {
  position: relative;
  background: var(--panel);
  background-image: repeating-linear-gradient(0deg, rgba(0,0,0,0.014) 0 1px, transparent 1px 3px);
  border: 3px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 0;
  margin-top: 22px;
}
.card .head {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 12px 18px 10px;
  border-bottom: 3px solid var(--border);
  background: var(--zine-y);
}
.card .head h2 { font-family: var(--font-chunk); font-size: 0.9375rem; margin: 0; flex: 1; letter-spacing: 0.6px; text-transform: uppercase; color: var(--ink-solid); }
.card .head .marker { font-family: var(--font-marker); font-size: 1.125rem; color: var(--ink-solid); transform: rotate(-1.5deg); }
/* The "System:" filter label lives in the yellow card head. The base `label`
 * rule uses --blk, which flips to chalk in dark → white-on-yellow. The head is
 * day-glo, so it takes the same always-black ink as the h2 beside it. */
.card .head label { color: var(--ink-solid); }
.card .body { padding: 16px 18px 20px; }

/* Alternating head colors + lean, mirroring the mock's collage rhythm. */
.card.tilt-a { transform: rotate(-0.5deg); }
.card.tilt-b { transform: rotate(0.4deg); }
.card.tilt-c { transform: rotate(-0.4deg); }
.card.tilt-d { transform: rotate(0.3deg); }
.card.tilt-e { transform: rotate(-0.3deg); }
.card.tilt-a .head, .card.tilt-c .head, .card.tilt-e .head { background: var(--zine-c); }
.card.tilt-b .head, .card.tilt-d .head { background: var(--zine-y); }

/* Scoped to the settings page: give every card head its own day-glo color
 * instead of the global c/y alternation (a/c/e -> cyan, b/d -> yellow). */
.settings .card.tilt-a .head { background: var(--zine-c); }
.settings .card.tilt-b .head { background: var(--zine-y); }
.settings .card.tilt-c .head { background: var(--zine-m); }
.settings .card.tilt-d .head { background: var(--zine-l); }
/* The fifth settings card completes the day-glo cycle. Orange is reserved
 * for buttons, so the devices head reuses the magenta band instead. */
.settings .card.tilt-e .head { background: var(--zine-m); }

/* Settings: the paired-devices list. Uses the sheet's tokens so the rows
 * match the /app device cards. */
.devices { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.devices li {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  background: var(--panel-2);
  border: 2px solid var(--border);
  box-shadow: 3px 3px 0 var(--border);
  padding: 10px 12px;
}
.devices .dev-info { display: grid; gap: 2px; min-width: 0; }
.devices .dev-name { font-family: var(--font-chunk); font-size: 0.9375rem; color: var(--blk); }
.devices .dev-actions { margin-left: auto; }

/* Account field labels need to read as labels, not body text: the body face
 * (Special Elite) has no true bold, so <strong> barely registered. Set them
 * in the heading/button face (Rubik Mono One) instead. */
.settings .lbl {
  display: block;
  font-family: var(--font-chunk);
  font-size: 0.8125rem;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--blk);
}

/* Auth-ish cards (/login /register /pair /verify) rest flat, no head band. */
.card.narrow { max-width: 420px; margin: 40px auto; padding: 28px; }
.card.narrow h1.title { border-bottom: 4px solid var(--zine-m); padding-bottom: 6px; }

h1.title {
  font-family: var(--font-chunk);
  font-size: 1.875rem;
  font-weight: 400;
  letter-spacing: 1px;
  color: var(--blk);
  text-transform: uppercase;
  text-shadow: 3px 3px 0 var(--zine-c);
  margin: 0 0 4px;
}

.section-title { font-family: var(--font-chunk); font-size: 0.875rem; font-weight: 400; letter-spacing: 0.6px; text-transform: uppercase; margin: 0 0 12px; }
.section-title.flush { margin: 0; }
.sub-title { margin: 0 0 12px; }
.sub { color: var(--muted); margin: 8px 0 18px; font-size: 1rem; }

/* Small markers / labels */
.marker { font-family: var(--font-marker); color: var(--blk); }
.muted { color: var(--muted); }
.small { font-size: 0.9375rem; }
.tight { margin: 4px 0 0; }
.accent-strong { color: var(--zine-m); font-weight: 700; }
.text-center { text-align: center; }
.grow { flex: 1; }
.mt { margin-top: 20px; }

label { display: block; font-size: 0.8125rem; font-weight: bold; color: var(--blk); margin: 14px 0 6px; text-transform: uppercase; letter-spacing: 0.4px; }
.label-inline { margin: 0; align-self: center; }

input[type="text"],
input[type="email"],
input[type="password"],
input[type="number"],
select {
  width: 100%;
  /* .w-auto opts a select into content sizing; cap it so a long option list
   * (the /app system filter) can't push the row wider than its container. */
  max-width: 100%;
  background: var(--panel);
  border: 2px solid var(--border);
  color: var(--text);
  border-radius: var(--radius);
  box-shadow: none;
  padding: 9px 8px;
  font-size: 1rem;
  font-family: var(--font-typo);
}
input:focus, select:focus { outline: 2px solid var(--zine-m); outline-offset: 1px; border-color: var(--zine-m); }

/* The version-history cap is a number field but should read like the theme
 * select beside it, so strip the native spinner arrows. */
input[type="number"] { appearance: textfield; -moz-appearance: textfield; }
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* Mismatched password-change fields (settings): light both up together. */
input.pw-mismatch, input.pw-mismatch:focus {
  border-color: var(--danger);
  background: #ffe3ec;
  outline-color: var(--danger);
}

.err {
  display: none;
  background: #ffe3ec;
  border: 2px dashed var(--danger);
  color: #8a0015;
  border-radius: var(--radius);
  box-shadow: 3px 3px 0 rgba(0,0,0,0.12);
  padding: 10px 14px;
  margin-top: 16px;
  font-size: 0.9375rem;
  font-weight: bold;
}
.err.show { display: block; }

.ok {
  display: none;
  background: #e8ffd9;
  border: 2px dashed var(--ok);
  color: #086100;
  border-radius: var(--radius);
  box-shadow: 3px 3px 0 rgba(0,0,0,0.12);
  padding: 10px 14px;
  margin-top: 16px;
  font-size: 0.9375rem;
  font-weight: bold;
}
.ok.show { display: block; }

/* Alert banners become rotated pink marker stickers. */
.banner {
  display: none;
  width: fit-content;
  max-width: 100%;
  font-family: var(--font-marker);
  font-size: 1.25rem;
  line-height: 1.3;
  color: var(--ink-solid);
  background: var(--zine-m);
  border: 3px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 6px 6px 0 var(--border);
  padding: 10px 18px;
  margin: 16px 0 4px;
  transform: rotate(-0.6deg);
}
.banner.show { display: block; }

/* The needs-a-system alert gets the mock's dark star chips (.alert i). */
#needs-category-banner.show::before,
#needs-category-banner.show::after {
  content: "★";
  font-family: var(--font-marker);
  font-size: 1.25rem;
  line-height: 1;
  background: #222;
  color: #fff;
  padding: 1px 6px;
  margin-right: 10px;
  vertical-align: baseline;
}
#needs-category-banner.show::after { margin-right: 0; margin-left: 10px; }

.banner button.inline {
  background: none;
  border: none;
  padding: 0;
  margin-left: 8px;
  color: var(--ink-solid);
  font-family: var(--font-marker);
  font-size: 1.0625rem;
  text-decoration: underline;
  cursor: pointer;
  vertical-align: baseline;
}
.banner button.inline:disabled { opacity: 0.6; cursor: not-allowed; }

.code-input { text-transform: uppercase; letter-spacing: 0.2em; }

.row { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; }
.row.chips { align-items: flex-start; }

/* ---------------------------------------------------------------------
 * App page: heading + the striped "save bar".
 * ------------------------------------------------------------------- */
.heading { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; padding-top: 8px; }
.usage-wrap { flex: 1; min-width: 240px; display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
.progress {
  height: 18px;
  width: 100%;
  max-width: 280px;
  border: 3px solid var(--border);
  background: var(--panel);
  box-shadow: 4px 4px 0 var(--border);
  overflow: hidden;
}
.progress > div {
  height: 100%;
  width: 0;
  background:
    repeating-linear-gradient(45deg,
      var(--zine-m) 0 12px,
      var(--zine-y) 12px 24px,
      var(--zine-c) 24px 36px);
}
#usage-text {
  color: var(--blk);
  font-weight: bold;
  font-size: 0.9375rem;
}

/* ---------------------------------------------------------------------
 * Saves table.
 * ------------------------------------------------------------------- */
.scroll-x { overflow-x: auto; -webkit-overflow-scrolling: touch; }
table.saves { width: 100%; border-collapse: collapse; margin-top: 4px; font-size: 0.9375rem; }
table.saves th {
  text-align: left;
  font-family: var(--font-chunk);
  font-size: 0.6875rem;
  font-weight: 400;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  color: #fff;
  background: var(--ink-solid);
  border: 0;
  border-bottom: 2px solid var(--border);
  padding: 9px 10px;
  white-space: nowrap;
}
table.saves th.sortable { cursor: pointer; user-select: none; }
table.saves th.sortable:hover { background: var(--zine-m); color: var(--ink-solid); }
table.saves th.sorted-asc::after,
table.saves th.sorted-desc::after {
  content: attr(data-arrow);
  display: inline-block;
  margin-left: 4px;
  color: var(--zine-y);
}
table.saves td {
  padding: 10px;
  border-bottom: 2px solid var(--border);
  background: var(--panel);
  vertical-align: middle;
}
table.saves tr:nth-child(even) td { background: var(--panel-2); }
/* Cyan hover is a day-glo fill, so the cell ink (and the .muted/.hashcode/.linky
 * text that sets its own colour) must go black — in dark, --text/--muted are
 * chalk. The button needs its own entry: it sets its colour directly, so the
 * inherited change on the td would never reach it. */
table.saves tbody tr:hover td { background: var(--zine-c); color: var(--ink-solid); }
table.saves tbody tr:hover td .muted,
table.saves tbody tr:hover td .hashcode,
table.saves tbody tr:hover td .linky { color: var(--ink-solid); }

.badge {
  display: inline-block;
  font-weight: bold;
  background: var(--zine-y);
  border: 2px solid var(--border);
  color: var(--ink-solid);
  padding: 1px 6px;
  font-size: 0.8125rem;
}
.badge.warn { background: var(--panel); border: 2px dashed var(--zine-o); color: #b34a00; }

/* A <button> carrying .badge is the platform control on /app: a <span> cannot
   be clicked or tabbed to. .badge sets no font-family and there is no global
   button reset, so without this the badge renders in the UA button font and
   reads as a different thing from every other badge on the page. This is a
   correctness fix, not a look, so it belongs in both stylesheets. */
button.badge {
  font-family: inherit;
  line-height: inherit;
  cursor: pointer;
}
button.badge:hover { filter: brightness(0.95); }
button.badge:focus-visible { outline: 2px solid var(--blk); outline-offset: 2px; }

/* Wrapper that systemControl() swaps its contents into. `display: contents`
   dissolves it so the badge behaves as a direct child of the table cell and the
   editor as a direct child of the sheet card, instead of the wrapper boxing
   them and changing layout in the two places differently. */
.sysctl { display: contents; }

/* Legal pages: the "last updated" line used to be 13px muted Typewriter, which
 * was genuinely hard to read (thin distressed face, low weight, small). It gets
 * a chunky display-font stamp chip instead. */
.pdate-stamp {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px 10px;
  width: fit-content;
  max-width: 100%;
  margin: 0 0 20px;
  padding: 7px 14px;
  background: var(--zine-y);
  border: 3px solid var(--border);
  box-shadow: var(--shadow-sm, 4px 4px 0 var(--border));
  transform: rotate(var(--r, -0.8deg));
}
.pdate-stamp .lbl {
  font-family: var(--font-chunk);
  font-size: 0.75rem;
  font-weight: 400;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  color: var(--ink-solid);
  white-space: nowrap;
}
.pdate-stamp .val {
  font-family: var(--font-chunk);
  font-size: 1rem;
  color: var(--ink-solid);
}

.hashcode { font-family: "Courier New", Courier, monospace; font-size: 0.9375rem; color: var(--text); overflow-wrap: anywhere; }
/* API paths in the docs are long and unbreakable. Without this they push the
   page 7px wide at 390px, and body{overflow-x:hidden} clips them instead of
   scrolling — the path is simply gone on a phone. */
code { font-family: "Courier New", Courier, monospace; overflow-wrap: anywhere; }
.hashcode.copyable { cursor: pointer; }
.hashcode.copyable:hover { color: var(--accent); text-decoration: underline; }

.toast {
  position: fixed;
  bottom: 18px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--panel);
  border: 3px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 6px 6px 0 rgba(0,0,0,0.25);
  padding: 10px 18px;
  font-size: 0.9375rem;
  font-weight: bold;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s;
  z-index: 50;
}
.toast.show { opacity: 1; }
.toast.ok { border-color: var(--ok); color: #086100; background: #e8ffd9; }
.toast.err { border-color: var(--danger); color: #8a0015; background: #ffe3ec; }

.empty { text-align: center; color: var(--muted); padding: 40px 0; font-style: italic; }

/* ---------------------------------------------------------------------
 * Conflict / suggestion cards rendered by conflicts.js.
 * ------------------------------------------------------------------- */
#conflicts-list .card,
#fuzzy-list .card,
#archive-list .card {
  margin-top: 14px;
  padding: 14px 16px;
  box-shadow: 6px 6px 0 var(--border);
}
#conflicts-list .card:nth-of-type(odd),
#archive-list .card:nth-of-type(odd) { transform: rotate(-0.4deg); background: var(--zine-l); }
#conflicts-list .card:nth-of-type(even),
#archive-list .card:nth-of-type(even) { transform: rotate(0.4deg); }
#fuzzy-list .card:nth-of-type(odd) { transform: rotate(0.4deg); background: var(--zine-c); }
#fuzzy-list .card:nth-of-type(even) { transform: rotate(-0.4deg); }
/* The day-glo card fills (cyan/lemon) stay bright at night, so their ink has
 * to be the always-black token — --text flips to chalk in dark and would
 * vanish on the day-glo. Even cards keep the panel fill and their chalk.
 * chip-name needs its own rule: .hashcode sets color: var(--text) directly,
 * which would beat the inherited card ink. */
#conflicts-list .card:nth-of-type(odd),
#fuzzy-list .card:nth-of-type(odd) { color: var(--ink-solid); }
#conflicts-list .card:nth-of-type(odd) .chip-detail,
#fuzzy-list .card:nth-of-type(odd) .chip-detail,
#conflicts-list .card:nth-of-type(odd) .chip-name,
#fuzzy-list .card:nth-of-type(odd) .chip-name { color: var(--ink-solid); }
/* Archive rows sit on the same lemon fill on odd cards; the filename label is
 * .muted (light blue-grey at night), so it needs the always-black ink too. */
#archive-list .card:nth-of-type(odd) .small.muted { color: var(--ink-solid); }

#conflicts-list .card .section-title,
#fuzzy-list .card .section-title,
#archive-list .card .section-title {
  display: inline-block;
  background: var(--ink-solid);
  color: #fff;
  padding: 5px 9px;
  font-size: 0.75rem;
  margin: 0 0 10px;
  letter-spacing: 0.5px;
}
#conflicts-list .card .section-title .small,
#fuzzy-list .card .section-title .small { color: #cfe9ff; display: inline-block; margin-top: 4px; }

.chip-label {
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--ink-solid);
  font-weight: bold;
  font-family: var(--font-chunk);
  background: var(--zine-y);
  border: 2px solid var(--border);
  padding: 2px 6px;
}
.chip-detail { color: var(--text); }
.chip-name-row .chip-name { font-weight: bold; word-break: break-word; }
.archive-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* ---------------------------------------------------------------------
 * Upload zone — sticky-note.
 * ------------------------------------------------------------------- */
.upload-zone {
  display: block; /* it is a <label>; block keeps the div-era layout */
  border: 3px dashed var(--border);
  border-radius: var(--radius);
  background: var(--zine-y);
  background-image: repeating-linear-gradient(0deg, rgba(252,28,252,0.08) 0 2px, transparent 2px 12px);
  padding: 22px 16px;
  text-align: center;
  color: var(--ink-solid);
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}
/* The focusable file input is clipped, so its focus ring must be drawn on the
   zone it lives in or a keyboard user sees nothing when they Tab here. */
.upload-zone:focus-within { outline: 3px solid var(--zine-c); outline-offset: 2px; }
.upload-zone.hovering { border-color: var(--zine-m); background: #ffe3ec; }
.upload-zone strong { font-family: var(--font-chunk); font-weight: 400; text-transform: uppercase; }
.upload-zone .small { color: #333; }

dl.form { margin: 0; }

/* ---------------------------------------------------------------------
 * Index hero — the mock's two-column collage.
 * ------------------------------------------------------------------- */
.hero {
  position: relative;
  margin: 48px 0 18px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  align-items: center;
}
.hero .left { position: relative; transform: rotate(-1.2deg); }
.hero .right { position: relative; transform: rotate(1deg); margin-top: 0; }
.hero h1 {
  font-family: var(--font-chunk);
  font-size: clamp(2.125rem, 5.2vw, 3.375rem);
  line-height: 1.08;
  margin: 0;
  color: var(--blk);
  letter-spacing: -1px;
  text-transform: uppercase;
  hyphens: none;
  overflow-wrap: normal;
  text-shadow: 4px 4px 0 #fff, 6px 6px 0 var(--zine-m), -5px -5px 0 var(--zine-c);
}
.hero h1 .hi { color: var(--zine-m); }
.hero .blurb {
  position: relative;
  background: var(--panel);
  border: 3px solid var(--border);
  box-shadow: 10px 10px 0 var(--border);
  padding: 18px 20px;
  font-size: 1.25rem;
  line-height: 1.65;
}
.hero .blurb p { margin: 0; }
.hero .blurb em {
  font-style: normal;
  font-family: var(--font-marker);
  font-size: 1.5625rem;
  background: var(--zine-m);
  color: var(--ink-solid);
  padding: 0 4px;
  text-shadow: none;
}
.hero p + p { margin-top: 16px; }
.hero .tagline {
  font-family: var(--font-marker);
  font-size: clamp(1.375rem, 3vw, 1.875rem);
  transform: rotate(-2deg);
  margin: 28px 6px 0;
  color: var(--blk);
  text-shadow: 3px 3px 0 var(--zine-c), -3px -3px 0 var(--zine-m);
}
.hero .tagline b { background: var(--zine-y); color: var(--ink-solid); padding: 0 3px; text-shadow: 3px 3px 0 var(--zine-c), -3px -3px 0 var(--zine-m); }

.sticker {
  position: absolute;
  font-family: var(--font-marker);
  font-size: 1.3125rem;
  color: var(--ink-solid);
  background: var(--zine-l);
  border: 2px solid var(--border);
  padding: 6px 14px;
  transform: rotate(4deg);
  box-shadow: 4px 4px 0 var(--border);
  z-index: 3;
}
.sticker.pink { background: var(--zine-m); color: var(--ink-solid); top: -22px; right: -14px; transform: rotate(6deg); z-index: 5; }
.tape {
  position: absolute;
  width: 120px;
  height: 28px;
  background: rgba(252, 252, 28, 0.85);
  border-left: 2px dashed rgba(0,0,0,0.4);
  border-right: 2px dashed rgba(0,0,0,0.4);
  z-index: 4;
}
.tape.tl { top: -14px; left: -10px; transform: rotate(-8deg); }
.tape.br { bottom: -14px; right: -18px; transform: rotate(-6deg); }
.tape.tr { top: -14px; right: -12px; transform: rotate(5deg); }
.tape.bl { bottom: -14px; left: -12px; transform: rotate(6deg); }
.hero .arrows {
  position: absolute;
  right: -18px;
  top: 14%;
  font-family: var(--font-chunk);
  font-size: 2.5rem;
  color: var(--zine-m);
  transform: rotate(12deg);
  z-index: 0;
  pointer-events: none;
}

/* Shout buttons (the mock's stacking CTA blocks). */
.shout { display: flex; gap: 16px; margin: 10px 0 22px; flex-wrap: wrap; justify-content: center; }
.shout a {
  font-family: var(--font-chunk);
  font-size: 0.9375rem;
  letter-spacing: 0.5px;
  color: var(--ink-solid);
  border: 3px solid var(--border);
  padding: 16px 24px;
  box-shadow: 8px 8px 0 var(--border);
  background: var(--zine-c);
  transform: rotate(-1deg);
  text-decoration: none;
  text-transform: uppercase;
}
.shout a:nth-child(2) { background: var(--zine-m); color: var(--ink-solid); transform: rotate(1.4deg); box-shadow: 8px 8px 0 var(--zine-l); }
.shout a:hover { transform: rotate(0) translateY(-2px); background: var(--ink-solid); color: #fff; box-shadow: none; text-decoration: none; }

/* Spray band. */
.spray { text-align: center; font-family: var(--font-marker); font-size: 1.5625rem; color: var(--blk); transform: rotate(-1deg); margin: 4px 0 12px; }
.spray span { background: var(--panel); border: 3px solid var(--border); box-shadow: 6px 6px 0 rgba(0,0,0,0.18); padding: 4px 14px; display: inline-block; }

/* ---------------------------------------------------------------------
 * Zigzag rules (conic teeth), plain black and pink variants. In the mock
 * the strip lives inside the 1000px wrap rather than spanning the viewport.
 * ------------------------------------------------------------------- */
.zz {
  height: 26px;
  max-width: 1000px;
  margin: 0 auto;
  position: relative;
  z-index: 2;
}
.zz::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    conic-gradient(from 135deg at 12.5px 12.5px, #000 0 90deg, #fff 90deg 180deg, #000 180deg 270deg, #fff 270deg 360deg) 0 0 / 25px 25px;
}
.zz.pink::after {
  background:
    conic-gradient(from 135deg at 12.5px 12.5px, var(--zine-m) 0 90deg, #fff 90deg 180deg, var(--zine-m) 180deg 270deg, #fff 270deg 360deg) 0 0 / 25px 25px;
}

footer.foot {
  position: relative;
  z-index: 1;
  display: flex;
  gap: 18px;
  justify-content: center;
  flex-wrap: wrap;
  max-width: 1000px;
  margin: 0 auto;
  padding: 26px 20px 40px;
}
footer.foot a {
  color: var(--ink-solid);
  margin: 0;
  font-family: var(--font-typo);
  font-size: 1rem;
  font-weight: bold;
  letter-spacing: 0;
  background: var(--zine-l);
  border: 2px solid var(--border);
  padding: 4px 10px;
  transform: rotate(0.5deg);
  text-decoration: none;
  text-transform: uppercase;
}
footer.foot a:hover { background: var(--ink-solid); color: #fff; }

/* Legal pages: the numbered section headings were plain uppercase 15px and read
 * as a wall of text. Each gets a highlighter swipe and a hand-set rule, with the
 * color and tilt alternating so the run of sections stays lively. */
.prose h2 {
  display: inline-block;
  font-family: var(--font-chunk);
  font-size: 1rem;
  font-weight: 400;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--ink-solid);
  margin: 30px 0 12px;
  padding: 2px 12px 6px;
  background-repeat: no-repeat;
  background-size: 100% 58%;
  background-position: 0 34%;
  border-bottom: 3px solid var(--border);
}
.prose h2:nth-of-type(4n + 1) {
  background-image: linear-gradient(var(--zine-y), var(--zine-y));
  transform: rotate(-0.6deg);
}
.prose h2:nth-of-type(4n + 2) {
  background-image: linear-gradient(var(--zine-c), var(--zine-c));
  transform: rotate(0.4deg);
}
.prose h2:nth-of-type(4n + 3) {
  background-image: linear-gradient(var(--zine-l), var(--zine-l));
  transform: rotate(-0.3deg);
}
.prose h2:nth-of-type(4n + 4) {
  background-image: linear-gradient(var(--zine-m), var(--zine-m));
  transform: rotate(0.7deg);
}
.prose ul { padding-left: 22px; }
.prose li { margin: 6px 0; }
.prose .notice {
  background: var(--zine-y);
  background-image: repeating-linear-gradient(0deg, rgba(0,0,0,0.02) 0 1px, transparent 1px 3px);
  border: 2px dashed var(--border);
  border-radius: var(--radius);
  box-shadow: 4px 4px 0 rgba(0,0,0,0.12);
  padding: 14px 16px;
  /* Prose, not metadata -- reads at body size. */
  font-size: 1rem;
  color: var(--ink-solid);
}

.grid-features { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin-top: 10px; }
.grid-features .f {
  background: #fff;
  border: 2px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 4px 4px 0 var(--border);
  padding: 18px;
}
.grid-features .f h3 { margin: 0 0 6px; font-size: 1rem; font-family: var(--font-chunk); font-weight: 400; text-transform: uppercase; }
.grid-features .f p { margin: 0; font-size: 0.875rem; color: var(--muted); }

/* Layout utilities (inline styles are disallowed by our CSP). */
.w-full { width: 100%; }
select.w-auto { width: auto; max-width: 100%; }
.min-180 { min-width: 180px; }
.min-220 { min-width: 220px; }
.min-260 { min-width: 260px; }
.self-end { align-self: flex-end; }
.container.wide .card { margin-left: 2px; margin-right: 2px; } /* keep tilted cards from hugging the edge */

[hidden] { display: none !important; }

/* ---------------------------------------------------------------------
 * Hamburger toggle (mobile).
 * ------------------------------------------------------------------- */
.menu-btn {
  display: none;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  flex: none;
  background: var(--zine-y);
  color: var(--ink-solid);
  border: 2px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 3px 3px 0 var(--border);
  cursor: pointer;
}
.menu-btn .mi { width: 23px; height: 23px; fill: none; stroke: currentColor; stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
.menu-btn .menu-close { display: none; }
.menu-btn.open .menu-open { display: none; }
.menu-btn.open .menu-close { display: block; }
.menu-btn[aria-expanded="true"] .mi-open { display: none; }
.menu-btn[aria-expanded="true"] .mi-close { display: block; }

/* ---------------------------------------------------------------------
 * Responsive.
 * ------------------------------------------------------------------- */
@media (max-width: 820px) {
  header.top { padding: 18px 16px 4px; }
  .hero { grid-template-columns: 1fr; gap: 26px; }
  .hero .right { margin-top: 0; }
  .hero .arrows { display: none; }
  .sticker { top: -14px; right: 4px; }
  .container { padding: 0 16px 56px; }
  .card.narrow { margin: 32px auto; }
}

@media (max-width: 760px) {
  header.top { gap: 12px; }
  header.top .brand { margin-right: auto; order: 1; }
  .menu-btn { display: inline-flex; order: 2; }
  nav.links {
    display: none;
    order: 3;
    flex: 1 1 100%;
    flex-direction: column;
    align-items: stretch;
    gap: 8px;
    padding: 12px;
    background: var(--bg);
    border: 3px solid var(--border);
    border-radius: var(--radius);
    box-shadow: 5px 5px 0 rgba(0,0,0,0.18);
  }
  nav.links.open { display: flex; }
  nav.links a { padding: 11px 12px; }
  nav.links a { transform: none; }
  nav.links a:hover { background: var(--ink-solid); color: #fff; }
  .heading { padding-top: 4px; }
}

@media (max-width: 640px) {
  table.saves th:nth-child(4), table.saves td:nth-child(4) { display: none; }
  input[type="text"], input[type="email"], input[type="password"], input[type="number"], select {
    font-size: 1.0625rem; /* >=16px => no iOS focus zoom */
  }
  .banner { font-size: 1.0625rem; }
  .usage-wrap { min-width: 200px; }
  .progress { max-width: 200px; }
  .card { box-shadow: 6px 6px 0 var(--border); }
  .card .head { padding: 12px 14px 10px; }
  .card .body { padding: 14px 14px 18px; }
}

@media (max-width: 480px) {
  table.saves th:nth-child(3), table.saves td:nth-child(3) { display: none; }
  .hero h1 { font-size: 2.125rem; text-shadow: 3px 3px 0 #fff, 4px 4px 0 var(--zine-m); }
  .hero .blurb { font-size: 1.0625rem; box-shadow: 7px 7px 0 var(--border); }
  .shout a { padding: 13px 18px; font-size: 0.8125rem; }
}

@media (max-width: 400px) {
  /* Hide Updated, not System. This rule used to drop column 2, which back when
     it held plain text was a fair density trade -- but System is now the only
     control in the table: a button that changes a save's platform. Hiding the
     column made the feature unreachable on a phone, and the responsive overflow
     probe passed *because* of it, so the control was absent and the check was
     green at the same time. Losing a timestamp is recoverable; losing the sole
     editor for a field is not. */
  table.saves th:nth-child(5), table.saves td:nth-child(5) { display: none; }
    table.saves td:nth-child(2) { max-width: min(200px, calc(100vw - 90px)); overflow: hidden; }
    /* At 390px the select + buttons in a table cell still forced the row past
     * the viewport even with a width cap. Drop the actions column as well: the
     * detail sheet already exposes download/delete, so the main action
     * (change platform) remains reachable and the table stops overflowing. */
    table.saves td:nth-child(6), table.saves th:nth-child(6) { display: none; }
  }

/* touch devices: bigger tap targets */
@media (pointer: coarse) {
  .icon-btn { width: 44px; height: 44px; }
  .btn { min-height: 46px; }
  .menu-btn { width: 44px; height: 44px; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { transition: none !important; }
}

/* ---------------------------------------------------------------------
 * Dark mode — the palette above is the light default. Dark mode turns the
 * paper surfaces near-black and the universal ink (--blk: borders, offset
 * shadows, paper text) into a chalk line, while the day-glo tiles stay
 * bright and their ink (--ink-solid) stays black so the collage keeps its
 * hard contrast. "auto" users carry no data-theme attribute and follow
 * prefers-color-scheme; explicit Light/Dark force the attribute (server-
 * injected on <html>, so there is no flash on reload). Zine-only: stoic is
 * dark by design and ignores data-theme entirely.
 *
 * NOTE: the two blocks below carry the SAME palette — the media block is
 * the auto path (dark OS, no explicit choice) and the second is the
 * explicit dark choice. Keep them in sync.
 * ------------------------------------------------------------------- */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]):not([data-theme="dark"]) {
        --bg: #15161b;
        --bg-alt: #1b1f26;
        --panel: #1f222b;
        --panel-2: #272b37;
        --border: #000;
        --text: #ece9da;
        --muted: #98a1b4;
        --accent-2: #56d7ff;
        --ok: #58db8f;
        --warn: #ffb454;
        --danger: #ff8a94;
        --blk: #f4f1e2;
        --dots: rgba(255, 255, 255, 0.24);
        color-scheme: dark;
  }
  :root:not([data-theme="light"]):not([data-theme="dark"]) .err { background: rgba(214, 0, 34, 0.14); color: #ffb3bd; }
  :root:not([data-theme="light"]):not([data-theme="dark"]) .ok { background: rgba(10, 143, 0, 0.16); color: #b8f0ac; }
  :root:not([data-theme="light"]):not([data-theme="dark"]) .toast.ok { background: #0d3520; color: #c6f8d0; }
  :root:not([data-theme="light"]):not([data-theme="dark"]) .toast.err { background: #4a1420; color: #ffc8d1; }
  :root:not([data-theme="light"]):not([data-theme="dark"]) .badge.warn { color: var(--warn); }
  :root:not([data-theme="light"]):not([data-theme="dark"]) input.pw-mismatch,
  :root:not([data-theme="light"]):not([data-theme="dark"]) input.pw-mismatch:focus { background: rgba(214, 0, 34, 0.18); }
  :root:not([data-theme="light"]):not([data-theme="dark"]) .upload-zone.hovering { background: rgba(214, 0, 34, 0.18); }
  :root:not([data-theme="light"]):not([data-theme="dark"]) .icon-btn.danger:hover { background: rgba(214, 0, 34, 0.18); }
  :root:not([data-theme="light"]):not([data-theme="dark"]) .btn.danger { background: #c8102e; box-shadow: 4px 4px 0 #8a0015; }
  :root:not([data-theme="light"]):not([data-theme="dark"]) .hero h1 { text-shadow: 5px 5px 0 #000, 9px 9px 0 var(--zine-m), -9px -9px 0 var(--zine-c); }
  :root:not([data-theme="light"]):not([data-theme="dark"]) .hero .tagline { text-shadow: 2px 2px 0 #000, 3px 3px 0 var(--zine-c), -3px -3px 0 var(--zine-m); }
  :root:not([data-theme="light"]):not([data-theme="dark"]) .hero .tagline b { text-shadow: 3px 3px 0 var(--zine-c), -3px -3px 0 var(--zine-m); }
  :root:not([data-theme="light"]):not([data-theme="dark"]) .hero .blurb em { text-shadow: 3px 3px 0 var(--zine-c), -3px -3px 0 var(--zine-m); }
  :root:not([data-theme="light"]):not([data-theme="dark"]) h1.title { text-shadow: 3px 3px 0 #000, 6px 6px 0 var(--zine-c); }

  @media (max-width: 480px) {
    :root:not([data-theme="light"]):not([data-theme="dark"]) .hero h1 { text-shadow: 3px 3px 0 #000, 6px 6px 0 var(--zine-m); }
  }
  :root:not([data-theme="light"]):not([data-theme="dark"]) #conflicts-list .card:nth-of-type(odd),
  :root:not([data-theme="light"]):not([data-theme="dark"]) #fuzzy-list .card:nth-of-type(odd),
  :root:not([data-theme="light"]):not([data-theme="dark"]) #archive-list .card:nth-of-type(odd) { color: var(--ink-solid); }
  :root:not([data-theme="light"]):not([data-theme="dark"]) #conflicts-list .card:nth-of-type(odd) .chip-detail,
  :root:not([data-theme="light"]):not([data-theme="dark"]) #fuzzy-list .card:nth-of-type(odd) .chip-detail { color: var(--ink-solid); }
}
:root[data-theme="dark"] {
    --bg: #15161b;
    --bg-alt: #1b1f26;
    --panel: #1f222b;
    --panel-2: #272b37;
    --border: #000;
    --text: #ece9da;
    --muted: #98a1b4;
    --accent-2: #56d7ff;
    --ok: #58db8f;
    --warn: #ffb454;
    --danger: #ff8a94;
    --blk: #f4f1e2;
    --dots: rgba(255, 255, 255, 0.24);
    color-scheme: dark;
}
:root[data-theme="dark"] .err { background: rgba(214, 0, 34, 0.14); color: #ffb3bd; }
:root[data-theme="dark"] .ok { background: rgba(10, 143, 0, 0.16); color: #b8f0ac; }
:root[data-theme="dark"] .toast.ok { background: #0d3520; color: #c6f8d0; }
:root[data-theme="dark"] .toast.err { background: #4a1420; color: #ffc8d1; }
:root[data-theme="dark"] .badge.warn { color: var(--warn); }
:root[data-theme="dark"] input.pw-mismatch,
:root[data-theme="dark"] input.pw-mismatch:focus { background: rgba(214, 0, 34, 0.18); }
:root[data-theme="dark"] .upload-zone.hovering { background: rgba(214, 0, 34, 0.18); }
:root[data-theme="dark"] .icon-btn.danger:hover { background: rgba(214, 0, 34, 0.18); }
:root[data-theme="dark"] .btn.danger { background: #c8102e; box-shadow: 4px 4px 0 #8a0015; }
:root[data-theme="dark"] .hero h1 { text-shadow: 5px 5px 0 #000, 9px 9px 0 var(--zine-m), -9px -9px 0 var(--zine-c); }
:root[data-theme="dark"] .hero .tagline { text-shadow: 2px 2px 0 #000, 3px 3px 0 var(--zine-c), -3px -3px 0 var(--zine-m); }
:root[data-theme="dark"] .hero .tagline b { text-shadow: 3px 3px 0 var(--zine-c), -3px -3px 0 var(--zine-m); }
:root[data-theme="dark"] .hero .blurb em { text-shadow: 3px 3px 0 var(--zine-c), -3px -3px 0 var(--zine-m); }
:root[data-theme="dark"] h1.title { text-shadow: 3px 3px 0 #000, 6px 6px 0 var(--zine-c); }

@media (max-width: 480px) {
  :root[data-theme="dark"] .hero h1 { text-shadow: 3px 3px 0 #000, 6px 6px 0 var(--zine-m); }
}
  :root[data-theme="dark"] #conflicts-list .card:nth-of-type(odd),
  :root[data-theme="dark"] #fuzzy-list .card:nth-of-type(odd),
  :root[data-theme="dark"] #archive-list .card:nth-of-type(odd) { color: var(--ink-solid); }
  :root[data-theme="dark"] #conflicts-list .card:nth-of-type(odd) .chip-detail,
  :root[data-theme="dark"] #fuzzy-list .card:nth-of-type(odd) .chip-detail { color: var(--ink-solid); }

/* ── Save detail sheet ─────────────────────────────────────────────────────
 * Clicking a filename in /app opens this: where the file lives on every
 * paired device, whether that folder is trustworthy, and the per-device
 * folder/exclusion controls. An overlay rather than an inline expansion
 * because the list is paginated — a row that grows would reflow the table
 * under the reader and lose their place.
 */
.sheet-wrap { position: fixed; inset: 0; z-index: 60; }
.sheet-back { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.55); }
.sheet {
  position: relative;
  margin: 4vh auto;
  width: min(720px, calc(100% - 24px));
  max-height: 92vh;
  overflow-y: auto;
  background: var(--panel);
  border: 3px solid var(--border);
  box-shadow: var(--shadow);
}
.sheet-head {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 16px 18px 12px;
  background: var(--zine-y);
  border-bottom: 3px solid var(--border);
}
.sheet-head h2 { color: var(--ink-solid); }
/* The X sits on the button's --panel background, which goes dark at night:
 * an always-black glyph (--ink-solid) would vanish on it, so the glyph flips
 * with the paper token. Hover keeps an always-light glyph so it still reads
 * on the always-black hover fill. */
.sheet-head .icon-btn { color: var(--blk); }
.sheet-head .icon-btn:hover { background: var(--ink-solid); color: var(--zine-y); }
.sheet-body { padding: 16px 18px 20px; }
body.sheet-open { overflow: hidden; }

.dev-list { display: grid; gap: 12px; margin-top: 12px; }
.dev {
  background: var(--panel-2);
  border: 2px solid var(--border);
  padding: 12px 14px;
}
.dev-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.dev-name { font-family: var(--font-chunk); font-size: 0.9375rem; color: var(--blk); }
.dev-tags { display: flex; align-items: center; gap: 8px; margin-left: auto; }
/* flex-start, not center: the "moving to …" / "not seen yet" note makes the
 * state column wrap onto extra lines, and a centred pencil then drifts down to
 * the middle of the taller row, so the edit button visibly jumps away from the
 * box it edits.
 *
 * `flex: 1 1 0; min-width: 0` is the other half of that fix, and it is the load-
 * bearing half. .dev-where wraps, so a state column with a long path, a badge
 * and a note could claim a min-content width past the container and push
 * .dev-actions onto a line of its own — the pencil then sat *below* the path it
 * edits, which is worse than the drift. Letting the state column take the
 * spare space and wrap its own children (the path chip has
 * overflow-wrap: anywhere) keeps the button on the first line, and
 * `flex: 0 0 auto` on the actions says the button never gives up its width. */
.dev-where { display: flex; align-items: flex-start; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.dev-path {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.875rem;
  background: var(--panel);
  border: 1px solid var(--border);
  padding: 2px 6px;
  max-width: 100%;
  overflow-wrap: anywhere;
}
.dev-form { display: flex; gap: 8px; flex-wrap: wrap; }
.dev-form input { flex: 1 1 220px; min-width: 0; }
.dev-where .dev-form { flex: 1 1 auto; min-width: 0; }
.dev-state { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; flex: 1 1 0; min-width: 0; }
/* The read row holds one button (the pencil), so this needs no sizing override
 * any more — the text "Use auto-detect" button that lived here is now an icon
 * inside .dev-form, which already sizes its own icons. */
  .dev-actions { display: flex; gap: 6px; margin-left: auto; flex: 0 0 auto; align-items: center; }
    .sys-row { display: flex; align-items: center; gap: 8px; flex: 1 1 auto; justify-content: space-between; }
  /* The editor is the same element as the chip it replaces: input.dev-path
   * outranks the base input rule so editing reads as the same monospace box. */
input.dev-path {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 3px;
  padding: 2px 6px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.875rem;
}
/* A sentence, not a field label: the global `label` rule shouts these in
 * Rubik Mono One uppercase, which reads as a warning rather than as prose. */
.dev-exclude {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  font-size: 0.9375rem;
  font-weight: normal;
  text-transform: none;
  letter-spacing: normal;
}
.dev-exclude input { width: auto; }
.dev-all { background: var(--panel); }
.dev-all .dev-exclude { margin-top: 0; font-weight: 700; color: var(--blk); }

/* The filename is the row's affordance, so it needs a pointer and an underline
 * to read as clickable — but it is a filename, not a link, so it keeps the
 * body ink. --blk rather than --ink-solid: the table surface is a panel that
 * goes dark in dark mode, and --ink-solid is always black. */
.linky {
  font: inherit;
  color: var(--blk);
  background: none;
  border: 0;
  padding: 0;
  text-align: left;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}
