/* The glass design system: tokens, the aurora backdrop, the page shell, the nav,
   the buttons, the section/hero/card primitives, the footer. Loaded by `head()` on
   every page, before any page's own stylesheet.

   It is loaded unconditionally because it is a *base layer* like app.css: every rule
   here hangs off a class, so on a page that doesn't use them (the login form, the
   admin tables) it costs one cached request and changes nothing. The exception is
   `body.glassy`, which a page opts into by name — that is what switches the shell
   on.

   It used to live in landing.css, which claimed in its header that it could not
   affect any other page. That was true only while nothing else loaded it, and the
   download pages need the same material — so the rules moved here verbatim rather
   than being written a second time. Change one and both pages change, which is the
   point: `/` and `/download` are one product.

   Two things about this file are deliberate and easy to undo by accident:

   1. NO EXTERNAL FONT. The obvious way to make a page look like Apple's is Inter
      from Google Fonts. It is the wrong trade here: the site is for users in
      mainland China, where fonts.googleapis.com does not load at all, and the page
      is Chinese, so a CJK webfont would be megabytes for one headline. The whole
      site has zero external requests; the glass reads as glass because of the
      material (blur, translucency, layering), not the typeface.

   2. backdrop-filter, the expensive part, is on a handful of purpose-chosen
      surfaces and never on a repeating one — the nav, the landing page's two
      panel mocks, its download card, and the card its hero headline sits on.
      Feature cards, FAQ rows, changelog entries and the release history list are
      translucent fills with a hairline border and no blur: eight-plus
      simultaneous backdrop-filter layers is what the "performance: moderate-poor"
      warning on this style is actually about, and on a soft gradient the two are
      almost indistinguishable. */

