/*
 * HighMemo site — shared stylesheet.
 *
 * WHY A SHARED FILE AND NOT INLINE CSS: web/privacy.html keeps its CSS inline because it is a
 * single published legal page that must never change shape when anything else does. The four
 * pages added around it (landing + three guides) are the opposite case — they share a header,
 * footer, callouts and a screenshot layout, and five inline copies of that would drift within a
 * release. This is still a static site: one plain .css file, no build step, no external fonts.
 *
 * The palette is NOT invented here. Light/dark values come from src/presentation/theme/themes.ts
 * (`lightTheme` / `darkTheme` in appChrome) and the sage-green brand ramp comes from
 * assets/icon.svg. The link accent matches web/privacy.html so the published policy and these
 * pages read as one site.
 */

:root {
  color-scheme: light dark;

  /* Brand — assets/icon.svg bgGradient stops */
  --sage-light: #a3c392;
  --sage: #88ad83;
  --sage-deep: #657f63;

  /* Light theme — themes.ts lightTheme */
  --bg: #ffffff;
  --bg-alt: #f2f2f7;          /* lightTheme.surface.backdrop */
  --panel: #fbfbfd;
  --fg: #1c1c1e;              /* lightTheme.text.primary */
  --fg-soft: rgba(44, 44, 46, 0.92);
  --muted: rgba(60, 60, 67, 0.72);
  --rule: rgba(0, 0, 0, 0.14); /* lightTheme.border.subtle */
  --rule-strong: rgba(0, 0, 0, 0.24);
  /*
   * 4.08:1 on white was below WCAG AA (4.5:1) and every body link in the three guides used it —
   * 3.84:1 on `--bg-alt`, worse. Raised to 5.39 / 5.06. The dark value was already 9.01:1.
   *
   * `privacy.html` carries this same value in its own inline CSS, so both move together or the
   * privacy page ends up a different blue from the rest of the site.
   */
  --accent: #2569c4;          /* matches privacy.html — change both together */
  --code-bg: rgba(0, 0, 0, 0.05);

  --note-bg: rgba(0, 122, 255, 0.06);
  --note-border: rgba(0, 122, 255, 0.28);
  --warn-bg: rgba(255, 149, 0, 0.1);
  --warn-border: rgba(255, 149, 0, 0.42);
  --shot-bg: #f2f2f7;

  --radius: 12px;             /* appChrome radius.md */
  --radius-lg: 16px;          /* appChrome radius.lg */
  --measure: 46rem;
}

@media (prefers-color-scheme: dark) {
  :root {
    /* Dark theme — themes.ts darkTheme, with privacy.html's page background kept for continuity */
    --bg: #111111;
    --bg-alt: #0b0b0b;        /* darkTheme.surface.backdrop */
    --panel: #1d1d1f;
    --fg: #efefef;            /* darkTheme.text.primary */
    --fg-soft: rgba(226, 226, 226, 0.96);
    --muted: rgba(188, 188, 188, 0.9);
    --rule: rgba(255, 255, 255, 0.16);
    --rule-strong: rgba(255, 255, 255, 0.26);
    --accent: #5dbbff;
    --code-bg: rgba(255, 255, 255, 0.08);

    --note-bg: rgba(72, 167, 255, 0.1);
    --note-border: rgba(72, 167, 255, 0.34);
    --warn-bg: rgba(255, 196, 89, 0.12);
    --warn-border: rgba(255, 196, 89, 0.44);
    --shot-bg: #191919;
  }
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font: 16px/1.65 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
        "Helvetica Neue", "Apple SD Gothic Neo", "Malgun Gothic", sans-serif;
  overflow-wrap: break-word;
}

/*
 * `height: auto` is not cosmetic here — without it the screenshots were STRETCHED.
 *
 * Every `<img>` carries `width`/`height` attributes (they reserve layout space and stop the page
 * from jumping as images load). Those attributes set the used height, so `max-width: 100%` shrank
 * the width alone and left the height at its declared 880px: the hero shot rendered 230×880 from a
 * 405×880 source — squeezed 43% horizontally — and the grid shots 335×880. A phone screenshot
 * distorted that way reads as a broken image, which is exactly how it was reported.
 */
img, svg { max-width: 100%; height: auto; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
a:focus-visible,
button:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }

/* ---------------------------------------------------------------- site chrome */

