/* The product page, on the same document as the scanner.
   ---------------------------------------------------------------------------
   The app and the page it is described on are one URL now, so these rules and
   the app's own share a document. That makes two things load-bearing, and both
   were measured rather than assumed before this file was written:

   1. EVERY CLASS HERE IS PREFIXED `site-`. Unprefixed, the two sheets collided
      on seven names — btn, card, dot, grid, hint, prefix, primary — all of
      which the scanner uses. The marketing sheet loads second, so it would
      have quietly won, and the app's buttons would have taken marketing
      padding.

   2. NO BARE ELEMENT SELECTORS. The previous version styled a, body, code,
      footer, h1, h2, html and section directly; on a shared document each of
      those reaches into the app. Everything here is scoped under `.site-copy`,
      `.site-header` or `.site-footer`.

   `e2e/scan.spec.mjs` asserts the overlap is still zero, because the next
   person to add `.card` here will not read this comment.

   Colour comes from the semantic token aliases only — the same set the app is
   built from, so the page and the product cannot drift into two palettes. */

/* On :root, not on the three section wrappers. Declared there, `.site-wrap`
   inside `.site-hero` — which is none of them — resolved `var(--site-page)` to
   nothing, `max-width` fell back to `none`, and the hero ran the full width of
   a 1440px window while every other section sat at 1040. Custom properties
   inherit; they do not reach sideways. */
:root {
  --site-measure: 68ch;
  --site-page: 1040px;

  /* The header's column, shared with every other site in the suite: 1200
     capped, 48px gutters, 64px tall. `--site-page` stays for prose. */
  --container-max: 1200px;
  --gutter-page-lg: 48px;
  --header-height: 64px;
}

.site-wrap {
  /* The same column as the header, so the brand sits above the first word of
     the hero rather than 32px inside it. Prose is capped at --site-measure. */
  max-width: var(--container-max);
  margin: 0 auto;
  padding-inline: var(--gutter-page-lg);
}
@media (max-width: 900px) {
  .site-wrap { padding-inline: var(--space-6); }
}

/* ---------- masthead ---------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 20;
  background: color-mix(in oklab, var(--bg-page) 88%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border-hairline);
}

/* The sticky header carries its own wrap rather than sitting inside a
   page-level one: nested, the flex row never applies and the nav wraps under
   the wordmark.

   A fixed height, not padding: padding around the 26px mark measured 59px and
   moved every time anything in the row changed size. */
.site-header .site-wrap {
  display: flex;
  align-items: center;
  gap: 32px;
  height: var(--header-height);
}

/* The family wordmark: "Open" muted, the product word at full strength, then a
   full stop in the product's own accent. Set in the display face at
   --logo-tracking, which is tighter than heading tracking; using heading
   tracking is what makes a lockup read as a different family. */
.site-brand {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  text-decoration: none;
  color: var(--text-strong);
  font: var(--weight-medium) var(--text-lg) / 1 var(--font-display);
  letter-spacing: var(--logo-tracking);
  white-space: nowrap;
}
/* One flex item, not two: left as a bare text node beside the dot's span, the
   container's gap opens between the word and its own full stop. */
.site-brand > span { display: inline; }
.site-brand img {
  width: 26px;
  height: 26px;
  border-radius: var(--radius-sm);
  display: block;
}
.site-brand .site-prefix { color: var(--text-muted); }
.site-brand .site-dot {
  color: color-mix(in oklab, var(--app-docscan) 68%, var(--white));
}

/* Three zones: brand, tabs, actions. `flex: 1` on the tabs is what pushes the
   actions to the right edge — `justify-content: space-between` did it with two
   children and spaced all three evenly the moment the actions became their own
   zone, leaving "Account" stranded in the middle of the bar. */
.site-nav { display: flex; gap: 24px; flex: 1; align-items: center; }
.site-nav a {
  color: var(--text-muted);
  text-decoration: none;
  font: var(--type-ui);
  transition: color var(--duration-fast) var(--ease-standard);
}
.site-nav a:hover { color: var(--text-strong); }

