/* FutureOS engineering blog — single stylesheet, no build step.
   Layout: one 44rem column. Theme: dark base, switched to light by the OS
   preference (`prefers-color-scheme`) or explicitly by the header toggle
   (data-theme on <html>). */

:root {
  /* Tells the browser to render its own UI (scrollbars, form controls,
     caret, autofill) in the dark palette, so the chrome matches the page
     instead of staying light on a dark background. */
  color-scheme: dark;
  --bg: #0a0d13;
  --bg-raise: #10151f;
  --bg-code: #0d1117;
  --fg: #e7eaf0;
  --fg-soft: #aeb6c3;
  --fg-muted: #8a94a5;
  --border: #1e2634;
  --border-strong: #2b3547;
  --accent: #5dd0ff;
  --accent-strong: #2fb7f0;
  --accent-soft: rgba(93, 208, 255, 0.14);
  --link-visited: #7fb0ff;
  --code-fg: #d9e3f2;
  --shadow: 0 6px 24px rgba(0, 0, 0, 0.28);
  --wrap: 44rem;
  --radius: 10px;
  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
    Arial, "Noto Sans", "PingFang SC", "Microsoft YaHei", sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
    "Liberation Mono", monospace;
}

:root[data-theme="light"] {
  color-scheme: light;
  --bg: #fbfbfd;
  --bg-raise: #ffffff;
  --bg-code: #f6f8fb;
  --fg: #16191f;
  --fg-soft: #454e5c;
  --fg-muted: #68737f;
  --border: #dde3eb;
  --border-strong: #c8d2df;
  --accent: #0a6dd6;
  --accent-strong: #0956aa;
  --accent-soft: rgba(10, 109, 214, 0.09);
  --link-visited: #6a4fd8;
  --code-fg: #24292f;
  --shadow: 0 8px 28px rgba(27, 31, 36, 0.09);
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    color-scheme: light;
    --bg: #fbfbfd;
    --bg-raise: #ffffff;
    --bg-code: #f6f8fb;
    --fg: #16191f;
    --fg-soft: #454e5c;
    --fg-muted: #68737f;
    --border: #dde3eb;
    --border-strong: #c8d2df;
    --accent: #0a6dd6;
    --accent-strong: #0956aa;
    --accent-soft: rgba(10, 109, 214, 0.09);
    --link-visited: #6a4fd8;
    --code-fg: #24292f;
    --shadow: 0 8px 28px rgba(27, 31, 36, 0.09);
  }
}

* {
  box-sizing: border-box;
}

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-sans);
  font-size: 17px;
  line-height: 1.66;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

::selection {
  background: var(--accent-soft);
}

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin: 0 auto;
  padding: 0 1.25rem;
}

a {
  color: var(--accent);
  text-decoration: none;
  transition: color 0.15s ease;
}

a:hover {
  color: var(--accent-strong);
  text-decoration: underline;
  text-underline-offset: 0.16em;
}

a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 3px;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
}

.skip-link:focus {
  left: 0.5rem;
  top: 0.5rem;
  background: var(--bg-raise);
  padding: 0.5rem 0.75rem;
  border-radius: 6px;
  border: 1px solid var(--border);
  z-index: 10;
}

/* ── Header ─────────────────────────────────────────────────────────── */
.site-header {
  border-bottom: 1px solid var(--border);
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  position: sticky;
  top: 0;
  z-index: 5;
}

.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: 3.4rem;
}

/* Desktop: brand, then the nav pushed right, then the theme toggle, all on one
   row (the wrap rules further down reorder these into two rows on a phone). */
.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  color: var(--fg);
  font-weight: 650;
  letter-spacing: -0.012em;
  flex: 0 1 auto;
}

.brand:hover {
  text-decoration: none;
}

.brand:hover .brand-text {
  color: var(--accent);
}

.brand-mark {
  color: var(--accent);
  font-size: 0.85em;
  line-height: 1;
}

.site-nav {
  display: flex;
  align-items: center;
  gap: 1.15rem;
  font-size: 0.88rem;
  margin-left: auto;
}

.site-nav a {
  color: var(--fg-soft);
}

.site-nav a:hover {
  color: var(--accent);
  text-decoration: none;
}

.site-nav .nav-external {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
}

.site-nav .nav-external::after {
  content: "↗";
  font-size: 0.82em;
  opacity: 0.75;
}

.theme-toggle {
  border: 1px solid var(--border);
  background: var(--bg-raise);
  color: var(--fg-muted);
  border-radius: 999px;
  width: 2rem;
  height: 2rem;
  flex: 0 0 auto;
  cursor: pointer;
  line-height: 1;
  transition: border-color 0.15s ease, color 0.15s ease;
}

.theme-toggle:hover {
  color: var(--accent);
  border-color: var(--accent);
}

