/* TangMeo — “A day above the menu bar.”
   One fixed notch, real app pixels inside it; eleven full-viewport scenes
   carry a day from dawn to night. Marketing chrome obeys Apple dark-page
   restraint: text ramp #f5f5f7/#a1a1a6/#6e6e73, CTA #0071e3, accent #2997ff,
   no gradient text, no glows. The app's info colors live only inside the
   panel renders. Motion mirrors NotchAnimations.swift (balanced). */

:root {
  color-scheme: dark;
  --bg: #0a0a0d;
  --hairline: rgba(255, 255, 255, 0.08);
  --hairline-strong: rgba(255, 255, 255, 0.14);
  --t-hi: #f5f5f7;
  --t-mid: #a1a1a6;                 /* 5.50:1 worst wall */
  /* Apple's own #6e6e73 assumes an opaque surface. Over these walls it measures
     2.79:1 on noon — and it carries the version string, the requirements line
     and the one sentence that tells you the notch is live. Raised to clear AA
     (4.69:1 worst wall) while staying a visible step below --t-mid. */
  --t-low: #94949a;
  --t-faint: #6e6e73;               /* only for text on an opaque fill */
  --accent: #2997ff;

  --shadow-panel: 0 12px 34px rgba(0, 0, 0, 0.42);
  --shadow-card: none;   /* nothing outside the notch casts a shadow */
  --panel-glow: transparent;

  /* Motion contract — NotchAnimations.swift, balanced preset:
     shell = interactiveSpring(duration .5, bounce 0), symmetric open/close;
     content in .22s / out .07s; route crossfade .22s; hover highlight .16s. */
  --shell-dur: .5s;
  --ease-shell: linear(0.0, 0.0575, 0.1802, 0.3206, 0.4549, 0.5727, 0.6709, 0.7499, 0.812, 0.86, 0.8965, 0.9239, 0.9444, 0.9596, 0.9707, 0.9789, 0.9848, 0.9891, 0.9922, 0.9944, 0.996, 0.9972, 0.998, 0.9986, 1);
  --hub-route-dur: .22s;
  --hub-highlight-dur: .16s;
  --ease-spring: cubic-bezier(.22, 1, .36, 1);
  --hub-shell-w: min(658px, 96vw);
  --hub-open-h: calc(var(--hub-shell-w) * 223 / 658);

  /* The marketing layer needs its OWN timing scale. The tokens above mirror
     NotchAnimations.swift and are correct inside a 658px panel, but 0.16s reads
     as a flinch across a full page. poly.app splits it the other way round:
     transforms are slow and certain (1s ease-in-out), opacity is nearly instant
     (0.1s). Position moves deliberately; presence just appears. */
  --mk-move: .9s;
  --mk-tone: .42s;
  --mk-show: .12s;
  --mk-ease: cubic-bezier(.22, .61, .24, 1);
  --mk-ease-io: cubic-bezier(.65, 0, .35, 1);
}

* { margin: 0; padding: 0; box-sizing: border-box; }

/* smooth only after load, so /#anchor visits jump instead of touring the page */
html { scrollbar-color: rgba(255, 255, 255, .18) transparent; }
html.smooth { scroll-behavior: smooth; }

