/* The landing page at /. Loaded *in addition to* app.css and glass.css, by that
   page alone — the same arrangement /document/ uses for doc.css.

   The design system — the tokens, the aurora, the shell, the nav, the buttons, the
   hero/section/card primitives, the footer — lives in glass.css, which every page
   loads. What is left here is only what is true of *this* page: the picture of the
   dock, the shortcut list, the two panel mocks, the FAQ and the changelog. So:

   nothing in this file may be written in a way that would change /login, /download
   or /admin. */

/* ---------- the picture in the hero ---------- */

/* Wider than the 1120px the shared `.hero` sets, and the override is safe from
   here for the reason this file exists: `/` is the only page that loads it, so
   /download and /download/history keep the shared number. 1120 is a *text*
   measure — the length at which a line of prose stays readable — and this
   section stopped being a block of prose the moment the headline moved onto the
   screen. What needs the room now is the picture. */
.hero { max-width: 1280px; }

/* The picture hangs in a monitor: `.monitor` is the object, `.monitor-screen` is
   the bezel and the glass, `.monitor-neck` + `.monitor-base` hold it up, and
   `.desk` is the desktop inside.

   1120px is the size of the object, and everything inside follows from it rather
   than the other way round: the bezel takes 12px a side, `.desk` takes 22px of
   desktop on three sides and 8px on the dock's, which leaves 1066px for the
   dock and the headline card together. It was 814px, and that number was built
   up from the 760px picture the hero used to be about (12 + 22 + 760 + 8 + 12).
   The headline moved onto the screen, so the frame is now the thing with a size
   — and the window mock that used to sit inside it has since been dropped
   altogether, which leaves the desktop empty behind the card.

   Every name here is prefixed. All three stylesheets share one document, and
   `app.css` already defines `.foot` — the `<p class="foot">` footnote under the
   login and download forms. A bare `.foot` for the monitor's base would have
   been styled by it and vice versa.

   The bezel is the one piece of flat hardware colour on the page, and it is the
   same in both themes on purpose: a monitor does not change colour when you
   switch your OS to light. (`.dm-tile:nth-child(3)::after` is the other one.) */
.monitor {
  width: min(1120px, 100%);
  margin: 40px auto 0;
}
/* The chin. A monitor's bottom bezel is thicker than the other three — it is
   where the buttons and the wordmark go — and it is the single cheapest thing
   that says "screen" rather than "black rectangle": at a uniform 12px the frame
   reads as a picture mount. 22px, and the radius below comes down to match, now
   that there is a flat edge for it to be small on. */