/* The glyph names what a click will switch *to*, so the button visibly changes
   with the theme — a fixed glyph made a working toggle look inert. */
.theme-toggle-icon {
  display: block;
  font-size: 0.95rem;
  line-height: 1;
}

.theme-toggle-icon::before {
  content: "☀";
}

:root[data-theme="light"] .theme-toggle-icon::before {
  content: "☾";
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .theme-toggle-icon::before {
    content: "☾";
  }
}

/* ── Index ──────────────────────────────────────────────────────────── */
.hero {
  padding: 3.25rem 0 1.75rem;
}

.hero h1 {
  margin: 0 0 0.55rem;
  font-size: 2.15rem;
  line-height: 1.15;
  letter-spacing: -0.022em;
  text-wrap: balance;
}

.tagline {
  margin: 0 0 1.2rem;
  color: var(--fg-soft);
  font-size: 1.02rem;
  max-width: 34em;
}

.tag-cloud {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0;
}

.tag {
  display: inline-block;
  padding: 0.18rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 0.76rem;
  color: var(--fg-muted);
  background: var(--bg-raise);
  transition: color 0.15s ease, border-color 0.15s ease;
}

.tag:hover {
  color: var(--accent);
  border-color: var(--accent);
  text-decoration: none;
}

.count {
  color: var(--fg-muted);
  opacity: 0.7;
  font-size: 0.92em;
}

.post-list {
  display: flex;
  flex-direction: column;
  gap: 0;
  padding-bottom: 3rem;
}

.post-card {
  padding: 1.6rem 0 1.7rem;
  border-top: 1px solid var(--border);
}

.post-card-cover {
  display: block;
  margin-bottom: 0.9rem;
}

.post-card-cover img {
  display: block;
  width: 100%;
  aspect-ratio: 21 / 9;
  object-fit: cover;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--bg-raise);
}

.post-card h2 {
  margin: 0.4rem 0 0.5rem;
  font-size: 1.3rem;
  line-height: 1.28;
  letter-spacing: -0.015em;
  text-wrap: balance;
}

.post-card h2 a {
  color: var(--fg);
}

.post-card h2 a:hover {
  color: var(--accent);
  text-decoration: none;
}

.post-meta {
  margin: 0;
  font-size: 0.8rem;
  color: var(--fg-muted);
  font-family: var(--font-mono);
  letter-spacing: 0.01em;
  display: flex;
  gap: 0.7rem;
  align-items: baseline;
}

.post-summary {
  margin: 0 0 0.7rem;
  color: var(--fg-soft);
}

.post-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0;
}

.tag-index {
  list-style: none;
  padding: 1rem 0 3rem;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}

/* ── Article ────────────────────────────────────────────────────────── */
.post-header {
  padding: 3.25rem 0 1.75rem;
  border-bottom: 1px solid var(--border);
}

.post-header h1 {
  margin: 0 0 0.85rem;
  font-size: 2rem;
  line-height: 1.22;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.post-header .post-meta {
  gap: 0.85rem;
}

.post-author::before {
  content: "·";
  margin-right: 0.85rem;
  opacity: 0.55;
}

/* The cover leads the article. Shown whole rather than cropped to the cards'
   21:9, with the header's rule separating it from the text below. */
.post-cover {
  display: block;
  width: 100%;
  height: auto;
  margin: 2.75rem 0 0;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--bg-raise);
}

/* The cover already supplies the top space, so the header does not need its
   full opening padding when it follows one. */
.post-cover + .post-header {
  padding-top: 1.75rem;
}

.prose {
  padding: 1.75rem 0;
  /* break-word lets a single long token wrap instead of overflowing; pretty
     (where supported) avoids leaving one orphan word on the last line. */
  overflow-wrap: break-word;
  text-wrap: pretty;
}

.prose h2,
.prose h3,
.prose h4 {
  margin: 2.3rem 0 0.8rem;
  line-height: 1.28;
  letter-spacing: -0.014em;
  text-wrap: balance;
}

.prose h2 {
  font-size: 1.42rem;
  padding-bottom: 0.3rem;
  border-bottom: 1px solid var(--border);
}

.prose h3 {
  font-size: 1.14rem;
}

.prose h4 {
  font-size: 1rem;
  color: var(--fg-soft);
}

.prose p,
.prose ul,
.prose ol,
.prose blockquote,
.prose table,
.prose .table-wrap,
.prose pre {
  margin: 0 0 1.05rem;
}

.prose ul,
.prose ol {
  padding-left: 1.45em;
}

.prose li + li {
  margin-top: 0.32rem;
}

.prose li::marker {
  color: var(--fg-muted);
}

.prose img {
  max-width: 100%;
  height: auto;
  border-radius: var(--radius);
  border: 1px solid var(--border);
}