.site-head {
  border-bottom: 1px solid var(--rule);
  background: var(--bg);
  position: sticky;
  top: 0;
  z-index: 10;
}
.site-head-in {
  max-width: 62rem;
  margin: 0 auto;
  padding: .7rem 1.25rem;
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
}
.brand {
  display: inline-flex;
  align-items: center;
  gap: .55rem;
  font-weight: 700;
  font-size: 1.02rem;
  letter-spacing: -0.01em;
  color: var(--fg);
}
.brand:hover { text-decoration: none; }
.brand img { width: 26px; height: 26px; border-radius: 6px; }
.site-nav {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: .25rem 1.1rem;
  flex-wrap: wrap;
  font-size: .92rem;
}
.site-nav a { color: var(--muted); }
.site-nav a[aria-current="page"] { color: var(--fg); font-weight: 600; }

main { max-width: var(--measure); margin: 0 auto; padding: 2.5rem 1.25rem 4rem; }
main.wide { max-width: 62rem; }

footer.site-foot {
  border-top: 1px solid var(--rule);
  background: var(--bg-alt);
  color: var(--muted);
  font-size: .86rem;
}
.site-foot-in {
  max-width: 62rem;
  margin: 0 auto;
  padding: 2rem 1.25rem 2.5rem;
  display: flex;
  gap: .5rem 1.5rem;
  flex-wrap: wrap;
  align-items: baseline;
}
.site-foot a { color: var(--muted); text-decoration: underline; }
.site-foot-links { margin-left: auto; display: flex; gap: .25rem 1.2rem; flex-wrap: wrap; }

/* ---------------------------------------------------------------- typography */

h1 {
  font-size: 1.95rem;
  line-height: 1.2;
  letter-spacing: -0.025em;
  margin: 0 0 .6rem;
}
h2 {
  font-size: 1.28rem;
  line-height: 1.3;
  letter-spacing: -0.015em;
  margin: 3rem 0 .7rem;
  padding-top: 1.6rem;
  border-top: 1px solid var(--rule);
}
h2:first-of-type { border-top: 0; padding-top: 0; }
h3 {
  font-size: 1.04rem;
  line-height: 1.35;
  letter-spacing: -0.01em;
  margin: 2rem 0 .5rem;
}
h4 {
  font-size: .95rem;
  margin: 1.4rem 0 .4rem;
}
p, li { margin: 0 0 .9rem; }
ul, ol { padding-left: 1.3rem; margin: 0 0 1rem; }
li > ul, li > ol { margin-top: .4rem; }
strong { font-weight: 600; }

.lede { font-size: 1.06rem; color: var(--fg-soft); }
.sub { color: var(--muted); font-size: .92rem; }

code {
  background: var(--code-bg);
  padding: .12em .38em;
  border-radius: 4px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .88em;
  overflow-wrap: anywhere;
}
kbd {
  background: var(--code-bg);
  border: 1px solid var(--rule-strong);
  border-bottom-width: 2px;
  border-radius: 5px;
  padding: .06em .38em;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .85em;
}

/*
 * Where in the UI something lives. Used constantly in the guides, so it gets a token.
 * It must WRAP: these strings are long ("Settings → Local AI → Semantic Search Model") and a
 * nowrap version pushed the page 49px wider than a 280px viewport, which is the one failure this
 * site is not allowed to have. Breaking mid-path is ugly; a horizontally scrolling page is worse.
 */
.path {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .86em;
  color: var(--fg-soft);
  background: var(--code-bg);
  padding: .1em .4em;
  border-radius: 4px;
  overflow-wrap: anywhere;
}

/* ---------------------------------------------------------------- tables */

.table-wrap { overflow-x: auto; margin: 0 0 1.4rem; -webkit-overflow-scrolling: touch; }
table {
  border-collapse: collapse;
  width: 100%;
  min-width: 32rem;
  font-size: .93rem;
}
th, td {
  text-align: left;
  vertical-align: top;
  padding: .55rem .7rem;
  border-bottom: 1px solid var(--rule);
}
th { font-weight: 600; white-space: nowrap; }
thead th { border-bottom: 1px solid var(--rule-strong); }
tbody tr:last-child td { border-bottom: 0; }

/* ---------------------------------------------------------------- callouts */

.note, .warn {
  border: 1px solid var(--note-border);
  background: var(--note-bg);
  border-radius: var(--radius);
  padding: .9rem 1rem;
  margin: 0 0 1.4rem;
  font-size: .95rem;
}
.warn { border-color: var(--warn-border); background: var(--warn-bg); }
.note > :last-child, .warn > :last-child { margin-bottom: 0; }
.note-title, .warn-title {
  display: block;
  font-weight: 600;
  margin-bottom: .3rem;
  font-size: .9rem;
  letter-spacing: .01em;
}