.header-actions { display: flex; align-items: center; gap: 20px; }

/* 头部的 GitHub 链接：和导航里的链接同一套样式，其它站的头部也都有它 */
.site-nav-link {
  color: var(--text-muted);
  text-decoration: none;
  font: var(--type-ui);
  white-space: nowrap;
  transition: color var(--duration-fast) var(--ease-standard);
}
.site-nav-link:hover { color: var(--text-strong); }
/* `:not(.site-cta)`: the quiet-link colour must not reach the one solid button
   in this row. It did, and it outranks `.site-cta-primary` on specificity, so
   the label rendered grey on brass at 2.69:1 — under AA, on the single control
   the bar exists for. */
.header-actions > a:not(.site-cta) {
  color: var(--text-muted);
  text-decoration: none;
  font: var(--type-ui);
}
.header-actions > a:not(.site-cta):hover { color: var(--text-strong); }

/* Below 760px the tabs move to a second row rather than disappearing. The old
   rule hid every link but `.site-keep`, which left a phone with a wordmark and
   an "Account" link and no way to reach anything else on the page. */
@media (max-width: 760px) {
  .site-header .site-wrap {
    flex-wrap: wrap;
    height: auto;
    padding-block: 10px;
    gap: 0 16px;
  }
  .site-nav {
    order: 3;
    flex-basis: 100%;
    gap: 18px;
    padding-block: 8px;
    overflow-x: auto;
    scrollbar-width: none;
    margin-inline: calc(var(--space-6) * -1);
    padding-inline: var(--space-6);
  }
  .site-nav::-webkit-scrollbar { display: none; }
  .site-nav a { white-space: nowrap; }
  .header-actions { margin-left: auto; }
}

/* Anchor targets have to clear the sticky bar, or every tab lands with its own
   heading hidden underneath it. */
.site-hero,
.site-copy section { scroll-margin-top: 5.5rem; }
@media (max-width: 760px) {
  .site-hero,
  .site-copy section { scroll-margin-top: 7.5rem; }
}

/* ---------- the app, in the hero ---------- */

.site-hero {
  padding: var(--space-8) 0 var(--space-6);
}

/* The accent's only job as *text* is the wordmark's full stop. As an eyebrow it
   measured under AA on this ground, and this label sits above every section on
   the page. */
.site-eyebrow {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0 0 var(--space-3);
}

.site-title {
  font: var(--weight-medium) clamp(30px, 4.4vw, 48px) / 1.06 var(--font-display);
  letter-spacing: var(--tracking-display);
  color: var(--text-strong);
  margin: 0 0 var(--space-4);
  max-width: 20ch;
  text-wrap: balance;
}

.site-lede {
  font-size: var(--text-md);
  color: var(--text-muted);
  max-width: var(--site-measure);
  margin: 0 0 var(--space-6);
}

/* The scanner is the hero. It is a real running app, not a screenshot, which
   is the whole argument for putting the two on one page. */
.site-stage {
  margin: 0 auto;
  max-width: 520px;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: 0 24px 60px -30px rgb(0 0 0 / 0.55);
}

/* How the app sits in the stage. This rule lives here rather than in the app's
   own sheet so that sheet names no `site-` class at all — which is what lets
   the overlap assertion stay strict instead of carrying an exception.
   `.view` is absolutely positioned against `#app`, so it needs a real height:
   `auto` collapses every view to nothing and the app renders as an empty
   frame. */
.site-stage #app {
  height: min(78dvh, 760px);
}

.site-stage-note {
  margin: var(--space-4) 0 0;
  text-align: center;
  font-size: var(--text-sm);
  color: var(--text-faint);
}

/* ---------- prose sections ---------- */

/* The suite's rhythm: 96px desktop, 56px phone. This ran --space-16 (64px),
   as three other sites did, so a reader moving between two of our products
   felt the pages breathe differently for no reason either page could give. */