.prose hr {
  border: none;
  border-top: 1px solid var(--border);
  margin: 2.2rem 0;
}

.prose blockquote {
  border-left: 3px solid var(--accent);
  background: var(--accent-soft);
  padding: 0.75rem 1.05rem;
  border-radius: 0 8px 8px 0;
  color: var(--fg);
}

.prose blockquote p:last-child {
  margin-bottom: 0;
}

.prose code {
  font-family: var(--font-mono);
  font-size: 0.86em;
  background: var(--bg-code);
  border-radius: 6px;
  padding: 0.12rem 0.34rem;
  /* Long identifiers and paths (Noise_XXpsk0_25519_ChaChaPoly_BLAKE2b,
     scripts/compaction_experiment/…) are far wider than a phone column. With
     the default wrapping the whole token is pushed to the next line and the
     line before it ends up with a lone character — the ragged, fragmented
     look. `anywhere` gives those breaks a cost the line-breaker actually
     counts, so the token starts on the current line and splits only as much
     as it must. Note: no `word-break: break-word` alongside it — that legacy
     value maps to `overflow-wrap: break-word` and wins, restoring the
     push-the-whole-token behaviour. */
  overflow-wrap: anywhere;
}

/* Code inside a link — how a repo path is cited, e.g. [future.proto](…) —
   should read as a link, not as a code pill: keep the monospace face (it
   signals "this is a path"), drop the background and padding, and take the
   link's colour so it is obviously clickable. */
.prose a code {
  background: none;
  padding: 0;
  border-radius: 0;
  color: inherit;
}

.prose pre {
  background: var(--bg-code);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1rem 1.1rem;
  overflow-x: auto;
  font-size: 0.855rem;
  line-height: 1.6;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.02);
}

.prose pre code {
  background: none;
  border: none;
  padding: 0;
  color: var(--code-fg);
  font-size: inherit;
}

.table-wrap {
  overflow-x: auto;
  max-width: 100%;
}

.prose table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}

.prose table thead {
  white-space: nowrap;
}

.prose th,
.prose td {
  border: 1px solid var(--border);
  padding: 0.55rem 0.75rem;
  text-align: left;
}

.prose th {
  background: var(--bg-raise);
  font-weight: 600;
}

.prose tr:nth-child(even) td {
  background: color-mix(in srgb, var(--bg-raise) 55%, transparent);
}

.prose a:visited {
  color: var(--link-visited);
}

.post-footer {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  border-top: 1px solid var(--border);
  padding: 1.4rem 0 3.25rem;
  font-size: 0.9rem;
}

.post-footer a {
  color: var(--fg-soft);
}

.post-footer a:hover {
  color: var(--accent);
  text-decoration: none;
}

/* ── Footer ─────────────────────────────────────────────────────────── */
.site-footer {
  border-top: 1px solid var(--border);
  padding: 1.75rem 0 2.75rem;
  font-size: 0.84rem;
  color: var(--fg-muted);
}

.site-footer p {
  margin: 0.25rem 0;
}

/* ── Comments (giscus) ──────────────────────────────────────────────────────
   Only rendered when blog.json has giscus.enabled = true. Kept inside the
   reading column and separated from the article footer so the section reads
   as an invitation, not an afterthought. */
.comments {
  margin-top: 2.25rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--border);
}

.comments-heading {
  margin: 0 0 0.4rem;
  font-size: 1.14rem;
  letter-spacing: -0.012em;
}

.comments-hint {
  margin: 0 0 1rem;
  font-size: 0.86rem;
}

/* giscus mounts an iframe inside its own .giscus wrapper; let it span the
   column and not introduce an unexpected minimum width. */
.comments .giscus {
  width: 100%;
}

.muted {
  color: var(--fg-muted);
}

/* ── Chinese text ────────────────────────────────────────────────────────
   CJK glyphs are denser than Latin at the same size, so they want slightly
   different typography: a touch more leading than Latin (but not so much the
   column reads as scattered), no negative tracking on headings, and no
   hyphenation. Placed before the responsive blocks so small screens can
   tighten the leading further. */
:lang(zh-CN) body {
  line-height: 1.74;
}

:lang(zh-CN) .hero h1,
:lang(zh-CN) .post-header h1 {
  letter-spacing: 0;
  font-weight: 640;
}

:lang(zh-CN) .prose h2,
:lang(zh-CN) .prose h3,
:lang(zh-CN) .prose h4 {
  letter-spacing: 0;
}

/* `text-wrap: pretty` is tuned for Latin orphan control and can leave CJK
   paragraphs with odd spacing; keep CJK on the browser's native text layout. */
:lang(zh-CN) .prose {
  text-wrap: initial;
}