.monitor-screen {
  padding: 12px 12px 22px;
  border-radius: 10px;
  background: linear-gradient(180deg, #272d36, #14171c);
  box-shadow:
    /* A 2px grey rim around the whole bezel, and it is a shadow rather than a
       `border` on purpose: `* { box-sizing: border-box }` is in app.css, so a
       real border would have eaten 4px out of the 1066px the desktop inside is
       measured against, and every number in the picture with it. A shadow is
       also the only one of the two that can sit *outside* the box, which is
       where this one belongs — as a border it would have drawn itself over the
       bezel's own edge and shrunk the black frame the eye reads as the object.

       Two pixels, not one: at 1px this is a hairline that only shows up when
       you already know it is there. The composite numbers below are a property
       of the colour and do not change with width, but the *area* does, and 2px
       is what turns the top edge from something you can find into something you
       see. It is also what real bezels do — a 1px highlight on a 1100px object
       is a screen artifact, not a machined edge.

       Opaque and fixed, not the `rgb(255 255 255 / .09)` hairline this was. A
       translucent rim is a function of whatever is behind it, and this frame
       sits on both a light page and a dark one: the same 9% white that is a
       faint edge at night is nothing at all at noon, so the frame read as a
       black rectangle someone had cut out rather than as a piece of hardware.
       A monitor's case does not change colour when you flip the theme, so the
       rim doesn't either.

       The value is a balance point, and it had to be measured rather than
       picked by eye, because the two pages pull it in opposite directions.
       Behind the left edge the page runs `#AABFFF` (relative luminance 0.49) in
       light and `#162B59` (0.026) in dark, and the bezel itself is `#272d36`
       (0.015). Read off the rim's own columns at x=158..159 in a 1440px render,
       where the pixels hold the rim colour exactly:

         #6d7885   2.3:1 light   3.1:1 dark   3.6:1 vs bezel
         #8a94a1   1.6:1 light   4.5:1 dark   5.3:1 vs bezel

       Neither clears 3:1 both ways and no grey can — clearing 3:1 against the
       light page needs luminance ≤ 0.131, clearing it against the dark page
       needs ≥ 0.179, and the best a single grey can manage is ≈2.8:1 each way.
       `#6d7885` sits within a hair of that optimum. `#8a94a1` was rendered first
       and is the better rim on dark, but at 1.6:1 on light it stops being a
       border on the one theme where this line is the only thing marking the
       edge of the object. */
    0 0 0 2px #6d7885,
    0 26px 54px -26px rgb(0 0 0 / .55),
    inset 0 1px 0 rgb(255 255 255 / .08);
}
/* Clipped triangles rather than borders: the taper is the whole shape, and a
   border would follow the box, not the neck. Both the neck and the base run a
   step lighter than the bezel, which is the direction a real one leans in —
   studio light from above — and also the direction that stays visible when the
   page behind is almost this dark. */
.monitor-neck {
  width: 104px;
  height: 32px;
  margin: 0 auto;
  background: linear-gradient(180deg, #2e3540, #1a1e25);
  clip-path: polygon(31% 0, 69% 0, 79% 100%, 21% 100%);
}
.monitor-base {
  width: 196px;
  height: 10px;
  margin: 0 auto;
  border-radius: 999px;
  background: linear-gradient(180deg, #343c48, #1e232a);
  box-shadow: 0 12px 20px -12px rgb(0 0 0 / .65);
}

/* The desktop inside the screen. Its wallpaper is drawn here rather than taken
   from the page — and it is deliberately *not* the page's aurora, nor one of its
   token pairs: what is behind `.dockmock` is what tints it, and the
   one thing this picture has to show is a dock made of frosted glass. Frost is
   only visible against something with colour in it, so the wallpaper is a mesh
   of four saturated blobs; over a flat wash the dock's `backdrop-filter` has
   nothing to smear and reads as a solid panel, which is exactly what it looked
   like before.

   It is also the same wallpaper in both themes, like the bezel: a wallpaper is
   content, and content does not change colour when you flip the page to light.

   It is a grid of one cell holding two layers: `.desk-layer`, which is the
   drawing (the desktop and the dock), and `.hero-card`, which is the page's own
   headline block. Stacking them in one cell is what makes the card float over
   the desktop instead of shoving the picture sideways — and it is also what
   sizes the cell, since the row is as tall as the taller of the two. With the
   card positioned out of flow it would contribute no height at all, and a
   viewport short enough to make the card taller than the dock mock would push
   the card out through the bottom of the screen, with the bezel still drawn
   neatly around a picture that had escaped it.

   min-height, not height. The picture is allowed to be taller than the 56vh
   design height — never shorter. What is inside the dock mock is a fixed,
   non-shrinkable stack (18px bar + 6×35px tiles + 5×3px gaps + 16px of margin +
   18px foot + 12px padding = 289px), and 56vh drops below that somewhere around
   520px of viewport height: from there down, nothing could shrink, so the column
   simply overflowed its own panel and the "＋" foot ended up outside it. At the
   400px floor it would still be short by more than that, hence `height: auto`:
   the box is never shorter than what is in it, and min-height only adds back the
   "at least this tall" design intent on the screens where 56vh is the larger of
   the two.

   The 22px padding is the desktop the picture is lying on, and it is the only
   number in this picture that is *not* copied from the product: the real desktop
   runs flush to the screen edge, so a picture drawn to that number reads as a
   poster in a black frame rather than as a screen. Nothing here is measured
   against it — the 48px dock is still between the padding and the edge of the
   box inside it — and it is what gives the dock a band of wallpaper wide enough
   to see the blur through.

   The dock's side is the exception, and it is back to a real number: 8px is the
   margin the dock card leaves against the screen edge in the product (the
   AppBar is 76px and the card inside it 60px, so 8 either side). Three sides
   inset, one flush, is also just what a docked desktop looks like — the dock is
   bolted to the edge of the panel and nothing else is, and at 22px on all four
   the dock read as one more thing floating in the middle of the screen rather
   than as the thing the AppBar reserves. So the padding is asymmetric, and the
   monitor's width is 14px narrower to pay for it rather than the dock growing.

   Square corners, and they are what makes the frame read as a monitor: a panel
   is a right-angle rectangle and only the bezel around it is rounded. What is
   *inside* keeps its own radii — the dock card has its own 12px, the radius of
   a real window — because that is a drawing of a real window and this box is a
   drawing of a panel. Written out rather than left off, so it does not read as
   an oversight: nothing is inherited from `.monitor-screen`, and this was 8px
   until someone looked at it. */
.desk {
  /* One cell, two layers — see the comment above for why the card is not taken
     out of flow. `minmax(0, 1fr)` rather than `1fr` so the column can be smaller
     than the dock mock's content on a narrow screen, which is what lets the
     whole picture scale down instead of overflowing the bezel. */
  display: grid;
  grid-template: "stack" 1fr / minmax(0, 1fr);
  padding: 22px 8px 22px 22px;
  min-height: clamp(400px, 56vh, 560px);
  border-radius: 0;
  text-align: left;
  /* A mesh, in the order the eye reads it: four blobs over a diagonal base. The
     two on the right are the ones the dock sits on, and they are the two that
     differ most from each other — a blue-to-orange edge under the dock is what
     makes the smear along its height obvious. */
  background:
    radial-gradient(42% 40% at 12% 10%, #9fb3ff 0%, transparent 72%),
    radial-gradient(46% 44% at 88% 18%, #63d3e8 0%, transparent 74%),
    radial-gradient(52% 48% at 76% 92%, #bb9bff 0%, transparent 72%),
    radial-gradient(40% 38% at 16% 86%, #ffbe8e 0%, transparent 74%),
    linear-gradient(160deg, #aabfff, #98dfee 52%, #c3b0ff);
}
/* The desktop-and-dock drawing. `aria-hidden` is on this element rather than on
   the monitor (html.ts), because the page's `<h1>` is in the other layer. */
.desk-layer {
  grid-area: stack;
  display: flex;
  align-items: stretch;
  gap: 0;
}
/* The page's headline block, floating on the desktop. It is the second layer of
   the cell `.desk-layer` occupies — the stack, the `place-self` and the width
   cap are the whole of how it sits there.

   `--glass-strong` and not `--glass`, and this is the one place on the site
   where `--glass` was tried first. `--glass` is the nicer-looking of the two:
   the wallpaper's colour comes through it and the card reads unmistakably as
   frosted glass, which is what the picture is for. It is also, here, text that
   fails. The alpha in glass.css was worked out against the page's aurora; what
   is behind this card is the picture's own wallpaper, which is a *saturated*
   mesh on purpose (four blobs, so that the dock's blur has something to smear).
   `--dim` on `--glass` over it measures 4.27:1 light and 2.98:1 dark — 2.98
   being the number that settles it, because the dark card over a bright cyan is
   the worst surface anywhere on the page.

   Measured composites, WCAG 2.1, the worst pixel behind the lede rather than the
   average one (the lede spans the full card width, so the brightest part of the
   wallpaper is genuinely under real glyphs):
     light  card #EEF9FF → 正文 15.3:1, 次要(--dim) 4.5:1
     dark   card #2B3845 → 正文  9.8:1, 次要(--dim) 4.6:1
   Both clear 4.5:1 and neither by much, and the reason is the token rather than
   the alpha: `--dim` is a mid grey, so its best case over white is 4.8:1 and
   over this wallpaper's brightest pixel nothing translucent can reach it. If the
   wallpaper is ever brightened, or a blob added, those two numbers are the ones
   to remeasure — and the answer is not another alpha, it is giving the lede a
   colour of its own.

   The blur is one more than glass.css's header counted (the nav, the two panel
   mocks, the download card). It earns the fifth: every other blurred surface on
   the page has a flat gradient behind it, and this is the only one with
   *structure* — a wallpaper and a dock — to be seen through. */
.hero-card {
  grid-area: stack;
  place-self: center;
  /* The 24px is for the narrow end. On a wide screen the card is 700 of the
     desktop's 1066 and the margins are whatever is left; on a phone the desk is
     barely wider than the card, and at a flat 100% the card would sit flush to
     all four edges and read as the screen itself rather than as a card lying on
     it. `place-self: center` centres the margin box, so the inset has to be in
     the width — margins here would push the card out by half of what they add. */
  width: min(700px, calc(100% - 24px));
  padding: 34px 36px 36px;
  border-radius: 20px;
  background: var(--glass-strong);
  border: 1px solid var(--glass-line);
  box-shadow:
    0 30px 60px -28px rgb(0 0 0 / .5),
    inset 0 1px 0 var(--glass-hi);
  backdrop-filter: blur(26px) saturate(150%);
  -webkit-backdrop-filter: blur(26px) saturate(150%);
  /* `.desk` sets `text-align: left` for the mock; the card is back to the
     centred column the hero primitives in glass.css assume. */
  text-align: center;
}
/* The desktop to the left of the dock: a spacer that takes whatever is left.
   It was a fixed 8px — the reserved AppBar strip (appbar.rs: DOCK_MARGIN_DIP =
   8), which is what a *maximized* window stops at, and the number the picture
   existed to advertise. There is no reservation left to draw: what the dock
   reserves on a 1920 screen is a strip down the right edge, not a column of
   desktop in the middle, and drawing one here is the picture lying about the
   product. The dock's own 8px margin to the screen edge is still real and is
   still drawn — it is `.desk`'s 8px of right padding, on the other side of
   `.dockmock`.

   Empty on purpose. It used to carry a dashed centre-line so the caption under
   the picture had something to point at; the caption is gone, and over the
   wallpaper the dashes were the one thing on screen that was neither the
   product nor the desktop — and, in the dark theme, all but invisible over a
   bright cyan. It is also what is left of the window mock that used to sit
   here: with `.win` gone there is nothing on this side but wallpaper, which is
   the point — the card lies over it. */
.gap { flex: 1 1 0; }

/* 48px wide. The real dock is 60 (appbar.rs: DOCK_WIDTH_DIP 76, less the 8px
   margin on each side), and this was drawn at 60 until it was measured against
   what it is sitting in: 60 out of this picture's 760 is 8% of the width, where
   the same dock on a 1920 screen is 3%. Nothing in the picture is at 1:1 — the
   headline card here is 700px wide — so the honest thing is the proportion, and
   48 is 6%: still chunky enough to read at a glance, which is the job the picture
   has.

   Everything in the column scales with it, 0.8: 35px tiles, 18px bar buttons,
   3px gaps. The stack they make is 289px (bar 18 + tiles 241 + foot 18 + 12 of
   panel padding), which is the number `.desk`'s min-height has to keep clear of.

   The fill is `--glass` thinned to about half its alpha rather than `--glass`
   itself (74% light / 72% dark). At 72% the panel is a solid card that happens to
   have a `backdrop-filter` attached — the blur was always running, there was just
   nothing left of the wallpaper to see through it. `color-mix` against
   `transparent` scales the alpha and leaves the colour alone, so this thins the
   material rather than changing it, and does so in both themes from the one
   declaration.

   Half, not less: at 43% the panel did not read as a dark card any more, it read
   as coloured glass — the wallpaper behind it is vivid enough that anything
   thinner hands the panel's identity over to whatever it happens to be floating
   on. 68% keeps the tint in charge and still lets the wallpaper through. */
.dockmock {
  flex: 0 0 48px;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 6px 0;
  border-radius: 10px;
  background: color-mix(in srgb, var(--glass) 68%, transparent);
  border: 1px solid var(--glass-line);
  box-shadow:
    0 16px 32px -20px rgb(0 0 0 / .5),
    inset 0 1px 0 var(--glass-hi);
  backdrop-filter: blur(22px) saturate(140%);
  -webkit-backdrop-filter: blur(22px) saturate(140%);
}
.dm-bar, .dm-foot { display: flex; justify-content: center; gap: 1px; flex: none; }
.dm-foot { margin-top: auto; }
.dm-btn {
  display: grid;
  place-items: center;
  width: 18px; height: 18px;
  border-radius: 5px;
  color: var(--dim);
  font-size: 12.5px;
  line-height: 1;
}
.dm-btn .ic { width: 13px; height: 13px; }
/* flex: none, like the bar and the foot above and below it. It was the one item
   in this column that could still be asked to shrink; min-height:auto was
   incidentally protecting the tiles, and a guarantee this page now makes
   explicitly should not rest on that. */
.dm-tiles {
  display: flex;
  flex-direction: column;
  flex: none;
  gap: 3px;
  margin: 8px 0;
}
.dm-tile {
  position: relative;
  display: grid;
  place-items: center;
  width: 35px;
  height: 35px;
  border-radius: 8px;
  color: var(--fg);
  background: color-mix(in srgb, var(--fg) 6%, transparent);
  /* Ambient motion, so the slower curve is right here — this is the one place
     the page shows off the "fluid" part of the style. */
  transition: transform .45s cubic-bezier(.22, 1, .36, 1), background-color .45s ease;
}
.dm-tile .ic { width: 19px; height: 19px; }
.dm-tile:hover { transform: scale(1.08); background: color-mix(in srgb, var(--accent) 22%, transparent); }
/* A running icon's state dot, the way the real dock draws it. */
.dm-tile:nth-child(3)::after {
  content: "";
  position: absolute;
  right: 4px; top: 4px;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: #22c55e;
}

/* ---------- the shortcut list ---------- */

.chords {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  margin: 0;
  padding: 0;
  list-style: none;
}
.chords li {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  padding: 0 16px;
  border-radius: 14px;
  background: color-mix(in srgb, var(--fg) 5%, transparent);
  font-size: 14.5px;
}
.chords span { color: var(--dim); }

/* ---------- the two panel mocks ---------- */

.panels {
  display: grid;
  gap: 16px;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
.panel {
  padding: 16px;
  border-radius: 20px;
  backdrop-filter: blur(18px) saturate(150%);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
}
.p-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  padding: 0 14px;
  border: 1px solid var(--glass-line);
  border-radius: 12px;
  background: color-mix(in srgb, var(--fg) 5%, transparent);
  color: var(--dim);
  font-size: 14.5px;
}
.p-chips { display: flex; gap: 6px; margin: 12px 0; flex-wrap: wrap; }
.p-chips span {
  padding: 3px 10px;
  border: 1px solid var(--glass-line);
  border-radius: 999px;
  color: var(--dim);
  font-size: 12.5px;
}
.p-chips .on {
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  border-color: color-mix(in srgb, var(--accent) 40%, transparent);
  color: var(--accent);
}
.p-rows { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 2px; }
.p-rows li {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 36px;
  padding: 0 10px;
  border-radius: 9px;
  font-size: 14px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.p-rows li:first-child { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.p-ini {
  flex: none;
  width: 18px;
  color: var(--dim);
  font: 600 11.5px/1 var(--mono);
  text-align: center;
}
.p-kind {
  flex: none;
  padding: 2px 7px;
  border: 1px solid var(--glass-line);
  border-radius: 6px;
  color: var(--dim);
  font-size: 11.5px;
}
.p-note { margin: 12px 0 0; color: var(--dim); font-size: 12px; }

/* ---------- the download call to action ---------- */

.dl-card {
  display: flex;
  align-items: center;
  gap: 24px;
  flex-wrap: wrap;
  padding: clamp(22px, 3vw, 32px);
  border-radius: 24px;
  backdrop-filter: blur(20px) saturate(150%);
  -webkit-backdrop-filter: blur(20px) saturate(150%);
}
.dl-card h2 { margin: 0 0 8px; font-size: clamp(20px, 2.6vw, 26px); }
.dl-sub { margin: 0 0 6px; color: var(--dim); font-size: 14.5px; max-width: 620px; }
.dl-card .fine { margin: 0; }
.dl-card .btn { margin-left: auto; }

/* ---------- faq ---------- */

/* <details>: opens with no JavaScript, reachable by Tab, announced correctly. */
.faq { display: flex; flex-direction: column; gap: 10px; }
.faq details { border-radius: 16px; }
.faq summary {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 56px;
  padding: 12px 18px;
  cursor: pointer;
  list-style: none;
  font-weight: 550;
  font-size: 16px;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::marker { content: ""; } /* Firefox draws its own triangle */
.chev { margin-left: auto; color: var(--dim); transition: transform .22s ease; }
.faq details[open] .chev { transform: rotate(180deg); }
.faq details p {
  margin: 0;
  padding: 0 18px 18px;
  color: var(--dim);
  font-size: 14.5px;
  max-width: 78ch;
}
.faq code { font-size: 12.5px; }

/* ---------- changelog ---------- */

.log { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 10px; }
/* `.empty` comes from app.css (13px, left-aligned) — right for the admin tables it
   was written for, wrong under a centred heading. This is the state a fresh
   `wrangler dev` shows, because the local D1 starts empty. */
.sec .empty { text-align: center; font-size: 14.5px; }
.log li { padding: 16px 20px; border-radius: 16px; }
.log-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.log-ver { font-weight: 600; font-size: 16px; }
.log-new {
  padding: 0 8px;
  border: 1px solid currentColor;
  border-radius: 999px;
  color: var(--gold);
  font-size: 11.5px;
  line-height: 18px;
}
.log-date { color: var(--dim); font: 12px/1.6 var(--mono); }
.log p { margin: 6px 0 0; color: var(--dim); font-size: 14.5px; white-space: pre-line; }

/* The theme button's rules are not here but in glass.css, beside `.nav` and
   `.nav-do`: /document/ has the same button and does not load this file.
   Nothing else about the nav moved. */

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

@media (max-width: 720px) {
  .dl-card .btn { margin-left: 0; width: 100%; }
  /* Pins the picture to one size on phones instead of tracking 46vh of a long
     viewport — but a floor, not a cap, for the same reason as the base rule:
     the dock mock needs 289px and this must not cut it off. */
  .desk { min-height: 300px; }
}

/* The theme button is a third item in .nav-do, and it is what pushes the bar over
   its own edge on a small phone. Measured in same-origin iframes — a headless
   window will not go below ~495px, so a narrow viewport has to be an iframe — at
   320px the bar's contents came to 297px inside a 279px box: the wordmark, the
   toggle and the two buttons no longer fit on one line, and 下载 ended up 5px past
   the viewport with the wordmark right behind it.

   (The page itself already scrolled sideways at 320px before any of this — the
   two panel mocks and the download card's code block want ~337px. Re-measured
   with the toggle hidden: 337 either way, so that one is not this button's, and
   not fixed here.)

   Everything below comes out of gaps, insets and type size, never out of a touch
   target: the toggle keeps its 44px, the two beside it keep their height. Only
   under 480px, and it is the difference between the bar fitting and not. */
@media (max-width: 480px) {
  .nav { gap: 8px; padding: 8px 10px; }
  .brand { font-size: 15px; gap: 7px; }
  .nav-do { gap: 4px; }
  .nav .btn { padding: 0 10px; }
}

@media (prefers-reduced-motion: reduce) {
  .dm-tile:hover { transform: none; }
  *, *::before, *::after { transition-duration: .01ms !important; }
}