.site-copy section {
  padding: var(--space-24) 0;
  border-top: 1px solid var(--border-hairline);
}
@media (max-width: 900px) {
  .site-copy section { padding: var(--space-14) 0; }
}

.site-copy h2 {
  font: var(--weight-medium) clamp(24px, 3vw, 34px) / 1.15 var(--font-display);
  letter-spacing: var(--tracking-heading);
  color: var(--text-strong);
  margin: 0 0 var(--space-4);
  max-width: 24ch;
  text-wrap: balance;
}

.site-copy h3 {
  font: var(--weight-medium) var(--text-base) / var(--leading-snug) var(--font-display);
  color: var(--text-strong);
  margin: 0 0 var(--space-2);
}

.site-copy p {
  color: var(--text-muted);
  max-width: var(--site-measure);
  margin: 0 0 var(--space-4);
}

.site-copy a { color: var(--text-link); text-underline-offset: 2px; }
.site-copy a:hover { color: var(--text-link-hover); }

.site-copy code {
  font-family: var(--font-mono);
  font-size: 0.9em;
  background: var(--bg-sunken);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-xs);
  padding: 1px 5px;
}

.site-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);
  margin-top: var(--space-10);
}
@media (max-width: 900px) { .site-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px) { .site-grid { grid-template-columns: 1fr; } }

.site-card {
  background: var(--surface-card);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
}
.site-card p { margin: 0; font-size: var(--text-sm); }

/* The numbers. Tabular, in the mono face, which carries everything numeric in
   this system. */
.site-stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-4);
  margin-top: var(--space-10);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  background: var(--surface-card);
}
@media (max-width: 700px) { .site-stats { grid-template-columns: repeat(2, 1fr); } }
.site-stat b {
  display: block;
  font: var(--weight-medium) var(--text-xl) / 1.1 var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--text-strong);
}
.site-stat span {
  display: block;
  margin-top: var(--space-2);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--text-faint);
}

/* Each line is something the product does not do. */
.site-claims { list-style: none; padding: 0; margin: var(--space-6) 0 0; max-width: var(--site-measure); }
.site-claims li {
  padding: var(--space-4) 0;
  border-top: 1px solid var(--border-hairline);
  color: var(--text-muted);
  font-size: var(--text-sm);
}
.site-claims li strong { color: var(--text-strong); font-weight: var(--weight-medium); display: block; }

/* Three ways to run it, each with its real state. Not cards: a border and a
   fill on all three would say "three equal products", and one is finished
   while two are not. */
.site-platforms {
  list-style: none;
  margin: var(--space-8) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-6);
}
@media (max-width: 780px) { .site-platforms { grid-template-columns: 1fr; gap: var(--space-5); } }
.site-platforms li {
  border-top: 1px solid var(--border-hairline);
  padding-top: var(--space-4);
  font-size: var(--text-sm);
  color: var(--text-muted);
}
.site-platforms b {
  display: block;
  color: var(--text-strong);
  font-weight: var(--weight-medium);
  font-size: var(--text-base);
  margin: var(--space-2) 0 var(--space-1);
}

/* A status, not an action: the ready one takes the system's success colour
   rather than the brand green the buttons use. */
.site-state {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--text-faint);
  border: 1px solid var(--border-hairline);
  border-radius: 999px;
  padding: 3px 9px;
}
/* 4.41:1 in the success colour — just under AA, which is the worst kind of
   miss because it looks fine. The tint and the border carry "ready"; the word
   itself takes the text colour. */
.site-state-ready {
  color: var(--text-strong);
  border-color: color-mix(in oklab, var(--success-fg) 40%, transparent);
  background: color-mix(in oklab, var(--success-fg) 12%, transparent);
}

.site-faq { margin: var(--space-8) 0 0; max-width: var(--site-measure); }
.site-faq dt {
  font: var(--weight-medium) var(--text-base) / var(--leading-snug) var(--font-display);
  color: var(--text-strong);
  margin-top: var(--space-7);
}
.site-faq dd { margin: var(--space-2) 0 0; color: var(--text-muted); font-size: var(--text-sm); }