/* Platform availability badge, inline in headings and lists. */
.plat {
  display: inline-block;
  font-size: .7rem;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  padding: .15em .5em;
  border-radius: 999px;
  border: 1px solid var(--rule-strong);
  color: var(--muted);
  vertical-align: .12em;
  white-space: nowrap;
}
/*
 * The light-mode text colour here is NOT --sage-deep, and that is deliberate. This badge paints its
 * own pale sage chip, so the text is measured against the chip and not against the page: #657f63 on
 * that composite is 3.94:1, under the 4.5:1 floor, and the badge is 0.7rem so it counts as body text
 * rather than large text. #4f6b4d measures 5.29:1 on the chip and 5.91:1 on bare white. The token is
 * left alone because --sage-deep is also the hero fill and the primary button, where it is a
 * background rather than text.
 * Dark mode keeps --sage-light: 7.85:1 on the chip over #111.
 */
.plat.on { border-color: var(--sage); color: #4f6b4d; background: rgba(136, 173, 131, 0.14); }
.plat.off { border-color: var(--rule-strong); }
@media (prefers-color-scheme: dark) {
  .plat.on { color: var(--sage-light); }
}

/* ---------------------------------------------------------------- hero */

/*
 * The hero art (img/hero-bg.jpg) is _workspace/promo/assets/concept/hero_bg_2400x1200.png, made
 * for exactly this: no text is baked into it, and its left half is empty so the headline can be
 * HTML. Do not re-render the words into the image — they would stop being translatable,
 * selectable, resizable and theme-aware, and every copy change would become an image change.
 *
 * The art is one fixed green in both themes, so the hero carries its own light-on-dark colours
 * instead of the page tokens. The dark scrim is not decoration: white on the bare art measures
 * about 3:1, which is under the 4.5:1 body-text floor. The scrim takes the text area to ~11:1.
 * Narrow screens get a flat scrim because the single-column text spans the lighter right side too.
 */
.hero {
  background-color: var(--sage-deep);
  background-image:
    linear-gradient(100deg, rgba(17, 28, 15, 0.64) 0%, rgba(17, 28, 15, 0.52) 46%, rgba(17, 28, 15, 0.26) 100%),
    url("./img/hero-bg.jpg");
  background-size: auto, cover;
  background-position: center, center right;
  background-repeat: no-repeat;
  border-bottom: 1px solid rgba(0, 0, 0, 0.28);
}
.hero h1 { color: #ffffff; }
.hero .btn {
  background: rgba(255, 255, 255, 0.16);
  border-color: rgba(255, 255, 255, 0.55);
  color: #ffffff;
}
.hero .btn:hover { background: rgba(255, 255, 255, 0.26); border-color: #ffffff; }
.hero .btn-primary { background: #ffffff; border-color: #ffffff; color: #1b2a18; }
.hero .btn-primary:hover { background: #ebefe8; border-color: #ebefe8; color: #1b2a18; }
.hero-in {
  max-width: 62rem;
  margin: 0 auto;
  padding: 3.5rem 1.25rem 3rem;
  display: grid;
  gap: 2.5rem;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr);
  align-items: center;
}
.hero h1 { font-size: 2.3rem; letter-spacing: -0.03em; }
.hero .tagline {
  font-size: 1.12rem;
  color: rgba(255, 255, 255, 0.95);
  margin: 0 0 1.1rem;
  max-width: 32rem;
}
.hero .promise {
  font-size: .98rem;
  color: rgba(255, 255, 255, 0.84);
  max-width: 32rem;
}
.hero-shot { justify-self: center; }
.hero-shot img {
  width: 100%;
  max-width: 300px;
  border-radius: 22px;         /* appChrome radius.xl */
  border: 1px solid rgba(255, 255, 255, 0.5);
  background: var(--shot-bg);
  box-shadow: 0 18px 44px rgba(0, 0, 0, 0.3);
  display: block;
}

@media (max-width: 46rem) {
  .hero {
    background-image:
      linear-gradient(rgba(17, 28, 15, 0.6), rgba(17, 28, 15, 0.6)),
      url("./img/hero-bg.jpg");
  }
  .hero-in { grid-template-columns: minmax(0, 1fr); gap: 2rem; padding: 2.5rem 1.25rem; }
  .hero h1 { font-size: 1.85rem; }
  .hero-shot { order: -1; }
  .hero-shot img { max-width: min(72vw, 280px); }
}

/* ---------------------------------------------------------------- buttons */

.btn-row { display: flex; flex-wrap: wrap; gap: .7rem; margin: 1.6rem 0 .6rem; }
.btn {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  padding: .62rem 1.1rem;
  border-radius: 999px;
  border: 1px solid var(--rule-strong);
  font-size: .94rem;
  font-weight: 600;
  color: var(--fg);
  background: var(--panel);
  cursor: pointer;
}
.btn:hover { text-decoration: none; border-color: var(--fg); }
.btn-primary {
  background: var(--sage-deep);
  border-color: var(--sage-deep);
  color: #ffffff;
}
.btn-primary:hover { background: #56704f; border-color: #56704f; }
@media (prefers-color-scheme: dark) {
  .btn-primary { background: var(--sage); border-color: var(--sage); color: #10160f; }
  .btn-primary:hover { background: var(--sage-light); border-color: var(--sage-light); }
}

/*
 * Disabled download control. It is a <button disabled>, not a styled <a>: the macOS release is a
 * draft, so there is no URL that would not 404, and a link element would still be focusable,
 * copyable and followable by a crawler. The reason is always printed next to it — a dead button
 * with no explanation reads as a broken page.
 */
.btn[disabled] {
  cursor: not-allowed;
  opacity: .55;
  color: var(--muted);
  background: var(--panel);
  border-color: var(--rule);
  border-style: dashed;
}
.btn[disabled]:hover { border-color: var(--rule); }

/* ---------------------------------------------------------------- feature blocks */

.features { display: grid; gap: 1.5rem 2rem; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); margin: 1.5rem 0 0; }
.feature h3 { margin-top: 0; }
.feature p { color: var(--fg-soft); font-size: .95rem; }

/* ---------------------------------------------------------------- screenshots */

figure.shot {
  margin: 1.6rem 0;
  padding: 0;
}
figure.shot img {
  display: block;
  width: 100%;
  max-width: 320px;
  border-radius: var(--radius-lg);
  border: 1px solid var(--rule-strong);
  background: var(--shot-bg);
}
figure.shot figcaption {
  font-size: .88rem;
  color: var(--muted);
  margin-top: .6rem;
  max-width: 32rem;
}
.shot-grid {
  display: grid;
  gap: 1.6rem 1.4rem;
  /*
   * `minmax(13rem, …)` let `auto-fit` add a column as soon as 208px fit, so a WIDER page made the
   * screenshots SMALLER: measured 229px at 480px of container, 218px at 700px (3 columns), 238px at
   * 1280px (5 columns) — never more than 0.65 of the image's own width at any size. A phone
   * screenshot at 220px has UI text at a sixth of its real size; it is a picture of a screen rather
   * than something you can read.
   *
   * Raising the floor to 17rem caps the row at three columns inside `max-width`, and `min(100%, …)`
   * is what lets it still collapse to one column on a narrow screen without overflowing.
   */
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
  max-width: 60rem;
  margin: 1.5rem 0 2rem;
}
.shot-grid figure.shot { margin: 0; }
.shot-grid figure.shot img { max-width: 100%; }

/*
 * ---------------------------------------------------------------- feature spotlight
 *
 * One screenshot beside the paragraphs that explain it (`.spot` on the landing page's #location
 * section). Flexbox rather than grid, and that is the whole reason this rule exists in this shape:
 * the figure is inside an HTML comment until its file is committed, and a two-column grid would
 * hold a 320px column open for an element that is not there, leaving a visible empty gutter beside
 * the text. With flex the copy simply takes the row on its own, so the section is presentable
 * before the screenshot lands and needs no CSS change when it does.
 *
 * `min-width: 0` on the copy is not decorative either — a flex item defaults to its min-content
 * width, and one long `.path` string is enough to push the row wider than the page.
 */
.spot {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem 2rem;
  align-items: flex-start;
  margin: 1.5rem 0 2rem;
}
.spot > figure.shot { margin: 0; flex: 0 0 320px; max-width: 100%; }
/*
 * `max-width` is what keeps this readable in BOTH states. Beside the screenshot the copy lands near
 * 600px on a wide window, which is fine; with the figure still commented out it took the whole
 * 952px row and the paragraphs ran to roughly 150 characters a line. The cap makes the interim
 * state look deliberate instead of unfinished, and changes nothing once the figure is wired.
 */
.spot > .spot-copy { flex: 1 1 20rem; min-width: 0; max-width: 34rem; }
.spot > .spot-copy > :last-child { margin-bottom: 0; }

/*
 * ---------------------------------------------------------------- light/dark pair
 *
 * Two screenshots of the same screen in the two themes, both always rendered.
 *
 * The tempting alternative is a `prefers-color-scheme` swap on a single figure, and the page does
 * use that in `.shot-grid`. It is wrong here: a visitor in light mode would be served the light cut
 * and would never learn a dark theme exists — on the one block whose only job is to show that it
 * does. Auto-switching hides exactly what is being advertised.
 *
 * `max-width: 44rem` is what keeps the two frames readable. At two columns inside it each image
 * lands near 320px, the same size the `.shot-grid` figures reach at three columns, so the pair does
 * not become the small-print version of the gallery above it. `min(100%, 15rem)` is what lets the
 * row collapse to one column on a narrow screen instead of overflowing it.
 */
.pair {
  display: grid;
  gap: 1.2rem 1.4rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
  max-width: 44rem;
  margin: 1.5rem 0 1rem;
}
.pair figure.shot { margin: 0; }
/* The 320px cap belongs to a lone figure; inside the pair the column already limits the width. */
.pair figure.shot img { max-width: 100%; }
/*
 * The pair's captions are labels ("Light", "Dark"), not sentences, so they are set apart from the
 * descriptive `figure.shot figcaption` above rather than inheriting its body-text look.
 */
.pair figure.shot figcaption {
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--muted);
  margin-top: .5rem;
}

/*
 * ---------------------------------------------------------------- wide (Mac) screenshots
 *
 * The Mac captures are 1440 × 960 — a 3:2 LANDSCAPE frame, where every other screenshot on this
 * site is a 405 × 880 or 396 × 880 phone frame.
 *
 * They must not be put in `.shot-grid` or `.pair`. Those lay out columns around 320px because that
 * is a readable width for a tall phone frame; a 3:2 window at 320px is 213px tall, and the Mac
 * window's whole subject is that three panels have content in them at once. 04_visual_manifest.md
 * §14-6 argues the same thing from the other side and is why the files were written at 1440 rather
 * than the phone set's long-edge-880 rule.
 *
 * So `.wide` lifts the 320px cap and lets the figure take the column: ~950px on the landing page,
 * ~700px in a guide, against a 1440px file — still oversampled, the way the phone cuts are (405
 * file / 320 shown). `height: auto` from the global `img` rule does the scaling; the width/height
 * attributes stay on the tag because they reserve the space and stop the page jumping.
 */
figure.shot.wide { max-width: 100%; }
figure.shot.wide img { max-width: 100%; }
/* Stacked wide figures need the caption clear of the next frame. */
figure.shot.wide + figure.shot.wide { margin-top: 2rem; }

/*
 * A `.shot-todo` rule used to live here: a loud dashed box for a platform whose screenshots had
 * not been captured. Android is now shot (11 cuts) and the Mac guide has its own captures as of
 * 2026-07-31 (visual-manifest §14), so nothing uses it and dead CSS in a hand-maintained file is a
 * trap. If a future page needs a placeholder again, take it back out of git history rather than
 * guessing at it.
 */

/* ---------------------------------------------------------------- guide index */

.toc {
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--bg-alt);
  padding: 1rem 1.2rem;
  margin: 1.8rem 0 2.4rem;
}
.toc h2 {
  font-size: .8rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--muted);
  border: 0;
  padding: 0;
  margin: 0 0 .6rem;
}
.toc ol { margin: 0; padding-left: 1.2rem; columns: 2; column-gap: 2rem; font-size: .93rem; }
.toc li { margin: 0 0 .35rem; break-inside: avoid; }
@media (max-width: 34rem) { .toc ol { columns: 1; } }

/* Cross-platform guide switcher at the top of each manual. */
.guide-switch { display: flex; flex-wrap: wrap; gap: .5rem; margin: 1.4rem 0 2rem; }
.guide-switch a {
  padding: .4rem .85rem;
  border-radius: 999px;
  border: 1px solid var(--rule);
  font-size: .9rem;
  color: var(--muted);
}
.guide-switch a:hover { text-decoration: none; border-color: var(--fg); color: var(--fg); }
.guide-switch a[aria-current="page"] {
  border-color: var(--sage);
  background: rgba(136, 173, 131, 0.16);
  color: var(--fg);
  font-weight: 600;
}

/* Anchored section headings get a quiet self-link on hover. */
.anchor { color: var(--muted); font-weight: 400; text-decoration: none; opacity: 0; padding-left: .4rem; }
h2:hover .anchor, h3:hover .anchor, .anchor:focus-visible { opacity: 1; }

hr { border: 0; border-top: 1px solid var(--rule); margin: 2.5rem 0; }

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