:root {
  /* Glass fills. The alpha is not a taste decision: it is set so that text on a
     card clears 4.5:1 against the worst thing that can sit underneath it. A blur
     preserves luminance, so the backdrop can't be brighter than the brightest thing
     in it — which is the lightest gradient stop, plus every blob that can overlap
     there. Measured composites (WCAG 2.1, ratios recomputed from the composited
     colours, not eyeballed):
       light  card #FDFDFE → 正文 16.2:1, 次要(--dim) 4.8:1, 金色标签 4.9:1
       dark   card #202D3B → 正文 11.4:1, 次要(--dim) 5.4:1, 金色标签 7.1:1
     The dark card is the three blobs *stacked* on the lightest stop (#2C4F68 under
     it) — brighter than any single blob, and the case that matters, because --dim
     is the tightest of the four at 5.4:1. Brighten a blob and that is the number to
     recheck first. */
  --glass: color-mix(in srgb, #ffffff 74%, transparent);
  --glass-strong: color-mix(in srgb, #ffffff 86%, transparent);
  --glass-line: color-mix(in srgb, #0f172a 13%, transparent);
  --glass-hi: color-mix(in srgb, #ffffff 75%, transparent);
  --sky-1: #e9eef7;
  --sky-2: #f7f9fc;
  --blob-1: color-mix(in srgb, #2563eb 30%, transparent);
  --blob-2: color-mix(in srgb, #06b6d4 26%, transparent);
  --blob-3: color-mix(in srgb, #a16207 18%, transparent);
  /* White on #2563eb is 5.2:1. In dark mode --accent is a *light* blue, so the
     button has to flip to dark text — white on it would be 2.4:1 and look fine
     to whoever shipped it. */
  --accent-fg: #ffffff;
  --gold: #a16207;
}

/* The dark palette, written twice on purpose — here and in the block below — for
   the one reason a media query cannot be part of a selector list. The two bodies
   have to stay identical; `landing.test.ts` fetches this file and compares them,
   because the theme button on `/` reads the second one and the OS default reads
   this one, so a value edited in only one of them is a theme that is right until
   someone touches the button.

   `:not([data-theme="light"])` is what lets an explicit *light* choice hold on a
   machine set to dark: the attribute rule below cannot outrank a `:root` this
   file declares later, so the only way to win is to not match here at all. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --glass: color-mix(in srgb, #1b202a 72%, transparent);
    --glass-strong: color-mix(in srgb, #232935 92%, transparent);
    --glass-line: color-mix(in srgb, #ffffff 11%, transparent);
    --glass-hi: color-mix(in srgb, #ffffff 9%, transparent);
    --sky-1: #090c12;
    --sky-2: #141a24;
    --blob-1: color-mix(in srgb, #2563eb 34%, transparent);
    --blob-2: color-mix(in srgb, #0891b2 26%, transparent);
    --blob-3: color-mix(in srgb, #a16207 16%, transparent);
    --accent-fg: #0d1117;
    --gold: #e0b34a;
  }
}

/* The theme button's dark. Set by the head script on `/` (html.ts), always to the
   *resolved* theme — the stored choice, or the OS's answer when there is none — so
   this also covers a dark OS with no stored choice and the media query above is
   only what an early paint, before the script, would have used. */
:root[data-theme="dark"] {
  --glass: color-mix(in srgb, #1b202a 72%, transparent);
  --glass-strong: color-mix(in srgb, #232935 92%, transparent);
  --glass-line: color-mix(in srgb, #ffffff 11%, transparent);
  --glass-hi: color-mix(in srgb, #ffffff 9%, transparent);
  --sky-1: #090c12;
  --sky-2: #141a24;
  --blob-1: color-mix(in srgb, #2563eb 34%, transparent);
  --blob-2: color-mix(in srgb, #0891b2 26%, transparent);
  --blob-3: color-mix(in srgb, #a16207 16%, transparent);
  --accent-fg: #0d1117;
  --gold: #e0b34a;
}

/* ---------- page shell ---------- */

/* Opt in with `class="glassy"` on <body>. 16px, not app.css's 14px: that is a dense
   admin font size, these are pages someone reads once. Line-height 1.6 keeps CJK
   readable. */
body.glassy {
  background: var(--sky-1);
  font-size: 16px;
  line-height: 1.6;
}

/* Every glass page's sections bring their own column and fluid gutter, so `main`
   contributes none. Leaving app.css's rule in place is what once left the landing
   page hugging the left edge of a wide window: a 1200px column nested inside
   another 1200px column is just two left edges. */
body.glassy main { display: block; max-width: none; padding: 0; }

/* The gradient and the colour blobs live on a fixed layer instead of on body:
   `background-attachment: fixed` is the other way to do this, and it janks. */
.aurora {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  background: linear-gradient(170deg, var(--sky-1), var(--sky-2) 55%, var(--sky-1));
}
.aurora i {
  position: absolute;
  display: block;
  border-radius: 50%;
  filter: blur(80px);
  /* transform-only keyframes below: anything that changes layout would repaint
     the whole page every frame. */
  animation: drift 30s ease-in-out infinite alternate;
}
.aurora i:nth-child(1) {
  width: 46vw; height: 46vw; min-width: 300px; min-height: 300px;
  left: -8vw; top: -10vw;
  background: var(--blob-1);
}
.aurora i:nth-child(2) {
  width: 38vw; height: 38vw; min-width: 260px; min-height: 260px;
  right: -6vw; top: 8vh;
  background: var(--blob-2);
  animation-duration: 38s;
  animation-delay: -8s;
}
.aurora i:nth-child(3) {
  width: 34vw; height: 34vw; min-width: 240px; min-height: 240px;
  left: 22vw; bottom: -14vh;
  background: var(--blob-3);
  animation-duration: 44s;
  animation-delay: -18s;
}

@keyframes drift {
  to { transform: translate3d(5vw, 4vh, 0) scale(1.14); }
}

.glass {
  background: var(--glass);
  border: 1px solid var(--glass-line);
  box-shadow: inset 0 1px 0 var(--glass-hi);
}

.ic { width: 20px; height: 20px; flex: none; }

/* Every interactive target is at least 44px tall (touch), and the focus ring is
   drawn rather than removed. */
a:focus-visible,
summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 10px;
}

/* ---------- nav ---------- */

.nav {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: 18px;
  min-height: 64px;
  /* Full-bleed bar, but its contents are inset to the same line as the column
     below it. 1120px is the column `.hero`/`.sec` use further down; this is written
     the long way round because the bar is not inside that column and can't inherit
     its margin. */
  padding: 8px calc(max(0px, (100% - 1120px) / 2) + clamp(16px, 4vw, 40px));
  background: var(--glass);
  border-bottom: 1px solid var(--glass-line);
  backdrop-filter: blur(20px) saturate(150%);
  -webkit-backdrop-filter: blur(20px) saturate(150%);
}
/* app.css styles the bare `header` element for the admin pages' title bar; the nav
   is a <header class="nav">, and a class outranks an element selector. */
.brand {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  min-height: 44px;
  color: var(--fg);
  font-weight: 600;
  font-size: 17px;
  text-decoration: none;
  letter-spacing: .01em;
}
.mark { width: 24px; height: 24px; color: var(--accent); }
/* The wordmark: two weights on two colours. Split into two spans rather than
   left as one word because light "Duiqi" against a heavy accent "Dock" is what
   keeps a 17px nav from reading as a line of text. The two louder ways to do
   this were rendered at the real size before being dropped: `-webkit-text-
   stroke` fills the counters of D-o-c-k in at 17px, and a `--fg`→`--accent`
   gradient reads as mud across the middle of the word.

   The wrapper is load-bearing, not decoration — .brand is a flex row with a 9px
   gap, so without it that gap would open between the two words instead of
   between the mark and the wordmark. */
.brand .wm-1 { font-weight: 300; }
/* The 0.05em is not a word space — "DuiqiDock" is one word, and adding a real
   space would put one in the text a screen reader and a copy-paste both see. It
   is the width the light "q" was missing: at 300 weight its sidebearings shrink
   and its tail ran into the bold "D" behind it. Sized in em so the 480px rule
   below (15px) keeps the same seam. */
.brand .wm-2 { font-weight: 700; color: var(--accent); margin-left: .05em; }
.nav-links {
  display: flex;
  gap: 4px;
  margin-left: 10px;
  flex-wrap: wrap;
}
.nav-links a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 12px;
  border-radius: 10px;
  color: var(--dim);
  font-size: 15px;
  text-decoration: none;
  transition: color .18s ease, background-color .18s ease;
}
.nav-links a:hover { color: var(--fg); background: color-mix(in srgb, var(--fg) 7%, transparent); }
.nav-do { display: flex; gap: 10px; margin-left: auto; }

/* The dark-mode toggle, and the one thing in this file two pages share without
   `head()` in between: /document/ is a hand-written static file and loads this
   stylesheet directly. It sits here rather than in a page's own stylesheet for
   that reason — and here rather than in app.css because it is nav chrome, which
   is what this section is. The button itself is written out on both pages (the
   manual cannot call `icon()`); the script that drives it is html.ts's
   THEME_SCRIPT, duplicated verbatim into the manual's <head> and held to it by
   document.test.ts.

   Square, one 20px glyph, no text — `.btn quiet` like the language switch to its
   left on both pages (the manual has ended its row with these two all along).

   Which glyph shows is decided by `html[data-theme]` and nothing else. The head
   script always resolves it (stored choice, else the OS), so there is no second
   state in the markup to keep in step; and with no script the moon is what stays
   visible, which is the right default — a no-JS visitor gets nothing from the
   click either way, and a moon at least says "dark mode". */
#theme { width: 44px; padding: 0; }
#theme .when-dark { display: none; }
html[data-theme="dark"] #theme .when-dark { display: block; }
html[data-theme="dark"] #theme .when-light { display: none; }

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

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 0 18px;
  border: 1px solid var(--glass-line);
  border-radius: 12px;
  background: var(--glass-strong);
  color: var(--fg);
  font-size: 15px;
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
  transition: transform .18s ease, background-color .18s ease, border-color .18s ease;
}
.btn:hover {
  transform: translateY(-1px);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
}
/* Pressing is faster than hovering: the feedback has to land *now*, while the
   400-600ms curves elsewhere on these pages are ambient and may take their time. */
.btn:active { transform: translateY(0); transition-duration: .07s; }
.btn.primary {
  background: var(--accent);
  border-color: transparent;
  color: var(--accent-fg);
}
.btn.primary:hover { background: color-mix(in srgb, var(--accent) 86%, var(--fg)); }
.btn.lg { min-height: 50px; padding: 0 24px; font-size: 16px; }
.btn.quiet { background: transparent; border-color: transparent; color: var(--dim); }
.btn.quiet:hover { color: var(--fg); background: color-mix(in srgb, var(--fg) 7%, transparent); }

/* A key cap, for the shortcut lists. */
kbd {
  font: 500 12px/1 var(--mono);
  padding: 4px 6px;
  border: 1px solid var(--glass-line);
  border-radius: 6px;
  background: color-mix(in srgb, var(--fg) 8%, transparent);
  white-space: nowrap;
}

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

.hero {
  max-width: 1120px;
  margin: 0 auto;
  padding: clamp(48px, 9vh, 96px) clamp(16px, 4vw, 40px) 0;
  text-align: center;
}
.hero h1 {
  margin: 0 0 18px;
  font-size: clamp(30px, 5.4vw, 52px);
  line-height: 1.16;
  font-weight: 700;
  letter-spacing: -.01em;
  text-wrap: balance;
}
.lede {
  max-width: 640px;
  margin: 0 auto 30px;
  color: var(--dim);
  font-size: clamp(16px, 1.9vw, 18px);
}
.lede b { color: var(--fg); }
.cta { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }
.fine { color: var(--dim); font-size: 13px; margin: 14px 0 0; }
.centerline { text-align: center; }

/* ---------- sections and cards ---------- */

.sec {
  max-width: 1120px;
  margin: 0 auto;
  padding: clamp(64px, 10vh, 104px) clamp(16px, 4vw, 40px) 0;
}
/* The nav is sticky: without this an anchor jump parks the heading underneath it. */
section[id] { scroll-margin-top: 84px; }
.sec h2 {
  margin: 0 0 26px;
  font-size: clamp(22px, 3vw, 30px);
  line-height: 1.25;
  font-weight: 650;
  letter-spacing: -.005em;
  text-wrap: balance;
}
.sec > h2 { text-align: center; }

.grid {
  display: grid;
  gap: 16px;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}
.fcard {
  padding: 22px 22px 20px;
  border-radius: 20px;
}
.fcard-ic {
  display: grid;
  place-items: center;
  width: 40px; height: 40px;
  margin-bottom: 14px;
  border-radius: 12px;
  background: color-mix(in srgb, var(--accent) 15%, transparent);
  color: var(--accent);
}
.fcard-ic .ic { width: 22px; height: 22px; }
.fcard h3 {
  margin: 0 0 8px;
  font-size: 16.5px;
  font-weight: 600;
}
.fcard p { margin: 0; color: var(--dim); font-size: 14.5px; }

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

.site-foot {
  display: flex;
  align-items: center;
  gap: 14px 24px;
  flex-wrap: wrap;
  max-width: 1120px;
  margin: clamp(64px, 10vh, 104px) auto 0;
  padding: 24px clamp(16px, 4vw, 40px) 40px;
  border-top: 1px solid var(--glass-line);
  color: var(--dim);
  font-size: 14px;
}
.foot-links { display: flex; gap: 18px; margin-left: auto; }
.foot-links a, .site-foot a { color: var(--accent); text-decoration: none; }
.foot-links a:hover, .site-foot a:hover { text-decoration: underline; }
.site-foot .fine { margin: 0; }

/* ---------- narrow ---------- */

/* The nav can't hold its links plus the actions on one line at 375px, so the links
   go. Both glass pages can afford that for the same reason: what they lead to is
   reachable anyway — the landing page is short enough to scroll, and the download
   pages repeat 首页/文档/历史版本 as buttons in the hero, which are the links that
   have to survive. */
@media (max-width: 720px) {
  .nav { gap: 10px; }
  .nav-links { display: none; }
  .nav-do { gap: 8px; }
  .foot-links { margin-left: 0; }
}

/* The language switch is the fourth thing in the bar, and below 480px the bar has
   no room for it: measured in same-origin iframes at 320px it costs 51px in English
   and 13px in Chinese, which pushes the landing page — already at its limit there,
   see the note in landing.css — into 43px / 5px of horizontal overflow at 360px.
   So it goes from *the bar* only. Every page carries a second one where there is
   room — the footer for landing/download/history, the line under the login card
   for /login — so nobody is left without a way to change language, and the bar's
   bytes below 480px are exactly what they were before the switch existed. */
@media (max-width: 480px) {
  .nav .lang-switch { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .aurora i { animation: none; }
}