.site-cta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-6);
  border-radius: var(--radius-md);
  border: 1px solid var(--border-hairline);
  text-decoration: none;
  font: var(--weight-medium) var(--text-sm) / 1 var(--font-sans);
  color: var(--text-strong);
}
.site-cta:hover { background: var(--surface-hover); }
.site-cta-row { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-top: var(--space-6); }

/* The header's one solid button. Every other site in the suite ends its bar on
   one, and this one ended on a text link that read the same as the four tabs
   beside it. `-sm` is the header scale: a page-sized button in a 64px bar is
   what makes a header look like a toolbar. */
.site-cta-primary {
  background: var(--brand, var(--app-docscan));
  border-color: transparent;
  color: var(--brand-contrast, var(--black));
}
.site-cta-primary:hover {
  background: color-mix(in oklab, var(--brand, var(--app-docscan)) 88%, var(--black));
}
.site-cta-sm { height: 34px; padding: 0 16px; }

/* The app's two icon controls now sit in this bar. They keep the app's own
   `.icon-btn` styling — the sheets stay disjoint, which `e2e/scan.spec.mjs`
   asserts, because nothing new is declared here for them. */
.site-header .header-actions { gap: var(--space-3); }

/* ---------- footer ---------- */

.site-footer {
  border-top: 1px solid var(--border-hairline);
  padding: var(--space-8) 0 var(--space-16);
  color: var(--text-faint);
  font-size: var(--text-sm);
}
.site-footer nav { display: flex; flex-wrap: wrap; gap: var(--space-5); margin-bottom: var(--space-4); }
.site-footer a { color: var(--text-muted); text-decoration: none; }
.site-footer a:hover { color: var(--text-strong); }
.site-footer p { margin: 0; max-width: var(--site-measure); }

/* ---------- the standalone prose pages ---------- */

.site-prose { max-width: var(--site-measure); padding: var(--space-12) 0 var(--space-16); }
.site-prose h1 {
  font: var(--weight-medium) clamp(28px, 4vw, 42px) / 1.1 var(--font-display);
  letter-spacing: var(--tracking-display);
  color: var(--text-strong);
  margin: 0 0 var(--space-5);
}
.site-prose ul { padding-left: var(--space-6); color: var(--text-muted); }
.site-prose li { margin-bottom: var(--space-2); }
.site-prose .site-updated {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--text-faint);
}


/* ---------- the footer ---------- */
/* Five columns: who we are, then four groups of links. It was two links and a
   licence sentence — a sign-off, not a footer, and the only place a reader who
   got this far had to go next. */
