@font-face { font-family: "Homemade Apple"; src: url("assets/fonts/homemade-apple.woff2") format("woff2"); font-display: swap; }
@font-face { font-family: "SN Pro"; src: url("assets/fonts/sn-pro.woff2") format("woff2"); font-style: normal; font-weight: 100 900; font-display: swap; }
:root {
  color-scheme: light;
  --ink: #09090b;
  --body: #71717a;
  --line: #e4e4e7;
  --surface: #fafaf9;
  --paper: #ffffff;
  --button: #27272a;
  --button-ink: #ffffff;
  --button-hover: #3f3f46;
  --paper-rgb: 255 255 255;
  --dialog-paper: #ffffff;
  --dialog-shadow: 0 12px 32px rgb(9 9 11 / .18), 0 2px 8px rgb(9 9 11 / .06);
  --marker: #ffd43b;
  --marker-ink: #09090b;
  --sans: "SN Pro", -apple-system, BlinkMacSystemFont, "PingFang SC", "Segoe UI", Helvetica, Arial, sans-serif;
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --marker-ink: #18181b;
  --ink: #f4f4f5;
  --body: #b0b0b9;
  --line: #343438;
  --surface: #242427;
  --paper: #151517;
  --button: #ededf0;
  --button-ink: #18181b;
  --button-hover: #ffffff;
  --paper-rgb: 21 21 23;
  --dialog-paper: #1b1b1e;
  --dialog-shadow: 0 12px 40px rgb(0 0 0 / .45), 0 2px 8px rgb(0 0 0 / .24);
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; scroll-padding-top: 24px; scrollbar-gutter: stable; }
body { margin: 0; background: var(--paper); color: var(--body); font-family: var(--sans); font-size: 15px; line-height: 28px; -webkit-font-smoothing: antialiased; }
button, input { font: inherit; }
button, a { -webkit-tap-highlight-color: transparent; }
button { color: inherit; cursor: pointer; }
a { color: inherit; text-decoration: none; }
img { display: block; max-width: 100%; }
h1, h2, p, figure { margin: 0; }
h1, h2 { color: var(--ink); font: 500 15px/28px var(--sans); }
strong { color: var(--ink); font-weight: 500; }
button:focus-visible, a:focus-visible, input:focus-visible { outline: 2px solid var(--body); outline-offset: 4px; }
[hidden] { display: none !important; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
.skip-link { position: fixed; top: -60px; left: 24px; z-index: 100; padding: 8px 12px; color: var(--paper); background: var(--ink); }
.skip-link:focus { top: 12px; }
.page-sheet { width: 100%; max-width: 598px; margin-inline: auto; padding: 80px 24px 180px; }
/* Home has no images, so the whole block floats in the screen; taller content still scrolls normally.
   Spare height splits 2 : 3 above and below, lifting the block to the optical centre, which reads as centred where the true middle looks low. */
.home .page-sheet { display: flex; flex-direction: column; min-height: 100svh; }
.home .page-sheet::before { content: ""; flex: 2 1 0; }
.home .page-sheet::after { content: ""; flex: 3 1 0; }
/* Phones keep the template padding because the page scrolls there; wider screens hand that space to the 2 : 3 split, with a floor on each side. */
@media (min-width: 640px) {
  .home .page-sheet { padding-block: 0; }
  .home .page-sheet::before { min-height: 64px; }
  .home .page-sheet::after { min-height: 96px; }
}
.site-header { position: relative; display: flex; align-items: center; gap: 16px; min-height: 40px; width: 100%; }
.brand { display: inline-flex; align-items: center; gap: 8px; color: var(--ink); font-size: 16px; font-weight: 500; white-space: nowrap; }
/* Pull the toggle outward by its built-in padding so the visible icon lines up with the column edge. */
.brand-avatar { width: 24px; height: 24px; margin-right: 2px; flex-shrink: 0; border-radius: 50%; object-fit: cover; }
.theme-toggle { position: absolute; right: -13.5px; top: 50%; transform: translateY(-50%); display: grid; place-items: center; width: 44px; height: 44px; padding: 0; border: 0; border-radius: 2px; color: var(--body); background: transparent; }
.theme-toggle svg { position: absolute; width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; transition: opacity 140ms ease; }
.theme-sun { opacity: 0; }
:root[data-theme="dark"] .theme-moon { opacity: 0; }
:root[data-theme="dark"] .theme-sun { opacity: 1; }
.theme-toggle:active { background: var(--surface); }
@media (hover: hover) and (pointer: fine) {
  .theme-toggle:hover { color: var(--ink); background: var(--surface); }
}
.button { display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-height: 40px; padding: 6px 16px; color: var(--button-ink); background: var(--button); border: 0; border-radius: 2px; font-size: 15px; line-height: 28px; white-space: nowrap; transition: background .15s, transform .15s; }
.button:hover { background: var(--button-hover); }
.button:active { transform: translateY(1px); }
.theme-changing .button { transition-property: transform; }
.button-arrow { display: block; width: 14px; height: 14px; flex-shrink: 0; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
main { display: flex; flex-direction: column; gap: 40px; padding-top: 48px; }
main > section { display: flex; flex-direction: column; gap: 24px; width: 100%; }
.flow { width: 100%; }
.flow > .prose { margin-top: 24px; }
.hero-copy, .author-copy { display: flex; flex-direction: column; gap: 24px; }
.prose { display: flex; flex-direction: column; gap: 16px; }
:where(p):has(> .marker) { isolation: isolate; }
.marker { position: relative; transition: color 700ms ease-out; }
.marker.is-drawn { color: var(--marker-ink); }
.marker-svg { position: absolute; z-index: -1; top: 0; left: 0; width: 1px; height: 1px; overflow: visible; pointer-events: none; }
.marker-svg path { fill: none; stroke: var(--marker); }
.inline-link { color: inherit; text-decoration: underline; text-decoration-color: var(--line); text-decoration-thickness: 1px; text-underline-offset: 4px; transition: color .15s, text-decoration-color .15s; }
.inline-link:hover { color: var(--ink); text-decoration-color: currentColor; }
.inline-link::after, .chip::after {
  content: ""; display: inline-block; width: 10px; height: 10px; margin-left: 2px; vertical-align: 1px;
  background: var(--body); transition: transform .15s, background-color .15s;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.6'%3E%3Cpath d='M4 12 12 4M5 4h7v7'/%3E%3C/svg%3E") center / contain no-repeat;
}
.inline-link:hover::after, .chip:hover::after { transform: translate(1px, -1px); background: var(--ink); }
.small-copy { color: var(--body); font-size: 13px; line-height: 20px; }
.media-frame { width: 100%; aspect-ratio: 4 / 3; overflow: hidden; background: var(--surface); }
.media-frame > img { width: 100%; height: 100%; object-fit: cover; }
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .media-frame > img { transition: transform 600ms cubic-bezier(.25, .1, .25, 1); transform-origin: center; }
  .media-frame:hover > img { transform: scale(1.09); }
}
/* Home reads as one flow of short paragraphs: the opening line is ink, the rest is body grey, and every paragraph sits one line apart. */
.home-prose { display: flex; flex-direction: column; gap: 28px; }
.home-intro { color: var(--ink); font-weight: 400; }
/* Product names inside a sentence: the grey tile tells them apart from plain links.
   Top-aligned and nudged down 3px, so the 22px chip sits centred in the 28px line without growing it. */
.chip { position: relative; top: 3px; display: inline-block; height: 22px; margin: 0 4px; padding: 0 6px; vertical-align: top; background: var(--surface); color: var(--ink); font-size: 14px; line-height: 22px; white-space: nowrap; transition: background-color .15s; }
.chip:hover { background: var(--line); }
/* A full-width colon or semicolon carries half an em of blank space on its right, which piles onto the chip margin.
   Halve the punctuation and the margin so the gap matches a chip that follows a plain character. */
.punct-tight { font-feature-settings: "halt"; }
.punct-tight + .chip { margin-left: 2px; }
/* A chip followed by a comma drops its right margin; the comma's own blank space already makes the gap. */
.keep > .chip { margin-right: 0; }
/* A chip that wraps to the start of a line drops its left margin (site.js marks it), so that line stays flush with the rest. */
.chip.is-line-start { margin-left: 0; }
/* Hold a link and the punctuation after it on one line, so the arrow never lets a comma start the next line. */
.keep { white-space: nowrap; }
.final-section { align-items: flex-start; }
.final-section > .flow { display: flex; flex-direction: column; }
.final-section .prose { margin-top: 24px; margin-bottom: 24px; }

.site-footer {
  display: flex; flex-direction: column; align-items: center; gap: 24px;
  margin-top: 64px; padding-top: 40px; border-top: 1px dashed var(--line);
  color: var(--body); font-size: 14px; line-height: 24px; text-align: center;
}
.footer-sign { display: flex; flex-direction: column; align-items: center; gap: 8px; color: var(--body); transition: color .15s; }
.footer-sign:hover { color: var(--ink); }
.footer-pen { width: 64px; height: 64px; filter: contrast(1.4); mix-blend-mode: multiply; transform-origin: 30% 90%; transition: transform .3s cubic-bezier(.16, 1, .3, 1); }
.footer-sign:hover .footer-pen { transform: rotate(-6deg); }
:root[data-theme="dark"] .footer-pen { filter: contrast(1.4) invert(1) brightness(1.6); mix-blend-mode: screen; }
.footer-name { font-family: "Homemade Apple", cursive; font-size: 22px; line-height: 40px; }

/* Home fits in one screen, so the button sits right after "欢迎来找我" and the footer shrinks to one quiet line. */
/* Buttons that sit inside the page or the dialog, not in a floating bar, are one step smaller than the shared button. */
.home-join { align-self: flex-start; }
.home-join, .qr-sheet .button { min-height: 32px; padding: 2px 12px; gap: 6px; font-size: 14px; }
.home-join .button-arrow, .qr-sheet .button .button-arrow { width: 12px; height: 12px; }
.home-footer { flex-direction: row; justify-content: space-between; align-items: center; gap: 16px; margin-top: 56px; padding-top: 24px; text-align: left; }
.home-footer .footer-name { font-size: 15px; line-height: 26px; }
/* Pen and signature sit side by side, so the one-line footer keeps the template sign-off. */
.home-footer .footer-sign { flex-direction: row; gap: 12px; }
.home-footer .footer-pen { width: 26px; height: 26px; }
.social-links { display: flex; align-items: center; gap: 8px; }
.social-link { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 28px; border-radius: 6px; transition: transform .15s; }
.social-link:hover { transform: translateY(-2px); }
.social-link img { display: block; object-fit: contain; transform: scale(.874); }
.social-link .social-logo-x { transform: scale(.8303); }
.social-link .social-logo-gmail { transform: scale(.8303); }
:root[data-theme="dark"] .social-logo-monochrome { filter: brightness(0) invert(1); }
.footer-progressive-blur {
  position: fixed;
  z-index: 20;
  right: 0;
  bottom: 0;
  left: 0;
  height: 160px;
  pointer-events: none;
  transition: opacity 180ms ease, visibility 180ms ease;
}
.footer-progressive-blur span {
  position: absolute;
  z-index: var(--blur-layer);
  inset: 0;
  -webkit-backdrop-filter: blur(var(--blur-radius));
  backdrop-filter: blur(var(--blur-radius));
  -webkit-mask-image: var(--blur-mask);
  mask-image: var(--blur-mask);
}
.footer-progressive-blur span:nth-child(1) { --blur-layer: 1; --blur-radius: .1875px; --blur-mask: linear-gradient(transparent 0%, #000 12.5%, #000 25%, transparent 37.5%); }
.footer-progressive-blur span:nth-child(2) { --blur-layer: 2; --blur-radius: .375px; --blur-mask: linear-gradient(transparent 12.5%, #000 25%, #000 37.5%, transparent 50%); }
.footer-progressive-blur span:nth-child(3) { --blur-layer: 3; --blur-radius: .75px; --blur-mask: linear-gradient(transparent 25%, #000 37.5%, #000 50%, transparent 62.5%); }
.footer-progressive-blur span:nth-child(4) { --blur-layer: 4; --blur-radius: 1.5px; --blur-mask: linear-gradient(transparent 37.5%, #000 50%, #000 62.5%, transparent 75%); }
.footer-progressive-blur span:nth-child(5) { --blur-layer: 5; --blur-radius: 3px; --blur-mask: linear-gradient(transparent 50%, #000 62.5%, #000 75%, transparent 87.5%); }
.footer-progressive-blur span:nth-child(6) { --blur-layer: 6; --blur-radius: 6px; --blur-mask: linear-gradient(transparent 62.5%, #000 75%, #000 87.5%, transparent 100%); }
.footer-progressive-blur span:nth-child(7) { --blur-layer: 7; --blur-radius: 12px; --blur-mask: linear-gradient(transparent 75%, #000 87.5%, #000 100%); }
.footer-progressive-blur span:nth-child(8) { --blur-layer: 8; --blur-radius: 24px; --blur-mask: linear-gradient(transparent 87.5%, #000 100%); }
.footer-progressive-blur::after {
  position: absolute;
  z-index: 9;
  inset: 0;
  background: linear-gradient(
    to bottom,
    rgb(var(--paper-rgb) / 0) 0%,
    rgb(var(--paper-rgb) / .3) 40%,
    rgb(var(--paper-rgb) / .8) 75%,
    rgb(var(--paper-rgb) / .98) 100%
  );
  content: "";
}

.floating-join-wrap {
  position: fixed;
  z-index: 30;
  bottom: 48px;
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
  pointer-events: none;
  transition: transform 500ms cubic-bezier(.76, 0, .24, 1), opacity 220ms ease, visibility 220ms ease;
}
.floating-join {
  pointer-events: auto;
  min-height: 44px;
  padding: 0 24px;
  font-size: 15px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .18), 0 2px 6px rgba(0, 0, 0, .08);
}
.has-join-dialog .floating-join-wrap,
.has-join-dialog .footer-progressive-blur { visibility: hidden; opacity: 0; pointer-events: none; }
.has-join-dialog .floating-join-wrap { transform: translateY(10px); }
/* The panel sits in the middle of the screen, wherever the button that opened it is. */
.join-dialog { position: fixed; inset: 0; width: min(360px, calc(100% - 32px)); max-height: calc(100dvh - 96px); margin: auto; padding: 24px; border: 0; border-radius: 0; background: var(--dialog-paper); box-shadow: var(--dialog-shadow); color: var(--body); overflow-y: auto; overscroll-behavior: contain; transform-origin: center; }
.join-dialog:focus { outline: none; }
.join-dialog::backdrop { background: rgb(var(--paper-rgb) / .25); -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px); animation: join-backdrop-in 180ms ease; }
.join-dialog.is-closing::backdrop { animation: join-backdrop-out 180ms ease forwards; }
@keyframes join-backdrop-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes join-backdrop-out { from { opacity: 1; } to { opacity: 0; } }
.dialog-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.dialog-close { display: grid; place-items: center; width: 32px; height: 32px; padding: 0; background: none; border: 0; color: var(--body); }
.dialog-close:hover { color: var(--ink); background: var(--surface); }
.dialog-close svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; }
.contact-methods { display: flex; gap: 8px; min-width: 0; padding: 0; border: 0; border-radius: 0; margin: 16px 0; }
.contact-methods label { position: relative; flex: 1; font-size: 13px; cursor: pointer; text-align: center; }
.contact-methods input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.contact-methods span { display: grid; place-items: center; min-height: 38px; padding: 5px 8px; }
.contact-methods label:hover span { background: var(--surface); }
.contact-methods input:checked + span { background: var(--surface); color: var(--ink); }
.contact-methods input:focus-visible + span { outline: 2px solid var(--body); outline-offset: -3px; }
/* The QR image carries a white margin of its own (7 of its 400px) that light mode blends away; a negative margin pulls it back so gaps are measured from the code itself. */
.qr-sheet { --qr-size: 200px; --qr-quiet: calc(var(--qr-size) * 7 / 400); position: relative; padding: 20px; background: var(--surface); text-align: center; }
.qr-sheet img { width: var(--qr-size); height: var(--qr-size); object-fit: contain; margin: calc(-1 * var(--qr-quiet)) auto; mix-blend-mode: multiply; }
/* Dark mode shows the code as issued, on a white tile: an inverted or blended code is risky to scan, and scanners need a light margin around it.
   The tile edge is what the eye sees there, so the margin pull-back is dropped. */
:root[data-theme="dark"] .qr-sheet img { margin-block: 0; padding: 10px; background: #fff; mix-blend-mode: normal; }
/* Both channels share one grid cell, so switching tabs keeps the panel the same height. */
.contact-channels { display: grid; }
.contact-channels > .qr-sheet { grid-area: 1 / 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; padding-bottom: 24px; }
.contact-channels > :not(.is-active) { visibility: hidden; }
/* The email panel has no QR margin to make up for, so it pads evenly and its content stays centred. */
/* Email content stands on the panel itself, with no tiles; the address sits closer to the icon than to the button. */
.contact-channels > [data-contact-channel="email"] { padding-top: 24px; gap: 16px; }
[data-contact-channel="email"] .button { margin-top: 8px; }
/* The panel top, the code, the ID and the panel bottom sit the same 24px apart by eye.
   The ID's letters start 6px below the top of its 22px line and end 3px above its bottom, so those come off the gap and the bottom padding. */
.contact-channels > [data-contact-channel="wechat"] { --qr-gap: 24px; padding-top: var(--qr-gap); padding-bottom: calc(var(--qr-gap) - 3px); gap: calc(var(--qr-gap) - 6px); }
/* The ID and address are what people copy, so they read at the same size as the product chips. */
.qr-sheet .copy-id { gap: 6px; font-size: 14px; line-height: 22px; }
.qr-sheet .copy-id svg { width: 14px; height: 14px; }
.contact-mail-icon { display: block; color: var(--ink); }
.contact-mail-icon svg { display: block; width: 28px; height: 28px; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.contact-note { margin-top: 16px; text-align: center; font-size: 13px; line-height: 22px; }
.join-steps { display: flex; flex-direction: column; list-style: none; padding: 0; margin: 16px 0 0; }
.join-steps li { display: flex; align-items: baseline; gap: 8px; padding: 6px 0; font-size: 13px; line-height: 20px; }
.join-steps li > span { flex: 0 0 18px; color: var(--body); font-size: 12px; }
.copy-id { display: inline-flex; align-items: center; gap: 5px; padding: 1px 7px; border: 0; border-radius: 0; background: var(--surface); color: var(--ink); font-size: 12px; line-height: 20px; vertical-align: baseline; }
.copy-id svg { width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 1.5; }
.copy-id.is-copied svg { display: none; }
.copy-id.is-copied::after { content: "✓"; color: var(--body); }
.copy-status:not(.sr-only) { margin-top: 12px; }
.dialog-foot { margin-top: 16px; padding-top: 16px; border-top: 1px dashed var(--line); color: var(--body); font-size: 12px; line-height: 20px; text-align: center; }
@media (max-width: 639px) {
  .page-sheet { padding-top: 64px; padding-bottom: calc(180px + env(safe-area-inset-bottom, 0px)); }
  .site-header { gap: 12px; }
  /* Phones give the panel the same 24px side margins as the page text. */
  .join-dialog { width: calc(100% - 48px); max-height: calc(100dvh - 48px); }
  .qr-sheet { --qr-size: 176px; padding-inline: 16px; }
  /* No floating button on home, so the bottom only needs normal breathing room. */
  .home .page-sheet { padding-bottom: calc(64px + env(safe-area-inset-bottom, 0px)); }
  .home-footer { flex-direction: column; align-items: flex-start; gap: 4px; }
}
@media (max-width: 359px) {
  .site-header { gap: 8px; }
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { transition-duration: .001ms !important; animation-duration: .001ms !important; }
}
@media print {
  .site-header, .floating-join-wrap, .footer-progressive-blur, .join-dialog, .skip-link { display: none; }
  .page-sheet { padding-block: 0; }
  main { padding-top: 0; }
}
