  /* ============================================================
     HOORAY — pixel-faithful reproduction of `Hooray · Final EN`
     All values measured from Figma (file 1l4w5LwHomw2FdPR0UXUdq).
     Frame 402x874. Absolute positioning at exact Figma coords.
     ============================================================ */
  :root {
    /* text */
    --text-primary: #111827;
    --text-secondary: #4b5563;
    --text-tertiary: #5b6472;
    --text-placeholder: #9CA3AF;
    /* brand / accent (Figma var ground truth) */
    --accent: #ffc04d;
    --brand: #3b82f6;
    /* category identification (Tailwind-300 pastels, measured from Figma dot assets) */
    --cat-work: #D97706;
    --cat-study: #2563EB;
    --cat-fitness: #059669;
    --cat-life: #F43F5E;
    --cat-others: #6366F1;
    --destructive: #ef4444;
    /* surfaces */
    --surface-page: #f8f8f8;
    --surface-elevated: #ffffff;
    --surface-dark: #111827;
    /* glass */
    --glass-card: rgba(255,255,255,0.80);
    --glass-chip: rgba(255,255,255,0.90);
    --glass-bubble: #ffffff;
    /* borders */
    --border-glass: rgba(255,255,255,0.80);
    --border-input: #e6e8ec;
    --border-tab: rgba(17,24,39,0.20);
    --border-pill: rgba(255,255,255,0.20);
    /* ring */
    --ring-track: #e6e8ec;
    /* calendar states */
    --cal-green-bg: #ecfdf5;
    --cal-green-border: #a7f3d0;
    --cal-green-text: #047857;
    --cal-muted-bg: #f3f4f6;
    --cal-sel-bg: #eff6ff;
    /* radii — D3: one concentric system. Base radii are named tokens; every nested
       radius derives as  child = parent − gap  (floored at --r-min), via the .shape
       utility or an inline max(calc(...)) with the derivation written next to it.
       P0 lands the mechanism; P1 re-derives the chrome it rebuilds (audit deltas
       recorded in HOORAY-REDESIGN.md 阶段状态表·发现). */
    --r-min: 8px;
    --r-device: 54px;
    --r-card: 24px;
    --r-bubble: 14px;
    --r-menu: 14px;
    --r-input: 12px;
    --r-cell: 12px;     /* cal-cell; audit: NOT concentric with cal-grid (24 − ~15px inset ≈ 9) — P3 calendar pass decides */
    /* legacy chrome tokens — P1 replaces these surfaces (floatbar derives from --r-device) */
    --r-tab-pill: 20px;
    --r-pill: 33px;
    --r-tabbar: 37px;   /* audit: NOT concentric (device 54 − 24px gap = 30) — P1 floatbar fixes */
    /* shadows */
    --sh-card: 0px 18px 35px -18px rgba(35,36,37,0.11);
    --sh-card-strong: 0px 18px 35px -18px rgba(35,36,37,0.13);
    --sh-bubble: 0px 8px 25px -12px rgba(35,36,37,0.09);
    /* iOS 壳层: system font first — SF on Apple hardware (Dynamic Type metrics, optical
       sizes); Inter stays as the metric-compatible fallback for non-Apple platforms */
    --font: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Inter", "Segoe UI", Roboto, sans-serif;
  }

  * { box-sizing: border-box; margin: 0; padding: 0; -webkit-tap-highlight-color: transparent; }
  button { -webkit-appearance: none; appearance: none; background: none; border: 0; font: inherit; color: inherit; cursor: pointer; padding: 0; }

  html, body {
    height: 100%;
    overflow: hidden;                 /* the page itself never scrolls — only screens inside the device do */
    overscroll-behavior: none;        /* C5: no document rubber-band under the frame on iOS Safari */
    font-family: var(--font);
    background:
      radial-gradient(120% 80% at 50% 0%, #1a1d24 0%, #0b0d12 55%, #050608 100%);
    color: #fff;
  }

  .stage {
    height: 100vh;                    /* fallback where dvh is unsupported */
    height: 100dvh;                   /* C5: true visual height on iOS Safari (URL bar collapse) */
    display: flex;
    align-items: center;              /* the device stays dead-centre in the viewport, both axes */
    justify-content: center;
    overflow: hidden;
    /* C5: with viewport-fit=cover the page extends under notch + home indicator;
       the stage padding keeps the letterboxed frame inside the safe area
       (fitStage() measures the padded content box, so the scale follows) */
    padding: env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px)
             env(safe-area-inset-bottom, 0px) env(safe-area-inset-left, 0px);
  }

  /* screenshot/diff mode: device flush to viewport, no backdrop/note */
  body.shot { background: var(--surface-page); }
  body.shot .stage { padding: 0; }
  body.shot .device { border-radius: 0; box-shadow: none; transform: none !important; }
  body.shot .stage__note { display: none; }

  /* ---- device frame: 402 x 874 ---- */
  .device {
    position: relative;
    width: 402px;
    height: 874px;
    border-radius: var(--r-device);
    background: var(--surface-page);
    overflow: hidden;
    box-shadow: 0 40px 120px -20px rgba(0,0,0,0.7), 0 0 0 1px rgba(255,255,255,0.04);
    flex: none;
  }

  /* each screen fills the device; absolute children at exact Figma coords */
  .screen {
    position: absolute;
    inset: 0;
    width: 402px;
    height: 874px;
    display: none;
  }
  .screen.is-active { display: block; }
  /* Home + Day detail scroll vertically (12 tasks / a full day's list); scrollbar hidden */
  .screen[data-screen="home"], .screen[data-screen="calendarday"] { overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; overscroll-behavior-y: contain; }  /* scroll stays inside the screen; no chain to the page */
  .screen[data-screen="home"]::-webkit-scrollbar, .screen[data-screen="calendarday"]::-webkit-scrollbar { width: 0; height: 0; }
  body.shot .screen { overflow: hidden; }

  /* ---------- crossDissolve: plain screen→screen navigation fades the new screen in.
     NOT applied to onboarding swipes (internal track slide), the Add Task / Category / Settings
     modals (they slide via .is-open), or the Month↔Day morph (its own choreographed animation). ---------- */
  @keyframes crossDissolve { from { opacity: 0; } to { opacity: 1; } }
  .screen.crossDissolve { animation: crossDissolve .28s ease both; z-index: 2; }  /* incoming screen fades up from the page bg; the old screen is removed at once so it leaves no ghost */
  body.shot .screen.crossDissolve { animation: none; }

  .abs { position: absolute; }

  /* ---------- P0: structure isolation + iOS shell + D3 shape utilities ---------- */
  /* app layer. The modal layer (sheets/pushes) is its SIBLING inside .device — B6:
     when a sheet is open, #app-root may take a scale transform (background push-back)
     and the sheet must not inherit it. */
  #app-root { position: absolute; inset: 0; }
  /* kill the ~350ms double-tap-zoom wait on every control (tap = tap, instantly) */
  button, .task, .set-row, .pick-row, .cal-cell, .field, .ob-dot, .tab { touch-action: manipulation; }
  /* app chrome is not page text — long-press must not select labels like a web page */
  .device { -webkit-user-select: none; user-select: none; }
  .field__input { -webkit-user-select: text; user-select: text; }   /* form fields stay editable */
  .device img { -webkit-user-drag: none; }   /* images are UI, not draggable resources (JS dragstart guard is the cross-browser half) */
  /* D3 utilities: true capsule + concentric child derivation */
  .capsule { border-radius: calc(infinity * 1px); }
  .shape { border-radius: var(--r); padding: var(--pad); --r-child: max(calc(var(--r) - var(--pad)), var(--r-min)); }
  .shape > * { --r: var(--r-child); }

  /* D4: the self-drawn "9:41" status bar is deleted — a faked system surface reads
     as a mockup, and on a real iPhone it collides with the actual status bar. The
     home indicator stays: it is part of the desktop device-frame presentation
     (like the rounded bezel), not faked content. */

  /* ---------- home indicator ---------- */
  .home-indicator {
    position: absolute; left: 50%; transform: translateX(-50%);
    bottom: 8px; width: 144px; height: 5px; border-radius: calc(infinity * 1px);  /* D3 capsule */
    background: #000; z-index: 40;
  }

  /* ---------- icons ---------- */
  .icon { display: inline-block; color: var(--text-primary); }
  .icon svg { display: block; width: 100%; height: 100%; fill: currentColor; }
  /* duotone backing handled inside symbol via opacity 0.2 */

  /* ---------- Liquid Icon button (44pt, circular) ---------- */
  .liquid-btn {
    width: 44px; height: 44px; border-radius: calc(infinity * 1px);  /* D3 capsule (was magic 64px) */
    background: var(--glass-card);
    -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
    border: 1px solid var(--border-glass);
    box-shadow: var(--sh-card-strong);
    display: grid; place-items: center;
    color: var(--text-primary);
    cursor: pointer;
  }
  .liquid-btn .icon { width: 24px; height: 24px; }

  /* ---------- header ---------- */
  .h-kicker {
    font-weight: 600; font-size: 14px; line-height: 21px;
    letter-spacing: -0.28px; color: var(--text-tertiary);
    text-transform: uppercase; white-space: nowrap;
  }
  .h-title {
    font-weight: 700; font-size: 28px; line-height: 42px;
    letter-spacing: -0.84px; color: var(--text-primary);
  }
  .section-title {
    font-weight: 600; font-size: 18px; line-height: 27px;
    letter-spacing: -0.54px; color: var(--text-primary);
  }

  /* ---------- progress ring ---------- */
  .ring-wrap { width: 300px; height: 300px; }
  .ring-svg { width: 300px; height: 300px; transform: rotate(-90deg); }
  .ring-seg { fill: none; stroke-width: 16; stroke-linecap: round; }
  .ring-track { stroke: var(--ring-track); }     /* Figma Home: track = neutral grey #e6e8ec */
  .ring-done { stroke: var(--accent); transition: stroke-dasharray .45s cubic-bezier(.22,1,.36,1); }
  .ring-prog { stroke: var(--brand); transition: stroke-dasharray .45s cubic-bezier(.22,1,.36,1), stroke-dashoffset .45s cubic-bezier(.22,1,.36,1); }   /* Figma Home: progress arc = standard blue #3b82f6 */

  /* mascot slot inside ring */
  .mascot-slot {
    width: 228px; height: 228px; border-radius: 50%;
    background: var(--surface-page); /* Figma "Avatar Background" = #F8F8F8, blends into page */
    /* no overflow clip: the ears overflow the top (over the ring is fine) */
  }
  .mascot-slot img {
    position: absolute; left: 50%; top: -12px;
    width: 240px; height: 240px; transform: translateX(-50%);
    object-fit: cover; pointer-events: none;
    /* Figma "Subtract": top stays free, bottom is masked into the circle */
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='240'%20height='240'%3E%3Crect%20width='240'%20height='126'%20fill='%23fff'/%3E%3Ccircle%20cx='120'%20cy='126'%20r='114'%20fill='%23fff'/%3E%3C/svg%3E");
    mask-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='240'%20height='240'%3E%3Crect%20width='240'%20height='126'%20fill='%23fff'/%3E%3Ccircle%20cx='120'%20cy='126'%20r='114'%20fill='%23fff'/%3E%3C/svg%3E");
    -webkit-mask-size: 100% 100%; mask-size: 100% 100%;
    -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  }
  /* fun "pop" when the mascot changes on task toggle (keeps the translateX centering) */
  @keyframes mascot-pop {
    0%   { transform: translateX(-50%) scale(1); }
    28%  { transform: translateX(-50%) scale(0.82) rotate(-5deg); }
    60%  { transform: translateX(-50%) scale(1.09) rotate(3deg); }
    100% { transform: translateX(-50%) scale(1) rotate(0); }
  }
  .mascot-slot.mascot-pop img { animation: mascot-pop .46s cubic-bezier(.34, 1.4, .5, 1); }
  @media (prefers-reduced-motion: reduce) { .mascot-slot.mascot-pop img { animation: none; } }

  /* Home indicators encode STATUS, not category (matches Figma Home / Screen) */
  /* Figma: incomplete dot = standard blue #3b82f6, completed dot = neutral grey #e6e8ec */
  #home-list .task .task__dot { background: var(--brand) !important; }              /* not done -> standard blue */
  #home-list .task.is-done .task__dot { background: var(--ring-track) !important; } /* done -> neutral grey */

  /* progress chip */
  .chip {
    width: 94px; height: 44px; border-radius: var(--r-card);
    background: var(--glass-chip);   /* D1: content surface — translucent fill only, no live blur (nothing scrolls beneath it) */
    border: 1px solid var(--border-glass);
    display: grid; place-items: center;
  }
  .chip__num {
    font-weight: 700; font-size: 24px; line-height: 30px;
    letter-spacing: -1.2px; color: var(--text-primary);
    font-variant-numeric: tabular-nums;
  }

  /* sass bubble */
  .bubble {
    width: 307px; border-radius: var(--r-bubble);
    background: var(--glass-bubble);   /* D1: content surface — no live blur */
    box-shadow: var(--sh-bubble);
    display: grid; place-items: center; padding: 12px;   /* consistent 12px (was min-height:48 -> collapsed on 2 lines) */
  }
  .bubble__text {
    font-weight: 600; font-size: 16px; line-height: 24px;
    letter-spacing: -0.32px; color: var(--text-primary); text-align: center;
  }
  .bubble__q { font-weight: 400; }

  /* task list container */
  .list-card {
    width: 354px; border-radius: var(--r-card);
    background: var(--surface-elevated);   /* D1: content surface — no live blur */
    border: 1px solid var(--border-glass);
    box-shadow: var(--sh-card-strong);
    overflow: hidden;
  }
  .task {
    position: absolute; left: 0; width: 354px; height: 58px;
    cursor: pointer; background: transparent;
    /* transition (re-sort slide + press highlight) lives in the PRESS FEEDBACK block */
  }
  .task.is-moving { z-index: 5; background: var(--surface-elevated); }   /* the toggled item lifts above + covers the rows it passes */
  body.shot .task { transition: none; }
  .task__check {
    position: absolute; left: 16px; top: 17px; width: 24px; height: 24px;
    color: var(--text-tertiary);
  }
  /* the check icon follows the span's color: incomplete = #6b7280 (tertiary), completed = #111827 (primary).
     without this the <use> SVG falls back to its default black fill. */
  .task__check svg { fill: currentColor; }
  .task__title {
    position: absolute; left: 52px; top: 8px;
    font-weight: 600; font-size: 16px; line-height: 24px;
    letter-spacing: -0.32px; color: var(--text-primary);
    max-width: 290px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .task__dot {
    position: absolute; left: 52px; top: 38px; width: 6px; height: 6px;
    border-radius: 50%;
  }
  .task__meta {
    position: absolute; left: 64px; top: 32px;
    font-weight: 400; font-size: 12px; line-height: 18px;
    letter-spacing: -0.24px; color: var(--text-tertiary); white-space: nowrap;
  }
  .task.is-done .task__title {
    color: var(--text-secondary);
    text-decoration: line-through; text-decoration-skip-ink: none;
  }
  .task.is-done .task__check { color: var(--text-primary); }

  /* ---------- tab bar — Liquid Glass, Regular tier (D1) ----------
     Four layers per the adjudicated recipe:
       ① adaptive fill (translucent white)      ② backdrop stack blur+saturate+brightness
       ③ specular ring on ::after (mask trick)  ④ edge darkening via inset shadow
     A2: filter values are LITERALS. B2: the sliding pill inside is fill+ring only —
     glass nested in glass is forbidden. This bar + the liquid-btns are the only
     backdrop-filter surfaces in the app (B1 ≤3 per screen). */
  .tabbar {
    position: absolute; left: 24px; top: 776px;
    width: 354px; height: 74px;
    /* D3: the floating bar is a true CAPSULE (h74 → r37), and the pill nests
       capsule-in-capsule: uniform 4px inset ⇒ inner r = 37−4 = 33 = h66/2 —
       the two curves stay parallel the whole way around the rounded ends.
       (Device-frame concentricity does not apply here: that rule is for panels
       whose corners sit near the device corners, not free-floating capsules.) */
    border-radius: calc(infinity * 1px);
    background: rgba(255, 255, 255, 0.62);   /* C1: light-glass fill α within [0.55, 0.75] */
    -webkit-backdrop-filter: blur(14px) saturate(1.7) brightness(1.05);
    backdrop-filter: blur(14px) saturate(1.7) brightness(1.05);
    box-shadow: inset 0 -1px 1px rgba(0, 0, 0, 0.07),
                0 1px 2px rgba(0, 0, 0, 0.08), 0 12px 32px rgba(0, 0, 0, 0.10);
    isolation: isolate;
    z-index: 20;
  }
  .tabbar::after {   /* specular ring: light catches the top edge, falls away below */
    content: ""; position: absolute; inset: 0; border-radius: inherit;
    pointer-events: none; padding: 1px;
    background: linear-gradient(to bottom, rgba(255,255,255,0.70), rgba(255,255,255,0.15) 40%, rgba(0,0,0,0.12));
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    mask-composite: exclude;
  }
  .tabbar[hidden] { display: none; }
  /* A3: engines with no backdrop-filter at all (old WebView / Firefox ESR) get a
     near-solid bar — never translucent-over-content without blur */
  @supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
    .tabbar { background: rgba(250, 250, 250, 0.97); }
  }
  .tabbar__pill {
    position: absolute; left: 4px; top: 4px; width: 109px; height: 66px;
    border-radius: calc(infinity * 1px);     /* D3: the selected-tab pill is a true capsule */
    background: rgba(255, 255, 255, 0.92);   /* B2: translucent fill + ring — NO nested backdrop-filter */
    border: 1px solid rgba(255, 255, 255, 0.85);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06), 0 6px 16px -6px rgba(35, 36, 37, 0.22),
                inset 0 -1px 1px rgba(0, 0, 0, 0.04);
    transform: translateX(0);
    transition: transform .3s cubic-bezier(.22, 1, .36, 1);
    z-index: 1;
  }
  .tabbar[data-active="calendar"] .tabbar__pill { transform: translateX(118px); }
  /* 237, not the slot-width 236: at the right END the capsule rim must equal the
     4px inset everywhere (354 − 4 − 109 − 237 = 4). The icon-centre drift this
     causes is 0.5px — invisible; an uneven end rim is not. */
  .tabbar[data-active="profile"]  .tabbar__pill { transform: translateX(237px); }
  .tab {
    position: absolute; top: 0; width: 118px; height: 74px;
    display: grid; place-items: center; cursor: pointer; z-index: 2;
  }
  .tab[data-tab="home"] { left: 0; }
  .tab[data-tab="calendar"] { left: 118px; }
  .tab[data-tab="profile"] { left: 236px; }
  /* vibrancy on light glass (C1): inactive icons take the darker tertiary, not a washed grey */
  .tab .icon { width: 28px; height: 28px; color: var(--text-tertiary); position: relative; z-index: 2; }
  .tab.is-active .icon { color: var(--text-primary); }

  /* ---------- onboarding ---------- */
  /* onboarding avatar — Figma updated the corner to a full circle. The circle (frame) stays FIXED;
     only the image strip inside slides, so each avatar rises from the bottom and bounces into place. */
  /* fixed circular window; the avatars flip horizontally inside it (no gap — they sit edge to edge) */
  .ob-avs { width: 274px; height: 274px; border-radius: 50%; overflow: hidden; background: #93C5FD; }
  /* horizontal flip — driven by its own spring (slower response than the text track),
     so the avatar trails for depth during AND after the gesture. No CSS transition:
     a transition here would fight the per-frame transform the spring writes. */
  .ob-avs-strip { display: flex; will-change: transform; }
  /* each avatar is a 274px flex slot; the <picture> wrapper (webp→png) is the flex item, its <img> fills it */
  .ob-avs-strip > picture { display: block; flex: 0 0 274px; width: 274px; height: 274px; }
  .ob-avs-strip img { display: block; width: 274px; height: 274px; object-fit: cover; }   /* feathered side edges → the seam between avatars blends softly */
  .ob-title {
    width: 338px; transform: translateX(-50%);
    font-weight: 700; font-size: 28px; line-height: 42px;
    letter-spacing: -0.84px; color: var(--text-primary); text-align: center;
  }
  .ob-body {
    width: 312px; transform: translateX(-50%);
    font-weight: 400; font-style: italic; font-size: 16px; line-height: 24px;
    letter-spacing: -0.32px; color: var(--text-tertiary); text-align: center;
  }
  /* onboarding carousel: only the content track slides; frame + dots stay put.
     Track, dots and avatar are all written per-frame from one shared page value
     (see obPaintText / obPaintAvatar) — no CSS transition on any of them, so the
     motion stays grabbable mid-flight instead of running to completion. */
  .screen[data-screen="onboarding"] { overflow: hidden; cursor: grab; touch-action: pan-y; }
  .screen[data-screen="onboarding"]:active { cursor: grabbing; }
  .ob-track { position: absolute; left: 0; top: 0; width: 1206px; height: 874px; display: flex;
    will-change: transform; }
  .ob-slide { position: relative; flex: 0 0 402px; width: 402px; height: 874px; }
  /* animated dot indicator (active dot grows to a pill, follows the swipe) */
  .ob-dots { left: 50%; top: 728px; transform: translateX(-50%); display: flex; align-items: center; gap: 8px; }
  .ob-dot { position: relative; width: 8px; height: 8px; border-radius: calc(infinity * 1px); background: var(--text-primary); opacity: 0.2; cursor: pointer; }  /* D3 capsule — stays right as the active dot grows into a pill */
  /* an 8px dot is nowhere near a thumb-sized target. Grow the hit area vertically
     only: a 44px-wide box would swallow its neighbours (the dots sit 16px apart,
     and the later sibling wins the overlap, so every tap would land on the last
     dot). Bleeding 4px — half the gap — to each side tiles the row exactly, with
     no overlap and no dead zone, at whatever width the active pill has grown to. */
  .ob-dot::after { content: ""; position: absolute; left: -4px; right: -4px;
    top: 50%; transform: translateY(-50%); height: 44px; }
  .ob-dot:focus-visible { outline: 2px solid var(--brand); outline-offset: 4px; }
  /* Let's Go appears only on the last page */
  .ob-cta { opacity: 0; pointer-events: none; transition: opacity .3s ease; }
  .ob-cta.show { opacity: 1; pointer-events: auto; }

  /* ---------- primary CTA (354x48 yellow, dark text) ---------- */
  .cta {
    width: 354px; height: 48px; border-radius: var(--r-input);
    background: var(--accent); border: 1px solid var(--accent);
    display: grid; place-items: center; cursor: pointer;
    transition: transform .12s ease, filter .12s ease;
  }
  .cta:active { transform: scale(0.96); filter: brightness(0.92); }
  .cta__label {
    font-weight: 600; font-size: 16px; line-height: 24px;
    letter-spacing: -0.48px; color: var(--text-primary);
  }

  /* ---------- modal headers (Category / Settings centered; Add Task left) ---------- */
  .screen-title {
    position: absolute; top: 70px; left: 0; width: 402px; text-align: center;
    font-weight: 600; font-size: 24px; line-height: 32px; letter-spacing: -0.72px;
    color: var(--text-primary); pointer-events: none;
  }
  .modal-kicker {
    font-weight: 600; font-size: 14px; line-height: 21px; letter-spacing: -0.28px;
    color: var(--text-tertiary); text-transform: uppercase;
  }
  .modal-title {
    font-weight: 700; font-size: 28px; line-height: 42px; letter-spacing: -0.84px;
    color: var(--text-primary);
  }

  /* ---------- settings ---------- */
  .settings-section {
    font-weight: 600; font-size: 18px; line-height: 27px; letter-spacing: -0.54px;
    color: var(--text-primary);
  }
  .settings-group {
    width: 354px; border-radius: var(--r-card); background: var(--surface-elevated);
    border: 1px solid var(--border-glass); box-shadow: var(--sh-card); overflow: hidden;
  }
  .set-row { position: absolute; left: 0; width: 354px; height: 58px; cursor: pointer; }
  .set-row__icon { position: absolute; left: 16px; top: 17px; width: 24px; height: 24px; color: var(--text-primary); }
  .set-row__title { position: absolute; left: 52px; top: 8px; font-weight: 600; font-size: 16px; line-height: 24px; letter-spacing: -0.32px; color: var(--text-primary); }
  .set-row__sub { position: absolute; left: 52px; top: 32px; font-weight: 400; font-size: 12px; line-height: 18px; letter-spacing: -0.24px; color: var(--text-tertiary); }
  .set-row__caret { position: absolute; left: 314px; top: 17px; width: 24px; height: 24px; color: var(--text-tertiary); }
  /* modifiers AFTER the base — same specificity, so source order decides. The compact
     height:40px used to sit ABOVE the base and silently lose the cascade: the row
     stayed 58px tall and its press highlight bled 18px into the row below (the
     Help & About / Logout overlap). */
  .set-row--compact { height: 40px; }
  .set-row--compact .set-row__icon,
  .set-row--compact .set-row__title,
  .set-row--compact .set-row__caret { top: 8px; }
  .set-row--dest { height: 40px; }
  .set-row--dest .set-row__icon { top: 8px; color: var(--destructive); }
  .set-row--dest .set-row__title { top: 8px; color: var(--destructive); }

  /* ---------- category picker ---------- */
  .pick-card { width: 354px; border-radius: var(--r-input); background: var(--surface-elevated); box-shadow: var(--sh-card); overflow: hidden; }
  .pick-row { position: absolute; left: 0; width: 354px; height: 40px; cursor: pointer; }
  .pick-row__dot { position: absolute; left: 16px; top: 16px; width: 8px; height: 8px; border-radius: 50%; }
  .pick-row__label { position: absolute; left: 36px; top: 8px; font-weight: 500; font-size: 16px; line-height: 24px; letter-spacing: -0.32px; color: var(--text-tertiary); }
  .pick-row.is-selected .pick-row__label { color: var(--text-primary); }
  .pick-row__check { position: absolute; left: 314px; top: 8px; width: 24px; height: 24px; color: var(--text-primary); display: none; }
  .pick-row.is-selected .pick-row__check { display: block; }

  /* ---------- add task fields ---------- */
  .field-label { font-weight: 500; font-size: 16px; line-height: 24px; letter-spacing: -0.48px; color: var(--text-tertiary); }
  /* borderless fields: transparent border (not none) keeps the border-box geometry
     byte-identical; the white fill + radius alone define the input surface */
  .field { position: absolute; height: 40px; border-radius: var(--r-input); background: var(--surface-elevated); border: 1px solid transparent; display: flex; align-items: center; padding: 0 16px; }
  .field--area { height: 80px; align-items: flex-start; padding-top: 8px; }
  .field__ph { font-weight: 300; font-size: 16px; line-height: 24px; letter-spacing: -0.48px; color: var(--text-placeholder); }
  .field__val { font-weight: 500; font-size: 16px; line-height: 24px; letter-spacing: -0.32px; color: var(--text-primary); }
  /* editable inputs (demo only — no save) */
  .field__input { flex: 1; width: 100%; min-width: 0; border: 0; background: transparent; outline: none; font-family: inherit; font-weight: 400; font-size: 16px; line-height: 24px; letter-spacing: -0.48px; color: var(--text-primary); padding: 0; }   /* typed text = regular */
  .field__input::placeholder { color: var(--text-placeholder); font-weight: 300; opacity: 1; }   /* hint = light */
  .field__input--val { font-weight: 400; letter-spacing: -0.32px; }
  .field__input--area { resize: none; height: 62px; align-self: stretch; }
  .field__dot { width: 8px; height: 8px; border-radius: 50%; margin-right: 12px; flex: none; }
  .field__caret { position: absolute; right: 8px; top: 8px; width: 24px; height: 24px; color: var(--text-tertiary); }
  .time-sep { position: absolute; font-weight: 500; font-size: 16px; line-height: 24px; color: var(--text-tertiary); }

  /* ---------- profile ---------- */
  .profile-card {
    position: absolute; left: 24px; top: 151px; width: 354px; height: 135px;
    border-radius: var(--r-card); background: var(--glass-card);   /* D1: content surface — no live blur */
    border: 1px solid var(--border-glass); box-shadow: var(--sh-card); overflow: hidden;
  }
  .profile-card__avatar { position: absolute; left: 23px; top: 23px; width: 72px; height: 72px; border-radius: 50%; background: #cfe0f4; border: 1px solid var(--border-input); overflow: hidden; }
  .profile-card__avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
  .pc-name { position: absolute; left: 111px; top: 21px; font-weight: 700; font-size: 16px; line-height: 24px; letter-spacing: -0.32px; color: var(--text-primary); }
  .pc-email { position: absolute; left: 111px; top: 45px; font-weight: 400; font-size: 12px; line-height: 18px; letter-spacing: -0.24px; color: var(--text-tertiary); }
  .pc-stat-label { position: absolute; top: 79px; font-weight: 400; font-size: 12px; line-height: 18px; letter-spacing: -0.24px; color: var(--text-tertiary); }
  .pc-stat-val { position: absolute; top: 97px; font-weight: 600; font-size: 14px; line-height: 21px; letter-spacing: -0.28px; color: var(--text-primary); }
  .profile-monthly { position: absolute; right: 24px; top: 313px; font-weight: 500; font-size: 16px; line-height: 24px; letter-spacing: -0.48px; color: var(--brand); }
  .cat-card {
    position: absolute; width: 169px; height: 120px; border-radius: var(--r-card);
    background: var(--glass-card);   /* D1: content surface — no live blur */
    box-shadow: var(--sh-card); overflow: hidden;
  }
  .cat-card__title { position: absolute; left: 15px; top: 15px; font-weight: 600; font-size: 16px; line-height: 24px; letter-spacing: -0.32px; color: var(--text-primary); }
  .cat-card__dot { position: absolute; left: 15px; top: 49px; width: 6px; height: 6px; border-radius: 50%; }
  .cat-card__count { position: absolute; left: 27px; top: 43px; font-weight: 400; font-size: 12px; line-height: 18px; letter-spacing: -0.24px; color: var(--text-tertiary); }
  .cat-card__pct { position: absolute; left: 15px; top: 76px; font-weight: 700; font-size: 18px; line-height: 27px; letter-spacing: -0.54px; color: var(--text-primary); }
  .cat-card__ring { position: absolute; left: 110px; top: 61px; width: 42px; height: 42px; transform: rotate(-90deg); overflow: visible; }
  .ccr-bg { stroke: var(--ring-track); stroke-width: 4.5; }
  .ccr-fg { stroke-width: 4.5; stroke-linecap: round; transition: stroke-dasharray .7s cubic-bezier(.22, 1, .36, 1); }
  body.shot .ccr-fg { transition: none; }
  .profile-sass {
    position: absolute; left: 24px; top: 625px; width: 354px;
    border-radius: var(--r-card); background: var(--surface-elevated);
    border: 1px solid var(--border-glass); box-shadow: var(--sh-card);
    display: flex; align-items: center; padding: 12px;   /* consistent 12px (was height:96 -> oversized padding) */
  }
  .profile-sass__text { font-weight: 600; font-size: 16px; line-height: 24px; letter-spacing: -0.32px; color: var(--text-primary); }

  /* ---------- calendar (Month + Day Detail) ---------- */
  .cal-grid {
    position: absolute; left: 24px; top: 153px; width: 354px;
    border-radius: var(--r-card); background: rgba(234,236,240,0.82);   /* D1: content surface — frosted fill only, no live blur */
    border: 1px solid var(--border-glass); box-shadow: var(--sh-card);
  }
  .cal-weekday {
    position: absolute; width: 30px; top: 12px; text-align: center;
    font-weight: 500; font-size: 12px; line-height: 18px; letter-spacing: -0.24px;
    color: var(--text-tertiary);
  }
  /* states: future = white (base), not-completed/past = grey (--muted), completed = green, selected = blue */
  .cal-cell { position: absolute; width: 42px; height: 42px; border-radius: var(--r-cell); box-sizing: border-box; background: #ffffff; border: 1px solid var(--border-input); box-shadow: 0px 4px 11px -5px rgba(35,36,37,0.13); overflow: hidden; }
  .cal-cell--green { background: var(--cal-green-bg); border-color: var(--cal-green-border); }
  .cal-cell--muted { background: var(--cal-muted-bg); }   /* not-completed (past, not done) — grey vs the white future cells */
  .cal-cell--sel   { background: var(--cal-sel-bg); border: 1px solid var(--brand); }
  .cal-num {
    position: absolute; left: 7px; top: 5px;
    font-weight: 600; font-size: 12px; line-height: 18px; letter-spacing: -0.24px;
    color: var(--text-primary);
  }
  .cal-cell--green .cal-num { color: var(--cal-green-text); }
  .cal-cell--muted .cal-num { color: var(--text-tertiary); }   /* dimmer number for not-completed days */
  .cal-cell--sel   .cal-num { color: var(--text-primary); }
  .cal-cell--selblue .cal-num { color: var(--brand); }
  /* selected state — applied to whichever Month date is tapped (overrides the base state) */
  .cal-cell.is-selected { background: var(--cal-sel-bg); border: 1px solid var(--brand); }
  .cal-cell.is-selected .cal-num { color: var(--brand); }   /* selected day's number is blue */
  /* Day-strip selected day — same blue cell, but the number turns blue (Figma day-view rule) */
  .cal-cell.is-day-selected { background: var(--cal-sel-bg); border: 1px solid var(--brand); }
  .cal-cell.is-day-selected .cal-num { color: var(--brand); }
  .cal-dot { position: absolute; top: 30px; width: 5px; height: 5px; border-radius: 50%; }
  .cal-legend__dot { position: absolute; width: 6px; height: 6px; border-radius: 50%; }
  .cal-legend__label {
    position: absolute; font-weight: 400; font-size: 12px; line-height: 18px;
    letter-spacing: -0.24px; color: var(--text-tertiary); white-space: nowrap;
  }

  /* day detail — selected-day hero (height is auto so it grows with the variable-length quote and always keeps its bottom padding) */
  .day-hero {
    position: absolute; left: 24px; top: 315px; width: 354px;
    box-sizing: border-box; padding: 21px 24px 24px;
    display: flex; flex-direction: column;
    border-radius: var(--r-card); background: var(--surface-elevated);
    border: 1px solid var(--border-glass); box-shadow: var(--sh-card);
  }
  .day-hero__row { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
  .day-hero__row--meta { margin-top: 7px; }
  .day-hero__title { margin: 0; font-weight: 700; font-size: 28px; line-height: 36px; letter-spacing: -0.84px; color: var(--text-primary); }
  .day-hero__pct   { margin: 0; text-align: right; white-space: nowrap; font-weight: 700; font-size: 28px; line-height: 36px; letter-spacing: -0.84px; color: var(--text-primary); }
  .day-hero__meta  { margin: 0; font-weight: 400; font-size: 12px; line-height: 18px; letter-spacing: -0.24px; color: var(--text-tertiary); }
  .day-hero__done  { margin: 0; text-align: right; white-space: nowrap; font-weight: 400; font-size: 12px; line-height: 18px; letter-spacing: -0.24px; color: var(--text-tertiary); }
  .day-hero__quote { margin: 14px 0 0; font-weight: 600; font-size: 16px; line-height: 24px; letter-spacing: -0.32px; color: var(--text-primary); }

  /* ---------- pressed/clicked state on icon buttons (item 7a) ---------- */
  .liquid-btn { transition: transform .12s ease, background-color .12s ease; }
  .liquid-btn:active { transform: scale(0.9); background: rgba(17,24,39,0.14); }

  /* ---------- slide-in modals: Add Task (sheet up) / Category & Settings (push from right) ---------- */
  .screen.modal-screen {
    display: block; background: var(--surface-page); z-index: 25;
    transition: transform .34s cubic-bezier(.22, 1, .36, 1); will-change: transform;
  }
  .screen.modal-screen.sheet { transform: translateY(100%); }
  .screen.modal-screen.push  { transform: translateX(100%); }
  .screen.modal-screen.is-open { transform: translate(0, 0); }
  [data-screen="settings"].modal-screen { z-index: 28; }
  [data-screen="category"].modal-screen { z-index: 31; } /* opens over Add Task */
  body.shot .modal-screen { transition: none; }

  /* ---------- profile period dropdown (item 10) ---------- */
  .period { position: absolute; z-index: 6; }
  .period__btn { display: inline-flex; align-items: center; gap: 4px; background: none; border: 0; padding: 0; cursor: pointer;
    font-weight: 500; font-size: 16px; line-height: 24px; letter-spacing: -0.48px; color: var(--brand); }
  .period__caret { margin-top: 1px; transition: transform .2s ease; }
  .period__btn[aria-expanded="true"] .period__caret { transform: rotate(180deg); }
  .period__menu { position: absolute; right: 0; top: 32px; min-width: 124px; list-style: none; margin: 0; padding: 6px;
    background: var(--surface-elevated); border: 1px solid var(--border-glass); border-radius: var(--r-menu); box-shadow: var(--sh-card-strong); }
  .period__menu[hidden] { display: none; }
  /* D3 concentric: item = menu radius 14 − padding 6 = 8 (was a hand-written 9) */
  .period__menu li { padding: 9px 12px; border-radius: max(calc(var(--r-menu) - 6px), var(--r-min)); font-weight: 500; font-size: 14px; line-height: 20px; color: var(--text-secondary); cursor: pointer; }
  /* hover + selected share the exact same highlight: same bg AND same radius (kept consistent) */
  .period__menu li:hover { background: var(--surface-page); }
  .period__menu li[aria-current="true"] { color: var(--brand); background: var(--surface-page); }

  /* ---------- Month grid compresses the non-today weeks; the today-week (row 3 = 18-24) rises to the top ---------- */
  .screen[data-screen="calendar"] .cal-grid { transition: height .43s cubic-bezier(.22, 1, .36, 1); }
  .screen[data-screen="calendar"] .cal-cell { transition: transform .43s cubic-bezier(.22, 1, .36, 1), opacity .3s ease; }
  .screen[data-screen="calendar"].is-compressing .cal-grid { height: 138px !important; }
  .screen[data-screen="calendar"].is-compressing .cal-cell.is-collapse { opacity: 0; transform: scaleY(0.16); }
  .screen[data-screen="calendar"].is-compressing .cal-cell.is-keep { transform: translateY(var(--keep-dy, -150px)); }
  .screen[data-screen="calendar"].is-compressing .cal-legend__dot,
  .screen[data-screen="calendar"].is-compressing .cal-legend__label { opacity: 0; transition: opacity .25s ease; }
  /* the Month Tasks section stays put during compression (no fade) — the Day's Tasks slide in from this exact spot, so there's no flash */
  body.shot .screen[data-screen="calendar"] .cal-grid,
  body.shot .screen[data-screen="calendar"] .cal-cell,
  body.shot .screen[data-screen="calendar"] .section-title,
  body.shot .screen[data-screen="calendar"] .list-card { transition: none; }

  /* ---------- Day: hero rises in from BEHIND the week-strip grid (lower z-index); the Tasks slide up from the Month spot over the SAME time ---------- */
  .screen[data-screen="calendarday"] .cal-grid { z-index: 3; }    /* the glass grid sits above the hero, so the hero emerges from behind it */
  .screen[data-screen="calendarday"] .day-hero { z-index: 1; }
  .screen[data-screen="calendarday"].is-active:not(.is-cal-exit) .day-hero {
    transform-origin: top center; animation: day-hero-backin .72s cubic-bezier(.22, 1, .36, 1) both;
  }
  .screen[data-screen="calendarday"].is-active:not(.is-cal-exit) .section-title,
  .screen[data-screen="calendarday"].is-active:not(.is-cal-exit) .list-card { animation: day-tasks-settle .72s cubic-bezier(.22, 1, .36, 1) both; }
  .screen[data-screen="calendarday"].is-cal-exit .day-hero {
    transform-origin: top center; animation: day-hero-backout .42s cubic-bezier(.4, 0, .7, 1) both;
  }
  .screen[data-screen="calendarday"].is-cal-exit .section-title,
  .screen[data-screen="calendarday"].is-cal-exit .list-card { animation: day-tasks-settle-out .42s cubic-bezier(.4, 0, .7, 1) both; }
  @keyframes day-hero-backin {
    0%   { transform: translateY(-240px) scale(0.82); opacity: 0; }
    70%  { transform: translateY(0) scale(0.82); opacity: 1; }
    100% { transform: translateY(0) scale(1); opacity: 1; }
  }
  @keyframes day-hero-backout {
    0%   { transform: translateY(0) scale(1); opacity: 1; }
    35%  { transform: translateY(0) scale(0.82); opacity: 1; }
    100% { transform: translateY(-240px) scale(0.82); opacity: 0; }
  }
  /* the Tasks start 30px lower — exactly where the Month Tasks sit — and ease up to their Day position; no opacity change = no blink */
  @keyframes day-tasks-settle { from { transform: translateY(30px); } to { transform: translateY(0); } }
  @keyframes day-tasks-settle-out { from { transform: translateY(0); } to { transform: translateY(30px); } }
  body.shot [data-screen="calendarday"] .cal-grid,
  body.shot [data-screen="calendarday"] .day-hero,
  body.shot [data-screen="calendarday"] .section-title,
  body.shot [data-screen="calendarday"] .list-card { animation: none !important; }

  /* ============================================================
     PRESS FEEDBACK
     Every tappable surface acknowledges the press itself, not the
     release. .cta and .liquid-btn already did; the rows — which are
     the app's most-tapped targets — did not, so a tap read as dead
     until the list finished re-sorting.

     Asymmetric on purpose: the highlight arrives instantly (0s in)
     and releases over 160ms, so a committed tap reads as sharp and a
     cancelled one (dragged off the row) fades instead of snapping.
     ============================================================ */
  /* the task row also carries the .44s re-sort slide, so its transition list
     is declared whole here rather than appended to */
  .task { transition: top .44s cubic-bezier(.22, 1, .36, 1), background-color .16s ease-out; }
  .task:active { background-color: rgba(17, 24, 39, 0.055); transition-duration: .44s, 0s; }
  .pick-row, .set-row { transition: background-color .16s ease-out; }
  .pick-row:active, .set-row:active { background-color: rgba(17, 24, 39, 0.055); transition-duration: 0s; }
  /* the small calendar squares are objects, not rows — they take a press the way
     a key does, by depressing, rather than by lighting up */
  .cal-cell { transition: transform .16s cubic-bezier(.23, 1, .32, 1); }
  .cal-cell:active { transform: scale(0.96); transition-duration: 0s; }
  body.shot .task, body.shot .pick-row, body.shot .set-row, body.shot .cal-cell { transition: none; }

  /* ============================================================
     Translucency is load-bearing here (8 blurred surfaces), so it
     needs the same opt-out treatment reduced-motion already gets:
     solid backgrounds, no blur, and a real border to replace the
     edge the blur used to imply.
     ============================================================ */
  @media (prefers-reduced-transparency: reduce) {
    /* live glass is now only the tab bar + liquid buttons; everything else is
       already a translucent fill over an opaque page (effectively solid) */
    .liquid-btn, .tabbar {
      -webkit-backdrop-filter: none !important; backdrop-filter: none !important;
    }
    .liquid-btn { background: var(--surface-elevated) !important; }
    .tabbar { background: rgba(250, 250, 250, 0.98) !important; }
    .chip, .bubble, .list-card, .profile-card, .cat-card, .tabbar__pill {
      background: var(--surface-elevated) !important;
    }
    .cal-grid { background: var(--cal-muted-bg) !important; }
    .bubble, .list-card, .profile-card, .cat-card { border: 1px solid var(--border-input); }
  }

  @media (prefers-reduced-motion: reduce) {
    .task, .pick-row, .set-row, .cal-cell { transition: none; }
    .cal-cell:active { transform: none; }
    .ring-done, .ring-prog { transition: none; }
    .tabbar__pill { transition: none; }
    .screen.modal-screen { transition: none; }
    .screen[data-screen="calendarday"].is-active .cal-grid,
    .screen[data-screen="calendarday"].is-active .day-hero,
    .screen[data-screen="calendarday"].is-active .section-title,
    .screen[data-screen="calendarday"].is-active .list-card { animation: none; }
    /* P1-B: parity with shot — kill the remaining motion the shot block already stills */
    .screen.crossDissolve { animation: none; }   /* plain screen→screen fade */
    .task { transition: none; }                   /* re-sort top-slide */
  }

  /* ============================================================
     RUNTIME AFFORDANCES (Role A) — discoverability + a11y aids.
     Purely additive: every element lives in dead space, carries no
     Figma-measured pixel, and is display:none under body.shot so the
     ?shot capture is byte-identical to before. ============================================================ */
  /* Day: subtle "pull down to go back" caret, sitting in the gap between the week strip (bottom ≈291) and the hero (top 315) */
  .cal-pull {
    position: absolute; left: 0; right: 0; top: 295px; height: 14px;
    display: flex; align-items: center; justify-content: center;
    color: var(--text-tertiary); opacity: 0.7; pointer-events: none; z-index: 4;
  }
  .cal-pull__caret { width: 14px; height: 14px; display: inline-block; }
  .cal-pull__caret svg { width: 100%; height: 100%; fill: currentColor; display: block; }
  /* hide every affordance in shot mode so visual diff stays zero */
  body.shot .cal-pull, body.shot .cal-back { display: none; }
  /* Day detail: the explicit back button sits at top-left where the (month-duplicated) header was.
     Hide that redundant kicker/title in interactive mode only so the back button doesn't collide with it;
     shot mode keeps the original header (back button is shot-hidden) so the frozen baseline is unchanged. */
  /* Fix (2026-07-27, owner call): match the Figma Day Detail exactly — header stays
     at its original spot and the explicit back button is hidden in ALL modes (it was
     a desktop affordance, never part of the design). Back navigation remains via
     swipe/wheel down at the top plus the cal-pull caret. */
  .cal-back { display: none; }

  /* P1-A: keyboard focus ring — :focus-visible only (mouse clicks never show it; shot never triggers it).
     No border-radius here: the outline follows each element's own corners, so focus never reshapes the box. */
  .task:focus-visible, .cal-cell:focus-visible, .pick-row:focus-visible,
  .set-row:focus-visible {
    outline: 2px solid var(--brand); outline-offset: 2px;
  }

  /* caption note under the device (not part of the frame) */
  .stage__note {
    position: fixed; left: 50%; bottom: 12px; transform: translateX(-50%);   /* pinned to the viewport bottom; doesn't shift the device */
    margin: 0; text-align: center; color: rgba(255,255,255,0.5);
    font-size: 12.5px; line-height: 1.5; max-width: 92vw; pointer-events: none;
  }
  .stage__note a { color: rgba(255,255,255,0.8); }

  /* ============================================================
     Status bar (restored 2026-07-27, owner call) — the original demo's
     self-drawn 9:41 bar, one global copy on the device layer (the old
     build kept it in Home and cloned it up via relocateChrome; a single
     static copy needs no JS). Opaque page bg so scrolled content passes
     behind it. Time color uses the token so dark mode stays legible. */
  .statusbar {
    position: absolute; left: 0; top: 0;
    width: 402px; height: 62px;
    display: flex; align-items: center; justify-content: space-between;
    padding: 21px 24px 19px;
    background: var(--surface-page);
    z-index: 30; pointer-events: none;
  }
  .statusbar__time {
    font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "SF Pro", "Helvetica Neue", sans-serif;
    font-weight: 590; font-size: 17px; line-height: 22px;
    color: var(--text-primary); letter-spacing: 0;
    font-variant-numeric: tabular-nums;
  }
  .statusbar__icons { display: flex; align-items: center; gap: 7px; color: var(--text-primary); }
  .statusbar__icons svg { display: block; }