.site-footer .site-wrap {
  display: grid;
  grid-template-columns: 1.4fr repeat(4, 1fr);
  gap: var(--space-7);
  padding-block: var(--space-14) var(--space-8);
}
@media (max-width: 900px) {
  .site-footer .site-wrap { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 600px) {
  .site-footer .site-wrap { grid-template-columns: repeat(2, 1fr); }
}
.site-footer .footer-about p {
  margin: var(--space-3) 0 0;
  color: var(--text-muted);
  font-size: var(--text-sm);
  line-height: var(--leading-relaxed);
  max-width: 34ch;
}
.site-footer .footer-group {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
}
.site-footer .footer-group .site-eyebrow { margin: 0 0 var(--space-1); }
.site-footer a {
  color: var(--text-muted);
  text-decoration: none;
  font: var(--type-ui);
}
.site-footer a:hover { color: var(--text-strong); }
/* The licence line and the social link used to sit in a second bar below the
   footer, which read as a second footer and put the only X link in the one
   place nobody scrolls to. Both now live in the about column, where the
   sentence they belong to already is. */
.site-footer .footer-licence {
  margin: var(--space-3) 0 0;
  color: var(--text-faint);
  font-size: var(--text-xs);
  line-height: var(--leading-relaxed);
}
.site-footer .footer-social {
  display: inline-flex;
  align-items: center;
  margin-top: var(--space-3);
}

/* The language picker. Plain links in the footer, one row, every locale on
   every page — no JavaScript, no <select>, nothing to hydrate: a crawler and
   a reader with scripts off both see eight real hrefs. It spans the footer
   grid rather than taking a sixth column, so the five columns above keep
   their widths. */
.site-footer .lang-row {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
  padding-top: var(--space-6);
  border-top: 1px solid var(--border-hairline);
  font-size: var(--text-sm);
}
.site-footer .lang-row a { color: var(--text-faint); }
.site-footer .lang-row a:hover { color: var(--text-strong); text-decoration: underline; }
.site-footer .lang-row a[aria-current="page"] {
  color: var(--text-strong);
  font-weight: var(--weight-medium);
}

/* One line, in the middle of the page, larger than the copy around it.
   The footer's MIT-or-Apache line and the last FAQ answer already say this,
   and neither reaches the person who is deciding halfway down: a licence
   name is for people who already know what it implies, and the FAQ is at the
   end. Sized and placed to what scripts/site-check.py item 10 measures —
   at least 1.3x the body size, between 15% and 90% of the copy, outside the
   footer and outside the FAQ. */
.site-copy .site-bigline {
  font: var(--weight-medium) clamp(24px, 3vw, 32px) / 1.25 var(--font-display);
  letter-spacing: var(--tracking-heading);
  color: var(--text-strong);
  max-width: 28ch;
  margin: 0;
  padding: var(--space-16) 0;
  border-top: 1px solid var(--border-hairline);
  text-wrap: balance;
}
@media (max-width: 900px) {
  .site-copy .site-bigline { padding: var(--space-10) 0; }
}

/* ── 语言切换器 ─────────────────────────────────────────────────────
   页脚一排真链接（.lang-row）+ 右上角地球（.lang-switch）。两处指向同一组
   URL，都是 <a href>：<option> 爬虫跟不进去，真链接才是 hreflang 的可见对应物。
   当前项用 aria-current="page"（导航里 "true" 语义不准）。 */
.lang-switch{--ls-fg:var(--text-muted);--ls-fg-strong:var(--text-strong);--ls-bg:var(--surface-card, #fff);--ls-border:var(--border-hairline);--ls-hover:var(--surface-hover)}
.lang-switch{position:relative;display:inline-flex;flex:none}
.lang-switch>summary{display:inline-flex;align-items:center;justify-content:center;
  width:32px;height:32px;border-radius:var(--radius-md, 8px);color:var(--ls-fg);cursor:pointer;
  list-style:none;transition:color .15s,background-color .15s}
.lang-switch>summary::-webkit-details-marker{display:none}
.lang-switch>summary::marker{content:""}
.lang-switch>summary:hover,.lang-switch[open]>summary{color:var(--ls-fg-strong);background:var(--ls-hover)}
.lang-switch>summary:focus-visible{outline:2px solid var(--ls-fg-strong);outline-offset:2px}
.lang-menu{position:absolute;top:calc(100% + 8px);right:0;z-index:70;display:flex;
  flex-direction:column;min-width:172px;padding:6px;border:1px solid var(--ls-border);
  border-radius:12px;background:var(--ls-bg);box-shadow:0 12px 32px rgba(0,0,0,.16)}
.lang-menu a{display:block;padding:8px 10px;border-radius:var(--radius-md, 8px);font-size:13px;
  line-height:1.3;white-space:nowrap;color:var(--ls-fg);text-decoration:none}
.lang-menu a:hover{background:var(--ls-hover);color:var(--ls-fg-strong);text-decoration:none}
.lang-menu a[aria-current="page"]{color:var(--ls-fg-strong);font-weight:500}
/* 44px 是可点击元素的下限（openapps-design 的硬要求）。 */
@media (pointer:coarse){.lang-switch>summary{width:44px;height:44px}
  .lang-menu a{padding:11px 12px;font-size:14px}}
@media (prefers-reduced-motion:reduce){.lang-switch>summary{transition:none}}