/* ── Small tablets / large phones ───────────────────────────────────────── */
@media (max-width: 640px) {
  body {
    font-size: 16px;
    line-height: 1.72;
  }

  :lang(zh-CN) body {
    line-height: 1.68;
  }

  .wrap {
    padding: 0 1.05rem;
  }

  /* The header carries brand + four nav links + a language switch + a theme
     toggle. On a phone the nav cannot share a row with the brand without
     squashing, and letting the toggle fall to a third row wastes a whole line.
     So: row one is brand + toggle, row two is the nav. */
  .header-inner {
    flex-wrap: wrap;
    gap: 0.1rem 0.75rem;
    min-height: 0;
    padding: 0.55rem 0 0.15rem;
  }

  .brand {
    flex: 1 1 auto;
    min-height: 2.4rem;
  }

  .theme-toggle {
    order: 2;
  }

  .site-nav {
    order: 3;
    flex: 1 1 100%;
    justify-content: flex-start;
    flex-wrap: wrap;
    gap: 0 0.15rem;
    font-size: 0.88rem;
    margin-left: 0;
  }

  /* Touch targets: give every nav control a comfortable height instead of a
     bare text run the thumb has to hit exactly. Negative first margin keeps
     the row flush with the brand above it despite the padding. */
  .site-nav a {
    display: inline-flex;
    align-items: center;
    min-height: 2.3rem;
    padding: 0 0.55rem;
    border-radius: 8px;
  }

  .site-nav a:first-child {
    margin-left: -0.55rem;
  }

  /* RSS is the one nav item a phone reader cannot use, and it is still offered
     in the footer. Dropping it here lets the remaining four links sit with real
     breathing room instead of filling 91% of the row. */
  .site-nav .nav-rss {
    display: none;
  }

  .site-nav .nav-external::after {
    font-size: 0.78em;
  }

  .hero {
    padding-top: 2rem;
  }

  .hero h1 {
    font-size: 1.72rem;
    line-height: 1.22;
  }

  .hero .tagline {
    font-size: 0.98rem;
  }

  .post-header {
    padding-top: 2rem;
  }

  .post-header h1 {
    font-size: 1.55rem;
    line-height: 1.26;
  }

  /* Long post titles, tag rows and the source line must wrap, not overflow. */
  .post-header .post-meta,
  .post-tags,
  .post-footer {
    flex-wrap: wrap;
  }

  .post-footer {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.6rem;
  }

  /* Cards sit closer to the edge on a phone; a full-height 21:9 cover eats the
     whole first screen, so crop it shorter. */
  .post-card-cover img {
    aspect-ratio: 16 / 9;
  }

  /* The article cover opens the page on a phone — keep it tight to the top so
     the title is not pushed far down the first screen. */
  .post-cover {
    margin: 1.5rem 0 0;
    border-radius: 10px;
  }

  .post-cover + .post-header {
    padding-top: 1.25rem;
  }

  .post-card h2 {
    font-size: 1.2rem;
  }

  .prose {
    padding: 1.5rem 0;
  }

  .prose h2 {
    font-size: 1.3rem;
    margin-top: 2rem;
  }

  .prose h3 {
    font-size: 1.1rem;
  }

  .prose blockquote {
    margin-left: 0;
    margin-right: 0;
    padding-left: 0.9rem;
  }

  .prose pre {
    padding: 0.85rem 0.95rem;
    font-size: 0.82rem;
    border-radius: 10px;
  }

  .prose table {
    font-size: 0.88rem;
  }
}

/* ── Small phones ───────────────────────────────────────────────────────── */
@media (max-width: 400px) {
  .wrap {
    padding: 0 0.85rem;
  }

  :lang(zh-CN) body {
    line-height: 1.64;
  }

  .brand-text {
    font-size: 0.95rem;
  }

  .site-nav {
    font-size: 0.84rem;
  }

  .hero h1 {
    font-size: 1.5rem;
  }

  .post-header h1 {
    font-size: 1.35rem;
  }

  .prose h2 {
    font-size: 1.2rem;
  }

  .tag {
    font-size: 0.72rem;
  }
}

/* ── Touch devices ───────────────────────────────────────────────────────
   Respect the notch/home indicator and avoid sticky-header surprises when the
   on-screen keyboard changes the visual viewport. */
@supports (padding: max(0px)) {
  .wrap {
    padding-left: max(1.05rem, env(safe-area-inset-left));
    padding-right: max(1.05rem, env(safe-area-inset-right));
  }

  .site-footer {
    padding-bottom: max(2.75rem, calc(1.5rem + env(safe-area-inset-bottom)));
  }
}

@media (hover: none) {
  /* No hover on touch: don't leave links looking stuck in the hover colour. */
  a:hover {
    text-decoration: none;
  }
}

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