
/* ===== Muletto =====

   Light ground throughout. No dark sections, no glass, no pills, no gradient
   words, no centred hero. Corners stay between 4 and 12px: the giveaway of a
   page assembled rather than designed is that everything is a soft rounded
   card, so the radius scale deliberately stops short of that.

   Colour, type and spacing values come from the reference set rather than from
   taste - accent, inks and the 112px section rhythm are measured, not chosen. */
@font-face {
  font-family: "Host Grotesk";
  src: url("fonts/host-grotesk-var.woff2") format("woff2-variations");
  font-weight: 300 800;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Host Grotesk";
  src: url("fonts/host-grotesk-var-italic.woff2") format("woff2-variations");
  font-weight: 300 800;
  font-style: italic;
  font-display: swap;
}

:root {
  --bg: #f6f6f6;
  --surface: #ffffff;
  --surface-2: #fafafa;
  --ink: #0c0c0c;
  --ink-2: #000000;
  --body: #3c3c3c;
  --muted: #767676;
  --line: #e0e0e0;
  --line-2: #eeeeee;
  /* Greyscale, to match the front page. The site used to run on a purple
     accent and the home page on none at all, so the two read as two products.
     Everything the accent did - emphasis, selection, the primary button - is
     done by ink and weight instead, which is what the front page proved works.
     The variable names stay so every component that already asks for an accent
     keeps working; only what they resolve to has changed. */
  --accent: #0c0c0c;
  --accent-strong: #000000;
  --accent-soft: #f0f0f0;
  --accent-ink: #ffffff;
  --ok: #15925f;
  --ok-soft: #e7f6ee;

  /* 2px for hairline chips, 12px is the ceiling. Nothing rounder. */
  --radius-sm: 4px;
  --radius: 6px;
  --radius-lg: 10px;
  --radius-xl: 12px;

  /* Flat and close to the surface. The reference shadows are barely there;
     a deep soft drop shadow reads as a template. */
  --shadow-sm: 0 1px 2px rgba(19,19,22,.05);
  --shadow: 0 1px 2px rgba(19,19,22,.05), 0 4px 12px rgba(19,19,22,.05);
  --shadow-lg: 0 10px 19px 4px rgba(19,19,22,.08), 0 2px 5px rgba(19,19,22,.06);

  --font: "Host Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --mono: ui-monospace, "SFMono-Regular", "GeistMono", Menlo, Consolas, monospace;

  /* The dot grid the reference sections sit on. Subtle enough to read as
     paper texture rather than decoration. */
  --dots: radial-gradient(#e6e6e6 1px, transparent 1px);
  /* The one accented-panel fill. Flat, not a gradient. */
  --tint: #f6f6f6;

  --wrap: 1200px;
  /* Two rhythms, on purpose. Marketing sections get the reference's 112px;
     a policy or pricing page is dense prose and reads better tighter. */
  --section: 112px;
  --section-tight: 64px;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  color: var(--body);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
h1, h2, h3, h4 { color: var(--ink); line-height: 1.08; letter-spacing: -.02em; margin: 0; font-weight: 680; }
p { margin: 0; }
a { color: inherit; text-decoration: none; }
::selection { background: var(--accent-soft); color: var(--accent-strong); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }

/* scrollbar */
* { scrollbar-width: thin; scrollbar-color: #cdd0da transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-thumb { background: #c9ccd6; border-radius: 4px; border: 3px solid var(--bg); }
*::-webkit-scrollbar-thumb:hover { background: #b7bbc8; }

.wrap { max-width: var(--wrap); margin: 0 auto; padding-left: 32px; padding-right: 32px; }
.row { display: flex; align-items: center; gap: 16px; }
.between { justify-content: space-between; }
.muted { color: var(--muted); }

/* ===== wordmark ===== */
.wordmark {
  display: inline-block; vertical-align: middle;
  aspect-ratio: 430 / 117; height: 26px; width: auto;
  background-color: var(--ink);
  -webkit-mask: url(wordmark.png) left center / contain no-repeat;
  mask: url(wordmark.png) left center / contain no-repeat;
  text-indent: -9999px; overflow: hidden; white-space: nowrap;
}

/* ===== nav ===== */
.nav {
  position: sticky; top: 0; z-index: 40;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}
.nav .wrap { display: flex; align-items: center; justify-content: space-between; height: 64px; }
.nav-left { display: flex; align-items: center; gap: 32px; }
.nav .wordmark { height: 27px; }
.nav-links { display: flex; gap: 26px; }
.nav-links a { color: var(--muted); font-size: 15px; font-weight: 500; transition: color .12s; }
.nav-links a:hover, .nav-links a.active { color: var(--ink); }
.nav-right { display: flex; align-items: center; gap: 14px; }

/* ===== buttons ===== */
.btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 18px; border-radius: var(--radius);
  font-size: 15px; font-weight: 560; cursor: pointer;
  border: 1px solid transparent; transition: background .12s, border-color .12s, color .12s, transform .04s;
  white-space: nowrap;
}
.btn:active { transform: translateY(1px); }
.btn.primary { background: var(--accent); color: var(--accent-ink); box-shadow: var(--shadow-sm); }
.btn.primary:hover { background: var(--accent-strong); }
.btn.secondary { background: var(--surface); color: var(--ink); border-color: var(--line); box-shadow: var(--shadow-sm); }
.btn.secondary:hover { border-color: #d6d8e2; background: var(--surface-2); }
.btn.ghost { color: var(--muted); }
.btn.ghost:hover { color: var(--ink); }
.btn.lg { padding: 13px 22px; font-size: 16px; }
.btn.block { display: flex; width: 100%; justify-content: center; }
/* The arrow is drawn, not typed. "->" is two characters pretending to be a
   mark, and it sets on the text baseline at whatever weight the font decides.

   On hover the shaft grows and the head slides, so the whole thing extends
   rather than shunting sideways. Both halves are one path each with a shared
   stroke, so it stays a hairline at any size. */
.btn .arrow, .linklike .arrow { width: 20px; height: 12px; margin-left: 2px;
  display: inline-block; vertical-align: -1px; overflow: visible;
  fill: none; stroke: currentColor; stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round; }
.arrow .a-line { transition: transform .16s var(--ease, ease); transform-origin: left center; }
.arrow .a-head { transition: transform .16s var(--ease, ease); }
.btn:hover .arrow .a-line, .linklike:hover .arrow .a-line { transform: scaleX(1.18); }
.btn:hover .arrow .a-head, .linklike:hover .arrow .a-head { transform: translateX(3px); }
@media (prefers-reduced-motion: reduce) {
  .arrow .a-line, .arrow .a-head { transition: none; }
  .btn:hover .arrow .a-line, .btn:hover .arrow .a-head { transform: none; }
}

/* ===== sections ===== */
section { padding-top: 112px; padding-bottom: 112px; }
.eyebrow-none {}
.h-sub { color: var(--muted); font-size: 18px; max-width: 60ch; margin-top: 20px; }
.section-head { max-width: 660px; margin-bottom: 64px; }
.section-head h2 { font-size: clamp(28px, 4vw, 40px); }
.section-head p { color: var(--muted); font-size: 17px; margin-top: 16px; }

/* ===== hero ===== */
.hero { padding-top: 104px; padding-bottom: 96px; }
.hero-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 80px; align-items: center; }
@media (max-width: 980px) { .hero-grid { grid-template-columns: 1fr; gap: 48px; } }
.hero h1 { font-size: clamp(40px, 6vw, 64px); letter-spacing: -.035em; }
.hero .h-sub { font-size: 19px; }
.hero-cta { display: flex; gap: 12px; margin-top: 32px; flex-wrap: wrap; }
.hero-note { margin-top: 18px; color: var(--muted); font-size: 14px; }

/* ===== screenshots =====

   One component, used by the home page and by every guide step. It was two,
   and they collided on the class name: the guide caption ended up inside a
   frame with overflow:hidden and no padding, so it read as part of the
   screenshot rather than as a note about it.

   No fake browser chrome. A drawn window bar with an invented URL and three
   traffic-light dots is decoration that claims to be evidence, and it is
   instantly recognisable as neither. The screenshot is the evidence. */
.figshot { margin: 0; }
.figshot-frame {
  display: block; overflow: hidden; background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius-lg);
}
.figshot-frame img { display: block; width: 100%; height: auto; }

/* In the home page row every frame is the same shape and crops from the top
   left, where the interesting part of an interface screenshot is. */
.shot-row .figshot-frame { position: relative; display: block; }
.shot-row .figshot-frame img {
  aspect-ratio: 16 / 10; object-fit: cover; object-position: left top;
  transition: transform .5s var(--ease-out);
}
/* Hovering shows more of the screenshot rather than just enlarging the box:
   the image scales inside a fixed frame, so the row does not reflow. */
.shot-row .figshot:hover .figshot-frame img,
.shot-row .figshot:focus-within .figshot-frame img { transform: scale(1.06); }
.shot-row .figshot-frame img { cursor: zoom-in; }

@media (prefers-reduced-motion: reduce) {
  .shot-row .figshot-frame img { transition: none; }
  .shot-row .figshot:hover .figshot-frame img { transform: none; }
}

/* Outside the frame, and spaced like a caption rather than crammed under the
   bottom edge of the image. */
.figshot figcaption {
  margin-top: 14px; font-size: 13.5px; line-height: 1.6; color: var(--muted);
  max-width: 62ch;
}

/* Waiting for a real screenshot, and saying so. Deliberately flat: a
   placeholder that looks finished gets shipped as finished. */
.figshot-ph {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 8px; aspect-ratio: 16 / 10; padding: 28px; text-align: center;
  background-color: var(--surface-2);
  background-image: var(--dots); background-size: 22px 22px;
}
.figshot-ph b {
  font-family: var(--mono); font-size: 11px; font-weight: 500;
  letter-spacing: .08em; text-transform: uppercase; color: var(--muted);
}
.figshot-ph span { font-size: 13px; color: var(--muted); max-width: 34ch; line-height: 1.55; }

.shot-row { display: grid; gap: 32px 24px; grid-template-columns: repeat(2, 1fr); }
@media (max-width: 860px) { .shot-row { grid-template-columns: 1fr; } }

/* ===== logo strip ===== */
.logos { padding-top: 44px; padding-bottom: 44px; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.logos p { text-align: center; color: var(--muted); font-family: var(--mono); font-size: 11.5px;
  letter-spacing: .1em; text-transform: uppercase; margin-bottom: 22px; }
.logos-row { display: flex; flex-wrap: wrap; justify-content: center; gap: 34px; align-items: center; }
.logos-row svg { width: 26px; height: 26px; color: #9aa0ad; }

/* ===== steps (how it works) =====

   Three panels joined by a drawn curve rather than butted against hard 1px
   dividers, and no 01/02/03. Numbering a three-item sequence that already
   reads left to right adds a label without adding information, and the
   monospace ordinal is its own small cliche. The curve does the same job by
   suggesting the order instead of announcing it. */
.steps { display: flex; align-items: stretch; gap: 0; }
.step {
  flex: 1 1 0; min-width: 0; padding: 30px 28px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}
.step h3 { font-size: 18px; margin: 0 0 10px; }
.step p { color: var(--muted); font-size: 14.5px; line-height: 1.62; }

.step-link {
  flex: 0 0 56px; align-self: center; height: 40px;
  color: #c9cddd; overflow: visible;
}
.step-link path { fill: none; stroke: currentColor; stroke-width: 1.5;
  stroke-linecap: round; stroke-dasharray: 4 5; }

@media (max-width: 900px) {
  .steps { flex-direction: column; }
  .step-link { flex: 0 0 32px; height: 32px; width: 40px; transform: rotate(90deg); }
}

/* ===== feature split ===== */
.split { display: grid; grid-template-columns: 1fr 1fr; gap: 80px; align-items: center; }
.split.alt { direction: rtl; } .split.alt > * { direction: ltr; }
.feat-list { list-style: none; padding: 0; margin: 20px 0 0; }
.feat-list li { position: relative; padding-left: 30px; margin: 18px 0; color: var(--body); }
.feat-list li svg { position: absolute; left: 0; top: 3px; width: 18px; height: 18px; color: var(--accent); }
.panel { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-xl); box-shadow: var(--shadow-sm); padding: 28px; }

/* ===== cards / guides ===== */
.grid { display: grid; gap: 16px; }
.grid.cards { grid-template-columns: repeat(auto-fill, minmax(232px, 1fr)); }
.card {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: 20px; cursor: pointer; transition: border-color .12s, box-shadow .12s, transform .12s;
  box-shadow: var(--shadow-sm);
}
.card:hover { border-color: #d7d9e4; box-shadow: var(--shadow); transform: translateY(-2px); }
.card .glyph { width: 40px; height: 40px; border-radius: 10px; background: var(--surface-2); border: 1px solid var(--line); display: flex; align-items: center; justify-content: center; color: var(--ink); }
.card .glyph svg { width: 22px; height: 22px; }
.card h3 { font-size: 17px; margin: 14px 0 6px; }
.card .meta { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; color: var(--muted); font-size: 13px; }

.badge { font-family: inherit; font-size: 11.5px; font-weight: 560; letter-spacing: .01em; padding: 3px 7px; border-radius: 3px;
  border: 1px solid var(--line); color: var(--muted); background: var(--surface-2);
  font-weight: 500; letter-spacing: .04em; text-transform: uppercase; }
/* One neutral chip. The difficulty used to come in green, amber and red,
   which on a greyscale site was the only colour left on the page and read as
   a warning about the guide rather than a note about the task. Weight and a
   border carry it instead. */
.badge.easy, .badge.medium, .badge.hard, .badge.verified {
  color: var(--ink); background: var(--surface-2); border-color: var(--line); }

/* ===== dropzone / opener ===== */
.drop {
  border: 1.5px dashed #cdd0dc; border-radius: var(--radius-lg); background: var(--surface);
  padding: 52px 22px; text-align: center; color: var(--muted); cursor: pointer;
  transition: border-color .12s, background .12s;
}
.drop:hover { border-color: var(--accent); }
.drop.hot { border-color: var(--accent); background: var(--accent-soft); color: var(--accent-strong); }
.drop strong { color: var(--ink); }
.import-result { margin-top: 20px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: var(--shadow-sm); padding: 20px; }
.import-result .proof { color: var(--ok); font-size: 14px; margin: 0 0 14px; display: flex; align-items: center; gap: 8px; }
.import-result .proof svg { width: 16px; height: 16px; }
.tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; }
.tile { background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--radius); padding: 14px 16px; }
.tile.accent { border-color: var(--accent); background: var(--accent-soft); }
.tile-n { font-size: 23px; font-weight: 680; color: var(--ink); }
.tile.accent .tile-n { color: var(--accent-strong); }
.tile-l { font-size: 13px; margin-top: 2px; color: var(--body); }
.tile-s { font-size: 12px; color: var(--muted); margin-top: 4px; }
.filewrap { margin-top: 14px; }
.filewrap summary { cursor: pointer; color: var(--muted); font-size: 14px; padding: 6px 0; }
.filewrap summary:hover { color: var(--ink); }
.filelist { max-height: 320px; overflow: auto; font-family: var(--mono); font-size: 12.5px; margin-top: 8px; }
.filelist div { display: flex; justify-content: space-between; padding: 5px 0; border-bottom: 1px solid var(--line-2); gap: 12px; }
.filelist .sz { color: var(--muted); white-space: nowrap; }

/* ===== pricing ===== */
.plans { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; max-width: 820px; }
.plan { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 34px; box-shadow: var(--shadow-sm); }
.plan.featured { border-color: var(--accent); box-shadow: var(--shadow); position: relative; }
.plan .flag { position: absolute; top: -11px; left: 28px; background: var(--accent); color: #fff; font-size: 12px; font-weight: 600; padding: 3px 11px; border-radius: 999px; }
.plan h3 { font-size: 18px; }
.plan .price { font-size: 30px; font-weight: 700; color: var(--ink); margin: 12px 0 2px; letter-spacing: -.02em; }
.plan .price small { display: block; font-size: 14px; font-weight: 400; color: var(--muted); margin-top: 4px; }
.plan ul { list-style: none; padding: 0; margin: 20px 0; }
.plan li { position: relative; padding-left: 28px; margin: 11px 0; color: var(--body); font-size: 15px; }
.plan li svg { position: absolute; left: 0; top: 3px; width: 18px; height: 18px; color: var(--accent); }
.plan .fine { color: var(--muted); font-size: 13px; margin-top: 14px; }

/* ===== page header (guides/app) ===== */
.page-head { padding-top: 88px; padding-bottom: 0; }
.page-head h1 { font-size: clamp(32px, 5vw, 46px); }
.page-head p { color: var(--muted); font-size: 18px; margin-top: 14px; max-width: 60ch; }

/* ===== callout ===== */
.cta-band { background: var(--ink-2); border-radius: var(--radius-xl); padding: 56px 56px; color: #fff; display: flex; align-items: center; justify-content: space-between; gap: 24px; flex-wrap: wrap; }
.cta-band h2 { color: #fff; font-size: 30px; }
.cta-band p { color: #b7b9c4; margin-top: 8px; }

/* ===== footer ===== */
main { flex: 1 0 auto; }
/* The footer is the dark end of the page, and the page walks down to it.

   The home page already steps from paper into near black; every other page
   stopped dead at a hairline instead, which made the two look like different
   sites again. ::before carries the same step wedge - flat tones, no
   gradient, because a fade is the thing that was being avoided - and it sits
   above the footer's own black so the join has no edge to show. */
footer.site { position: relative; margin-top: 80px; padding: 74px 0 52px;
  border-top: 0; background: #111111; color: #909090; }
footer.site::before { content: ""; position: absolute; left: 0; right: 0; bottom: 100%;
  height: 156px;
  background:
    linear-gradient(to bottom, var(--bg) 0 16.666%, #d2d2d2 16.666% 33.333%,
      #a2a2a2 33.333% 50%, #6e6e6e 50% 66.666%, #3d3d3d 66.666% 83.333%,
      #111111 83.333% 100%); }
footer.site .wrap { display: flex; justify-content: space-between; align-items: center; gap: 20px; flex-wrap: wrap; }
footer.site .wordmark { height: 22px; background-color: #909090; }
footer.site .fine { color: #909090; font-size: 14px; }
.foot-links { display: flex; gap: 22px; }
.foot-links a { color: #909090; font-size: 14px; }
.foot-links a:hover { color: #ffffff; }

/* A page whose last section is already dark needs no run-up to one. */
footer.site.flush { margin-top: 0; }
footer.site.flush::before { display: none; }
@media (max-width: 700px) { footer.site::before { height: 108px; } }

/* ===== modal ===== */
.modal { position: fixed; inset: 0; background: rgba(16,18,27,.42); backdrop-filter: blur(2px); display: flex; align-items: center; justify-content: center; padding: 20px; z-index: 60; }
.modal[hidden] { display: none; }
.modal-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg); max-width: 620px; width: 100%; max-height: 86vh; overflow: auto; padding: 30px; position: relative; box-shadow: var(--shadow-lg); }
.modal-close { position: absolute; top: 16px; right: 18px; width: 30px; height: 30px; border-radius: var(--radius); border: 1px solid var(--line); background: var(--surface); color: var(--muted); font-size: 16px; cursor: pointer; line-height: 1; }
.modal-close:hover { color: var(--ink); background: var(--surface-2); }
.g-head { display: flex; align-items: center; gap: 14px; margin-bottom: 6px; }
.g-head .glyph { width: 44px; height: 44px; border-radius: 11px; background: var(--surface-2); border: 1px solid var(--line); display: flex; align-items: center; justify-content: center; }
.g-head .glyph svg { width: 24px; height: 24px; color: var(--ink); }
.step-row { padding: 14px 0; border-bottom: 1px solid var(--line-2); }
.step-row .n { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border-radius: 6px; background: var(--accent-soft); color: var(--accent-strong); font-size: 12px; font-weight: 700; margin-right: 10px; }
.step-row .detail { color: var(--muted); font-size: 14px; margin: 6px 0 0 32px; }
.note { color: var(--body); font-size: 14px; background: var(--surface-2); border: 1px solid var(--line); border-left: 3px solid var(--accent); padding: 10px 14px; margin: 10px 0; border-radius: 0 var(--radius) var(--radius) 0; }
.loading { color: var(--muted); }

@media (max-width: 860px) {
  .hero-grid, .split, .plans { grid-template-columns: 1fr; }
  .split.alt { direction: ltr; }
  .steps { grid-template-columns: 1fr; }
  .nav-links { display: none; }
  section { padding-top: 72px; padding-bottom: 72px; }
}

/* ---------- Detected-provider banner ---------- */
.detected { display: flex; align-items: center; gap: 10px; padding: 12px 16px; margin-bottom: 14px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--accent-soft); color: var(--ink); font-size: 15px; }
.detected strong { font-weight: 640; }
.detected .d-ic { width: 22px; height: 22px; display: inline-flex; color: var(--ink); }
.detected .d-ic svg { width: 22px; height: 22px; }
.detected.unknown { background: var(--surface-2); color: var(--muted); }

/* ===== What you get (home switcher) ===== */
.switcher { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 26px; }
.sw-btn { display: inline-flex; align-items: center; gap: 9px; padding: 9px 16px; border-radius: var(--radius);
  border: 1px solid var(--line); background: var(--surface); color: var(--muted);
  font-size: 14.5px; font-weight: 550; cursor: pointer; transition: all .12s; font-family: inherit; }
.sw-btn:hover { color: var(--ink); border-color: #d6d8e2; }
.sw-btn.on { background: var(--ink-2); color: #fff; border-color: var(--ink-2); }
.sw-ic { width: 17px; height: 17px; display: inline-flex; }
.sw-ic svg { width: 17px; height: 17px; }
.sw-panel { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-xl);
  box-shadow: var(--shadow-sm); padding: 32px; }
.sw-meta { display: flex; flex-wrap: wrap; gap: 40px; padding-bottom: 26px; margin-bottom: 26px; border-bottom: 1px solid var(--line-2); }
.sw-meta .k { display: block; font-size: 12.5px; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; margin-bottom: 5px; }
.sw-meta b { font-size: 17px; color: var(--ink); font-weight: 620; }
.sw-rows { display: flex; flex-direction: column; gap: 18px; margin-bottom: 28px; }
.sw-row { display: grid; grid-template-columns: 220px 1fr 200px; gap: 20px; align-items: center; }
.sw-label { font-size: 15px; color: var(--ink); font-weight: 520; }
.sw-track { height: 9px; background: var(--line-2); border-radius: 999px; overflow: hidden; }
.sw-track i { display: block; height: 100%; background: var(--accent); border-radius: 999px; }
.sw-note { font-size: 13.5px; color: var(--muted); }

/* ===== Library viewer ===== */
.tabs { display: flex; flex-wrap: wrap; gap: 4px; margin: 22px 0 18px; border-bottom: 1px solid var(--line); }
.tab { padding: 10px 16px; border: 0; background: none; color: var(--muted); font-size: 14.5px;
  font-weight: 550; cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -1px; font-family: inherit; }
.tab:hover { color: var(--ink); }
.tab.on { color: var(--accent); border-bottom-color: var(--accent); }
.tabpanel { min-height: 120px; }
.small { font-size: 13.5px; }

.thumbs { display: grid; grid-template-columns: repeat(auto-fill, minmax(112px, 1fr)); gap: 12px; margin-top: 14px; }
.thumb { margin: 0; }
.thumb .ph { aspect-ratio: 1; border-radius: var(--radius); background: var(--surface-2) center/cover no-repeat;
  border: 1px solid var(--line); display: flex; align-items: center; justify-content: center;
  color: var(--muted); font-size: 11px; font-family: var(--mono); }
.thumb .ph.has-img { border-color: var(--line-2); }
.thumb figcaption { font-size: 11.5px; color: var(--muted); margin-top: 6px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }

.chatwrap { display: grid; grid-template-columns: 260px 1fr; gap: 18px; margin-top: 14px; }
.convlist { max-height: 460px; overflow: auto; border: 1px solid var(--line); border-radius: var(--radius); padding: 6px; }
.conv { display: flex; justify-content: space-between; gap: 10px; width: 100%; text-align: left;
  padding: 9px 11px; border: 0; background: none; border-radius: var(--radius-sm); cursor: pointer;
  font-size: 14px; color: var(--body); font-family: inherit; }
.conv:hover { background: var(--surface-2); }
.conv.on { background: var(--accent-soft); color: var(--accent-strong); font-weight: 560; }
.conv .cn { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.conv .cc { color: var(--muted); font-size: 12.5px; font-family: var(--mono); flex: none; }
.msgs { max-height: 460px; overflow: auto; display: flex; flex-direction: column; gap: 10px; padding-right: 4px; }
.msg { max-width: 78%; }
.msg.out { align-self: flex-end; text-align: right; }
.msg .mh { font-size: 11.5px; color: var(--muted); margin-bottom: 3px; }
.msg .mb { display: inline-block; padding: 9px 13px; border-radius: 12px; background: var(--surface-2);
  border: 1px solid var(--line); font-size: 14.5px; text-align: left; white-space: pre-wrap; word-break: break-word; }
.msg.out .mb { background: var(--accent-soft); border-color: var(--line); }

.chart { display: flex; align-items: flex-end; gap: 6px; height: 150px; margin: 18px 0 24px;
  padding: 12px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface-2); }
.chart .bar { flex: 1; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; height: 100%; gap: 6px; }
.chart .bar i { display: block; width: 100%; max-width: 42px; background: var(--accent); border-radius: 4px 4px 0 0; }
.chart .bar span { font-size: 11px; color: var(--muted); font-family: var(--mono); }
.evlist { max-height: 300px; overflow: auto; }
.ev { display: flex; gap: 16px; padding: 7px 0; border-bottom: 1px solid var(--line-2); font-size: 14px; }
.ev .when { color: var(--muted); font-family: var(--mono); font-size: 12.5px; flex: none; width: 108px; }
.ev .what { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.scatter { width: 100%; height: auto; border: 1px solid var(--line); border-radius: var(--radius); margin-top: 14px; }
.sc-bg { fill: var(--surface-2); }
/* Everything on the map is drawn in world units, so zooming in would inflate
   the dots and thicken the coastline along with the geography. --z carries the
   current zoom (view width over world width) so marks can be divided back down
   to a steady size on screen, and strokes opt out of scaling entirely. */
.sc-grid { stroke: var(--line); stroke-width: 1; vector-effect: non-scaling-stroke; }
.sc-dots circle { fill: var(--accent); fill-opacity: .55;
  r: calc(var(--r, 3) * var(--z, 1) * 1px); }
.scatter { cursor: grab; touch-action: none; }
.scatter.grabbing { cursor: grabbing; }
.scatter .mm-land { vector-effect: non-scaling-stroke; }

.pills { display: flex; flex-wrap: wrap; gap: 7px; margin: 14px 0 16px; }
.pill { padding: 6px 13px; border-radius: var(--radius); border: 1px solid var(--line); background: var(--surface);
  color: var(--muted); font-size: 13px; cursor: pointer; font-family: inherit; }
.pill:hover { color: var(--ink); }
.pill.on { background: var(--accent-soft); color: var(--accent-strong); border-color: var(--line); font-weight: 560; }
.tablewrap { overflow: auto; max-height: 460px; border: 1px solid var(--line); border-radius: var(--radius); }
.tablewrap table { border-collapse: collapse; width: 100%; font-size: 13px; }
.tablewrap th, .tablewrap td { text-align: left; padding: 8px 12px; border-bottom: 1px solid var(--line-2); white-space: nowrap; }
.tablewrap th { position: sticky; top: 0; background: var(--surface-2); color: var(--ink); font-weight: 600; z-index: 1; }
.tablewrap td { color: var(--body); max-width: 320px; overflow: hidden; text-overflow: ellipsis; }

@media (max-width: 860px) {
  .sw-row { grid-template-columns: 1fr; gap: 6px; }
  .chatwrap { grid-template-columns: 1fr; }
  .sw-panel { padding: 22px; }
}

/* ===== Guide article pages ===== */
.article { padding-top: 40px; padding-bottom: 96px; }
.crumbs { display: flex; gap: 9px; align-items: center; font-size: 13.5px; color: var(--muted); margin-bottom: 26px; }
.crumbs a { color: var(--muted); }
.crumbs a:hover { color: var(--ink); }
.art-head { max-width: 760px; margin-bottom: 44px; }
.art-head h1 { font-size: clamp(30px, 4.4vw, 44px); }
.art-meta { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin: 18px 0; font-size: 14px; }
.art-intro { color: var(--muted); font-size: 18px; }
.art-grid { display: grid; grid-template-columns: minmax(0, 1fr) 296px; gap: 64px; align-items: start; }
.art-main h2 { font-size: 25px; margin: 44px 0 18px; }
.art-main > p { margin-bottom: 16px; }
.steps-ol { list-style: none; counter-reset: s; padding: 0; margin: 0; }
.steps-ol li { counter-increment: s; position: relative; padding-left: 52px; padding-bottom: 28px; }
.steps-ol li::before {
  content: counter(s); position: absolute; left: 0; top: 0;
  width: 32px; height: 32px; border-radius: 9px; background: var(--accent-soft);
  color: var(--accent-strong); font-weight: 680; font-size: 14px;
  display: flex; align-items: center; justify-content: center;
}
.steps-ol li:not(:last-child)::after {
  content: ""; position: absolute; left: 15.5px; top: 38px; bottom: 6px; width: 1px; background: var(--line);
}
.steps-ol h3 { font-size: 17.5px; margin-bottom: 6px; padding-top: 5px; }
.steps-ol p { color: var(--muted); font-size: 15.5px; }
.art-side { position: sticky; top: 88px; display: flex; flex-direction: column; gap: 16px; }
.side-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 22px; box-shadow: var(--shadow-sm); }
.side-card h4 { font-size: 13px; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); margin-bottom: 14px; font-weight: 620; }
.side-card dl { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: 8px 16px; font-size: 14px; }
.side-card dt { color: var(--muted); }
.side-card dd { margin: 0; color: var(--ink); text-align: right; }
.side-links { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 2px; }
.side-links a { display: block; padding: 7px 0; font-size: 14.5px; color: var(--body); border-bottom: 1px solid var(--line-2); }
.side-links li:last-child a { border-bottom: 0; }
.side-links a:hover { color: var(--accent); }
a.card { color: inherit; }

@media (max-width: 900px) {
  .art-grid { grid-template-columns: 1fr; gap: 40px; }
  .art-side { position: static; }
}

/* ===== Library toolbar (search + save) ===== */
.toolbar { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; margin: 22px 0 6px; }
.search {
  flex: 1 1 260px; min-width: 220px; padding: 10px 14px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); color: var(--ink); font-family: inherit; font-size: 14.5px;
}
.search::placeholder { color: var(--muted); }
.search:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.search::-webkit-search-cancel-button { cursor: pointer; }
.tool-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.tool-actions .btn { font-size: 14px; padding: 9px 15px; }
.save-status { margin: 8px 0 0; min-height: 18px; }

/* ===== Save option row ===== */
.opt { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; margin-top: 12px; font-size: 14.5px; color: var(--body); cursor: pointer; }
.opt input { width: 16px; height: 16px; accent-color: var(--accent); cursor: pointer; }
.tag-plus { font-size: 11.5px; font-weight: 650; letter-spacing: .02em; color: var(--accent-strong); background: var(--accent-soft); border: 1px solid var(--line); padding: 2px 8px; border-radius: 999px; }
.opt-note { color: var(--muted); font-size: 13.5px; }

/* ===== Similar-photo groups ===== */
.dupe-group { border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 16px; margin-bottom: 14px; background: var(--surface-2); }
.dupe-head { font-size: 13.5px; color: var(--muted); margin-bottom: 12px; }
.dupe-group .thumbs { margin-top: 0; }
.thumb .keep { font-size: 10.5px; font-weight: 650; color: var(--ok); background: var(--ok-soft); border: 1px solid #cdeadb; padding: 1px 6px; border-radius: 999px; }
.src-chip { display: inline-flex; align-items: center; gap: 6px; margin-right: 10px; }
.src-chip .d-ic, .src-chip .d-ic svg { width: 16px; height: 16px; }
.btn:disabled { opacity: .55; cursor: default; }

/* ===== Resume an interrupted save ===== */
.resume-card { display: flex; align-items: center; justify-content: space-between; gap: 20px; flex-wrap: wrap;
  border: 1px solid var(--accent); background: var(--accent-soft); border-radius: var(--radius-lg);
  padding: 18px 22px; margin-bottom: 18px; }
.resume-card strong { color: var(--ink); }

/* ===== Structure report + video posters ===== */
.report { margin: 0; padding: 16px; font-family: var(--mono); font-size: 12px;
  line-height: 1.5; color: var(--body); white-space: pre-wrap; word-break: break-word; }
.thumb .ph.is-video { position: relative; }
.thumb .ph.is-video::after { content: ""; position: absolute; right: 6px; bottom: 6px;
  border-style: solid; border-width: 5px 0 5px 8px; border-color: transparent transparent transparent #fff;
  filter: drop-shadow(0 0 2px rgba(0,0,0,.6)); }

/* Inline text button (reads as a link, behaves as a button) */
.linklike { background: none; border: 0; padding: 0; font: inherit; color: var(--accent);
  font-weight: 560; cursor: pointer; text-decoration: none; }
.linklike:hover { text-decoration: underline; }
.linklike:disabled { color: var(--muted); cursor: default; text-decoration: none; }

/* Guide screenshots use .figshot, defined above. */
.figshot { margin: 22px 0 6px; }

/* How recently a guide's flow was walked by hand */
.confirmed-line { margin: -4px 0 26px; font-size: 14px; color: var(--muted); line-height: 1.6; }

/* Steps still written from documentation rather than experience */
.unconfirmed { margin: 14px 0 0; padding: 14px 16px; border-radius: 10px;
  background: #fdf4e3; border: 1px solid #f0e0bd; color: #7a5510;
  font-size: 14px; line-height: 1.6; }
.unconfirmed strong { color: #6a4708; }

/* Brand marks beside the sidebar links */
.side-links a { display: flex; align-items: center; gap: 9px; }
.side-ic { display: inline-flex; width: 17px; height: 17px; flex: 0 0 17px; color: var(--muted); }
.side-ic svg { width: 100%; height: 100%; }
.side-links a:hover .side-ic { color: var(--ink); }

/* Expand a screenshot to full size */
.shot img { cursor: zoom-in; }
.shot img:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
body.no-scroll { overflow: hidden; }
.lightbox { position: fixed; inset: 0; z-index: 200; display: flex;
  align-items: center; justify-content: center; padding: 40px;
  background: rgba(16, 18, 22, 0.82); overflow: auto; }
.lightbox[hidden] { display: none; }
.lb-figure { margin: auto; max-width: 100%; }
.lb-figure img { display: block; max-width: 100%; width: auto; height: auto;
  border-radius: 8px; background: #fff;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45); cursor: default; }
.lb-figure figcaption { margin: 16px auto 0; max-width: 720px; text-align: center;
  color: rgba(255, 255, 255, 0.82); font-size: 14px; line-height: 1.55; }
.lb-close { position: fixed; top: 20px; right: 24px; width: 40px; height: 40px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 999px; border: 1px solid rgba(255, 255, 255, 0.28);
  background: rgba(255, 255, 255, 0.1); color: #fff; cursor: pointer; }
.lb-close:hover { background: rgba(255, 255, 255, 0.2); }
.lb-close:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

@media (max-width: 640px) {
  /* Fitting a 1300px UI screenshot to a phone gives the same unreadable size
     it already had inline. Show it at full size and let the reader pan. */
  .lightbox { padding: 56px 16px 16px; justify-content: flex-start; }
  .lb-figure { margin: auto; }
  .lb-figure img { max-width: none; }
  .lb-figure figcaption { max-width: 90vw; position: sticky; left: 0; }
  .lb-close { top: 10px; right: 12px; }
}

/* ---------- View switcher ---------- */
.views { display: flex; flex-wrap: wrap; gap: 4px; margin: 26px 0 0;
  padding: 4px; border-radius: 12px; background: var(--surface-2);
  border: 1px solid var(--line); }
.views .view { display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 15px; border: 0; border-radius: 9px; background: none;
  font: inherit; font-size: 14px; font-weight: 520; color: var(--muted);
  cursor: pointer; }
.views .view:hover { color: var(--ink); }
.views .view.on { background: var(--surface); color: var(--ink);
  box-shadow: 0 1px 2px rgba(16, 18, 22, 0.08); }
.view-ic { display: inline-flex; width: 16px; height: 16px; flex: 0 0 16px; }
.view-ic svg { width: 100%; height: 100%; }

/* ---------- Timeline ---------- */
.tl-head { margin: 22px 0 6px; }
.tl-legend { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.tl-chip { display: inline-flex; align-items: center; gap: 7px; padding: 4px 11px;
  border-radius: 999px; border: 1px solid var(--line); background: var(--surface);
  font-size: 12.5px; color: var(--muted); }
.tl-chip i { width: 8px; height: 8px; border-radius: 50%; background: var(--muted); }
.tl-chip.t-photo i { background: #3b82f6; }
.tl-chip.t-video i { background: #8b5cf6; }
.tl-chip.t-chat i { background: #10b981; }
.tl-chip.t-place i { background: #f59e0b; }
.tl-chip.t-event i { background: #64748b; }

.tl { margin-top: 24px; }
.tl-day { margin-bottom: 34px; }
.tl-date { position: sticky; top: 0; z-index: 2; padding: 10px 0 12px;
  background: linear-gradient(var(--bg) 72%, rgba(255, 255, 255, 0)); }
.tl-date h3 { margin: 0; font-size: 15.5px; font-weight: 620; letter-spacing: -0.01em; }
.tl-date span { display: block; margin-top: 2px; }

.tl-items { position: relative; padding-left: 108px; }
.tl-items::before { content: ""; position: absolute; left: 92px; top: 4px; bottom: 4px;
  width: 1px; background: var(--line); }

.tl-item { position: relative; display: flex; align-items: flex-start; gap: 12px;
  padding: 11px 14px 11px 0; cursor: pointer; border-radius: 10px; }
.tl-item:hover { background: var(--surface-2); }
.tl-item:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.tl-item.is-open { background: var(--surface-2); }
.tl-time { position: absolute; left: -108px; width: 66px; text-align: right;
  font-size: 12.5px; color: var(--muted); font-variant-numeric: tabular-nums;
  padding-top: 1px; }
.tl-dot { position: absolute; left: -20px; top: 16px; width: 9px; height: 9px;
  border-radius: 50%; background: var(--muted); box-shadow: 0 0 0 3px var(--bg); }
.t-photo .tl-dot { background: #3b82f6; }
.t-video .tl-dot { background: #8b5cf6; }
.t-chat .tl-dot { background: #10b981; }
.t-place .tl-dot { background: #f59e0b; }
.t-event .tl-dot { background: #64748b; }

.tl-body { flex: 1; min-width: 0; }
.tl-title { font-size: 14.5px; font-weight: 500; line-height: 1.45;
  overflow-wrap: anywhere; }
.tl-sub { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px;
  margin-top: 3px; font-size: 12.5px; color: var(--muted); }
.tl-src { display: inline-flex; align-items: center; gap: 5px; }
.tl-ic { display: inline-flex; width: 13px; height: 13px; flex: 0 0 13px; }
.tl-ic svg { width: 100%; height: 100%; }
.tl-chev { flex: 0 0 auto; width: 22px; height: 22px; display: inline-flex;
  align-items: center; justify-content: center; border-radius: 6px;
  color: var(--muted); font-size: 15px; line-height: 1; }
.tl-item:hover .tl-chev { background: var(--surface); color: var(--ink); }

.tl-detail { margin: 2px 0 10px; padding: 16px 18px; border-radius: 11px;
  background: var(--surface); border: 1px solid var(--line); }
.tl-preview { margin-bottom: 14px; }
.tl-preview img { display: block; max-width: 100%; max-height: 420px; width: auto;
  border-radius: 8px; border: 1px solid var(--line); }
.tl-msgs { max-height: 460px; overflow-y: auto; padding-right: 4px; }
.tl-more { padding: 8px 0 40px; text-align: center; }

/* ---------- Data tree ---------- */
.tv { font-size: 13.5px; line-height: 1.65; }
.tv-node > summary { cursor: pointer; color: var(--muted); font-size: 12.5px;
  padding: 1px 0; list-style: none; }
.tv-node > summary::-webkit-details-marker { display: none; }
.tv-node > summary::before { content: "+ "; font-family: ui-monospace, monospace; }
.tv-node[open] > summary::before { content: "- "; }
.tv-kids { margin-left: 14px; padding-left: 12px; border-left: 1px solid var(--line); }
.tv-row { display: flex; gap: 12px; align-items: baseline; padding: 2px 0; }
.tv-k { flex: 0 0 auto; min-width: 150px; color: var(--muted); font-size: 12.5px; }
.tv-s { overflow-wrap: anywhere; }
.tv-num, .tv-date { font-variant-numeric: tabular-nums; }
.tv-date { color: var(--accent); }
.tv-null { color: var(--muted); font-style: italic; }

/* ---------- Chat viewer ---------- */
.chatapp { display: grid; grid-template-columns: 290px 1fr; gap: 0;
  margin-top: 18px; border: 1px solid var(--line); border-radius: 14px;
  overflow: hidden; background: var(--surface); height: 620px; }
.ca-list { overflow-y: auto; border-right: 1px solid var(--line);
  background: var(--surface-2); }
.ca-person { display: flex; align-items: center; gap: 11px; width: 100%;
  padding: 12px 14px; border: 0; border-bottom: 1px solid var(--line);
  background: none; font: inherit; text-align: left; cursor: pointer; }
.ca-person:hover { background: var(--surface); }
.ca-person.on { background: var(--surface); box-shadow: inset 3px 0 0 var(--accent); }
.ca-av { flex: 0 0 36px; width: 36px; height: 36px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent-soft); color: var(--accent); font-weight: 620; font-size: 15px; }
.ca-meta { min-width: 0; flex: 1; }
.ca-name { display: block; font-size: 14px; font-weight: 520;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ca-sub { display: flex; align-items: center; gap: 5px; margin-top: 2px;
  font-size: 12px; color: var(--muted); }
.ca-ic { display: inline-flex; width: 12px; height: 12px; flex: 0 0 12px; }
.ca-ic svg { width: 100%; height: 100%; }

.ca-thread { display: flex; flex-direction: column; min-width: 0; }
.ca-head { display: flex; align-items: flex-start; justify-content: space-between;
  gap: 16px; padding: 16px 20px; border-bottom: 1px solid var(--line); }
.ca-head h3 { margin: 0 0 2px; font-size: 15.5px; font-weight: 600; }
.ca-plats { display: flex; flex-wrap: wrap; gap: 6px; }
.ca-plat { display: inline-flex; align-items: center; gap: 6px; padding: 3px 10px;
  border-radius: 999px; background: var(--surface-2); border: 1px solid var(--line);
  font-size: 12px; color: var(--muted); }
.ca-plat i { display: inline-flex; width: 12px; height: 12px; }
.ca-plat svg { width: 100%; height: 100%; }
.ca-msgs { flex: 1; overflow-y: auto; padding: 18px 20px; }
.ca-daysep { margin: 18px 0 12px; text-align: center; font-size: 12px;
  color: var(--muted); }
.ca-daysep::before, .ca-daysep::after { content: ""; display: inline-block;
  width: 46px; height: 1px; background: var(--line); vertical-align: middle;
  margin: 0 10px; }
.ca-msgs .msg .mh { display: flex; align-items: center; gap: 6px; }

/* ---------- Map ---------- */
.map-head { display: flex; align-items: flex-start; justify-content: space-between;
  gap: 20px; margin: 20px 0 12px; }
.map-years { font-size: 13px; color: var(--muted); white-space: nowrap; }
.map-years select { margin-left: 8px; padding: 5px 9px; border-radius: 8px;
  border: 1px solid var(--line); background: var(--surface); font: inherit;
  font-size: 13px; color: var(--ink); }
.mapwrap { position: relative; }
.map-tip { position: absolute; transform: translate(-50%, -140%);
  padding: 5px 10px; border-radius: 7px; background: var(--ink); color: #fff;
  font-size: 12px; white-space: nowrap; pointer-events: none; z-index: 3; }
.map-tip[hidden] { display: none; }
.sc-eq { stroke: var(--line); stroke-width: 1; stroke-dasharray: 4 4; }
.map-foot { margin-top: 10px; }

@media (max-width: 860px) {
  .chatapp { grid-template-columns: 1fr; height: auto; }
  .ca-list { max-height: 230px; border-right: 0; border-bottom: 1px solid var(--line); }
  .ca-msgs { max-height: 460px; }
  .tl-items { padding-left: 20px; }
  .tl-items::before { left: 4px; }
  .tl-time { position: static; width: auto; text-align: left; display: block;
    margin-bottom: 2px; }
  .tl-dot { left: -20px; top: 18px; }
  .tl-item { flex-wrap: wrap; }
  .map-head { flex-direction: column; gap: 10px; }
}

/* Joining one person's threads across platforms, by hand */
.ca-link { display: inline-flex; align-items: center; gap: 6px; font-size: 12px;
  color: var(--muted); }
.ca-link select { padding: 3px 8px; border-radius: 999px; border: 1px solid var(--line);
  background: var(--surface); font: inherit; font-size: 12px; color: var(--ink);
  max-width: 160px; }
.ca-unlink { padding: 3px 11px; border-radius: 999px; border: 1px solid var(--line);
  background: var(--surface); font: inherit; font-size: 12px; color: var(--muted);
  cursor: pointer; }
.ca-unlink:hover { color: var(--ink); border-color: var(--muted); }
.ca-known { margin: 0; padding: 10px 20px; border-bottom: 1px solid var(--line);
  background: var(--surface-2); }

/* ---------- Data explorer ---------- */

/* The explorer reuses semantic elements the marketing pages already style, so
   it has to opt out of those first. section carries 112px of vertical padding
   for the landing page, which turned every one-item day in the timeline into a
   quarter of a screen of nothing. */
#explorer section { padding: 0; }
#explorer h1, #explorer h2, #explorer h3, #explorer h4 { line-height: 1.3; }
#explorer main { flex: initial; }


/* Sidebar */
.ex-side { display: flex; flex-direction: column; gap: 22px; min-height: 0;
  padding: 20px 16px; overflow-y: auto;
  border-right: 1px solid var(--line); background: var(--surface-2); }
.ex-brand { display: flex; align-items: center; gap: 11px; padding: 0 6px; }
.ex-logo { display: inline-flex; width: 34px; height: 34px; flex: 0 0 34px;
  align-items: center; justify-content: center; border-radius: 9px;
  background: var(--accent); color: #fff; }
.ex-logo svg { width: 20px; height: 20px; }
.ex-brand strong { display: block; font-size: 14.5px; font-weight: 620; }
.ex-brand em { display: block; font-size: 12px; color: var(--muted); font-style: normal; }

.ex-nav { display: flex; flex-direction: column; gap: 2px; }
.ex-navi { display: flex; align-items: center; gap: 11px; width: 100%;
  padding: 9px 11px; border: 0; border-radius: 9px; background: none;
  font: inherit; font-size: 13.5px; color: var(--muted); cursor: pointer;
  text-align: left; }
.ex-navi i { display: inline-flex; width: 17px; height: 17px; flex: 0 0 17px; }
.ex-navi i svg { width: 100%; height: 100%; }
.ex-navi span { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
.ex-navi em { font-style: normal; font-size: 11.5px; font-variant-numeric: tabular-nums;
  color: var(--muted); }
.ex-navi:hover { background: var(--surface); color: var(--ink); }
.ex-navi.on { background: var(--accent-soft); color: var(--accent); font-weight: 560; }
.ex-navi.on em { color: var(--accent); }

.ex-sources h4 { margin: 0 0 8px; padding: 0 11px; font-size: 11.5px;
  font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--muted); }
.ex-source { display: flex; align-items: center; gap: 11px; padding: 7px 11px;
  font-size: 13px; }
.ex-source i { display: inline-flex; width: 16px; height: 16px; flex: 0 0 16px;
  color: var(--muted); }
.ex-source i svg { width: 100%; height: 100%; }
.ex-source span { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
.ex-source em { font-style: normal; font-size: 11.5px; color: var(--muted);
  font-variant-numeric: tabular-nums; }
.ex-add { width: 100%; margin-top: 8px; padding: 9px; border-radius: var(--radius);
  border: 1px dashed var(--line); background: none; font: inherit; font-size: 13px;
  color: var(--muted); cursor: pointer; }
.ex-add:hover { border-color: var(--accent); color: var(--accent); }

.ex-privacy { margin-top: auto; padding: 13px 14px; border-radius: 11px;
  background: var(--ok-soft); border: 1px solid #cdeadb; }
.ex-privacy strong { display: block; font-size: 12.5px; color: var(--ok); }
.ex-privacy p { margin: 5px 0 0; font-size: 11.5px; line-height: 1.55; color: #3f6b53; }

/* Main column */
.ex-main { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.ex-top { display: flex; align-items: center; gap: 16px; flex-shrink: 0;
  padding: 13px 26px; border-bottom: 1px solid var(--line); background: var(--surface); }
.ex-search { position: relative; flex: 1; max-width: 540px; }
.ex-search i { position: absolute; left: 13px; top: 50%; transform: translateY(-50%);
  width: 16px; height: 16px; color: var(--muted); display: inline-flex; }
.ex-search i svg { width: 100%; height: 100%; }
.ex-search input { width: 100%; padding: 9px 14px 9px 38px; border-radius: 10px;
  border: 1px solid var(--line); background: var(--surface-2); font: inherit;
  font-size: 13.5px; color: var(--ink); }
.ex-search input:focus { outline: 2px solid var(--accent); outline-offset: -1px;
  background: var(--surface); }
.ex-topact { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.btn.sm { padding: 7px 13px; font-size: 13px; }

.ex-scroll { flex: 1; min-height: 0; overflow-y: auto; padding: 26px 26px 60px; }
.ex-headline h1 { margin: 0; font-size: 25px; font-weight: 640; letter-spacing: -0.02em; }
.ex-headline p { margin: 4px 0 0; font-size: 14px; }

.ex-stats { display: grid; gap: 12px; margin: 22px 0 8px;
  grid-template-columns: repeat(auto-fit, minmax(168px, 1fr)); }
.ex-stat { position: relative; padding: 15px 17px; border-radius: 12px;
  border: 1px solid var(--line); background: var(--surface); }
.ex-stat.wide { grid-column: span 2; }
.ex-stat-l { display: block; font-size: 12px; color: var(--muted); }
.ex-stat-v { display: block; margin-top: 5px; font-size: 19px; font-weight: 620;
  letter-spacing: -0.01em; }
.ex-stat i { position: absolute; top: 15px; right: 16px; width: 17px; height: 17px;
  color: var(--muted); display: inline-flex; opacity: 0.7; }
.ex-stat i svg { width: 100%; height: 100%; }
.ex-status:empty { display: none; }
.ex-count { margin: 20px 0 6px; }

/* Timeline rows */
.ex-day { margin-bottom: 10px; }
.ex-dayh { position: sticky; top: -26px; z-index: 2; display: flex;
  align-items: baseline; justify-content: space-between; gap: 14px;
  margin: 22px 0 4px; padding: 9px 2px; font-size: 13.5px; font-weight: 620;
  background: var(--bg); border-bottom: 1px solid var(--line); }
.ex-dayh span { font-size: 12px; font-weight: 400; color: var(--muted); }

.ex-rows { display: flex; flex-direction: column; }
.ex-row { display: grid; align-items: center; gap: 14px;
  grid-template-columns: 62px 34px 132px minmax(0, 1fr) 66px;
  padding: 9px 10px; border-radius: 10px; cursor: pointer; }
.ex-row:hover { background: var(--surface-2); }
.ex-row.on { background: var(--accent-soft); }
.ex-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.ex-time { font-size: 12.5px; color: var(--muted); font-variant-numeric: tabular-nums; }
.ex-ic { display: inline-flex; width: 34px; height: 34px; align-items: center;
  justify-content: center; border-radius: 9px; }
.ex-ic svg { width: 17px; height: 17px; }
.ex-ic.sm { width: 24px; height: 24px; border-radius: 7px; }
.ex-ic.sm svg { width: 13px; height: 13px; }
.ex-ic.lg { width: 40px; height: 40px; border-radius: 11px; }
.ex-ic.lg svg { width: 20px; height: 20px; }
.c-blue { background: #e8f0fe; color: #1a5fd0; }
.c-violet { background: #f0e9fd; color: #6d3fc4; }
.c-green { background: #e3f6ec; color: #12805a; }
.c-amber { background: #fdf1dc; color: #9a6a11; }
.c-slate { background: #eceef2; color: #4b5563; }

.ex-what { min-width: 0; }
.ex-what b { display: block; font-size: 13.5px; font-weight: 540;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ex-what em { display: block; font-style: normal; font-size: 12px; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ex-desc { min-width: 0; }
.ex-desc1 { display: block; font-size: 13.5px; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }
.ex-desc2 { display: block; font-size: 12px; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ex-thumb { width: 66px; height: 42px; border-radius: 7px; overflow: hidden;
  background: transparent; }
.ex-thumb img { width: 100%; height: 100%; object-fit: cover; display: block;
  border-radius: 7px; }
.ex-more { padding: 18px 0 10px; text-align: center; }

/* Detail panel */
.ex-detail { display: flex; flex-direction: column; min-width: 0; min-height: 0;
  border-left: 1px solid var(--line); background: var(--surface); overflow: hidden; }
.ex-detail[hidden] { display: none; }
.ex-dh { display: flex; align-items: center; gap: 12px; flex-shrink: 0;
  padding: 16px 20px; border-bottom: 1px solid var(--line); }
.ex-dh-t { flex: 1; min-width: 0; }
.ex-dh-t b { display: block; font-size: 14.5px; font-weight: 600; }
.ex-dh-t em { display: block; font-style: normal; font-size: 12px; color: var(--muted); }
.ex-dh-a { display: flex; gap: 5px; }
.ex-nav-btn { width: 30px; height: 30px; display: inline-flex; align-items: center;
  justify-content: center; border-radius: 8px; border: 1px solid var(--line);
  background: var(--surface); font-size: 15px; line-height: 1; color: var(--muted);
  cursor: pointer; }
.ex-nav-btn:hover:not(:disabled) { color: var(--ink); border-color: var(--muted); }
.ex-nav-btn:disabled { opacity: 0.4; cursor: default; }

.ex-dbody { flex: 1; min-height: 0; overflow-y: auto; padding: 18px 20px 40px; }
.ex-when { margin: 0 0 14px; font-size: 13.5px; font-weight: 520; }
.ex-dh4 { margin: 22px 0 10px; font-size: 11.5px; font-weight: 600;
  letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); }
.ex-dpreview img { display: block; width: 100%; height: auto; border-radius: 0;
  border: 1px solid var(--line); }
.ex-dpreview video { display: block; width: 100%; height: auto; border-radius: 0;
  background: #000; max-height: 60vh; }
.ex-dmsgs { max-height: 300px; overflow-y: auto; padding-right: 4px; }
.ex-dl { display: grid; grid-template-columns: auto 1fr; gap: 6px 16px;
  margin: 14px 0 0; font-size: 13px; }
.ex-dl dt { color: var(--muted); }
.ex-dl dd { margin: 0; font-variant-numeric: tabular-nums; }
.ex-dfoot { margin: 26px 0 0; padding-top: 14px; border-top: 1px solid var(--line); }

.ex-minimap { border-radius: 10px; overflow: hidden; border: 1px solid var(--line); }
.ex-minimap svg { display: block; width: 100%; height: 200px; }
.mm-sea { fill: #eaf1f8; }
.mm-land { fill: #dfe5ec; stroke: #c6ced9; stroke-width: 0.4; }
.mm-dots circle { fill: var(--accent); fill-opacity: 0.55; }
.mm-here { fill: #e5484d; stroke: #fff; stroke-width: 0.8; }

.ex-near { display: flex; flex-direction: column; gap: 2px; }
.ex-nearrow { display: grid; align-items: center; gap: 10px;
  grid-template-columns: 46px 24px 108px minmax(0, 1fr); padding: 6px 4px;
  border-radius: 8px; }
.ex-nearrow:hover { background: var(--surface-2); }
.ex-nearw { min-width: 0; }
.ex-nearw b { display: block; font-size: 12.5px; font-weight: 520;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ex-nearw em { display: block; font-style: normal; font-size: 11px; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ex-neard { font-size: 12.5px; color: var(--muted); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }

.reopen { display: flex; align-items: center; justify-content: space-between;
  gap: 20px; padding: 20px 22px; border-radius: 12px; border: 1px solid var(--line);
  background: var(--surface); }
.reopen p { margin: 0; font-size: 14px; }
.reopen strong { display: block; }

@media (max-width: 1180px) {
  .ex:has(.ex-detail:not([hidden])) { grid-template-columns: 278px minmax(0, 1fr) 360px; }
  .ex-row { grid-template-columns: 56px 34px minmax(0, 1fr) 60px; }
  .ex-row .ex-what { display: none; }
}
@media (max-width: 900px) {
  .ex, .ex:has(.ex-detail:not([hidden])) { grid-template-columns: 1fr; }
  .ex-side { display: none; }
  .ex-detail { position: fixed; inset: 0; z-index: 70; border-left: 0; }
  .ex-stat.wide { grid-column: span 1; }
}

/* ---------- Chat bubble alignment ----------
   The sender line has to sit over its own bubble, and anything the account
   owner sent belongs on the right. Aligning the row rather than the text keeps
   the name, the time and the bubble travelling together. */
.msg { display: flex; flex-direction: column; max-width: 76%; margin-bottom: 14px; }
.msg.in { align-items: flex-start; margin-right: auto; }
.msg.out { align-items: flex-end; margin-left: auto; }
.msg .mh { display: flex; align-items: center; gap: 6px; margin-bottom: 4px;
  padding: 0 4px; font-size: 11.5px; color: var(--muted); }
.msg.out .mh { flex-direction: row-reverse; }
.msg .mb { display: inline-block; padding: 9px 13px; border-radius: 14px;
  font-size: 13.5px; line-height: 1.5; overflow-wrap: anywhere; }
.msg.in .mb { background: var(--surface-2); border: 1px solid var(--line);
  border-bottom-left-radius: 5px; }
.msg.out .mb { background: var(--accent-soft); border: 1px solid transparent;
  color: var(--ink); border-bottom-right-radius: 5px; }

/* Chat header: name and the link control on one line, platforms beneath */
.ca-head { display: block; }
.ca-htop { display: flex; align-items: center; justify-content: space-between;
  gap: 16px; }
.ca-htop h3 { margin: 0; }
.ca-hbot { display: flex; align-items: center; justify-content: space-between;
  gap: 16px; margin-top: 8px; }
.ca-hbot p { margin: 0; text-align: right; }
.ca-link { flex-shrink: 0; margin-left: auto; }

/* ---------- Date rail ----------
   The scrollbar for time. --w is how close a mark is to what is on screen
   (0..1) and --s is the matching scale; both are set per mark on every scroll
   frame, which is what makes the magnification move as one piece. */
.ex-main { position: relative; }
.rail { position: absolute; top: 64px; right: 6px; bottom: 12px; z-index: 5;
  display: flex; flex-direction: column; justify-content: flex-start;
  align-items: flex-end; width: 62px; padding: 4px 0;
  pointer-events: none; touch-action: none; }
.rail > * { pointer-events: auto; }

/* Every mark takes an equal share of the rail's real height. space-between
   distributed the leftovers but never shrank a mark below its label, so with
   enough months the column overran its own box and the oldest ones fell off
   the bottom of the screen. flex-basis 0 with min-height 0 means the marks
   divide what there is, however many of them there are. The label is scaled
   with a transform, which does not feed back into layout, so magnifying one
   cannot reopen the overflow. */
.rail-m { --w: 0; --s: 1; position: relative; display: flex; align-items: center;
  justify-content: flex-end; gap: 6px; flex: 1 1 0; min-height: 0; padding: 0;
  border: 0; background: none; cursor: pointer; font: inherit; line-height: 1;
  transform-origin: right center; }

/* Too many months to name them all: keep every tick, drop the ordinary labels,
   and let the years and whatever is under the cursor speak for the rest. */
.rail.dense .rail-m .rail-t { visibility: hidden; }
.rail.dense .rail-m.y .rail-t,
.rail.dense .rail-m.near .rail-t { visibility: visible; }
.rail-t { font-size: 9.5px; font-variant-numeric: tabular-nums; white-space: nowrap;
  color: var(--muted);
  opacity: calc(0.16 + 0.84 * var(--w));
  transform: scale(var(--s));
  transform-origin: right center;
  transition: color 0.12s linear; }
.rail-b { width: calc(5px + 9px * var(--w)); height: 1.5px; border-radius: 1px;
  background: var(--line);
  opacity: calc(0.35 + 0.65 * var(--w)); }

.rail-m.y .rail-t { font-weight: 640; color: var(--ink);
  opacity: calc(0.3 + 0.7 * var(--w)); }
.rail-m.empty .rail-t { opacity: calc(0.08 + 0.4 * var(--w)); }
.rail-m.near .rail-t { color: var(--accent); }
.rail-m.near .rail-b { background: var(--accent); }
.rail-m:hover .rail-t { color: var(--accent); }
.rail-m:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Leave room so long rows do not run under the rail. */
.ex-scroll { padding-right: 78px; }

/* ---------- Image library ---------- */
.ex-lib { margin-top: 4px; }
.lib-month { margin-bottom: 26px; }
/* A contact sheet, not a gallery of cards. Square, edge to edge, no gap and no
   radius, so the pictures form one block and you can take in a month at a
   glance. Rounded tiles floating on gaps meant about a dozen fitted on screen
   at a time, which is the wrong unit for a library of twelve thousand. */
.lib-grid { display: grid; gap: 0; margin-top: 12px;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
.lib-cell { margin: 0; cursor: pointer; border-radius: 0; }
.lib-cell:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px;
  z-index: 2; position: relative; }
.lib-img { display: block; aspect-ratio: 1 / 1; border-radius: 0;
  background: var(--surface-2) center/cover no-repeat;
  border: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 10px; font-weight: 600; color: var(--muted); letter-spacing: .04em; }
/* Lift on hover would break the grid apart again; brightness keeps it flush. */
.lib-cell:hover .lib-img { filter: brightness(1.12); }
.lib-cell.on { position: relative; z-index: 2; }
.lib-cell.on .lib-img { outline: 3px solid var(--accent); outline-offset: -3px; }

/* A video reads as a video whether or not its poster frame came back. The old
   rule set content and position on a box that was not positioned and drew
   nothing at all, so an mp4 whose first frame failed to decode was a white
   square with no clue what it was. */
/* Which service a picture came from, in the corner of the picture.

   It used to be a caption under each tile, which is what put a gap between
   every row and turned a contact sheet back into a grid of cards. The mark is
   the same one the sidebar uses for that source, so the two read as one
   system, and the full label and size are on the tile's tooltip. */
.src-badge { position: absolute; top: 4px; left: 4px; z-index: 2;
  width: 17px; height: 17px; border-radius: 3px; pointer-events: none;
  display: flex; align-items: center; justify-content: center;
  background: rgba(14, 16, 20, .62); color: #fff; }
.src-badge svg { width: 11px; height: 11px; display: block; }
.lib-cell, .cl-cell { position: relative; }
/* Small tiles cannot carry a 17px badge without covering the picture. */
@media (max-width: 900px) { .src-badge { width: 15px; height: 15px; } }

/* A poster frame covers this; without one the tile is still obviously a video
   rather than a white square. */
.lib-img.vid { position: relative; background-color: #dfe3e9; }
.lib-img.vid::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  width: 26px; height: 26px; margin: -13px 0 0 -13px; border-radius: 50%;
  background: rgba(12, 14, 18, .58);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .35);
}
/* The triangle sits above the disc: both are positioned, and z-index lifts it
   past a pseudo-element that would otherwise paint over it. */
.lib-img.vid::before {
  content: ""; position: absolute; left: 50%; top: 50%; z-index: 1;
  width: 0; height: 0; margin: -5.5px 0 0 -3px;
  border-left: 9px solid #fff;
  border-top: 5.5px solid transparent;
  border-bottom: 5.5px solid transparent;
}

/* Row previews in the timeline, big enough to recognise the picture */
.ex-row { grid-template-columns: 62px 34px 132px minmax(0, 1fr) 92px; }
.ex-thumb { width: 92px; height: 62px; border-radius: 8px; overflow: hidden; }
.ex-thumb.has { background: var(--surface-2); border: 1px solid var(--line); }
.ex-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ex-thumb.nodecode { background: var(--surface-2); }

/* Tighter days: a one-item day should be one item tall */
.ex-dayh { margin: 14px 0 2px; }
.ex-day { margin-bottom: 4px; }

/* ==========================================================================
   Motion
   One place for easing and duration, so everything moves the same way.
   Everything here is subtle on purpose: the point is that the interface feels
   settled, not that it performs.
   ========================================================================== */
:root {
  --ease: cubic-bezier(0.32, 0.72, 0, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --t-fast: 0.14s;
  --t: 0.22s;
  --t-slow: 0.36s;
}

/* Someone who has asked their system to stop animations means it. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

@keyframes m-rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
@keyframes m-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes m-slide-in { from { transform: translateX(100%); } to { transform: none; } }
@keyframes m-pop { from { opacity: 0; transform: scale(0.97); } to { opacity: 1; transform: none; } }

/* Site chrome */
.btn, .linklike, .tab, .view, .card, .pill, .sw-btn, .conv, .nav-links a,
.foot-links a, .side-links a, .ex-navi, .ex-add, .ex-nav-btn, .ca-person,
.rail-m, .lib-cell, .ex-row, .thumb {
  transition: background-color var(--t) var(--ease), color var(--t) var(--ease),
    border-color var(--t) var(--ease), box-shadow var(--t) var(--ease),
    transform var(--t) var(--ease), opacity var(--t) var(--ease);
}
.btn:active { transform: translateY(1px); }
.card:hover { transform: translateY(-3px); }
.nav-links a { position: relative; }
.nav-links a::after { content: ""; position: absolute; left: 0; right: 0; bottom: -6px;
  height: 2px; border-radius: 2px; background: var(--accent);
  transform: scaleX(0); transform-origin: left;
  transition: transform var(--t) var(--ease); }
.nav-links a:hover::after, .nav-links a.active::after { transform: scaleX(1); }

/* Pages settle in rather than appearing */
main > section, .article > *, .page-head { animation: m-rise var(--t-slow) var(--ease-out) both; }
main > section:nth-child(2) { animation-delay: 0.04s; }
main > section:nth-child(3) { animation-delay: 0.08s; }

/* "Your data" in the nav, once an export is open in this tab */
.nav-explore { display: inline-flex; align-items: center; gap: 7px; }
.nav-explore .dot { width: 6px; height: 6px; border-radius: 50%;
  background: var(--ok); box-shadow: 0 0 0 0 rgba(18, 128, 90, 0.5);
  animation: m-pulse 2.4s var(--ease) infinite; }
@keyframes m-pulse {
  0% { box-shadow: 0 0 0 0 rgba(18, 128, 90, 0.45); }
  70% { box-shadow: 0 0 0 7px rgba(18, 128, 90, 0); }
  100% { box-shadow: 0 0 0 0 rgba(18, 128, 90, 0); }
}

/* ==========================================================================
   Explorer motion and layout corrections
   ========================================================================== */

.ex-main { position: relative; }

/* The explorer owns its own scrolling, and the rail is its scrollbar. A native
   thumb would measure only what has been rendered so far and claim the bottom
   was near while years were still unrendered. */
/* No scroll-behavior here. It applies to the wheel as well as to
   scrollIntoView, which puts a smoothing delay on every ordinary scroll and
   makes the rail look like it is lagging behind. Programmatic jumps ask for
   smooth explicitly instead. */
.ex-scroll { scrollbar-width: none; }
.ex-scroll::-webkit-scrollbar { width: 0; height: 0; }

/* Views that manage their own panes fill the height instead of scrolling the
   page, otherwise their inner lists sit below the fold out of reach. */
.ex-scroll.fills { overflow: hidden; display: flex; flex-direction: column;
  padding-bottom: 20px; }
.ex-scroll.fills #ex-body { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.ex-scroll.fills .chatapp { height: auto; flex: 1; min-height: 0; }
.ex-scroll.fills .mapwrap { flex: 1; min-height: 0; }
.ex-scroll.fills .scatter { height: 100%; }

/* Flex children need this before a descendant's overflow can work at all. */
.ca-thread, .ca-msgs, .ex-main, .ex-detail, .ex-dbody { min-height: 0; }

.ex-row, .lib-cell, .ex-day, .lib-month { animation: m-rise var(--t) var(--ease-out) both; }
.ex-row:hover { transform: translateX(2px); }
.ex-navi.on { animation: m-pop var(--t) var(--ease-out); }
.ex-thumb img, .lib-img { animation: m-fade var(--t-slow) var(--ease-out) both; }
#ex-body { animation: m-fade var(--t) var(--ease-out); }

/* Rail thumb: where you are, measured in months rather than rendered pixels */
.rail { --p: 0; }
.rail-thumb { position: absolute; right: 1px; top: 0; width: 3px; height: 34px;
  border-radius: 2px; background: var(--accent); opacity: 0.28;
  transform: translateY(calc(var(--p) * (100cqh - 34px)));
  transition: transform var(--t) var(--ease), opacity var(--t) var(--ease);
  pointer-events: none; }
.rail { container-type: size; }
.rail:hover .rail-thumb, .rail.dragging .rail-thumb { opacity: 0.75; }
.rail.dragging { cursor: grabbing; }
.rail.dragging .rail-t { transition: none; }
.rail-m { cursor: pointer; }
.rail::before { content: attr(data-label); position: absolute; right: 100%; top: 50%;
  margin-right: 10px; padding: 5px 10px; border-radius: 8px;
  background: var(--ink); color: #fff; font-size: 12px; white-space: nowrap;
  opacity: 0; transform: translateY(-50%) translateX(6px);
  transition: opacity var(--t) var(--ease), transform var(--t) var(--ease);
  pointer-events: none; }
.rail.dragging::before { opacity: 1; transform: translateY(-50%); }

/* Site identity and links inside the explorer sidebar */
.ex-brand { display: block; padding: 2px 6px 0; }
.ex-brand .wordmark { font-size: 21px; }
.ex-sitenav { display: flex; gap: 14px; margin-top: 8px; }
.ex-sitenav a { font-size: 12.5px; color: var(--muted); text-decoration: none; }
.ex-sitenav a:hover { color: var(--accent); }

/* ==========================================================================
   Explorer layout, second pass
   ========================================================================== */

/* One nav across the whole site, this page included. The explorer sits under
   it rather than replacing it, so the wordmark and links never move. */
body.exploring { overflow: hidden; }
body.exploring > main, body.exploring > footer.site { display: none; }
body.exploring > nav.nav { display: block; position: relative; z-index: 70; }

#explorer { position: fixed; left: 0; right: 0; bottom: 0;
  top: var(--navh, 61px); z-index: 60; background: var(--bg); }

/* The detail panel is a real column again.

   Sliding it over the top meant it covered the rail and blocked every control
   under it, which is worse than the reflow it avoided. It takes space now, and
   the reflow is bearable because every row is a fixed grid with ellipsis
   truncation: the columns narrow, the text clips, nothing re-wraps or changes
   height. */
.ex { display: grid; grid-template-columns: 278px minmax(0, 1fr) 0px;
  height: 100%;
  transition: grid-template-columns var(--t-slow) var(--ease); }
.ex.detail-open { grid-template-columns: 278px minmax(0, 1fr) clamp(340px, 30vw, 460px); }

.ex-detail { position: relative; inset: auto; width: auto; z-index: 1;
  border-left: 1px solid var(--line); box-shadow: none;
  animation: none; overflow: hidden; }
.ex-detail[hidden] { display: none; }
/* Fade the contents in rather than sliding the panel, so nothing is in motion
   while the column is still resizing. */
.ex-dbody, .ex-dh { animation: m-fade var(--t-slow) var(--ease-out) both; }

/* Rows keep their shape while the column narrows */
.ex-row, .ex-nearrow { contain: layout style; }

/* ==========================================================================
   Rail, second pass: legible, and cheap to move
   ========================================================================== */
.rail { width: 86px; top: 60px; right: 4px; }
.ex-scroll { padding-right: 100px; }

.rail-t { font-size: 12px; font-weight: 500; }
.rail-m.y .rail-t { font-size: 13.5px; font-weight: 660; }
/* The magnified end of the scale has to be worth reading, which is the whole
   point of the rail. */
.rail-m.near .rail-t { font-weight: 620; }
.rail-b { width: calc(6px + 12px * var(--w)); height: 2px; }

/* Only the newest page animates in. Several hundred rows each running their
   own entry animation is a large part of what made scrolling stutter. */
.ex-row, .lib-cell { animation: none; }
.ex-day.fresh .ex-row, .lib-month.fresh .lib-cell {
  animation: m-rise var(--t) var(--ease-out) both; }
.ex-row:hover { transform: none; background: var(--surface-2); }

@media (max-width: 1180px) {
  .ex.detail-open { grid-template-columns: 278px minmax(0, 1fr) 340px; }
}
@media (max-width: 900px) {
  .ex, .ex.detail-open { grid-template-columns: 1fr; }
  .ex-side { display: none; }
  .ex-detail { position: fixed; inset: var(--navh, 61px) 0 0 0; z-index: 70; }
}

/* The card behind the explorer, shown when it is closed */
.reopen { display: flex; align-items: center; justify-content: space-between;
  gap: 24px; padding: 18px 22px; border-radius: 12px;
  border: 1px solid var(--line); background: var(--surface); }
.reopen p { margin: 3px 0 0; }
.reopen strong { font-size: 14.5px; }

/* ---------- Source filter ---------- */
.ex-source { display: flex; align-items: center; gap: 11px; width: 100%;
  padding: 7px 11px; border: 0; border-radius: 8px; background: none;
  font: inherit; font-size: 13px; text-align: left; color: var(--ink);
  cursor: pointer; }
.ex-source:hover { background: var(--surface); }
.ex-source.on { background: var(--accent-soft); color: var(--accent); font-weight: 560; }
.ex-source.on i, .ex-source.on em { color: var(--accent); }
.ex-source.off { opacity: 0.42; }
.ex-clearsrc { width: 100%; margin-top: 6px; padding: 7px; border-radius: 8px;
  border: 0; background: none; font: inherit; font-size: 12.5px;
  color: var(--accent); cursor: pointer; text-align: left; }
.ex-clearsrc:hover { text-decoration: underline; }
.ex-clearsrc[hidden] { display: none; }

/* ==========================================================================
   Rail, third pass: room to grow
   The labels scale up, so the space they need is the magnified size, not the
   resting one. Sizing the rail for the resting text is what clipped them.
   ========================================================================== */
.rail { width: 118px; padding: 4px 10px 4px 0; overflow: visible; }
.ex-scroll { padding-right: 132px; }
.ex-main { overflow: visible; }

/* Each mark reserves height in proportion to how magnified it is, so the ones
   either side move out of the way instead of being overlapped. The resting
   height and the lift are set by rail.js from the height the rail actually
   has; fixed values meant enough months overran the box and the oldest years
   were cut off the bottom of the screen. */
.rail-m { min-height: calc(var(--rest, 11px) + var(--lift, 13px) * var(--w));
  padding-right: 2px; }
.rail-t { transform-origin: right center; will-change: transform, opacity; }

/* Big enough to actually read at the focused end. */
.rail-t { font-size: 11.5px; }
.rail-m.y .rail-t { font-size: 12.5px; }
.rail-m.near .rail-t { letter-spacing: -0.01em; }

/* ==========================================================================
   Stop the library jumping while the detail panel opens
   auto-fill recomputes the column count continuously as the column narrows,
   so pictures hop between rows for the whole animation. A fixed count, set
   once when the width settles, just narrows them.
   ========================================================================== */
.lib-grid { grid-template-columns: repeat(var(--cols, 6), minmax(0, 1fr)); }
.lib-img { transition: none; }

/* Rows and pictures should not re-render mid-transition either. */
.ex-scroll { contain: paint; }
.ex-detail { contain: paint; }

.ex-forget { margin-top: 10px; padding: 0; border: 0; background: none;
  font: inherit; font-size: 11.5px; color: #3f6b53; text-decoration: underline;
  cursor: pointer; opacity: 0.75; }
.ex-forget:hover { opacity: 1; }
.ex-forget[data-sure="1"] { color: #b23b3b; font-weight: 600; opacity: 1; }

.ex-source.superseded { opacity: 0.7; }
.ex-source .tagline { font-style: normal; font-size: 10px; font-weight: 600;
  letter-spacing: .04em; text-transform: uppercase; color: var(--muted);
  padding: 1px 5px; margin-left: 6px; border-radius: 4px;
  background: var(--surface-2); border: 1px solid var(--line); }

.ex-work { display: flex; flex-direction: column; gap: 4px; margin-top: 10px;
  padding-top: 10px; border-top: 1px solid #cdeadb; }
.ex-worklink { padding: 0; border: 0; background: none; font: inherit;
  font-size: 11.5px; color: #2f7a58; text-align: left; cursor: pointer; }
.ex-worklink:hover { text-decoration: underline; }

/* A picture that has not been decoded yet still holds its place, so the page
   is its true height from the first frame and nothing shifts as they arrive. */
/* The shimmer lives on the element's own background rather than on ::after.

   Two problems with the pseudo-element version. It swept white over a #fafafa
   base, so on a white page there was nothing to see and a tile that had not
   decoded yet looked like a tile that had decoded to blank. And ::after is
   also the video badge's disc, so on a clip the two rules fought and left a
   half-drawn circle sitting on the placeholder.

   Animating background-position needs neither pseudo-element and lets both
   stay free for the badge. */
.lib-img.skeleton { position: relative; overflow: hidden;
  background-color: #e4e8ee;
  background-image: linear-gradient(100deg,
    rgba(255, 255, 255, 0) 38%, rgba(255, 255, 255, .85) 50%, rgba(255, 255, 255, 0) 62%);
  background-size: 260% 100%;
  background-repeat: no-repeat;
  animation: m-shimmer 1.25s linear infinite; }
@keyframes m-shimmer { from { background-position: 160% 0; } to { background-position: -160% 0; } }
@media (prefers-reduced-motion: reduce) {
  .lib-img.skeleton { animation: none; background-image: none; }
}
/* No badge over a placeholder - it says "video" about a tile that has not
   said anything yet. */
.lib-img.vid.skeleton::after, .lib-img.vid.skeleton::before { display: none; }
/* Decoded but undecodable: a flat tone, not a shimmer that never ends. */
.lib-img.nodecode { background-color: #e4e8ee; animation: none; }

/* ==========================================================================
   Explorer chrome, second pass
   ========================================================================== */

/* The explorer is its own place, not a page of the site with a site nav on
   top. The mark stays top left and goes home; everything else is the app. */
body.exploring > nav.nav { display: none; }
#explorer { top: 0; }

.ex-home { display: block; padding: 2px 8px 4px; text-decoration: none; }
.ex-home .wordmark { font-size: 22px; }

/* Even rhythm between sections on the content pages.

   The gap between two sections comes from exactly one rule, rather than from
   each section's own padding plus whatever its contents happen to add. A
   heading with cards under it and a heading with only a paragraph produced
   different gaps otherwise, because .section-head carries a margin only when
   something follows it. */
section.wrap.tight { padding-top: 0; padding-bottom: 0; }
section.wrap.tight + section.wrap.tight { padding-top: var(--section-tight); }
section.wrap.tight:last-of-type { padding-bottom: 96px; }
.page-head + section.wrap.tight { padding-top: var(--section-tight); }
section.wrap.tight .section-head { margin-bottom: 0; }
section.wrap.tight .section-head:not(:last-child) { margin-bottom: 22px; }

/* ---------- Opening curtain ---------- */
#curtain { position: fixed; inset: 0; z-index: 200; display: flex;
  align-items: center; justify-content: center; background: var(--bg);
  opacity: 0; pointer-events: none; transition: opacity var(--t-slow) var(--ease); }
body.curtained #curtain { opacity: 1; pointer-events: auto; }
.curtain-in { text-align: center; max-width: 420px; padding: 0 24px; }
.curtain-in .wordmark { font-size: 30px; }
.curtain-bar { width: 220px; height: 3px; margin: 26px auto 20px; border-radius: 2px;
  background: var(--surface-2); overflow: hidden; }
.curtain-bar i { display: block; width: 40%; height: 100%; border-radius: 2px;
  background: var(--accent); animation: m-slide 1.15s var(--ease) infinite; }
@keyframes m-slide {
  0% { transform: translateX(-110%); }
  100% { transform: translateX(360%); }
}
.curtain-msg { font-size: 14.5px; min-height: 1.4em; }
.curtain-fine { margin-top: 8px; font-size: 12.5px; color: var(--muted); }
@media (prefers-reduced-motion: reduce) { .curtain-bar i { animation: none; width: 100%; } }

/* ==========================================================================
   Notification centre
   Everything the app says appears in one corner and stays in a list, instead
   of a line of text next to whichever button started the work.
   ========================================================================== */
/* Bottom right, not top right.

   At the top it landed exactly on the close button of the detail panel - same
   corner, higher z-index - so once a photo was open the panel could not be
   shut. Column-reverse keeps the bell nearest the corner with toasts stacking
   above it, which is the order the markup already has.

   The container itself ignores the pointer so the gap between the bell and a
   toast is not an invisible plate over whatever is underneath. */
#notify { position: fixed; bottom: 16px; right: 16px; z-index: 300;
  display: flex; flex-direction: column-reverse; align-items: flex-end; gap: 10px;
  pointer-events: none; }
#notify > * { pointer-events: auto; }

.nt-bell { position: relative; width: 38px; height: 38px; display: inline-flex;
  align-items: center; justify-content: center; border-radius: 10px;
  border: 1px solid var(--line); background: var(--surface); color: var(--muted);
  cursor: pointer; box-shadow: var(--shadow-sm);
  transition: color var(--t) var(--ease), border-color var(--t) var(--ease); }
.nt-bell:hover { color: var(--ink); border-color: var(--muted); }
.nt-bell svg { width: 19px; height: 19px; }
.nt-dot { position: absolute; top: 7px; right: 8px; width: 8px; height: 8px;
  border-radius: 50%; background: var(--accent); border: 2px solid var(--surface); }
.nt-dot[hidden] { display: none; }

.nt-toasts { display: flex; flex-direction: column; gap: 8px; align-items: flex-end; }
.nt-toast { display: flex; align-items: flex-start; gap: 10px; width: min(370px, calc(100vw - 40px));
  padding: 12px 13px; border-radius: 12px; border: 1px solid var(--line);
  background: var(--surface); box-shadow: 0 12px 32px rgba(16, 18, 22, .13);
  animation: m-rise var(--t) var(--ease-out) both; }
.nt-toast.out { animation: m-fade var(--t) var(--ease) reverse both; }
.nt-toast.go { cursor: pointer; }
.nt-toast.go:hover { border-color: var(--accent); }
.nt-toast b { display: block; font-size: 13.5px; font-weight: 560; line-height: 1.4; }
.nt-toast em { display: block; margin-top: 3px; font-style: normal; font-size: 12.5px;
  color: var(--muted); line-height: 1.5; }
.nt-toast > span:nth-child(2) { flex: 1; min-width: 0; }
.nt-x { flex: 0 0 auto; width: 20px; height: 20px; border: 0; background: none;
  color: var(--muted); font-size: 16px; line-height: 1; cursor: pointer; border-radius: 5px; }
.nt-x:hover { color: var(--ink); background: var(--surface-2); }

.nt-ic { flex: 0 0 26px; width: 26px; height: 26px; border-radius: 8px;
  display: inline-flex; align-items: center; justify-content: center; }
.nt-ic svg { width: 15px; height: 15px; }
.nt-ic.k-info { background: var(--surface-2); color: var(--muted); }
.nt-ic.k-work { background: var(--accent-soft); color: var(--accent); }
.nt-ic.k-done { background: var(--ok-soft); color: var(--ok); }
.nt-ic.k-warn { background: #fdf4e3; color: #9a6a11; }
.nt-toast.k-work .nt-ic svg { animation: m-spin 1.6s linear infinite; }
@keyframes m-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .nt-toast.k-work .nt-ic svg { animation: none; } }

.nt-panel { width: min(380px, calc(100vw - 40px)); max-height: 66vh; display: flex;
  flex-direction: column; border-radius: var(--radius-xl); border: 1px solid var(--line);
  background: var(--surface); box-shadow: 0 20px 48px rgba(16, 18, 22, .18);
  overflow: hidden; animation: m-rise var(--t) var(--ease-out) both; }
.nt-panel[hidden] { display: none; }
.nt-panel header { display: flex; align-items: center; justify-content: space-between;
  padding: 12px 14px; border-bottom: 1px solid var(--line); font-size: 13.5px; }
.nt-clear { border: 0; background: none; font: inherit; font-size: 12.5px;
  color: var(--muted); cursor: pointer; }
.nt-clear:hover { color: var(--accent); }
.nt-list { overflow-y: auto; padding: 6px; }
.nt-empty { padding: 22px 14px; text-align: center; font-size: 13px; color: var(--muted); }
.nt-item { display: flex; gap: 10px; width: 100%; padding: 10px; border: 0; border-radius: 9px;
  background: none; font: inherit; text-align: left; }
.nt-item.go { cursor: pointer; }
.nt-item.go:hover { background: var(--surface-2); }
.nt-item b { display: block; font-size: 13px; font-weight: 540; line-height: 1.4; }
.nt-item em { display: block; margin-top: 2px; font-style: normal; font-size: 12px;
  color: var(--muted); line-height: 1.5; }
.nt-item time { display: block; margin-top: 4px; font-size: 11px; color: var(--muted); }
.nt-item > span:nth-child(2) { flex: 1; min-width: 0; }

/* The explorer's own buttons sit under the bell, so give them room. */
.ex-topact { padding-right: 46px; }

/* ==========================================================================
   Clean up
   ========================================================================== */
.cl-summary { display: flex; flex-wrap: wrap; align-items: center; gap: 26px;
  margin: 18px 0 8px; padding: 16px 20px; border-radius: 12px;
  border: 1px solid var(--line); background: var(--surface); }
.cl-stat span { display: block; font-size: 21px; font-weight: 640; letter-spacing: -.01em; }
.cl-stat em { font-style: normal; font-size: 12px; color: var(--muted); }
.cl-stat.out span { color: #b23b3b; }
.cl-explain { flex: 1 1 260px; margin: 0; font-size: 12.5px; color: var(--muted); line-height: 1.55; }

.cl-h { margin: 30px 0 4px; font-size: 15.5px; font-weight: 620; }
.cl-note { margin-bottom: 14px; }
.cl-groups { display: flex; flex-direction: column; gap: 14px; }
.cl-group { padding: 14px 16px; border-radius: 12px; border: 1px solid var(--line);
  background: var(--surface); }
.cl-group header { display: flex; align-items: center; justify-content: space-between;
  gap: 14px; margin-bottom: 11px; font-size: 12.5px; color: var(--muted); }
.cl-keepall { border: 1px solid var(--line); background: var(--surface-2); font: inherit;
  font-size: 12px; padding: 4px 11px; border-radius: 999px; color: var(--ink); cursor: pointer; }
.cl-keepall:hover { border-color: var(--accent); color: var(--accent); }
.cl-row { display: flex; flex-wrap: wrap; gap: 0; }
.cl-cell { position: relative; width: 116px; margin: 0; cursor: pointer; }
.cl-cell .lib-img { aspect-ratio: 1 / 1; border-radius: 0; }
.cl-mark { position: absolute; top: 8px; right: 8px; width: 22px; height: 22px;
  border-radius: 50%; border: 2px solid #fff; background: var(--ok);
  box-shadow: 0 1px 4px rgba(0,0,0,.3);
  transition: background var(--t) var(--ease); }
.cl-mark::after { content: ""; position: absolute; inset: 0;
  background: no-repeat center/11px url("data:image/svg+xml;utf8,\
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' \
stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'><path d='m5 12 5 5L20 7'/></svg>"); }
.cl-cell.dropped .lib-img { opacity: .38; filter: grayscale(1); }
.cl-cell.dropped .cl-mark { background: #b23b3b; }
.cl-cell.dropped .cl-mark::after { background-image: url("data:image/svg+xml;utf8,\
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' \
stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'><path d='M6 6l12 12M18 6L6 18'/></svg>"); }
.cl-cell:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; z-index: 3; }

/* Save-your-work: a button, not a whisper */
.ex-savework { display: flex; align-items: center; justify-content: center; gap: 8px;
  width: 100%; margin-top: 12px; padding: 9px 12px; border-radius: 9px;
  border: 1px solid #bfe2d0; background: var(--surface); font: inherit;
  font-size: 12.5px; font-weight: 540; color: #1d6b4b; cursor: pointer; }
.ex-savework:hover { background: var(--ok-soft); border-color: var(--ok); }
.ex-savework svg { width: 15px; height: 15px; }

/* ==========================================================================
   Clean up, second pass: breathing room and the rule controls
   ========================================================================== */
.cl-group { padding: 18px 20px 20px; }
.cl-group header { margin-bottom: 14px; gap: 18px; flex-wrap: wrap; }
.cl-cell { width: 124px; }
.cl-groups { gap: 16px; }
.cl-h { margin-top: 36px; }

.cl-rules { display: flex; flex-wrap: wrap; align-items: center; gap: 20px;
  margin: 14px 0 4px; padding: 14px 20px; border-radius: 12px;
  background: var(--surface-2); border: 1px solid var(--line); }
.cl-rules label { display: inline-flex; align-items: center; gap: 9px;
  font-size: 13px; color: var(--muted); }
.cl-rules select, .cl-grule { padding: 6px 10px; border-radius: 8px;
  border: 1px solid var(--line); background: var(--surface); font: inherit;
  font-size: 12.5px; color: var(--ink); }

.cl-gtools { display: inline-flex; align-items: center; gap: 12px; }
.cl-keepn { display: inline-flex; align-items: center; gap: 8px; font-size: 12.5px; }
.cl-keepn.off { opacity: .4; pointer-events: none; }
.cl-keepn b { font-weight: 540; color: var(--ink); min-width: 54px; text-align: center; }
/* Superseded by the .cl-step rule further down; kept only as the fallback
   for the older cleanup markup. */
.cl-step { width: 24px; height: 24px; border-radius: var(--radius); border: 1px solid var(--line);
  background: var(--surface); color: var(--ink); font: inherit; line-height: 1; cursor: pointer; }
.cl-step:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.cl-step:disabled { opacity: .35; cursor: default; }

/* ==========================================================================
   Export wizard
   ========================================================================== */
body.exporting { overflow: hidden; }
#exportx, #captionx { position: fixed; inset: 0; z-index: 400; display: flex;
  align-items: center; justify-content: center; padding: 28px; }
.xw-scrim { position: absolute; inset: 0; background: rgba(16, 18, 22, .5);
  animation: m-fade var(--t) var(--ease) both; }
.xw { position: relative; width: min(680px, 100%); max-height: calc(100vh - 56px);
  display: flex; flex-direction: column; border-radius: 16px; background: var(--surface);
  box-shadow: 0 30px 80px rgba(16, 18, 22, .3);
  animation: m-rise var(--t-slow) var(--ease-out) both; overflow: hidden; }

.xw-head { display: flex; align-items: flex-start; justify-content: space-between;
  gap: 16px; padding: 22px 24px 14px; }
.xw-head h2 { margin: 0 0 3px; font-size: 20px; font-weight: 640; }
.xw-x { width: 30px; height: 30px; border-radius: var(--radius); border: 1px solid var(--line);
  background: var(--surface); color: var(--muted); font-size: 17px; line-height: 1; cursor: pointer; }
.xw-x:hover { color: var(--ink); }

.xw-steps { display: flex; gap: 6px; margin: 0; padding: 0 24px 16px; list-style: none; }
.xw-steps li { flex: 1; padding-top: 9px; border-top: 2px solid var(--line);
  font-size: 11.5px; color: var(--muted); }
.xw-steps li.on { border-color: var(--accent); color: var(--accent); font-weight: 560; }
.xw-steps li.done { border-color: var(--ok); color: var(--ok); }

.xw-body { flex: 1; min-height: 0; overflow-y: auto; padding: 4px 24px 20px; }
.xw-opt, .xw-radio { display: flex; gap: 12px; padding: 13px 14px; margin-bottom: 8px;
  border-radius: 11px; border: 1px solid var(--line); background: var(--surface);
  cursor: pointer; transition: border-color var(--t) var(--ease), background var(--t) var(--ease); }
.xw-opt:hover, .xw-radio:hover { border-color: var(--muted); }
.xw-opt input, .xw-radio input { margin-top: 2px; accent-color: var(--accent); flex: 0 0 auto; }
.xw-opt b, .xw-radio b { display: block; font-size: 13.5px; font-weight: 540; }
.xw-opt em, .xw-radio em { display: block; margin-top: 3px; font-style: normal;
  font-size: 12.5px; color: var(--muted); line-height: 1.55; }
.xw-opt:has(input:checked), .xw-radio:has(input:checked) {
  border-color: var(--accent); background: var(--accent-soft); }
.xw-radio.disabled { opacity: .55; cursor: default; }

.xw-group { margin: 0 0 16px; padding: 0; border: 0; }
.xw-group legend { padding: 0 0 8px; font-size: 11.5px; font-weight: 600;
  letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.xw-fine { margin-top: 6px; font-size: 12.5px; color: var(--muted); }

.xw-foot { display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 16px 24px; border-top: 1px solid var(--line);
  background: var(--surface-2); }

.xw-tiles { display: grid; gap: 10px; margin-bottom: 18px;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); }
.xw-tiles div { padding: 14px 16px; border-radius: 11px; border: 1px solid var(--line);
  background: var(--surface-2); }
.xw-tiles b { display: block; font-size: 20px; font-weight: 640; letter-spacing: -.01em; }
.xw-tiles span { font-size: 12px; color: var(--muted); }
.xw-tiles .bad b { color: #b23b3b; }
.xw-did { margin: 0 0 4px; padding-left: 20px; font-size: 13.5px; line-height: 1.75; }
.xw-h3 { margin: 24px 0 10px; font-size: 13px; font-weight: 600;
  letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.xw-guides { display: grid; gap: 8px; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
.xw-guide { display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 12px 14px; border-radius: 10px; border: 1px solid var(--line);
  background: var(--surface); font-size: 13.5px; text-decoration: none; color: var(--ink); }
.xw-guide:hover { border-color: var(--accent); color: var(--accent); }

@media (max-width: 620px) {
  #exportx, #captionx { padding: 0; align-items: stretch; }
  .xw { width: 100%; max-height: none; border-radius: 0; }
  .xw-steps li { font-size: 0; }
  .xw-steps li.on { font-size: 11.5px; }
}

/* ==========================================================================
   Clean up and library, third pass: spacing and controls that match the rest
   ========================================================================== */

/* Selects were the browser's own: square, grey, and with the arrow jammed
   against the text. One shared look, with room for the chevron drawn in. */
.cl-rules select, .cl-grule {
  appearance: none; -webkit-appearance: none;
  padding: 7px 34px 7px 13px; border-radius: 999px;
  border: 1px solid var(--line); background-color: var(--surface);
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' \
viewBox='0 0 24 24' fill='none' stroke='%236b6c73' stroke-width='2.2' stroke-linecap='round' \
stroke-linejoin='round'><path d='m6 9 6 6 6-6'/></svg>");
  background-repeat: no-repeat; background-position: right 11px center; background-size: 14px;
  font: inherit; font-size: 12.5px; color: var(--ink); cursor: pointer;
  transition: border-color var(--t) var(--ease), background-color var(--t) var(--ease); }
.cl-rules select:hover, .cl-grule:hover { border-color: var(--accent); }
.cl-rules select:focus-visible, .cl-grule:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px; }
.cl-grule { max-width: 210px; }

/* The group header had its controls hard against the corner. */
.cl-group { padding: 16px 18px 20px; }
.cl-group header { padding: 2px 2px 0; margin-bottom: 16px; row-gap: 10px; }
.cl-gtools { gap: 14px; flex-wrap: wrap; }

/* The stepper reads as one control rather than three loose pieces. */
.cl-keepn { gap: 0; padding: 2px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface); }
.cl-keepn b { padding: 0 12px; font-size: 12.5px; }
.cl-step { width: 26px; height: 26px; border: 0; border-radius: var(--radius);
  background: transparent; font-size: 15px; color: var(--muted); }
.cl-step:hover:not(:disabled) { background: var(--accent-soft); color: var(--accent); }

.cl-rules { padding: 16px 20px; row-gap: 14px; }
.cl-rules label { gap: 11px; }

/* ---------- Comparison controls on the image library ---------- */
.lib-bar { display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: 16px; margin: 18px 0 6px;
  padding: 14px 18px; border-radius: 12px; background: var(--surface);
  border: 1px solid var(--line); }
.lib-bar > p { margin: 0; }
.lib-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }
.lib-found { font-size: 12.5px; color: var(--muted); }

/* ---------- A notice that leads somewhere ---------- */
.nt-go { display: inline-flex; align-items: center; gap: 6px; margin-top: 9px;
  padding: 6px 12px; border-radius: 999px; border: 1px solid var(--accent);
  background: var(--accent); color: var(--accent-ink); font: inherit;
  font-size: 12.5px; font-weight: 540; cursor: pointer; }
.nt-go:hover { background: var(--accent-strong); border-color: var(--accent-strong); }
.nt-golink { display: block; margin-top: 4px; font-size: 11.5px; font-weight: 560;
  color: var(--accent); }

/* The `#explorer section { padding: 0 }` reset outranks any class selector, so
   a section that does want padding has to say so at the same specificity. */
#explorer .cl-group { padding: 16px 18px 20px; }
#explorer .cl-rules { padding: 16px 20px; }
#explorer .lib-bar { padding: 14px 18px; }

/* ---------- Structure report ---------- */
.rp-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 12px;
  margin: 16px 0 14px; }
#explorer pre.report { max-height: 60vh; overflow: auto; margin: 0;
  padding: 16px 18px; border-radius: 12px; border: 1px solid var(--line);
  background: var(--surface); font-size: 12px; line-height: 1.65;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  white-space: pre; color: var(--ink); }

/* ---------- What is in here ---------- */
.rp-lead { max-width: 68ch; margin: 18px 0 6px; font-size: 14px; line-height: 1.65; }
.rp-src { margin-top: 30px; padding-bottom: 8px; }
#explorer .rp-src { padding: 0; }
.rp-head h3 { margin: 0 0 2px; font-size: 17px; font-weight: 640; }
.rp-cards { display: grid; gap: 10px; margin: 16px 0 6px;
  grid-template-columns: repeat(auto-fit, minmax(136px, 1fr)); }
.rp-card { padding: 13px 15px; border-radius: 11px; border: 1px solid var(--line);
  background: var(--surface); }
.rp-card b { display: block; font-size: 19px; font-weight: 640; letter-spacing: -.01em; }
.rp-card span { font-size: 12px; color: var(--muted); }
.rp-card.quiet { background: var(--surface-2); }

.rp-h { margin: 26px 0 8px; font-size: 11.5px; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted); }
.rp-note { max-width: 70ch; margin-top: 8px; }
.rp-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.rp-table th { text-align: left; font-weight: 560; font-size: 11.5px; color: var(--muted);
  padding: 8px 12px; border-bottom: 1px solid var(--line); }
.rp-table td { padding: 9px 12px; border-bottom: 1px solid var(--line); vertical-align: top; }
.rp-table code { font-size: 12px; overflow-wrap: anywhere; }

.rp-chips { display: flex; flex-wrap: wrap; gap: 7px; }
.rp-chip { display: inline-flex; align-items: center; gap: 7px; padding: 5px 12px;
  border-radius: 999px; border: 1px solid var(--line); background: var(--surface);
  font-size: 12.5px; color: var(--muted); }
.rp-chip b { color: var(--ink); font-weight: 560; }
.rp-chip.odd { border-color: #f0e0bd; background: #fdf4e3; color: #8a6210; }
.rp-chip.odd b { color: #6a4708; }

.rp-more { margin-top: 10px; padding: 0; border: 0; background: none; font: inherit;
  font-size: 12.5px; color: var(--accent); cursor: pointer; }
.rp-more:hover { text-decoration: underline; }
.rp-hidden { margin-top: 10px; }

.rp-shapes { display: flex; flex-direction: column; gap: 6px; }
.rp-shape { border: 1px solid var(--line); border-radius: 10px; background: var(--surface); }
.rp-shape summary { padding: 10px 14px; cursor: pointer; font-size: 13px; }
.rp-shape pre { margin: 0; padding: 0 14px 14px; font-size: 11.5px; line-height: 1.6;
  overflow-x: auto; color: var(--muted); }

.rp-foot { margin: 40px 0 20px; padding: 20px 22px; border-radius: 12px;
  border: 1px solid var(--line); background: var(--surface-2); }
.rp-foot h4 { margin: 0 0 6px; font-size: 14px; font-weight: 620; }
.rp-foot p { max-width: 70ch; }

/* ---------- Sources as toggles ---------- */
.ex-source.off { opacity: .4; }
.ex-source.off span { text-decoration: line-through; }
.ex-source.on { background: none; color: var(--ink); font-weight: 400; }

/* ---------- Empty sections ---------- */
.ex-empty { max-width: 58ch; margin: 40px 0; padding: 26px 28px; border-radius: 13px;
  border: 1px dashed var(--line); background: var(--surface); }
.ex-empty h3 { margin: 0 0 8px; font-size: 16px; font-weight: 620; }
.ex-empty p { margin: 0; font-size: 13.5px; line-height: 1.65; }
.ex-empty .btn { margin-top: 16px; }

/* ---------- Date range ---------- */
button.ex-stat { display: block; width: 100%; text-align: left; cursor: pointer;
  font: inherit; }
button.ex-stat:hover { border-color: var(--accent); }
.ex-stat.set { border-color: var(--accent); background: var(--accent-soft); }
.ex-stat.set .ex-stat-v { color: var(--accent); }

.cal { position: absolute; z-index: 120; width: 316px; padding: 12px;
  border-radius: 14px; border: 1px solid var(--line); background: var(--surface);
  box-shadow: 0 22px 54px rgba(16, 18, 22, .2);
  animation: m-rise var(--t) var(--ease-out) both; }
.cal-presets { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px;
  padding-bottom: 12px; border-bottom: 1px solid var(--line); }
.cal-preset { padding: 5px 11px; border-radius: 999px; border: 1px solid var(--line);
  background: var(--surface); font: inherit; font-size: 12px; color: var(--muted);
  cursor: pointer; }
.cal-preset:hover { border-color: var(--accent); color: var(--accent); }
.cal-head { display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 8px; font-size: 13.5px; font-weight: 560; }
.cal-nav { width: 28px; height: 28px; border-radius: 8px; border: 1px solid var(--line);
  background: var(--surface); font-size: 16px; line-height: 1; color: var(--muted);
  cursor: pointer; }
.cal-nav:hover { color: var(--accent); border-color: var(--accent); }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.cal-dow { display: flex; align-items: center; justify-content: center; height: 24px;
  font-size: 11px; color: var(--muted); }
.cal-day { height: 34px; border: 0; border-radius: 8px; background: none;
  font: inherit; font-size: 12.5px; color: var(--muted); cursor: default; }
.cal-day.empty { visibility: hidden; }
/* A day with nothing in it is not worth picking, and saying so up front beats
   an empty result afterwards. */
.cal-day:disabled { opacity: .3; }
.cal-day.has { color: var(--ink); cursor: pointer; font-weight: 520; }
.cal-day.has:hover { background: var(--surface-2); }
.cal-day.in { background: var(--accent-soft); border-radius: 0; }
.cal-day.edge { background: var(--accent); color: var(--accent-ink); border-radius: 8px; }
.cal-foot { display: flex; align-items: center; justify-content: space-between;
  gap: 10px; margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--line);
  font-size: 12.5px; color: var(--muted); }

/* ---------- Workflow guides ---------- */
.flow-card p { margin: 8px 0 0; font-size: 13.5px; line-height: 1.6; }
.flow-why { max-width: 68ch; margin: 26px 0 8px; padding: 18px 22px; border-radius: 12px;
  background: var(--surface-2); border: 1px solid var(--line); }
.flow-why h3 { margin: 0 0 6px; font-size: 13px; font-weight: 620; }
.flow-why p { margin: 0; font-size: 14px; line-height: 1.65; }
.flow-warn { max-width: 68ch; margin: 14px 0 26px; }

.flow-step { display: flex; gap: 20px; padding: 28px 0; border-top: 1px solid var(--line); }
.flow-step:first-of-type { border-top: 0; }
.flow-n { flex: 0 0 38px; width: 38px; height: 38px; display: inline-flex;
  align-items: center; justify-content: center; border-radius: 50%;
  background: var(--accent-soft); color: var(--accent); font-weight: 640; font-size: 15px; }
.flow-body { min-width: 0; max-width: 68ch; }
.flow-body h2 { margin: 4px 0 10px; font-size: 19px; font-weight: 640; }
.flow-body p { margin: 0 0 12px; font-size: 14.5px; line-height: 1.7; }
.flow-list { margin: 0 0 12px; padding-left: 20px; font-size: 14.5px; line-height: 1.7; }
.flow-list li { margin-bottom: 8px; }
.flow-end { max-width: 68ch; margin: 10px 0 40px; padding: 22px 24px; border-radius: 12px;
  border: 1px solid #cdeadb; background: var(--ok-soft); }
.flow-end h3 { margin: 0 0 8px; font-size: 15px; font-weight: 620; color: var(--ok); }
.flow-end p { margin: 0 0 8px; font-size: 14px; line-height: 1.65; }

@media (max-width: 640px) {
  .flow-step { gap: 14px; padding: 22px 0; }
  .flow-n { flex-basis: 32px; width: 32px; height: 32px; font-size: 14px; }
}

/* ---------- describing pictures ----------

   Built on the export wizard's shell (.xw) rather than a second panel style,
   because they are the same kind of thing: a short sequence of decisions
   ending in something being written. Only the parts that do not exist there
   are new. */
.cx-field { display: grid; grid-template-columns: 92px 1fr; align-items: center;
  gap: 10px; margin-bottom: 8px; font-size: 13px; }
.cx-field span { color: var(--muted); }
.cx-field input { width: 100%; padding: 9px 11px; border-radius: 9px;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink);
  font: inherit; font-size: 13px; }
.cx-field input:focus { outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 16%, transparent); }

.cx-note { margin: 12px 0 0; padding: 10px 12px; border-radius: 9px; font-size: 13px;
  border: 1px solid var(--line); background: var(--surface-2); color: var(--ink); }
.cx-note.ok { border-color: color-mix(in srgb, var(--ok) 45%, var(--line)); }
.cx-note.warn { border-color: color-mix(in srgb, #b23b3b 40%, var(--line)); }

.cx-where { margin: 4px 0 12px; font-size: 13.5px; line-height: 1.6; color: var(--ink); }
.cx-where b { font-weight: 600; }

.cx-bar { height: 8px; border-radius: 999px; background: var(--surface-2);
  overflow: hidden; margin: 6px 0 10px; }
.cx-bar i { display: block; height: 100%; background: var(--accent);
  transition: width .25s ease; }
.cx-count { margin: 0 0 14px; font-size: 13px; color: var(--muted); }

/* The last description, so a run that is going wrong is visible while it runs
   rather than at the end of it. Fixed height so the panel does not jump as
   sentences of different lengths arrive. */
.cx-last { margin: 0 0 14px; padding: 12px 14px; border-radius: 10px;
  border: 1px solid var(--line); background: var(--surface-2); min-height: 62px;
  display: flex; align-items: center; }
.cx-last figcaption { font-size: 13.5px; line-height: 1.55; color: var(--ink); }

/* ---------- tooltips ----------

   One bubble on <body>, position:fixed. Fixed means no ancestor can clip it
   and no panel can stack above it, which is the entire reason it is not
   rendered inside the thing it explains. See tips.js. */
.tip-dot { display: inline-flex; align-items: center; justify-content: center;
  width: 15px; height: 15px; margin-left: 5px; border-radius: 50%;
  border: 1px solid var(--line); background: var(--surface-2); color: var(--muted);
  font-size: 10px; font-weight: 700; line-height: 1; cursor: help;
  vertical-align: middle; user-select: none; flex: 0 0 auto;
  transition: color .12s ease, border-color .12s ease, background .12s ease; }
.tip-dot:hover, .tip-dot:focus-visible { color: var(--surface); background: var(--accent);
  border-color: var(--accent); outline: none; }

.tip-bubble { position: fixed; z-index: 9999; pointer-events: none;
  padding: 9px 12px; border-radius: 9px; font-size: 12.5px; line-height: 1.5;
  background: #1b1e26; color: #f2f3f6; box-shadow: var(--shadow-lg);
  opacity: 0; transform: translateY(-3px); transition: opacity .13s ease, transform .13s ease; }
.tip-bubble.above { transform: translateY(3px); }
.tip-bubble.on { opacity: 1; transform: translateY(0); }
.tip-bubble::after { content: ""; position: absolute; left: var(--tip-ax, 50%);
  margin-left: -5px; border: 5px solid transparent; }
.tip-bubble:not(.above)::after { bottom: 100%; border-bottom-color: #1b1e26; }
.tip-bubble.above::after { top: 100%; border-top-color: #1b1e26; }

/* A label with its own explanation beside it, used in the panels. */
.tip-label { display: inline-flex; align-items: center; }

/* The paragraph that says what a panel is for, before it asks for anything. */
.cx-lede { margin: 0 0 14px; font-size: 13.5px; line-height: 1.65; color: var(--muted); }
.cx-lede b { color: var(--ink); font-weight: 560; }

/* ---------- AI tagging ----------

   The one feature here that changes what a photo library can do was a ghost
   button, which in this palette is muted text on nothing - indistinguishable
   from a disabled control. It gets a filled button and, until it has been
   used, a block that says what it is for. */
/* Solid, not a gradient. A gradient button is the same tell as a gradient
   headline word - it reads as decoration applied to make something feel
   premium. The icon already carries the meaning. */
.btn.ai { background: var(--accent); color: var(--accent-ink); border-color: transparent;
  box-shadow: var(--shadow-sm); display: inline-flex; align-items: center; gap: 7px; }
.btn.ai:hover { background: var(--accent-strong); }
.ai-spark { width: 15px; height: 15px; flex: 0 0 auto; }

.ai-promo { display: grid; grid-template-columns: minmax(0, 1fr) 260px; gap: 26px;
  align-items: center; margin: 0 0 16px; padding: 18px 22px;
  border-radius: var(--radius-lg); border: 1px solid var(--line);
  background: var(--tint); }
.ai-promo h3 { margin: 0 0 6px; font-size: 16.5px; font-weight: 640; letter-spacing: -.01em; }
.ai-promo p { margin: 0 0 6px; font-size: 13px; line-height: 1.55; color: var(--muted); }
.ai-promo p:last-child { margin-bottom: 0; }
.ai-promo b { color: var(--ink); font-weight: 560; }
.ai-promo-cta { display: flex; flex-direction: column; align-items: stretch; gap: 9px; }
.ai-promo-cta .btn { justify-content: center; }
.ai-fine { font-size: 12px; line-height: 1.5; color: var(--muted); text-align: center; }

.ai-strip { display: flex; align-items: center; justify-content: space-between; gap: 14px;
  margin: 0 0 16px; padding: 11px 14px; border-radius: var(--radius);
  border: 1px solid var(--line); background: var(--surface); font-size: 13px; }
.ai-strip span { display: inline-flex; align-items: center; gap: 8px; color: var(--muted); }
.ai-strip .ai-spark { color: var(--accent); }
.ai-strip.done { background: var(--surface-2); }

@media (max-width: 780px) {
  .ai-promo { grid-template-columns: 1fr; gap: 18px; }
  .ai-strip { flex-direction: column; align-items: stretch; }
}

/* ---------- tabs inside the pictures view ----------

   Clean up is not a separate place in the app, it is the second thing you do
   to the pictures you are already looking at. */
.ph-tabs { display: flex; gap: 4px; margin: 0 0 18px; border-bottom: 1px solid var(--line); }
.ph-tab { display: inline-flex; align-items: center; gap: 7px; padding: 9px 14px;
  border: 0; border-bottom: 2px solid transparent; margin-bottom: -1px;
  background: none; color: var(--muted); font: inherit; font-size: 13.5px;
  font-weight: 520; cursor: pointer; transition: color .12s ease, border-color .12s ease; }
.ph-tab:hover { color: var(--ink); }
.ph-tab.on { color: var(--accent); border-bottom-color: var(--accent); }
.ph-tab em { font-style: normal; font-size: 11.5px; padding: 1px 6px; border-radius: 999px;
  background: var(--surface-2); color: var(--muted); }
.ph-tab.on em { background: var(--accent-soft);
  background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--accent); }

/* ---------- choosing a route, and buying credits ---------- */
.cx-route { padding: 18px 20px; margin-bottom: 12px; border-radius: var(--radius-lg);
  border: 1px solid var(--line); background: var(--surface); }
.cx-route.pick { border-color: color-mix(in srgb, var(--accent) 40%, var(--line));
  background: var(--tint); }
.cx-route-head { display: flex; align-items: center; gap: 10px; margin-bottom: 7px; }
.cx-route h3 { margin: 0; font-size: 15.5px; font-weight: 620; }
.cx-route p { margin: 0 0 10px; font-size: 13px; line-height: 1.58; color: var(--muted); }
.cx-route b { color: var(--ink); font-weight: 560; }
.cx-badge { font-size: 11px; font-weight: 600; letter-spacing: .02em; padding: 3px 8px;
  border-radius: 999px; background: var(--accent-soft);
  background: color-mix(in srgb, var(--accent) 13%, transparent);
  color: var(--accent); }
.cx-badge.quiet { background: var(--surface-2); color: var(--muted); }
.cx-pledge-h { margin-bottom: 6px !important; }

/* The promises about what happens to a picture that leaves the machine. Set
   as a list rather than a paragraph because each line is a separate
   commitment and a wall of prose hides that. */
.cx-pledge { margin: 0 0 12px; padding: 0; list-style: none; }
.cx-pledge li { position: relative; padding-left: 21px; margin-bottom: 5px;
  font-size: 12.5px; line-height: 1.55; color: var(--muted); }
.cx-pledge li::before { content: ""; position: absolute; left: 2px; top: 6px;
  width: 9px; height: 5px; border-left: 2px solid var(--ok);
  border-bottom: 2px solid var(--ok); transform: rotate(-45deg); }

/* The exact price for this library, itemised. Laid out as a receipt rather
   than a set of tiles, because there is nothing to choose between - there is
   one price, and the only line worth explaining is the floor. */
.cx-quote { margin: 0 0 16px; padding: 14px 16px; border-radius: var(--radius);
  border: 1px solid var(--line);
  border-color: color-mix(in srgb, var(--accent) 32%, var(--line));
  background: var(--tint); }
.cx-quote-line { display: flex; justify-content: space-between; gap: 12px;
  font-size: 13px; color: var(--muted); padding: 2px 0; }
.cx-quote-line.floor { color: var(--ink); }
.cx-quote-total { display: flex; justify-content: space-between; align-items: baseline;
  gap: 12px; margin-top: 8px; padding-top: 9px; border-top: 1px solid var(--line);
  font-size: 13px; color: var(--muted); }
.cx-quote-total b { font-size: 21px; font-weight: 660; letter-spacing: -.01em; color: var(--ink); }
.cx-quote .btn { margin-top: 12px; }
.cx-quote-note { margin: 9px 0 0; font-size: 12px; line-height: 1.55; color: var(--muted); }

.cx-other { margin: 0 0 12px; font-size: 13px; }
.cx-other summary { cursor: pointer; color: var(--muted); padding: 6px 0; }
.cx-other summary:hover { color: var(--ink); }
.cx-other .cx-field { margin-top: 8px; }
.cx-other .btn { margin-top: 4px; }

.cx-redeem { margin: 0 0 4px; font-size: 13px; }
.cx-redeem summary { cursor: pointer; color: var(--muted); padding: 6px 0; }
.cx-redeem summary:hover { color: var(--ink); }
.cx-redeem .cx-field { margin-top: 8px; }

.xw-footl { display: flex; gap: 6px; }

/* ---------- the ask ----------

   Last thing in the export summary, after the destination guides, so it is
   never in the way of anything. Quiet on purpose: a loud panel here would be
   the one moment in the product that felt like it wanted something. */
.dn { margin: 26px 0 0; padding: 18px 20px; border-radius: var(--radius-lg);
  border: 1px solid var(--line); background: var(--surface-2); }
.dn h3 { margin: 0 0 6px; font-size: 15.5px; font-weight: 620; }
.dn p { margin: 0 0 12px; font-size: 13px; line-height: 1.6; color: var(--muted); }
.dn-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.dn-amt { min-width: 62px; justify-content: center; }
.dn-no { margin-left: auto; }
.dn-fine { margin: 10px 0 0 !important; font-size: 11.5px; }
@media (max-width: 520px) { .dn-no { margin-left: 0; } }

/* ---------- earning the benefit of the doubt ----------

   Nobody arrives believing "nothing is uploaded". They have been told that by
   sites that then uploaded everything, so the sentence carries no weight - it
   is exactly what a site that did upload would also say.

   Two things do carry weight. First, not having to trust us at all on the first
   run: the sample exports show the whole product working on invented people.
   Second, a test the reader performs themselves - turning the network off and
   watching everything keep working is proof they generate rather than proof we
   assert. So those get real space here, not a footnote. */
.trust { display: grid; gap: 14px; margin-top: 22px; }
.trust-try { padding: 20px 22px; border-radius: var(--radius-lg);
  border: 1px solid color-mix(in srgb, var(--accent) 32%, var(--line));
  background: var(--tint); }
.trust-try h2 { margin: 0 0 7px; font-size: 17px; font-weight: 640; letter-spacing: -.01em; }
.trust-try p { margin: 0 0 14px; font-size: 13.5px; line-height: 1.6; color: var(--muted); }

.trust-check { padding: 20px 22px; border-radius: var(--radius-lg);
  border: 1px solid var(--line); background: var(--surface); }
.trust-check h3 { margin: 0 0 6px; font-size: 15px; font-weight: 620; }
.trust-check > p { margin: 0 0 12px; font-size: 13.5px; line-height: 1.6; color: var(--muted); }
.trust-check ol { margin: 0 0 14px; padding-left: 20px; }
.trust-check li { margin-bottom: 10px; font-size: 13.5px; line-height: 1.62; color: var(--muted); }
.trust-check li strong { color: var(--ink); font-weight: 560; }
.trust-check .fine { margin: 0; }

/* Section rhythm from the reference set: 112px, which is roughly double what
   this page had. The single biggest difference between a designed page and an
   assembled one is how much air it is willing to leave. */
main > section.wrap { padding-top: var(--section); padding-bottom: 0; }
main > section.wrap:last-child { padding-bottom: var(--section); }
.hero.wrap { padding-top: 96px; padding-bottom: 96px; }
.section-head { max-width: 62ch; margin-bottom: 40px; }
.section-head h2 { font-size: clamp(28px, 3.4vw, 40px); letter-spacing: -.03em; }
.section-head p { margin-top: 14px; font-size: 17px; color: var(--muted); line-height: 1.6; }

@media (max-width: 720px) {
  :root { --section: 72px; --section-tight: 48px; }
  .hero.wrap { padding-top: 64px; padding-bottom: 64px; }
}

/* ---------- sorting by instruction ----------

   The strip lives in Clean up because that tab is already where keep-or-drop
   decisions happen. It does not replace the duplicate groups: a rule is good at
   "all screenshots" and useless at "which of these four near-identical shots is
   sharpest", so the two answer different questions and sit next to each other. */
.pl-strip { display: flex; align-items: center; justify-content: space-between; gap: 18px;
  margin: 0 0 18px; padding: 16px 18px; border-radius: var(--radius-lg);
  border: 1px solid var(--line); background: var(--tint); }
.pl-strip b { display: block; font-size: 14px; font-weight: 600; color: var(--ink); }
.pl-strip p { margin: 4px 0 0; font-size: 13px; line-height: 1.55; color: var(--muted); }
.pl-strip .btn { flex: 0 0 auto; }

.pl-text { width: 100%; padding: 12px 14px; border-radius: var(--radius);
  border: 1px solid var(--line); background: var(--surface); color: var(--ink);
  font: inherit; font-size: 14px; line-height: 1.55; resize: vertical; }
.pl-text:focus { outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft); }

.pl-egs { display: flex; flex-direction: column; align-items: flex-start; gap: 6px;
  margin: 12px 0 14px; font-size: 12.5px; }
.pl-egs > span { color: var(--muted); }
.pl-eg { text-align: left; font-size: 12.5px; line-height: 1.5; }

/* One entry per bucket: what it is called, where it goes, how many matched, the
   rule in words, and six actual pictures. The samples are the point - a count
   alone cannot show that "notes" also caught every photo of a whiteboard. */
.pl-buckets { list-style: none; margin: 0 0 14px; padding: 0; counter-reset: none; }
.pl-bucket { padding: 14px 16px; margin-bottom: 10px; border-radius: var(--radius);
  border: 1px solid var(--line); background: var(--surface); }
.pl-bucket.out { border-color: color-mix(in srgb, #b23b3b 30%, var(--line));
  background: #fdf8f8; }
.pl-bhead { display: flex; align-items: center; gap: 9px; }
.pl-bhead b { font-size: 14px; font-weight: 600; color: var(--ink); }
.pl-bhead em { margin-left: auto; font-style: normal; font-size: 12.5px; color: var(--muted); }
.pl-rule { margin: 7px 0 0; font-size: 12.5px; line-height: 1.55; color: var(--muted); }
.pl-samples { display: flex; gap: 6px; margin-top: 11px; align-items: center; }
.pl-thumb { width: 46px; height: 46px; flex: 0 0 auto; border-radius: var(--radius-sm);
  background: var(--surface-2) center / cover no-repeat; border: 1px solid var(--line); }
.pl-thumb.nodecode { background: var(--line-2); }
.pl-more { font-size: 12px; color: var(--muted); font-family: var(--mono); }
.pl-empty { margin: 9px 0 0; font-size: 12.5px; color: var(--muted); }

@media (max-width: 620px) {
  .pl-strip { flex-direction: column; align-items: stretch; }
}

/* ---------- sorting by instruction, on the home page ----------

   Given its own section rather than a card in a grid. It is the one thing here
   that does not exist elsewhere, and a quarter of a four-up grid is where
   features go to be overlooked. The demo is markup rather than a screenshot so
   it stays legible at any width and cannot go stale. */
.prompt-feat { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 64px; align-items: center; }
.prompt-copy h2 { font-size: clamp(28px, 3.4vw, 40px); letter-spacing: -.03em; }
.prompt-copy .h-sub { margin-top: 16px; }
.prompt-copy p { margin-top: 14px; font-size: 15.5px; line-height: 1.65; color: var(--muted); }
.prompt-copy .fine { margin-top: 18px; }

.prompt-demo { margin: 0; display: grid; gap: 14px; }
.prompt-label { display: block; margin-bottom: 8px; font-family: var(--mono);
  font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.prompt-said { padding: 18px 20px; border-radius: var(--radius-lg);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--line)); background: var(--tint); }
.prompt-said p { margin: 0; font-size: 15px; line-height: 1.6; color: var(--ink); }
.prompt-plan { padding: 18px 20px; border-radius: var(--radius-lg);
  border: 1px solid var(--line); background: var(--surface); }
.prompt-plan ul { list-style: none; margin: 0; padding: 0; }
.prompt-plan li { display: flex; align-items: center; gap: 10px; padding: 9px 0;
  border-bottom: 1px solid var(--line-2); font-size: 13.5px; }
.prompt-plan li:last-child { border-bottom: 0; }
.prompt-plan b { color: var(--ink); font-weight: 560; min-width: 8.5em; }
.prompt-plan em { font-style: normal; font-family: var(--mono); font-size: 11px;
  padding: 2px 7px; border-radius: 3px; background: var(--surface-2);
  color: var(--muted); letter-spacing: .02em; }
.prompt-plan span { margin-left: auto; color: var(--muted); font-size: 12.5px; }
.prompt-plan li.out b { color: var(--muted); }
.prompt-plan li.out em { background: #fdf1f1; color: #a03535; }

@media (max-width: 900px) {
  .prompt-feat { grid-template-columns: 1fr; gap: 36px; }
}

/* The sample-data notice. Amber rather than the green of the privacy panel it
   sits above, because it is saying something different: not "you are safe" but
   "this is not yours". */
.ex-demo { margin: 0 14px 12px; padding: 14px 15px; border-radius: var(--radius);
  border: 1px solid #f0dcae; background: #fdf8ec; }
.ex-demo strong { display: block; font-size: 13px; color: #7a5a12; margin-bottom: 5px; }
.ex-demo p { margin: 0 0 11px; font-size: 12px; line-height: 1.55; color: #8a6a22; }
.ex-demo .btn { width: 100%; justify-content: center; }

/* ==========================================================================
   Forget this library
   ========================================================================== */
#forgetx { position: fixed; inset: 0; z-index: 400; display: flex;
  align-items: center; justify-content: center; padding: 28px; }
#forgetx .xw { width: min(560px, 100%); }

.fx-list { list-style: none; margin: 0 0 20px; padding: 0; }
.fx-list li { display: grid; grid-template-columns: 132px minmax(0, 1fr); gap: 14px;
  padding: 11px 0; border-top: 1px solid var(--line-2); font-size: 14px; line-height: 1.55; }
.fx-list li:first-child { border-top: 0; }
.fx-list b { color: var(--ink); font-weight: 600; }
.fx-list span { color: var(--body); }

.fx-keep { margin: 0 0 10px; font-size: 14px; font-weight: 600; color: var(--ink); }
.fx-outs { display: flex; gap: 8px; flex-wrap: wrap; }
.fx-note { margin-top: 10px; }

/* Indeterminate, because clearing a store gives no count to count towards.
   It says work is happening and names the stage, which is what was missing. */
.fx-bar { margin-top: 18px; }
.fx-bar[hidden] { display: none; }
.fx-bar i { display: block; height: 4px; border-radius: 2px; overflow: hidden;
  background: var(--line); position: relative; }
.fx-bar i::after { content: ""; position: absolute; inset: 0 60% 0 0;
  border-radius: 2px; background: var(--accent);
  animation: fx-slide 1.1s ease-in-out infinite; }
@keyframes fx-slide { 0% { left: -40%; right: 100%; } 60%, 100% { left: 100%; right: -40%; } }
.fx-bar span { display: block; margin-top: 8px; font-size: 13px; color: var(--muted); }
@media (prefers-reduced-motion: reduce) { .fx-bar i::after { animation: none; inset: 0; } }

.btn.danger { background: #b23b3b; border-color: #b23b3b; color: #fff; }
.btn.danger:hover { background: #9c3232; border-color: #9c3232; }
.btn.danger:disabled { opacity: .6; cursor: default; }

@media (max-width: 560px) { .fx-list li { grid-template-columns: 1fr; gap: 3px; } }

/* A concept explained here rather than linked away to.

   Sending someone to a third-party blog to learn what "3-2-1" means costs them
   the page they were reading, and puts the answer somewhere we cannot keep
   correct. Brief, in place, and it stays ours. */
.explain { margin: 0 0 26px; padding: 16px 18px; border: 1px solid var(--line);
  border-left: 3px solid var(--ink); border-radius: var(--radius); background: var(--surface-2); }
.explain h2 { margin: 0 0 6px; font-size: 15.5px; font-weight: 640; }
.explain p { margin: 0; font-size: 14.5px; line-height: 1.6; color: var(--body); }

/* The pitfalls specific to doing a whole job end to end, as opposed to the
   ones inside either half of it. */
.flow-stumbles { margin: 26px 0; }
.flow-stumbles h3 { margin: 0 0 10px; font-size: 17px; font-weight: 640; }
.flow-stumbles dl { margin: 0; border-top: 1px solid var(--line); }
.flow-stumbles dt { padding: 14px 0 0; font-weight: 600; color: var(--ink); font-size: 15px; }
.flow-stumbles dd { margin: 4px 0 0; padding: 0 0 14px; border-bottom: 1px solid var(--line-2);
  font-size: 14.5px; line-height: 1.6; color: var(--body); }

/* One radius for anything clickable.

   The nav's Open an export button is the reference. Buttons elsewhere had
   arrived at 999px, 9px, 8px and 7px depending on when they were written, so a
   page could show three different roundnesses at once. Everything that behaves
   like a button now takes --radius, and the few genuinely round things - a
   status dot, an avatar - are round because they are circles, not buttons. */
.btn, .sw-btn, .pill, .ex-add, .cl-step, .xw-x, .ex-nav-btn, .ex-forget,
.ex-savework, .nt-bell, .linklike.boxed { border-radius: var(--radius); }

/* "(free)" beside Pricing: a note, not a shout. */
.nav-free { font-style: normal; font-size: 12.5px; color: var(--muted); }
.nav-links a:hover .nav-free, .nav-links a.active .nav-free { color: inherit; }

/* The questions people ask before they trust a page enough to follow it.
   Collapsed, because someone who already knows should not have to scroll past
   six answers to reach the guides. */
.faq { border-top: 1px solid var(--line); }
.faq-item { border-bottom: 1px solid var(--line-2); }
.faq-item summary { cursor: pointer; padding: 15px 0; font-size: 16px;
  font-weight: 560; color: var(--ink); list-style: none; display: flex;
  align-items: center; justify-content: space-between; gap: 16px; }
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after { content: "+"; font-size: 19px; color: var(--muted);
  font-weight: 400; line-height: 1; flex: none; }
.faq-item[open] summary::after { content: "-"; }
.faq-item summary:hover { color: var(--accent); }
.faq-item p { margin: 0 0 16px; padding-right: 36px; font-size: 15px;
  line-height: 1.65; color: var(--body); max-width: 78ch; }