body {
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", "Segoe UI", sans-serif;
  background: var(--bg);
  color: var(--t-hi);
  line-height: 1.47059;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
:lang(zh-Hans) body { font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", "Noto Sans SC", sans-serif; }
:lang(zh-Hant) body { font-family: -apple-system, BlinkMacSystemFont, "PingFang TC", "Microsoft JhengHei", "Noto Sans TC", sans-serif; }
:lang(ja) body { font-family: -apple-system, BlinkMacSystemFont, "Hiragino Sans", "Yu Gothic UI", "Noto Sans JP", sans-serif; }
:lang(ko) body { font-family: -apple-system, BlinkMacSystemFont, "Apple SD Gothic Neo", "Malgun Gothic", "Noto Sans KR", sans-serif; }
:lang(zh-Hans) .slide-sub, :lang(zh-Hant) .slide-sub, :lang(ja) .slide-sub, :lang(ko) .slide-sub,
:lang(zh-Hans) .end-faq p, :lang(zh-Hant) .end-faq p, :lang(ja) .end-faq p, :lang(ko) .end-faq p { line-height: 1.6; }

::selection {
  background: rgba(127, 127, 127, .28);
  background: color-mix(in srgb, currentColor 22%, transparent);
  color: inherit;
}

.mono { font-family: "SF Mono", ui-monospace, Menlo, monospace; }

/* ================= time sky =================
   The reference uses three static gradient plates. The only motion is their
   opacity: an eight-second opening dissolve, then 1.2-second time changes. */
.sky {
  position: fixed; inset: 0; height: 100vh; z-index: 0;
  overflow: hidden; pointer-events: none; background: #000;
}
.sky-time-layer {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  opacity: 0;
  transition: opacity 1.2s ease-in-out;
  will-change: opacity;
}

/* With JavaScript unavailable, the authored opening time (09:41) stays Day. */
.sky-time-day { opacity: 1; }
.js .sky-time-day { opacity: 0; }
.js body[data-sky="day"] .sky-time-day,
.js body[data-sky="dusk"] .sky-time-dusk,
.js body[data-sky="night"] .sky-time-night { opacity: 1; }
.js body.sky-opening .sky-time-layer { transition-duration: 8s; }

.sky-time-day {
  background:
    linear-gradient(90deg, rgba(25, 74, 232, .2) 2.75%, rgba(255, 190, 10, .2) 99.26%),
    radial-gradient(170.32% 104.24% at 50% -.22%, rgba(38, 84, 144, .5) 17.02%, rgba(255, 255, 255, .5) 100%),
    radial-gradient(570.89% 154.84% at 50% -37.73%,
      rgba(0, 0, 0, .5) 0%, rgba(2, 32, 68, .5) 21%,
      rgba(31, 93, 135, .5) 45.71%, rgba(93, 109, 142, .5) 61.83%,
      rgba(159, 138, 145, .5) 73.67%, rgba(205, 155, 142, .5) 80.39%,
      rgba(255, 226, 97, .5) 88%, rgba(255, 126, 34, .5) 92.69%,
      rgba(205, 122, 21, .5) 97.68%),
    linear-gradient(180deg, #2c84d7, #84a9ff),
    linear-gradient(180deg,
      #000 -11.51%, #022044 18.08%, #1f5d87 52.9%,
      #5d6d8e 75.62%, #9f8a91 92.29%, #cd9b8d 101.77%,
      #ffc261 112.49%, #ffc422 119.1%, #cd4015 126.13%),
    linear-gradient(0deg, #000, #000),
    #fff;
  background-blend-mode: overlay, screen, overlay, normal, normal, normal, normal;
}
.sky-time-dusk {
  background:
    linear-gradient(90deg, rgba(25, 153, 232, .15) 2.75%, rgba(164, 91, 242, .15) 99.26%),
    linear-gradient(180deg,
      rgba(0, 0, 0, .5) .85%, rgba(0, 5, 46, .5) 25.81%,
      rgba(41, 40, 94, .5) 58.36%, rgba(84, 60, 123, .5) 79.52%,
      rgba(133, 90, 146, .5) 95.8%, rgba(195, 134, 171, .5) 107.19%),
    linear-gradient(180deg,
      #000 .85%, #112d72 33.4%, #4b52aa 49.68%,
      #a887dc 70.84%, #e6c4e7 95.8%, #fcdbef 107.19%),
    #000;
  background-blend-mode: overlay, normal, normal, normal;
}
.sky-time-night {
  background:
    linear-gradient(90deg, rgba(25, 153, 232, .1) 2.75%, rgba(164, 91, 242, .1) 99.26%),
    radial-gradient(83.41% 104.24% at 50% -.22%, rgba(38, 84, 144, 0) 72.08%, rgba(255, 117, 117, 0) 100%),
    radial-gradient(609.81% 213.79% at 50% -37.73%,
      rgba(0, 0, 0, .5) 0%, rgba(2, 32, 68, .5) 21%,
      rgba(31, 93, 135, .5) 45.71%, rgba(93, 109, 142, .5) 61.83%,
      rgba(159, 138, 145, .5) 73.67%, rgba(205, 155, 142, .5) 80.39%,
      rgba(255, 162, 97, .5) 88%, rgba(255, 126, 34, .5) 92.69%,
      rgba(205, 64, 21, .5) 97.68%),
    linear-gradient(180deg,
      #000 -11.51%, #022044 18.08%, #1f5d87 52.9%,
      #5d6d8e 75.62%, #9f8a91 92.29%, #cd9b8d 101.77%,
      #ffa261 112.49%, #ff7e22 119.1%, #cd4015 126.13%),
    #000;
  background-blend-mode: overlay, overlay, normal, normal, normal;
}

/* the notch's top flare radius: registered so it inherits into ::before/::after
   and interpolates with the shell (a plain var() does not re-resolve there) */
@property --notch-top-r { syntax: "<length>"; inherits: true; initial-value: 8px; }
/* hour light interpolates with the walls instead of snapping at scene switches */
@property --panel-glow { syntax: "<color>"; inherits: true; initial-value: transparent; }
/* Preserve the panel's authored per-scene underlight independently of the sky. */
body[data-wall="dawn"]    { --panel-glow: rgba(226, 138, 138, .34); }
body[data-wall="noon"]    { --panel-glow: rgba(176, 200, 240, .34); }
body[data-wall="dusk"]    { --panel-glow: rgba(232, 150, 88, .32); }
body[data-wall="evening"] { --panel-glow: rgba(96, 126, 190, .22); }
body[data-wall="night"]   { --panel-glow: rgba(60, 80, 150, .10); }


/* ================= quiet chrome ================= */
/* ================= the menu bar =================
   A macOS menu bar is identified by its CONTENTS, not by a visible strip:
   Apple mark, the frontmost app's name in semibold, its menu titles, then the
   status area and the clock. Geometry follows a real bar (34px tall, 13px/590
   items, 24px title gaps, 36×22 r20 status hit areas, no bottom rule). */
.chrome {
  position: fixed; top: 0; left: 0; right: 0;
  z-index: 40; /* under the notch (50): the open panel drops over the bar, like the real app */
  height: 34px; /* = closed-notch height; the notch is a cutout in this strip */
  display: flex; align-items: center; gap: 24px;
  padding: 0 16px;
  pointer-events: none;
  font-size: 13px; font-weight: 400; line-height: 1;
  color: #fff;
  /* the only concession to legibility, the same one macOS makes */
  text-shadow: 0 1px 2px rgba(0, 0, 0, .38);
}
.chrome > * { pointer-events: auto; }
.mb-apple { display: flex; pointer-events: none; }
.mb-apple svg { width: 12.06px; height: 15px; color: #fff; display: block; }
.brand {
  font: inherit; font-weight: 590; /* the one emphasised item: the frontmost app */
  color: #fff; text-decoration: none;
  letter-spacing: 0; cursor: default;
}
.mb-menus { display: flex; align-items: center; gap: 24px; }
/* File/Edit/Help are set dressing and must not be clickable; View is the
   page's navigation, so pointer-events come back for it alone. */
.mb-menus span { color: #fff; pointer-events: none; }
.mb-menu {
  appearance: none; background: none; border: 0; font: inherit; color: #fff;
  padding: 2px 7px; margin: 0 -7px; border-radius: 5px; cursor: default;
  transition: background-color var(--mk-tone) var(--mk-ease);
}
.mb-menu:hover, .mb-menu.open { background-color: rgba(255, 255, 255, .14); }
.mb-menu:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.chrome-right { margin-left: auto; display: flex; align-items: center; gap: 0; }
/* status area: 36×22 pills that light on hover, exactly like the real thing */
.mb-item {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  width: 36px; height: 22px; border-radius: 20px;
  color: #fff; text-decoration: none; cursor: default;
  transition: background-color var(--mk-tone) var(--mk-ease);
}
.mb-item svg { width: 15px; height: 15px; display: block; }
.mb-status svg { width: 19px; height: 19px; }
.mb-paw svg { width: 16px; height: 16px; }
.mb-item:hover { background-color: rgba(255, 255, 255, .14); }
.mb-lang { width: 44px; padding: 0; gap: 3px; }
.mb-lang svg { width: 13px; height: 13px; opacity: .92; }
.mb-lang-code { font: inherit; letter-spacing: .01em; }
.mb-lang:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.day-clock {
  display: flex; align-items: center; height: 22px;
  padding: 0 6px; border-radius: 20px;
  font: inherit; color: #fff;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* the language item opens the app's own menu (.p-menu), never a browser dropdown */
.mb-lang { border: none; font: inherit; }
/* the language control wears its selected fill at rest — it is the one item in
   the bar that is a live control rather than set dressing, so it stays legible
   instead of only appearing once you have already found it */
.mb-lang, .mb-lang.open { background-color: rgba(255, 255, 255, .14); }
.mb-lang:hover { background-color: rgba(255, 255, 255, .22); }

/* ================= the one fixed notch ================= */
.notch-fixed {
  position: fixed; top: 0; left: 50%; transform: translateX(-50%);
  z-index: 50;
  width: 190px; height: 34px;
  background: #000;
  border-radius: 0 0 12px 12px;
  /* the notch flares outward where it meets the top edge, exactly like
     NotchShape.swift: top radius = baseHeight/3 − 4 closed, 18 expanded */
  --notch-top-r: 8px;
  display: flex; justify-content: center;
  /* graceful fallback; inside @supports the true linear() spring takes over —
     a var()-routed linear() would win the cascade even where unsupported and
     then die at computed-value time, killing the transition entirely */
  transition:
    width var(--shell-dur) cubic-bezier(.23, 1, .32, 1),
    height var(--shell-dur) cubic-bezier(.23, 1, .32, 1),
    border-radius var(--shell-dur) cubic-bezier(.23, 1, .32, 1),
    box-shadow var(--shell-dur) cubic-bezier(.23, 1, .32, 1),
    --notch-top-r var(--shell-dur) cubic-bezier(.23, 1, .32, 1);
}
@supports (transition-timing-function: linear(0, 1)) {
  .notch-fixed {
    transition:
      width var(--shell-dur) var(--ease-shell),
      height var(--shell-dur) var(--ease-shell),
      border-radius var(--shell-dur) var(--ease-shell),
      box-shadow var(--shell-dur) var(--ease-shell),
      --notch-top-r var(--shell-dur) var(--ease-shell);
  }
}
.notch-fixed.open {
  width: var(--hub-shell-w);
  /* 223 = the app's 217 + 6px web-only headroom above the shoulder buttons
     (a real Mac has the bezel there; a browser viewport has nothing) */
  height: var(--hub-open-h);
  border-radius: 0 0 40px 40px;
  --notch-top-r: 18px;
  box-shadow:
    inset 0 -1px 0 rgba(255, 255, 255, .05),
    var(--shadow-panel),
    0 10px 26px var(--panel-glow);
}
.notch-fixed:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.notch-fixed.gulp { animation: gulp .45s var(--ease-spring); }
@keyframes gulp {
  50% { transform: translateX(-50%) scale(1.02); }
}
/* what the shell's own overflow used to do, so the shell can paint outside itself */
.notch-clip {
  position: absolute; inset: 0;
  overflow: hidden; border-radius: inherit;
  display: flex; justify-content: center;
}
/* The two flared top corners of a real notch: the screen surface curves into
   the cutout. Drawn as an SVG quarter-circle path (cubic k=0.5523 — the same
   geometry Supaste ships) rather than a radial-gradient: a gradient's colour
   stop aliases along the arc, a path antialiases. No outline anywhere — the
   flare and the shell are one continuous black silhouette, which is the only
   way the corner can read as smooth (a border traces the BOX, never the shape,
   so it left a 1px step exactly where the arc met the straight edge). */
.notch-fixed::before, .notch-fixed::after {
  content: ""; position: absolute; top: 0;
  width: var(--notch-top-r); height: var(--notch-top-r);
  background-size: 100% 100%; background-repeat: no-repeat;
  pointer-events: none;
}
.notch-fixed::before {
  left: calc(-1 * var(--notch-top-r));
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' preserveAspectRatio='none'%3E%3Cpath d='M0 0L20 0L20 20C20 8.954 11.046 0 0 0Z' fill='%23000'/%3E%3C/svg%3E");
}
.notch-fixed::after {
  right: calc(-1 * var(--notch-top-r));
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' preserveAspectRatio='none'%3E%3Cpath d='M20 0L0 0L0 20C0 8.954 8.954 0 20 0Z' fill='%23000'/%3E%3C/svg%3E");
}
.hub {
  flex: none;
  width: 658px; height: 223px;
  position: relative;
  /* Expanding content grows scale 0.5→1 anchored at the top (SuperIsland's
     content swap); narrow screens reflow at 1:1 instead of scaling the UI. */
  opacity: 0; transform: scale(calc(var(--hub-scale, 1) * .5)); transform-origin: 50% 0;
  visibility: hidden;
  transition: opacity .07s ease-out, transform .07s ease-out, visibility 0s .07s;
}
.notch-fixed.open .hub {
  opacity: 1; transform: scale(var(--hub-scale, 1)); visibility: visible;
  transition: opacity var(--hub-route-dur) cubic-bezier(.4, 0, .2, 1), transform var(--hub-route-dur) cubic-bezier(.32, .72, 0, 1), visibility 0s;
}
.hub-pages { position: absolute; inset: 0; }
/* hub route navigation: symmetric 0.22s easeInOut directional push
   (the 0.22/0.07 asymmetry belongs to the shell open/close, not to routes) */
.hpage {
  position: absolute; inset: 0;
  opacity: 0; pointer-events: none;
  transition: opacity var(--hub-route-dur) ease-in-out, transform var(--hub-route-dur) ease-in-out;
}
.hpage.active { opacity: 1; pointer-events: auto; }

/* dragging a real file over the page: the notch is the drop target */
.notch-fixed.drop-target .hub::after {
  content: ""; position: absolute; inset: 6px;
  border-radius: 0 0 34px 34px;
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, .28);
  pointer-events: none;
  z-index: 9;
}

/* ================= slides ================= */
main { position: relative; z-index: 1; }
.slide {
  min-height: 100vh;
  min-height: 100svh;
  display: grid; place-items: end center;
  padding: 0 28px 11vh;
  position: relative;
}
/* The per-section vignette is gone. It was a 36vh darkening at the FOOT of every
   .slide, which against a single continuous fixed sky produced a hard horizontal
   seam at every section boundary — the sky above the join was shaded, the sky
   below it was not. That edge, not the scroll ramp, was the harshness. The sky
   is one uninterrupted gradient now, so nothing may shade it per-section. */
/* the fixed panel owns the top band — hero, mids and the atlas centre
   against what is actually below it (padding-top = the panel's open height) */
.slide-hero {
  place-items: start center;
  padding-top: calc(var(--hub-open-h) + 9vh);
  padding-bottom: 0;
}
.slide-mid {
  /* Centring that cannot overflow upward. Plain `center` overflows in BOTH
     directions once content outgrows the space, which pushed headlines under the
     notch. `safe center` is the spec's answer but Chrome silently ignores the
     keyword on grid align-content (measured: centring stayed, no fallback). So
     the track is stretched and the caption centres itself with auto margins —
     when space runs out, auto margins resolve to zero and content starts at the
     clearance edge. Guaranteed by the box model, no keyword support needed. */
  grid-template-rows: 1fr; align-items: center; justify-items: center;
  padding-top: var(--hub-open-h); padding-bottom: 0;
  min-height: 78vh; min-height: 78svh;
}
.slide-quiet { place-items: center; padding-bottom: 0; }
@media (max-height: 760px) and (min-width: 721px) {
  /* Short viewport (a 900px laptop screen minus browser chrome lands here):
     caption-only scenes go back to end-anchored. But a scene WITH a stage still
     has to clear the panel — this shorthand used to reset padding-top to 0 and
     slide the whole pair under the notch. */
  /* Short viewports bottom-anchor the caption — but they must still clear the
     notch. This rule reserved no top padding, which was invisible while
     captions were short and put tall ones straight under the notch. The 1fr
     track's implied min=auto grows to fit the caption, so end-alignment can
     never overflow upward; the section grows downward instead. */
  .slide-mid { grid-template-rows: 1fr; align-items: end; justify-items: center;
    padding: var(--hub-open-h) 28px 9vh;
    min-height: 100vh; min-height: 100svh; }
}

/* Everything centres. The notch is fixed at the top CENTRE of the viewport, so
   it is the anchor the whole page hangs from — a caption pushed to one side
   fights it. The reference sites alternate sides because they have no fixed
   centre element; we do, and it is the product. */
.cap { position: relative; max-width: var(--cap-w, 820px); text-align: center; }
/* The entrance vocabulary. Position moves on the slow marketing clock and
   presence just appears — poly.app's split, and the reason their motion reads as
   confident rather than fussy. Each scene declares data-enter; one shared
   transform channel reads it, so a new verb costs one rule. */
.js-ready .cap > * {
  opacity: 0;
  transform: var(--enter-from, translateY(24px));
  transition: opacity var(--mk-show) linear, transform var(--mk-move) var(--mk-ease);
}
.js-ready .slide.revealed .cap > * { opacity: 1; transform: none; }
.js-ready .slide.revealed .cap > *:nth-child(2) { transition-delay: 0s, .06s; }
.js-ready .slide.revealed .cap > *:nth-child(3) { transition-delay: 0s, .12s; }
.js-ready .slide.revealed .cap > *:nth-child(4) { transition-delay: 0s, .18s; }
.js-ready .slide.revealed .cap > *:nth-child(5) { transition-delay: 0s, .24s; }
[data-enter="rise"]   { --enter-from: translateY(34px); }
[data-enter="settle"] { --enter-from: scale(.955); }
[data-enter="lead"]   { --enter-from: translateY(24px) scale(.985); }
[data-enter="trail"]  { --enter-from: translateY(28px); }
[data-enter="still"]  { --enter-from: none; }

.slide h1 {
  font-size: clamp(40px, 5.5vw, 80px);
  font-weight: 600; letter-spacing: -0.015em; line-height: 1.05;
  text-wrap: balance;
  color: var(--t-hi);
}
.slide h2 {
  font-size: 42px;
  font-weight: 600; letter-spacing: -0.015em; line-height: 1.08;
  text-wrap: balance;
  color: var(--t-hi);
}
:lang(zh-Hans) .slide h1, :lang(zh-Hant) .slide h1, :lang(ja) .slide h1, :lang(ko) .slide h1,
:lang(zh-Hans) .slide h2, :lang(zh-Hant) .slide h2, :lang(ja) .slide h2, :lang(ko) .slide h2 {
  letter-spacing: 0; line-height: 1.16;
  hanging-punctuation: allow-end;
}
.slide-sub {
  margin: 24px auto 0; max-width: 56ch;
  font-size: 20px; line-height: 1.4286; letter-spacing: .011em;
  color: var(--t-mid);
  text-wrap: pretty;
}
.slide-actions { margin-top: 32px; display: flex; gap: 20px; justify-content: center; align-items: center; flex-wrap: wrap; }
.btn {
  display: inline-block; text-decoration: none; font-weight: 400; font-size: 17px;
  padding: 12px 22px; border-radius: 999px;
  background: #0071e3;
  color: #fff;
  transition: background var(--mk-tone) var(--mk-ease), transform var(--hub-highlight-dur) ease-in-out;
}
.btn:hover { background: #0077ed; }
.btn:active { transform: scale(.98); }
.btn:focus-visible, .btn-link:focus-visible, .mb-paw:focus-visible, .brand:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.btn-link {
  font-size: 17px; color: var(--accent); text-decoration: none;
}
.btn-link::after { content: " ›"; }
.btn-link:hover { text-decoration: underline; }
.slide-meta { margin-top: 20px; font-size: 12px; font-weight: 400; color: var(--t-low); }
.slide-hint { margin-top: 12px; font-size: 12px; color: var(--t-low); }

/* 10:15 — the file rides the scroll into the notch (JS-driven transform) */
.fly-ghost {
  position: fixed; left: 50%; top: 0;
  z-index: 45;
  opacity: 0;
  pointer-events: none;
  will-change: transform, opacity;
}
.file-card {
  width: 118px;
  /* the fill is already 82% opaque, so the blur behind it was near-invisible —
     and backdrop-filter measured 9.6ms per frame, over half the 16.7ms budget */
  background: rgba(20, 20, 26, .93);
  border-radius: 10px; padding: 10px 8px 8px;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .08);
}
.fdoc {
  width: 30px; height: 30px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  color: rgba(255, 255, 255, .65); background: rgba(255, 255, 255, .08);
}
.fdoc svg { width: 15px; height: 15px; }
.file-card b { font-size: 10px; font-weight: 500; color: rgba(255, 255, 255, .5); }
.fly-cursor {
  position: absolute; right: -14px; bottom: -12px;
  width: 15px; height: 22px;
}

/* 17:30 — the day's debris: real cards drifting */
.debris { position: absolute; inset: 0; pointer-events: none; }
.dcard {
  position: absolute;
  height: auto;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .08);
  animation: drift 8s ease-in-out infinite;
  transition: opacity var(--mk-show) linear;
}
.js-ready .dcard { opacity: 0; }
.js-ready .slide.revealed .dcard { opacity: 1; }
.js-ready .slide.revealed .d2 { transition-delay: .1s; }
.js-ready .slide.revealed .d3 { transition-delay: .2s; }
.js-ready .slide.revealed .d4 { transition-delay: .3s; }
/* Live-activity cards, drawn with the app's own ladder: fills .04–.14, no
   strokes, text .85/.5/.4, 14/12/10, r12. Nothing here is an image. */
.dcard {
  width: 250px; padding: 12px 13px 13px;
  border-radius: 14px;
  background: rgba(30, 30, 34, .96);
  font-size: 12px;
}
.lc-head { display: flex; align-items: center; gap: 6px; margin-bottom: 9px; }
.lc-head svg { width: 13px; height: 13px; color: rgba(255,255,255,.5); }
.lc-head span { font-size: 10px; font-weight: 500; color: rgba(255,255,255,.5); letter-spacing: .02em; }
.lc-np, .lc-cal { display: flex; align-items: center; gap: 9px; }
.lc-art {
  width: 34px; height: 34px; border-radius: 7px; flex-shrink: 0;
  background: linear-gradient(145deg, #4a5a78, #2a2f42);
}
.lc-dot { width: 8px; height: 8px; border-radius: 50%; background: #4CC76B; flex-shrink: 0; }
.lc-npmeta { min-width: 0; }
.lc-npmeta b { display: block; font-size: 12px; font-weight: 600; color: rgba(255,255,255,.85);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lc-npmeta span { display: block; font-size: 10px; color: rgba(255,255,255,.4);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lc-dlname { margin-bottom: 8px; }
.lc-scrub, .lc-bar { height: 3px; border-radius: 2px; background: rgba(255,255,255,.14); overflow: hidden; }
.lc-scrub { margin: 11px 0 5px; }
.lc-bar { margin-top: 9px; }
.lc-scrub i, .lc-bar i { display: block; height: 100%; border-radius: 2px; background: rgba(255,255,255,.75);
  transition: width 1s linear; }
.lc-bar i { background: #4CC76B; }
.lc-times { display: flex; justify-content: space-between; font-size: 9px; color: rgba(255,255,255,.4); }
.lc-big { font-size: 26px; font-weight: 300; color: rgba(255,255,255,.92); letter-spacing: -.01em;
  font-variant-numeric: tabular-nums; }
/* The open panel owns the top ~223px and the caption owns the bottom third, so
   the cards live in the band between. Each drifts on its own axis and period —
   in unison they read as one sprite sheet rather than four separate activities. */
/* The panel owns the top ~223px and the caption owns a ~730px column down the
   middle, so the cards flank it: two down each margin, never behind the words. */
.d1 { width: 246px; left: 4%;  top: 27%; transform: rotate(-4deg); animation-duration: 8s; }
.d2 { width: 232px; right: 4%; top: 25%; transform: rotate(3.5deg); animation-duration: 9.4s; animation-delay: -2s; animation-name: drift-b; }
.d3 { width: 236px; left: 6%;  top: 53%; transform: rotate(2.5deg); animation-duration: 7.1s; animation-delay: -4s; animation-name: drift-c; }
.d4 { width: 244px; right: 5%; top: 50%; transform: rotate(-3deg); animation-duration: 10.6s; animation-delay: -6s; animation-name: drift-b; }
/* below this the margins stop being wide enough to hold a card beside the text */
@media (max-width: 1240px) {
  .d1, .d2, .d3, .d4 { width: 210px; }
  .d1 { left: 1%; } .d2 { right: 1%; } .d3 { left: 2%; top: 56%; } .d4 { right: 2%; top: 54%; }
}
@media (max-width: 900px) { .d3, .d4 { display: none; } }
@keyframes drift   { 0%, 100% { translate: 0 0; }      50% { translate: 0 -15px; } }
@keyframes drift-b { 0%, 100% { translate: 0 0; }      50% { translate: -9px -11px; } }
@keyframes drift-c { 0%, 100% { translate: 0 -4px; }   50% { translate: 7px -17px; } }

/* 19:30 — the atlas: Apple feature-grid, no pills */
/* max-width alone is dead here: `place-items: center` shrink-wraps the element,
   so the measure ends up dictated by whatever the longest subtitle string is.
   width forces it to actually claim the column. */
.cap-wide { width: min(1320px, 100%); max-width: 1320px; }
/* The row is one control: it discloses the detail AND routes the notch above.
   No fill, no border, no radius — the same hairline list the FAQ already uses. */
/* the row whose tool the notch is currently showing */
.js .js .slide.revealed @media (max-width: 620px) {
      }

/* ================= the end slide ================= */
.slide-end { place-items: center; padding-bottom: 8vh; }
.slide-end h2 { font-size: 52px; }
.end-price {
  margin-top: 24px;
  display: flex; align-items: baseline; justify-content: center; gap: 10px; flex-wrap: wrap;
  color: var(--t-mid); font-size: 17px;
}
.end-price b { font-size: 46px; font-weight: 600; letter-spacing: -0.015em; color: var(--t-hi); }

/* ================= fine print: FAQ + footer, plain flow ================= */
.faq-block {
  position: relative; z-index: 1;
  max-width: 860px; margin: 0 auto;
  /* The FAQ follows 21:00, where the notch is CLOSED — so it needs to clear 34px,
     not the 223px of an open panel. The old clearance left a void, and the rule
     across the top of it drew a line under nothing. */
  padding: 92px 28px 90px;
  scroll-margin-top: var(--hub-open-h);
}
.faq-block h2 {
  font-size: 30px; font-weight: 600; letter-spacing: -0.015em;
  color: var(--t-hi);
  text-align: center; margin-bottom: 28px;
}
:lang(zh-Hans) .faq-block h2, :lang(zh-Hant) .faq-block h2, :lang(ja) .faq-block h2, :lang(ko) .faq-block h2 {
  letter-spacing: 0; line-height: 1.16;
}
/* no rules between questions — the FAQ never had lines; spacing carries it */
.end-faq details { padding: 18px 0; }
.end-faq summary {
  cursor: pointer; font-size: 17px; font-weight: 600; color: var(--t-hi);
  list-style: none; display: flex; justify-content: space-between; align-items: center; gap: 16px;
  transition: color var(--mk-tone) var(--mk-ease);
}
.end-faq summary::-webkit-details-marker { display: none; }
.end-faq summary::after {
  content: "+"; font-size: 20px; font-weight: 300; line-height: 1; color: var(--t-low);
  width: 22px; height: 22px; display: grid; place-items: center;
  border-radius: 50%; flex-shrink: 0;
  transition:
    transform var(--hub-route-dur) ease-in-out,
    background-color .24s var(--mk-ease),
    box-shadow .24s var(--mk-ease);
}
.end-faq summary:hover { color: var(--t-hi); }
.end-faq summary:hover::after {
  background-color: color-mix(in srgb, var(--t-hi) 3%, transparent);
  box-shadow: 0 0 0 6px color-mix(in srgb, var(--t-hi) 4%, transparent);
}
.end-faq details[open] summary::after {
  transform: rotate(45deg);
  background-color: color-mix(in srgb, var(--t-hi) 6%, transparent);
  box-shadow:
    0 0 0 6px color-mix(in srgb, var(--t-hi) 8%, transparent),
    inset 0 0 0 1px color-mix(in srgb, var(--t-hi) 10%, transparent);
}
.end-faq summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.end-faq details p {
  margin-top: 10px;
  font-size: 14px; line-height: 1.4286;
  color: var(--t-mid);
}
.end-faq details[open] p { animation: faqReveal .3s var(--ease-spring); }
@keyframes faqReveal { from { opacity: 0; transform: translateY(-4px); } }

/* This is created by main.js after the document. Keep it out of normal flow:
   its path connects the visible selected capability glyph to the fixed notch. */
.more-route {
  position: fixed; z-index: 49; inset: 0;
  width: 100%; height: 100%;
  overflow: visible; pointer-events: none;
  opacity: 0;
  transition: opacity .22s var(--mk-ease);
}
.more-route.show { opacity: 1; }
.more-route path {
  fill: none;
  stroke: color-mix(in srgb, var(--t-hi) 30%, transparent);
  stroke-width: 1.2;
  stroke-linecap: round;
}

/* Fixed chrome yields to the final content as that content reaches its space.
   JavaScript publishes a continuous scroll-derived value; no-JS stays visible. */
.chrome, .notch-fixed { opacity: var(--footer-chrome-opacity, 1); }
body.footer-chrome-hidden .chrome,
body.footer-chrome-hidden .notch-fixed {
  visibility: hidden;
  pointer-events: none;
}
/* `.notch-fixed.open .hub` explicitly restores visibility, so the panel
   subtree must yield with its shell once the footer owns the viewport. */
body.footer-chrome-hidden .notch-fixed .hub {
  visibility: hidden;
  pointer-events: none;
}

/* ============= Supaste composition, native TangMeo Night sky ============== */
.site-footer {
  position: relative; z-index: 1; overflow: hidden;
  min-height: 100vh; min-height: 100svh;
  display: flex; flex-direction: column;
  color: #fff;
  /* The site owns one fixed sky. The footer reveals it directly instead of
     painting a second Night plate with a visible compositing boundary. */
  background: transparent;
  --footer-pad: clamp(28px, 3.2vw, 40px);
}
.site-footer::before {
  content: ""; position: absolute; z-index: 0; inset: 0;
  pointer-events: none;
  background: linear-gradient(180deg,
    rgba(2, 8, 22, 0) 0%,
    rgba(3, 8, 22, .42) 46%,
    rgba(5, 8, 24, .74) 100%);
}
.site-footer-grid {
  position: relative; z-index: 1;
  flex: 0 0 auto;
  width: 100%; max-width: 1200px; margin-inline: auto;
  display: grid;
  grid-template-columns: minmax(0, 2fr) repeat(3, minmax(0, 1fr));
  align-items: start; gap: 40px;
  padding: clamp(72px, 6.95vw, 100px) var(--footer-pad) 40px;
}
.site-footer-brand { min-width: 0; }
.footer-identity {
  display: inline-flex; align-items: center; gap: 10px;
  color: #fff; text-decoration: none;
}
.footer-app-icon {
  width: 30px; height: 30px; border-radius: 8px;
  display: block; flex-shrink: 0; overflow: hidden;
}
.footer-app-icon img { width: 100%; height: 100%; display: block; }
.footer-identity strong {
  font-size: 16px; font-weight: 600; letter-spacing: -.02em;
}
.footer-identity > span:last-child {
  font-size: 15px; letter-spacing: -.02em; color: rgba(255,255,255,.60);
}
.footer-claim {
  margin-top: 32px;
  font-size: 30px; font-weight: 700; line-height: 1; letter-spacing: -.05em;
  text-wrap: balance;
}
.footer-claim span, .footer-claim em { display: block; }
.footer-claim em {
  font-family: Georgia, "Songti SC", "STSong", "Times New Roman", serif;
  font-size: 1.06em; font-weight: 400; font-style: italic; letter-spacing: -.04em;
}
.footer-description {
  max-width: 38ch; margin-top: 24px;
  color: rgba(255,255,255,.72);
  font-size: 14px; line-height: 1.45; letter-spacing: -.01em;
}
.footer-actions {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  margin-top: 26px;
}
.footer-btn {
  min-height: 42px; padding: 0 16px; border-radius: 8px;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  font-size: 14px; font-weight: 600; line-height: 1.2;
  text-decoration: none;
  transition: background-color .2s ease, border-color .2s ease, color .2s ease;
}
.footer-btn svg { width: 14px; height: 17px; flex-shrink: 0; }
.footer-btn-primary { background: #fff; color: #0b0d12; }
.footer-btn-primary:hover { background: #eceef2; }
.footer-btn-secondary {
  color: #fff; border: 1px solid rgba(255,255,255,.18);
  background: rgba(255,255,255,.08);
}
.footer-btn-secondary:hover {
  border-color: rgba(255,255,255,.30);
  background: rgba(255,255,255,.13);
}
.footer-btn-secondary b { font-weight: 700; }
.footer-copyright {
  margin-top: 34px; max-width: 46ch;
  color: rgba(255,255,255,.60);
  font-size: 12px; line-height: 1.55;
}
.footer-copyright a { color: rgba(255,255,255,.76); text-decoration: none; }
.footer-copyright a:hover { color: #fff; text-decoration: underline; }
.footer-column {
  min-width: 0;
  display: flex; flex-direction: column; align-items: flex-start; gap: 15px;
}
.footer-column h3 {
  margin-bottom: 5px;
  color: rgba(255,255,255,.60);
  font-size: 15px; font-weight: 600; line-height: 1.3; letter-spacing: -.02em;
}
.footer-column a {
  color: rgba(255,255,255,.94);
  font-size: 14px; line-height: 1.4; text-decoration: none;
  transition: color .2s ease;
}
.footer-column a:hover { color: rgba(255,255,255,.70); }
.footer-directory {
  gap: 0;
}
.footer-directory h3 {
  margin-bottom: 20px;
}
.footer-directory-links {
  width: 100%;
  display: grid; gap: 8px;
}
.site-footer :is(a, .footer-btn):focus-visible {
  outline: 2px solid rgba(255,255,255,.92); outline-offset: 4px;
}
/* The CSS-only fallback keeps the authored Day plate. Preserve that native
   surface unmodified and use the established opaque Day ink instead. */
html:not(.js) .site-footer::before { background: none; }
html:not(.js) .site-footer { color: #101624; }
html:not(.js) .footer-identity,
html:not(.js) .footer-identity > span:last-child,
html:not(.js) .footer-description,
html:not(.js) .footer-copyright,
html:not(.js) .footer-copyright a,
html:not(.js) .footer-column h3,
html:not(.js) .footer-column a,
html:not(.js) .footer-column a:hover,
html:not(.js) .footer-copyright a:hover {
  color: #05070c;
}
html:not(.js) .footer-btn-secondary {
  color: #05070c;
  border-color: rgba(5,7,12,.22);
  background: rgba(5,7,12,.08);
}
html:not(.js) .footer-btn-secondary:hover {
  border-color: rgba(5,7,12,.30);
  background: rgba(5,7,12,.13);
}
html:not(.js) .site-footer :is(a, .footer-btn):focus-visible {
  outline-color: #05070c;
}
html:not(.js) .site-footer-wordmark { color: rgba(5,7,12,.08); }
.site-footer-wordmark {
  position: relative; z-index: 1;
  flex: 0 0 auto;
  width: 100%; max-width: 1200px;
  height: clamp(110px, 23vw, 340px);
  margin-top: auto; margin-inline: auto; overflow: hidden;
  display: flex; align-items: center;
  color: rgba(226,238,255,.09);
  font-size: clamp(78px, 19.2vw, 288px);
  font-weight: 700; line-height: 1; letter-spacing: -.025em;
  white-space: nowrap; user-select: none;
}
@media (max-width: 900px) {
  .site-footer-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
  .site-footer-brand { grid-column: 1 / -1; }
}
@media (max-width: 620px) {
  .site-footer { --footer-pad: 28px; }
  .site-footer-grid {
    grid-template-columns: minmax(0, 1fr);
    gap: 40px; padding-bottom: 32px;
  }
  .site-footer-brand { grid-column: auto; }
  .footer-claim { font-size: 29px; }
  .footer-description { max-width: 100%; }
}

/* ================= reduced motion ================= */
@media (prefers-reduced-motion: reduce) {
  html.smooth { scroll-behavior: auto; }
  .js .cap, .js .cap > *, .js .dcard, .js .idx-row { opacity: 1; transform: none; transition: none; }
  .dcard { animation: none; }
  .notch-fixed, .hub, .hpage, .sky-time-layer, .more-route { transition: none; }
  .notch-fixed.open .hub { transition: none; }
  .end-faq details[open] p { animation: none; }
  .chrome > *, .mb-menus, .chrome-right > * { transition: none; }
  .site-footer :is(a, .footer-btn) { transition: none; }
  .notch-fixed.gulp { animation: none; }
}

/* The bar is the screen and never blinks out. Its citizens step aside in the
   order the open panel actually reaches them — menu titles and the status
   glyphs first, the two corners (Apple mark + clock) last, so the bar keeps
   its identity for as long as geometry allows. `.bar-tight` is set by main.js
   from measured collision geometry: the threshold moves with the language,
   which a static breakpoint cannot follow (de/es collide 220px earlier than en).
   visibility rides along so faded items also leave the tab order. */
.chrome > *, .mb-menus, .chrome-right > * {
  transition: opacity var(--hub-route-dur) ease-in-out,
              visibility 0s linear var(--hub-route-dur);
}
body:not(.panel-open) .chrome > *,
body:not(.panel-open) .mb-menus,
body:not(.panel-open) .chrome-right > * { transition-delay: 0s, 0s; }
body.panel-open.bar-tight .mb-menus,
body.panel-open.bar-tight .chrome-right > *:not(.day-clock) {
  opacity: 0; visibility: hidden; pointer-events: none;
}
@media (max-width: 920px) {
  body.panel-open .chrome > * { opacity: 0; visibility: hidden; pointer-events: none; }
}

/* the closed notch owns 190px of the strip's centre: bar citizens retreat
   before they end up behind it (measured per locale, German is the widest) */
@media (max-width: 1040px) { .mb-menus { display: none; } }
@media (max-width: 940px)  { .mb-status { display: none; } }

@media (max-width: 430px) { .brand { display: none; } }
@media (max-width: 400px) { .mb-paw { display: none; } }

/* ================= responsive ================= */
@media (max-width: 720px) {
  :root {
    --hub-shell-w: min(658px, calc(100vw - 16px));
    --hub-open-h: min(320px, calc(100svh - 24px));
  }
  .notch-fixed.open { border-radius: 0 0 28px 28px; }
  .hub {
    width: 100%;
    height: 100%;
    transform: scale(.96);
  }
  .notch-fixed.open .hub { transform: none; }
  .slide { padding-bottom: 9vh; }
  .slide-hero { padding-top: calc(var(--hub-open-h) + 24px); }
  .slide-mid { padding-bottom: 0; }
  .fly-ghost { display: none; }
  .d1 { width: 150px; left: 8%; } .d2 { width: 140px; right: 7%; }
  .d3, .d4 { width: 130px; }
  .d3 { left: 12%; top: 52%; } .d4 { right: 10%; top: 49%; }
  .atlas-grid { grid-template-columns: repeat(2, 1fr); }
  .chrome { padding: 0 10px; gap: 12px; }
  .day-clock { display: none; }
}

@media (max-width: 480px) {
  .end-price { flex-direction: column; align-items: center; gap: 6px; }
}

/* ================= print ================= */
@media print {
  body { background: #fff; color: #000; }
  .sky, .notch-fixed, .chrome, .fly-ghost, .debris { display: none !important; }
  .cap, .cap > * { opacity: 1 !important; transform: none !important; color: #000 !important; }
}

/* ================= scene stages: show it, don't describe it =================
   Every feature scene had a caption and an empty half. These are miniature,
   animated demonstrations of the actual feature, built from the app's own
   ladder (fills .04–.14, no strokes, r10/r12). Deliberately almost textless —
   they read as UI, they are language-neutral, and they need no translation.
   Everything animates in CSS and is paused until the scene is revealed, so an
   unread page costs nothing. */
/* 10:15 — files land and sort themselves into groups */
@keyframes land { 0% { opacity:0; transform: translateY(-26px) scale(.86); } 14%, 88% { opacity:1; transform:none; } 100% { opacity:1; transform:none; } }
@keyframes fly {
  0% { opacity: 0; transform: translate(-60px, -70px) rotate(-12deg) scale(.8); }
  22%, 30% { opacity: 1; transform: translate(0,0) rotate(0) scale(1); }
  46%, 100% { opacity: 0; transform: translate(30px, 34px) scale(.9); }
}

/* 11:30 — the search field types and the list filters to the matches */
@keyframes typeq { 0%,6%{width:0} 34%,82%{width:74px} 92%,100%{width:0} }
@keyframes blink { 50% { opacity: 0; } }

/* 12:30 — the sentence comes back in another language, word by word */
/* the sentence keeps arriving — a scene with nothing moving reads as a picture */
@keyframes word { 0% { opacity: 0; transform: translateY(6px); } 12%, 78% { opacity: 1; transform: none; } 92%, 100% { opacity: 0; transform: translateY(6px); } }
/* the source dims while the translation lands, then comes back */
@keyframes srcdim { 0%, 8% { opacity: 1; } 30%, 78% { opacity: .4; } 94%, 100% { opacity: 1; } }

/* 14:00 — the waveform moves and the words arrive behind it */
@keyframes bar { from { height: 8px !important; } to { height: 52px !important; } }
@keyframes grow { 0%,4%{width:0 !important} 30%,86%{width:64px !important} 96%,100%{width:0 !important} }

/* 15:00 — the line draws itself; colour only where the number moved */
@keyframes draw { 0%{stroke-dashoffset:900} 55%,88%{stroke-dashoffset:0} 100%{stroke-dashoffset:0} }

/* 16:30 — the format flips and the bar fills */
@keyframes pop { 0%,30%{opacity:.25;transform:scale(.94)} 44%,100%{opacity:1;transform:none} }



/* ================= the hero gets a sky =================
   Colour is unbanned, but the band directly behind the notch stays near-black —
   a bright field there turns the notch from a screen opening into a rectangle.
   So the colour blooms outward and downward, and the top centre stays dark. */
.slide-hero { position: relative; }
.hero-sky {
  position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background:
    radial-gradient(58% 34% at 50% 0%, #05050a 0%, rgba(5,5,10,.92) 42%, transparent 76%),
    radial-gradient(70% 52% at 18% 88%, rgba(226, 108, 138, .30), transparent 66%),
    radial-gradient(64% 48% at 84% 74%, rgba(96, 132, 240, .34), transparent 68%),
    radial-gradient(90% 60% at 50% 118%, rgba(150, 110, 235, .26), transparent 70%),
    linear-gradient(178deg, #06060c 0%, #0b0d1c 38%, #16162c 68%, #241d33 100%);
}
.slide-hero h1 {
  font-size: 72px;
  letter-spacing: -0.028em; line-height: 1.02;
}
.slide-hero .slide-sub { font-size: 20px; max-width: 40ch; margin-inline: auto; }
/* a soft light under the buttons so the CTA sits in the scene, not on it */
.slide-hero .slide-actions { position: relative; }
.slide-hero .slide-actions::before {
  content: ""; position: absolute; left: 50%; top: 50%; translate: -50% -50%;
  width: 420px; height: 180px; border-radius: 50%;
  background: radial-gradient(closest-side, rgba(120,150,255,.20), transparent 72%);
  filter: blur(6px); z-index: -1;
}


/* ================= hero, rebuilt to superwhisper's measurements =============
   Sampled from their live page: sky #2263BE at the top through #CDCDE9 at the
   foot, headline 48/600 with a 1.06 leading sitting at 32% of the viewport, the
   button row 42px tall at 49%, and the scroll hint 14px in BLACK at 50% —
   black because their sky goes pale down there. Layout and type are copied;
   their two 3D renders are not, so the props stay CSS.  */
.slide-hero .hero-sky {
  background:
    radial-gradient(46% 26% at 50% 0%, rgba(4, 8, 20, .55), transparent 72%),
    linear-gradient(180deg,
      #2263be 0%, #256ac0 6%, #2b72c6 14%, #367ecd 24%,
      #468cd3 34%, #5d95d8 46%, #75a1dd 58%, #92ace3 70%,
      #aab8e5 82%, #bfc3e7 92%, #cdcde9 100%);
}
body[data-wall="dawn"] .slide-hero { --hero-ink: #10131c; }
.slide-hero h1 {
  font-size: clamp(34px, 3.34vw, 48px);
  font-weight: 600; letter-spacing: normal; line-height: 1.0625;
  color: #fafafa;
}
.slide-hero .slide-sub {
  font-size: 16px; line-height: 1.5; color: rgba(255,255,255,.86);
  max-width: 46ch; margin: 14px auto 0;
}
/* their button row: a light pill and a dark one, 42px tall, 16px/400 */
.slide-hero .slide-actions { display: flex; gap: 10px; justify-content: center; margin-top: 26px; }
.slide-hero .slide-actions::before { display: none; }
.slide-hero .btn, .slide-hero .btn-link {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: 42px; padding: 0 20px; border-radius: 10px;
  font-size: 16px; font-weight: 500; line-height: 24px; letter-spacing: normal;
}
.slide-hero .btn { background: #fff; color: #12141c; }
.slide-hero .btn:hover { background: #f2f2f5; }
.slide-hero .btn-link { background: #0d0f16; color: #fff; text-decoration: none; }
.slide-hero .btn-link:hover { background: #1b1e29; }
.slide-hero .slide-meta { color: rgba(255, 255, 255, .68); }
/* the hint sits at the pale foot of the sky, so it goes dark like theirs */
.slide-hero .slide-hint {
  position: absolute; left: 50%; translate: -50% 0; bottom: 5.2vh;
  font-size: 14px; font-weight: 400; color: rgba(255, 255, 255, .7);
  text-shadow: none; margin: 0; max-width: 62ch; text-align: center;
}
.slide-hero .slide-hint::after {
  content: "↓"; display: block; margin-top: 6px; font-size: 15px; opacity: .55;
}
/* props read as dark objects against a bright sky, as theirs do */
/* ================= ink: the page reads on light and on dark ================
   The day now runs bright morning -> night, so every token that used to assume
   a dark backdrop is swapped at one place. dayTick sets body[data-ink] from the
   sky's own luminance, so scenes never disagree with their background.
   Defaults above stay the DARK-background values; this block is the light one. */
:root {
  --fill-1: rgba(255, 255, 255, .05);
  --fill-2: rgba(255, 255, 255, .09);
  --fill-3: rgba(255, 255, 255, .14);
  --ink-shadow: 0 18px 40px rgba(0, 0, 0, .45);
}
/* the live-activity cards keep their dark glass on any sky — they are app UI */
/* the scroll hint and fine print sit at the pale foot on light scenes */
/* the vignette is a dark-scene device; on light it just dirties the foot */
/* the hero no longer paints its own sky — the ramp starts there */
.slide-hero .hero-sky { background: none; }
/* Fixed mid-greys cannot survive a sky that travels through mid-tones. At 17:30
   the sky sits at luminance .19 and #a1a1a6 lands at 1.69:1 — the paragraph
   washed out. On light ink the ramp becomes white-alpha, so
   it stays bright relative to whatever is behind it instead of holding a fixed
   grey that the sky can rise to meet. */
/* Both halves of this were wrong. The dangling `.slide-sub,` prelude — left by a
   bad regex edit — fused onto the rule below, so 13 of 14 scene subtitles took a
   dark-ground grey on white. And the FAQ no longer has a dark fill to justify
   the override; it sits on the same white as everything else. Both inherit the
   white-ground ramp from :root now. */
.faq-block * { text-shadow: none; }

/* .mb-apple is a <span> whose child <svg> carries its own colour, so the chrome
   ink rules never reached it — it stayed white on a bright sky. */
/* The props deliberately run past the viewport edges, but relying on
   body{overflow-x:hidden} left html.scrollWidth at 1604 on a 1440 viewport —
   enough for horizontal rubber-banding. They get cropped by their own section,
   which is what the reference does anyway. */
.slide-hero { overflow: hidden; }

/* ================= feature chapters — Sleeve's module, 1:1 =================
   Measured off replay.software/sleeve: 1200px container, ~410px text column,
   600×648 visual at r24, headline 48/700 with 1.0 leading, body 24/400 at 1.33,
   on white. Sides alternate. The visual is the product UI on a wallpaper, cut
   off by the container edge, exactly as theirs is. */
.feats { background: #fff; color: #000; padding: clamp(60px, 9vh, 110px) 24px; position: relative; z-index: 1; }
.feats-head { max-width: 1200px; margin: 0 auto clamp(40px, 6vh, 80px); text-align: center; }
.feats-head .eyebrow { color: #8a52d6; }
.feats-head h2 {
  font-size: clamp(34px, 4vw, 56px); font-weight: 700; line-height: 1.04;
  letter-spacing: -0.02em; color: #000;
}
.feat {
  max-width: 1200px; margin: 0 auto; display: flex; align-items: center;
  gap: clamp(32px, 6vw, 90px); padding: clamp(40px, 7vh, 90px) 0;
}
.feat:nth-child(even) { flex-direction: row-reverse; }
.feat-txt { flex: 0 1 410px; min-width: 0; }
.feat-ico {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; margin-bottom: 26px; color: var(--t-hi);
}
.feat-ico svg { width: 34px; height: 34px; color: inherit; }
.feat-txt h3 { font-size: clamp(32px, 3.4vw, 48px); font-weight: 700; line-height: 1; letter-spacing: -0.015em; color: #000; }
.feat-txt p { font-size: clamp(18px, 1.7vw, 24px); font-weight: 400; line-height: 1.3333; color: #000; margin-top: 22px; }
.feat-txt p + p { margin-top: 18px; color: #4b4b52; font-size: clamp(15px, 1.25vw, 18px); line-height: 1.5; }
:lang(zh-Hans) .feat-txt h3, :lang(zh-Hant) .feat-txt h3,
:lang(ja) .feat-txt h3, :lang(ko) .feat-txt h3 { letter-spacing: 0; line-height: 1.2; }
@supports not (color: rgb(from #fff r g b / 1)) { .feat-vis { background: linear-gradient(150deg,#33364a,#0b0c12); } }
@media (max-width: 1080px) {
  .feat, .feat:nth-child(even) { flex-direction: column; gap: 40px; }
  .feat-txt { flex: 1 1 auto; max-width: 600px; }
  }
@media (prefers-reduced-motion: no-preference) {
  .js   /* the scenes own the reveal now — .feats is gone */
  .js .slide.revealed }

/* The white feature block scrolls under the fixed menu bar, so the bar must ink
   over there too — data-barink follows the SKY, which knows nothing about a
   white section passing beneath it. */
body.over-light .chrome, body.over-light .chrome a,
body.over-light .mb-menus span, body.over-light .mb-menu,
body.over-light .mb-item, body.over-light .day-clock { color: #14161c; text-shadow: none; }
body.over-light .mb-apple svg, body.over-light .mb-status svg, body.over-light .mb-paw svg { color: #14161c; }
body.over-light .mb-item:hover, body.over-light .mb-menu:hover { background-color: rgba(10, 14, 26, .12); }
body.over-light .mb-lang, body.over-light .mb-lang.open { background-color: rgba(10, 14, 26, .10); }
body.over-light .mb-lang:hover { background-color: rgba(10, 14, 26, .16); }
/* and the block's own heading must clear the open panel */
.feats { padding-top: calc(var(--hub-open-h) + clamp(40px, 6vh, 80px)); }

/* ================= the time scenes ARE the feature modules =================
   Sleeve's module applied in place rather than bolted on beside: the caption
   keeps its hour, gains a bare 34px glyph and the 48/700 + 24/400 ramp,
   and the abstract grey-bar stage is replaced by the real product on a coloured
   wallpaper, cut off by its frame. One module per feature, at its own hour. */
.slide-mid .cap { max-width: 780px; }
.slide-mid .cap h2 { font-size: 42px; font-weight: 700; line-height: 1.04; letter-spacing: -0.018em; }
.slide:not(.slide-hero) .slide-sub { font-size: 16px; }
.slide-mid .cap .slide-sub { line-height: 1.36; max-width: none; margin-top: 20px; }
.slide-mid .feat-ico { margin-bottom: 20px; }
@media (max-width: 960px) {
  .slide-mid { gap: 30px; }
  .slide-mid .cap { flex: 1 1 auto; max-width: 560px; text-align: center; }
  .slide-mid .feat-ico { margin-inline: auto; }
  .slide-mid }

/* ================= 19:30 — bento, after Sleeve's "A catalogue of hits" =====
   Same job as their feature list, same device: mixed-size cards in a bento,
   each carrying prose plus its own number. No screenshots — the notch overhead
   is the live demonstration, so a still of it here would be dead weight. */
/* the first card of each group takes the wide slot, like their big tile */
@media (max-width: 900px) {
    }


/* ================= 19:30 — the rest of the toolbox =========================
   superwhisper's capability strip, verbatim in form: a small line icon, a bold
   name, two lines of description, four across. No card, no fill, no border, no
   shadow — the whole point of the device is that it stays quiet while covering
   a lot of ground. Interactive cells route the live notch above; the remaining
   summaries are intentionally static until their panel surfaces are available. */
.slide-more { overflow-x: clip; place-items: center; padding-left: clamp(24px, 4vw, 60px); padding-right: clamp(24px, 4vw, 60px); padding-top: calc(var(--hub-open-h) + 30px); padding-bottom: 5vh; }
.slide-more .cap { max-width: 1320px; }
.slide-more h2 { font-size: 42px; font-weight: 700; letter-spacing: -0.02em; line-height: 1.06; }
/* picmal.app's "what can you do with" module: a three-column grid, a 28px
   outline glyph above a 20px/600 title and a three-line description. No card,
   no fill, no border — the spacing does the separating. Cell measured at 405px
   with roughly 120px between columns and 200px between rows. */
.cap-grid {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  /* four across at 1320 gives ~275px a column; the gap comes down with it or
     the columns starve. 15 cells then fall as 4/4/4/3. */
  column-gap: clamp(28px, 3.4vw, 52px); row-gap: clamp(44px, 5vw, 66px);
  margin-top: 56px; text-align: left; width: 100%;
}
/* align-items:start is the default, but the icon must sit on the row's own top
   edge — without this a taller neighbour re-centres the whole cell and the
   glyphs step up and down across a row, which picmal's never do. */
.cap-cell { display: block; align-self: start; }
.cap-cell {
  appearance: none; background: none; border: 0; padding: 0; font: inherit;
  display: block; text-align: left; cursor: pointer;
  transition: opacity var(--mk-tone) var(--mk-ease);
}
.cap-cell svg {
  width: 28px; height: 28px; color: var(--t-hi); display: block;
  margin-bottom: 16px; border-radius: 7px; stroke-width: 1.4;
  transition:
    background-color .24s var(--mk-ease),
    box-shadow .24s var(--mk-ease);
}
.cap-cell b { display: block; font-size: 20px; font-weight: 600; letter-spacing: -0.01em; color: var(--t-hi); margin-bottom: 8px; }
.cap-cell span { display: block; font-size: 14.5px; line-height: 1.58; color: var(--t-mid); }
.cap-cell[data-page]:hover svg {
  background-color: color-mix(in srgb, var(--t-hi) 3%, transparent);
  box-shadow: 0 0 0 7px color-mix(in srgb, var(--t-hi) 4%, transparent);
}
.cap-cell:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; border-radius: 4px; }
.cap-cell[data-page][aria-current="true"] { opacity: 1; }
.cap-cell[data-page][aria-current="true"] b,
.cap-cell[data-page][aria-current="true"] svg { color: var(--t-hi); }
.cap-cell[data-page][aria-current="true"] svg {
  background-color: color-mix(in srgb, var(--t-hi) 6%, transparent);
  box-shadow:
    0 0 0 7px color-mix(in srgb, var(--t-hi) 8%, transparent),
    inset 0 0 0 1px color-mix(in srgb, var(--t-hi) 10%, transparent);
}
@media (max-width: 1080px) { .cap-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 760px)  { .cap-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* the promoted scenes lead with the tool's name, then its claim as the headline */
.scene-eyebrow {
  font-size: 13px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  color: var(--fc, var(--t-low)); margin-bottom: 8px;
}
.scene-kicker {
  display: flex; align-items: baseline; gap: .5em;
  width: fit-content; margin: 0 auto clamp(12px, 1.6vw, 18px);
  font-size: 12px; font-weight: 500; line-height: inherit; letter-spacing: .08em;
  color: var(--t-upper); white-space: nowrap;
}
.scene-kicker .scene-eyebrow {
  margin: 0; color: inherit;
  font-size: inherit; font-weight: inherit; line-height: inherit; letter-spacing: inherit;
  text-transform: none;
}
@media (max-width: 720px) { .scene-kicker { margin-bottom: 10px; } }
/* A feature with no panel surface of its own is not a control — clicking it
   could only route the notch to the dashboard, which would be a lie about
   where that feature lives. It reads the same; it just does not offer. */
.cap-cell-static { cursor: default; }
.cap-cell-static:hover { opacity: 1; }

/* Type is fixed, not fluid. clamp() with a vw term re-computes the size on every
   pixel of resize, so the headings visibly crawl while a window is dragged.
   These step at two breakpoints instead — the size is stable inside each. */
@media (max-width: 900px) {
  .slide-hero h1 { font-size: 46px; }
  .slide-mid .cap h2, .slide-more h2 { font-size: 32px; }
  .slide-end h2 { font-size: 36px; }
  .slide-hero .slide-sub { font-size: 18px; }
  .end-price b { font-size: 36px; }
}
@media (max-width: 560px) {
  .slide-hero h1 { font-size: 34px; }
  .slide-mid .cap h2, .slide-more h2 { font-size: 27px; }
  .slide-end h2 { font-size: 29px; }
  .slide-hero .slide-sub { font-size: 17px; }
}
/* CTA pair: the Apple mark sits on the download, the price on the purchase.
   Both are the same pill so neither reads as secondary — one says how, one says
   how much. The mark uses the ink-trimmed viewBox from the menu bar, so it
   optically matches the Apple logo already on screen. */
.btn-apple { width: 15px; height: 18px; flex-shrink: 0; margin-top: -1px; }
.btn-apple use { fill: currentColor; }
.btn-buy { gap: 7px; }
.btn-buy b { font-weight: 600; }
.btn-buy span { opacity: .72; font-weight: 400; }
.slide-hero .btn-buy { background: #0b0d12; color: #fff; }
.slide-hero .btn-buy:hover { background: #1c1f28; }
.slide-end .btn-buy { background: rgba(255, 255, 255, .14); color: #fff; }
.slide-end .btn-buy:hover { background: rgba(255, 255, 255, .2); }
.meta-dot { margin: 0 7px; opacity: .5; }
@media (max-width: 1180px) { .cap-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 900px)  { .cap-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px) { .cap-grid { grid-template-columns: 1fr; row-gap: 40px; } }

/* ================= two grounds, not eleven =================================
   The hero sits on the blue; everything past it sits on white. Ink is therefore
   a fact of position rather than something computed from a moving sky, which is
   why the palette can finally be fixed. */
/* the white-era dark-ink flip is retired: the fixed sky is the ground
   everywhere now, so the root's light ink serves the whole page */
.slide-hero { --t-hi: #fff; --t-mid: rgba(255,255,255,.88); --t-low: rgba(255,255,255,.7); }
.slide-hero h1, .slide-hero .slide-sub, .slide-hero .slide-meta { color: inherit; }
.slide-hero h1 { color: #fff; }
.slide-hero .slide-sub { color: #fff; }
.slide-hero .slide-meta { color: rgba(255, 255, 255, .82); }
.slide-hero .slide-hint { color: rgba(255, 255, 255, .78); }
html:not(.js) .slide-hero .slide-hint,
body[data-sky="day"] .slide-hero .slide-hint { color: rgba(16, 22, 36, .55); }
/* the menu bar rides the blue in the hero and inks over once the page is white */
body.over-light .chrome, body.over-light .chrome a, body.over-light .mb-menus span,
body.over-light .mb-menu, body.over-light .mb-item, body.over-light .day-clock,
body.over-light .mb-apple svg, body.over-light .mb-status svg, body.over-light .mb-paw svg { color: #14161c; }
.slide, .slide-mid, .slide-more { background: transparent; }

.hairline-fix { }
:root { --hairline: rgba(10, 14, 26, .10); --hairline-strong: rgba(10, 14, 26, .16);
        --fill-1: rgba(10, 14, 26, .04); --fill-2: rgba(10, 14, 26, .07); --fill-3: rgba(10, 14, 26, .12); }
/* The 16th cell fills the gap the 4/4/4/3 grid leaves. It is deliberately
   quieter than the others — it names something that does not exist yet, so it
   should not compete with the fifteen that do. */
.cap-cell-soon svg, .cap-cell-soon b { color: var(--t-low); }
.cap-cell-soon span { color: var(--t-faint); }

/* Plain native scrolling. Everything that used to live here — a wheel pager,
   scroll-snap in three flavours, a fixed-deck experiment, a smooth-scroll
   library — was removed after each one made the page worse than the browser's
   own default. Sections size to their content and the document scrolls. */
.slide { min-height: 100vh; min-height: 100svh; }
.slide-mid { min-height: 100vh; min-height: 100svh; }
#more { height: auto; min-height: 100vh; }


/* The drop, on a loop: fall, land, pause, again. It used to be driven by the
   wheel — it only moved while you scrolled and ran backwards if you scrolled
   up. It plays only while the scene is on screen, so it never runs unseen. */
@keyframes fly-drop {
  0%   { opacity: 0; transform: translate(calc(-50% + 240px), 62vh) rotate(8deg) scale(1); }
  12%  { opacity: 1; }
  56%  { opacity: 1; transform: translate(calc(-50% + 20px), 13vh) rotate(2deg) scale(.9); }
  70%  { opacity: 0; transform: translate(-50%, 5vh) rotate(0deg) scale(.7); }
  100% { opacity: 0; transform: translate(calc(-50% + 240px), 62vh) rotate(8deg) scale(1); }
}
.fly-ghost { opacity: 0; }
.fly-ghost.flying { animation: fly-drop 3.6s cubic-bezier(.45, 0, .5, 1) 1 both; }
@media (prefers-reduced-motion: reduce) { .fly-ghost.flying { animation: none; } }
/* Scattered, not lined up — they read as things left lying about rather than a
   toolbar. Same card as the one the demo drops, so what you throw and what the
   page throws are visibly the same object. */
/* above the notch (50): while you drag, the file has to stay visible OVER the
   panel — a card that disappears under the thing you are aiming at gives you
   nothing to aim with. The z-index here is a stacking context, so the dragging
   card cannot exceed it no matter what it sets. */
.drop-kit { position: absolute; inset: 0; pointer-events: none; z-index: 3; }
.dropfile {
  position: absolute; pointer-events: auto;
  appearance: none; border: 0; font: inherit;
  cursor: grab; touch-action: none;
  transition: transform .34s var(--mk-ease), box-shadow var(--mk-tone) var(--mk-ease);
}
/* Scattered, but outside the caption column: measured at 1440px, f3 was sitting
   2691px² on top of the body copy and f4 533px², while f2 hung 14px above the
   viewport. Pushed outward — the collage still reads as tossed around, it just
   no longer covers the words it is illustrating. */
.dropfile.f1 { left: 7%;  top: 32%; rotate: -7deg; }
.dropfile.f2 { right: 8%; top: 30%; rotate: 6deg; }
.dropfile.f3 { left: 9%;  bottom: 17%; rotate: 4deg; }
.dropfile.f4 { right: 8%; bottom: 22%; rotate: -5deg; }
.dropfile:hover { transform: translateY(-4px) scale(1.03); box-shadow: 0 0 0 1px rgba(255,255,255,.16); }
.dropfile:active,
.dropfile.dragging { cursor: grabbing; }
/* a file dropped on the notch is taken in, not left lying on top of it.
   This must NOT be joined to the :active selector above — doing so blanked the
   card for the whole drag, which no position or size assertion can catch. */
.dropfile.swallowed,
.dropfile.dragging.swallowed {   /* .dragging sets transition:none and won on
                                    specificity, so the shrink never animated */
  transition: transform .26s cubic-bezier(.4, 0, .55, 1), opacity .26s ease-in;
  opacity: 0;
  pointer-events: none;
}
/* bottom/right MUST be cleared while dragging. f2/f3/f4 are anchored with
   right/bottom, and the drag writes inline left/top — an element with both top
   and bottom set stretches to span them, so the pdf and xlsx cards deformed the
   moment you picked them up. */
.dropfile.dragging {
  position: fixed; rotate: 0deg; transition: none;
  scale: 1.06;
  right: auto; bottom: auto;
  pointer-events: none;   /* it must not eat the pointer it is following */
  z-index: 60;            /* meaningful: the card is a child of <body> while dragging */
}
.dropfile:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
/* the trail: a dot dropped each frame, fading as it ages */
.drag-dot {
  position: fixed; z-index: 59; width: 7px; height: 7px; border-radius: 50%;
  background: rgba(20, 20, 26, .5); pointer-events: none;
  animation: dot-fade .5s linear forwards;
}
@keyframes dot-fade { to { opacity: 0; scale: .3; } }
@media (max-width: 900px) { .drop-kit { display: none; } }
@media (prefers-reduced-motion: reduce) { .drag-dot { display: none; } }

/* ---- 11:30 clipboard: the copy you make yourself ----
   Four real, clickable clippings. Their surfaces and type follow the shared
   day/night text tokens so they stay neutral as the sky changes. */
.clip-board {
  margin: 32px auto 0; display: grid; gap: 12px;
  grid-template-columns: repeat(2, minmax(0, 300px)); justify-content: center;
}
.spec {
  appearance: none; font: inherit; text-align: left; cursor: pointer;
  color: var(--t-hi);
  display: flex; align-items: center; gap: 11px;
  padding: 11px 14px; border-radius: 13px;
  background: color-mix(in srgb, currentColor 4%, transparent);
  border: 1px solid transparent;
  transition:
    background-color .24s var(--mk-ease),
    border-color .24s var(--mk-ease);
}
.spec:hover {
  background: color-mix(in srgb, currentColor 7%, transparent);
  border-color: color-mix(in srgb, currentColor 12%, transparent);
}
.spec:focus-visible { outline: 2px solid var(--t-hi); outline-offset: 3px; }
.spec > span { min-width: 0; }
.spec i {
  flex: 0 0 30px; height: 30px; display: grid; place-items: center;
  font-style: normal;
  background: none;
  color: var(--t-mid);
}
.spec i svg { width: 18px; height: 18px; display: block; }
.spec b {
  display: block; font: 500 13.5px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--t-hi); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.spec s { display: block; text-decoration: none; font-size: 11.5px; color: var(--t-low); margin-top: 3px; }
.spec.trap b { color: var(--t-hi); }
.clip-board-note {
  grid-column: 1 / -1; text-align: center; margin: 4px 0 0;
  font-size: 12.5px; color: var(--t-low);
  display: flex; align-items: center; justify-content: center; gap: 8px;
}
.clip-board-note kbd {
  font: 590 11.5px/1 -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  padding: 4px 7px; border-radius: 6px; color: var(--t-mid);
  background: rgba(127, 127, 137, .14); border: 0;
}
@media (max-width: 760px) { .clip-board { grid-template-columns: minmax(0, 320px); } }
/* the clipping in flight: a ring, not a shadow */
.clip-fly {
  position: fixed; z-index: 60; pointer-events: none;
  font: 500 12.5px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
  padding: 9px 12px; border-radius: 10px;
  color: #fff; background: #1a1a1e;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .14) inset;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* ---- 12:30 translate: something you are actually reading ----
   A tinted box with a sentence in it stated the situation; a page you are
   plainly in the middle of reading depicts it. The chrome is deliberately
   minimal — it has to read as "a window behind your work", not as a browser
   mockup competing with the notch above it. */
.tl-read {
  margin: 30px auto 0; max-width: 620px; text-align: left;
  border-radius: 16px; overflow: hidden;
  background: rgba(15, 17, 32, .46); border: 0;
}
.tl-read-head {
  display: flex; align-items: center; gap: 6px;
  padding: 11px 14px 0;
}
.tl-dot { width: 8px; height: 8px; border-radius: 50%; background: rgba(255, 255, 255, .22); }
.tl-dot:first-child { margin-right: 1px; }
.tl-url {
  margin-left: 8px; font: 400 11.5px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: rgba(255, 255, 255, .45);
}
.tl-lang {
  margin-left: auto; font: 600 10.5px/1 ui-monospace, Menlo, monospace; letter-spacing: .06em;
  padding: 4px 7px; border-radius: 5px; color: rgba(255, 255, 255, .7);
  background: rgba(255, 255, 255, .14);
}
.tl-read-body { padding: 20px 22px 18px; }
.tl-headline {
  margin: 0 0 12px; font-size: 20px; font-weight: 650; line-height: 1.3; color: #fff;
  transition: opacity .26s ease;
}
.tl-passage {
  margin: 0; font-size: 16.5px; line-height: 1.62; color: rgba(255, 255, 255, .85);
  cursor: text; transition: opacity .26s ease;
}
.tl-byline { margin: 10px 0 0; font-size: 12px; color: rgba(255, 255, 255, .5); }
/* the page never changes; the only feedback here is that the selection was
   taken — the answer itself is in the notch, which is the whole point */
.tl-sent .tl-lang {
  color: #fff;
  background: rgba(255, 255, 255, .24);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .18);
}
.tl-hint { margin: 12px 0 0; font-size: 12.5px; color: var(--t-low); }
/* ---- 13:15 downloads: the page holds the link ---- */



/* ---- 14:00 voice: hold the key, the words land in the field ---- */

.pcl-demo { background: none; border: 0; }
.pcl-list li { padding: 6px 16px; }
.pcl-list li + li { border-top: 0; }
.pcl-list b { color: rgba(20, 20, 24, .38); font-size: 11px; }
.pcl-list i { opacity: 1; transform: none; transition: none; font-size: 10.5px; background: none; color: rgba(20, 20, 24, .35); }
.pcl-trap i, .pcl-trap.done i { color: rgba(180, 68, 58, .65); background: none; }
.pcl-trap b { color: rgba(20, 20, 24, .5); }
.pbt-line { margin: 26px 0 0; display: flex; justify-content: center; gap: 10px; font-size: 12px; }
.pbt-line b { font: 600 12px/1 ui-monospace, SFMono-Regular, Menlo, monospace; color: rgba(20, 20, 24, .55); }
.pbt-line i { font: 500 11.5px/1 ui-monospace, SFMono-Regular, Menlo, monospace; font-style: normal; color: rgba(20, 20, 24, .4); }
.pbt-meter.on i, .pbt-line.on i { color: #3a8a4a; }
.pbt-line s { text-decoration: none; font: 500 11.5px/1 ui-monospace, SFMono-Regular, Menlo, monospace; font-variant-numeric: tabular-nums; color: rgba(20, 20, 24, .35); }
.pst-demo { width: min(380px, 100%); }
.pds-ctl { margin-top: 26px; display: flex; justify-content: center; }

/* ================= ink pairing per sky state =================
   The sky is fixed while every chapter passes over it, so foreground colour is
   a property of the current plate rather than of an individual section. Day is
   pale through the copy zone and uses deep navy ink; Dusk and Night keep a
   high-key white ramp. Low-sitting hints get a separate dark "hem" ink because
   all three plates brighten at the foot. Registered colours let the ramp hold
   its old value until the background reaches a safe luminance crossover. */
@property --t-hi       { syntax: "<color>"; inherits: true; initial-value: #f5f5f7; }
@property --t-mid      { syntax: "<color>"; inherits: true; initial-value: #a1a1a6; }
@property --t-low      { syntax: "<color>"; inherits: true; initial-value: #94949a; }
@property --t-faint    { syntax: "<color>"; inherits: true; initial-value: #6e6e73; }
@property --t-upper    { syntax: "<color>"; inherits: true; initial-value: #f5f5f7; }
@property --t-hem      { syntax: "<color>"; inherits: true; initial-value: rgba(16, 22, 36, .70); }

body {
  /* The menu bar always occupies the dark upper band, independent of sky time. */
  --chrome-ink: #fff;
  --chrome-fill: rgba(255, 255, 255, .14);
  --chrome-fill-hover: rgba(255, 255, 255, .22);
  --chrome-shadow: 0 1px 2px rgba(0, 0, 0, .38);
  --ink-switch-delay: .45s;
  --ink-switch-duration: .22s;
  transition:
    --t-hi var(--ink-switch-duration) var(--mk-ease-io) var(--ink-switch-delay),
    --t-mid var(--ink-switch-duration) var(--mk-ease-io) var(--ink-switch-delay),
    --t-low var(--ink-switch-duration) var(--mk-ease-io) var(--ink-switch-delay),
    --t-faint var(--ink-switch-duration) var(--mk-ease-io) var(--ink-switch-delay),
    --t-upper var(--ink-switch-duration) var(--mk-ease-io) var(--ink-switch-delay),
    --t-hem var(--ink-switch-duration) var(--mk-ease-io) var(--ink-switch-delay);
}
/* Darkening plates can take light ink early; a brightening Day needs to clear
   more of the old upper layer before deep ink becomes the safer choice. */
body[data-sky="day"] { --ink-switch-delay: .75s; }
body.sky-opening { --ink-switch-delay: 7s; }

body[data-sky="day"] {
  --t-hi: #101624;
  --t-mid: rgba(16, 22, 36, .82);
  --t-low: rgba(16, 22, 36, .70);
  --t-faint: rgba(16, 22, 36, .60);
  --t-upper: rgba(16, 22, 36, .90);
  --t-hem: rgba(16, 22, 36, .70);
}
body[data-sky="dusk"] {
  --t-hi: #fff;
  --t-mid: rgba(255, 255, 255, .96);
  --t-low: rgba(255, 255, 255, .82);
  --t-faint: rgba(255, 255, 255, .74);
  --t-upper: rgba(255, 255, 255, .92);
  --t-hem: rgba(30, 20, 42, .90);
}
body[data-sky="night"] {
  --t-hi: #f8faff;
  --t-mid: rgba(255, 255, 255, .92);
  --t-low: rgba(255, 255, 255, .82);
  --t-faint: rgba(255, 255, 255, .72);
  --t-upper: rgba(255, 255, 255, .90);
  --t-hem: rgba(3, 5, 10, .94);
}

/* Without JavaScript the visible plate is Day even though the HTML carries a
   Dusk seed for the opening dissolve, so the foreground fallback must agree. */
html:not(.js) body {
  --t-hi: #101624;
  --t-mid: rgba(16, 22, 36, .82);
  --t-low: rgba(16, 22, 36, .70);
  --t-faint: rgba(16, 22, 36, .60);
  --t-upper: rgba(16, 22, 36, .90);
  --t-hem: rgba(16, 22, 36, .70);
}

/* Retire the hero's old local white ramp so it follows the same plate tokens as
   the rest of the document. Hierarchy comes from the token ramp, not opacity. */
body[data-sky] .slide-hero {
  --t-hi: inherit;
  --t-mid: inherit;
  --t-low: inherit;
}
body[data-sky] .slide-hero h1 { color: var(--t-hi); }
body[data-sky] .slide-hero .slide-sub { color: var(--t-mid); }
body[data-sky] .slide-hero .slide-meta { color: var(--t-low); }
body[data-sky] .slide-hero .slide-hint { color: var(--t-hem); }

.scene-eyebrow {
  color: var(--t-upper);
  opacity: 1;
}
.slide-hint { color: var(--t-hem); }
/* Menu-bar furniture stays on the dark upper band. Route every text and SVG
   through one fixed white chrome ramp, superseding the retired over-light flip. */
body[data-sky] .chrome,
body[data-sky] .chrome a,
body[data-sky] .mb-menus span,
body[data-sky] .mb-menu,
body[data-sky] .mb-item,
body[data-sky] .day-clock,
body[data-sky] .mb-apple svg,
body[data-sky] .mb-status svg,
body[data-sky] .mb-paw svg {
  color: var(--chrome-ink);
  text-shadow: var(--chrome-shadow);
}
body[data-sky] :is(.mb-item:hover, .mb-menu:hover, .mb-menu.open, .mb-lang, .mb-lang.open) {
  background-color: var(--chrome-fill);
}
body[data-sky] .mb-lang:hover { background-color: var(--chrome-fill-hover); }

/* Long copy can span more than one luminance band on Dusk/Night. A compact
   wallpaper-style halo protects the glyph edge without introducing a card. */
body[data-sky="dusk"] :is(.slide-sub, .cap-cell span) {
  text-shadow:
    0 1px 3px rgba(8, 5, 18, .62),
    0 0 1px rgba(8, 5, 18, .78);
}
body[data-sky="dusk"] :is(.cap-cell b, .cap-cell svg) {
  text-shadow: 0 1px 2px rgba(3, 6, 14, .34);
}
body[data-sky="night"] .faq-block :is(h2, summary, p) {
  text-shadow:
    0 1px 3px rgba(3, 6, 14, .56),
    0 0 1px rgba(3, 6, 14, .70);
}

@media (prefers-reduced-motion: reduce) {
  body { transition: none; }
}
