:root {
    color-scheme: light dark;

    /* ─── Colors ─────────────────────────────────────── */
    --bg: #f7f4ee;
    --surface: #efeae1;
    --fg: #1a1816;
    --muted: #888076;
    --faint: #d8d2c6;
    --accent: #2a261f;
    --heat-0: #e6e0d3;
    --heat-1: #cdb98f;
    --heat-2: #a98e57;
    --heat-3: #7a5e2e;
    --heat-4: #4a3713;

    /* ─── Type scale ─────────────────────────────────── */
    /* Conservative pass: each token is a unique size used in 2+
       places. Pick from this list before adding a new size; if
       nothing fits, add a token. */
    --text-1: 0.7rem;    /* uppercase labels, footer, cumulative-axis */
    --text-2: 0.72rem;   /* stat-meta, panel h2 */
    --text-3: 0.75rem;   /* day-detail-nbs, cumulative-tip, login-divider */
    --text-4: 0.78rem;   /* spatial-caption, bar-value */
    --text-5: 0.85rem;   /* hero-meta, login-status, bar-name, bar-row-nested */
    --text-6: 0.95rem;   /* day-detail-line, login-tagline, onboarding-steps */
    --text-7: 1.4rem;    /* stat-value */
    --text-8: 2.25rem;   /* login-title */

    /* ─── Font weights ───────────────────────────────── */
    --weight-thin:   200;   /* .hero-number */
    --weight-light:  300;   /* .stat-value, .login-title */
    --weight-medium: 500;   /* labels, buttons, folder rows */

    /* ─── Radii ──────────────────────────────────────── */
    --radius-md: 0.6rem;    /* inputs, buttons */
    --radius-lg: 0.75rem;   /* panels, .stat cards */

    /* ─── Spacing ────────────────────────────────────────
       4px grid. Pick the nearest step before adding a raw
       value; if something genuinely needs to live between
       steps, prefer adding a new token to keeping it raw. */
    --space-1: 0.25rem;  /* 4px  — tightest gaps below labels */
    --space-2: 0.5rem;   /* 8px  — common small gaps */
    --space-3: 0.75rem;  /* 12px — medium gaps, sub-section margins */
    --space-4: 1rem;     /* 16px — standard padding pair */
    --space-5: 1.5rem;   /* 24px — between sections */
    --space-6: 2rem;     /* 32px — between major sections, page padding */
    --space-7: 3rem;     /* 48px — page-level bottom */
    --space-8: 4rem;     /* 64px — full-page padding */

    /* ─── Motion ─────────────────────────────────────────
       Three durations cover every transition in the app.
       Wrap motion in @media (prefers-reduced-motion:
       no-preference) so users with the OS toggle on get
       instant state changes. */
    --motion-fast: 150ms;  /* tap feedback, focus rings */
    --motion-base: 200ms;  /* state changes (folder expand, refresh dip, chev) */
    --motion-slow: 400ms;  /* settling — load fade, asymmetric tap-release */
}

@media (prefers-color-scheme: dark) {
    :root {
        --bg: #15130f;
        --surface: #1f1c17;
        --fg: #ece5d6;
        --muted: #7a7368;
        --faint: #2e2a23;
        --accent: #f3ecdb;
        --heat-0: #2a261e;
        --heat-1: #5a4a2c;
        --heat-2: #8a6f3c;
        --heat-3: #b89556;
        --heat-4: #e8c574;
    }
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html, body {
    min-height: 100%;
    scrollbar-width: none;          /* Firefox */
    -ms-overflow-style: none;       /* legacy Edge / IE */
}
html::-webkit-scrollbar,
body::-webkit-scrollbar { display: none; }   /* WebKit / mobile Safari */

body {
    background: var(--bg);
    color: var(--fg);
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    padding: env(safe-area-inset-top) env(safe-area-inset-right)
             env(safe-area-inset-bottom) env(safe-area-inset-left);
}

main {
    max-width: 480px;
    margin: 0 auto;
    padding: var(--space-6) var(--space-4) var(--space-7);
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
}

.label, .stat-label, .legend-label {
    font-size: var(--text-1);
    font-weight: var(--weight-medium);
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--muted);
}

/* hero */
.hero { text-align: center; padding: var(--space-4) 0 var(--space-2); }
.hero .label { margin-bottom: var(--space-3); }
.hero-number {
    font-size: clamp(3.5rem, 18vw, 6rem);
    font-weight: var(--weight-thin);
    line-height: 1;
    letter-spacing: -0.04em;
    color: var(--accent);
    font-variant-numeric: tabular-nums;
    display: inline-flex;
    align-items: baseline;
    gap: var(--space-2);
}
.hero-unit {
    font-size: 0.32em;
    color: var(--muted);
    font-weight: 400;
    letter-spacing: 0.04em;
}
.hero-meta {
    margin-top: var(--space-3);
    font-size: var(--text-5);
    color: var(--muted);
    font-variant-numeric: tabular-nums;
}

/* stats row */
.stats {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-3);
}
.stat {
    background: var(--surface);
    border-radius: var(--radius-lg);
    padding: var(--space-3) var(--space-4);
}
.stat-label { margin-bottom: var(--space-1); }
.stat-value {
    font-size: var(--text-7);
    font-weight: var(--weight-light);
    color: var(--accent);
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.01em;
}
.stat-meta {
    margin-top: var(--space-1);
    font-size: var(--text-2);
    letter-spacing: 0.04em;
    opacity: 0.65;
    font-variant-numeric: tabular-nums;
}

/* cumulative-ink line chart */
.cumulative {
    width: 100%;
    margin-top: var(--space-2);
    position: relative;
    /* Stops vertical scroll-hijack on touch when scrubbing the chart. */
    touch-action: pan-x;
}
.cumulative-svg { width: 100%; height: 80px; display: block; }
.cumulative-line {
    stroke: var(--accent);
    stroke-width: 1.5;
    stroke-linejoin: round;
    stroke-linecap: round;
}
.cumulative-fill { fill: var(--accent); opacity: 0.10; }
.cumulative-dot { fill: var(--accent); }
.cumulative-hover-line {
    stroke: var(--accent);
    stroke-width: 1;
    opacity: 0.45;
    pointer-events: none;
}
.cumulative-hover-dot { fill: var(--accent); pointer-events: none; }
.cumulative-axis {
    display: flex;
    justify-content: space-between;
    margin-top: var(--space-2);
    font-size: var(--text-1);
    opacity: 0.55;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.02em;
}
.cumulative-tip {
    position: absolute;
    top: -0.4rem;
    background: var(--accent);
    color: var(--surface);
    font-size: var(--text-1);
    padding: var(--space-1) var(--space-2);
    border-radius: 0.3rem;
    pointer-events: none;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
    transform: translateY(-100%);
}

/* panels */
.panel {
    background: var(--surface);
    border-radius: var(--radius-lg);
    padding: var(--space-4);
}
.panel h2 {
    font-size: var(--text-2);
    font-weight: var(--weight-medium);
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--muted);
    margin-bottom: var(--space-3);
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-2);
}
.panel-aside {
    font-size: 0.78em;
    letter-spacing: 0.04em;
    text-transform: none;
    color: var(--muted);
}

/* day detail row (above heatmap) */
.day-detail {
    min-height: 2.3rem;
    margin-bottom: var(--space-3);
    line-height: 1.25;
}
.day-detail-line {
    font-size: var(--text-6);
    color: var(--fg);
    font-variant-numeric: tabular-nums;
}
#day-date { color: var(--muted); }
#day-total { font-weight: var(--weight-medium); }
.day-detail-nbs {
    font-size: var(--text-3);
    color: var(--muted);
    margin-top: var(--space-1);
    min-height: 0.95rem;          /* reserve one line so the panel doesn't grow when text lands */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* selected heatmap cell */
.heatmap .cell.selected {
    stroke: var(--accent);
    stroke-width: 1.5;
}

/* heatmap */
.heatmap {
    overflow-x: auto;
    padding-bottom: var(--space-2);
    /* Fade older months into the surface so recent activity reads first. */
    -webkit-mask-image: linear-gradient(to right, transparent 0, rgba(0,0,0,1) 80px, rgba(0,0,0,1) 100%);
            mask-image: linear-gradient(to right, transparent 0, rgba(0,0,0,1) 80px, rgba(0,0,0,1) 100%);
}
.heatmap svg { display: block; }
.heatmap .cell { cursor: pointer; }
.heatmap .month-label {
    font-size: 8px;
    fill: var(--muted);
    font-family: inherit;
}
.legend {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-1);
    margin-top: var(--space-2);
}
.legend-cell {
    width: 10px; height: 10px;
    border-radius: 2px;
    display: inline-block;
}
.legend-cell[data-level="0"] { background: var(--heat-0); }
.legend-cell[data-level="1"] { background: var(--heat-1); }
.legend-cell[data-level="2"] { background: var(--heat-2); }
.legend-cell[data-level="3"] { background: var(--heat-3); }
.legend-cell[data-level="4"] { background: var(--heat-4); }

/* spatial heatmap: where ink lands on the page */
.spatial {
    display: flex;
    justify-content: center;
}
.spatial svg {
    display: block;
    width: auto;
    max-width: 100%;
    height: auto;
    max-height: 60vh;
}
.spatial .page-frame {
    fill: none;
    stroke: var(--faint);
    stroke-width: 0.5;
    rx: 4;
}
.spatial .marg-bar { fill: var(--heat-2); cursor: pointer; }
.spatial .marg-bar.selected { fill: var(--accent); }
.spatial .cell-tap { cursor: pointer; }
.spatial .cell-tap.selected {
    stroke: var(--accent);
    stroke-width: 1.5;
}
.spatial .selection-overlay {
    fill: none;
    stroke: var(--accent);
    stroke-width: 1.5;
    pointer-events: none;
}
.spatial-caption {
    margin-top: var(--space-3);
    font-size: var(--text-4);
    color: var(--muted);
    text-align: center;
    font-variant-numeric: tabular-nums;
}

/* when-you-write panel: 24 hour-of-day bars */
.hours svg { display: block; width: 100%; height: auto; }
.hours .hour-bar { fill: var(--accent); cursor: pointer; }
.hours .hour-bar-empty { fill: var(--faint); cursor: pointer; }
.hours .hour-bar.selected,
.hours .hour-bar-empty.selected { fill: var(--heat-4); }
.hours .hour-tick { fill: var(--muted); font-size: 8px; font-family: inherit; }
.hours .hour-axis { stroke: var(--faint); stroke-width: 1; }

/* bar chart */
.bars { display: flex; flex-direction: column; gap: var(--space-2); }
.bar-row {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: var(--space-2);
    align-items: center;
}
.bar-row-folder {
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}
.bar-name {
    font-size: var(--text-5);
    color: var(--fg);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    display: flex;
    align-items: center;
    gap: var(--space-2);
}
.bar-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.bar-chev {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 12px;
    height: 12px;
    color: var(--muted);
    transform: rotate(0deg);
    transform-origin: 50% 50%;
    transition: transform var(--motion-base) ease;
}
.bar-chev.open { transform: rotate(90deg); }
.bar-chev svg { display: block; }
.bar-row-folder .bar-label { color: var(--accent); font-weight: var(--weight-medium); }
.bar-row-folder .bar-chev  { color: var(--accent); }
.bar-value {
    font-size: var(--text-4);
    color: var(--muted);
    font-variant-numeric: tabular-nums;
    text-align: right;
}
.bar-track {
    grid-column: 1 / -1;
    height: 6px;
    background: var(--faint);
    border-radius: 3px;
    overflow: hidden;
}
.bar-fill {
    height: 100%;
    background: var(--accent);
    border-radius: 3px;
}
.bar-row-folder .bar-fill { opacity: 0.55; }

/* nested rows: only the label shrinks slightly so nesting reads as a
   secondary level without breaking the row's rhythm. */
.bar-row-nested .bar-label { font-size: var(--text-5); }

/* collapsible folder children — grid-template-rows trick lets us animate
   to the content's natural height without measuring it in JS.
   minmax(0, …) defeats the grid track's automatic min-content size, which
   would otherwise leak a sliver of nested content through when collapsed. */
.bar-children {
    display: grid;
    grid-template-rows: minmax(0, 0fr);
    transition: grid-template-rows var(--motion-base) ease;
    /* Belt-and-suspenders: the minmax(0, 0fr) trick gets the track
       to ~0 height, but on iOS Safari sub-pixel rounding can leave
       1-3px through which the inner's padding-top peeks. Hard-clip
       the container so nothing escapes the track. */
    overflow: hidden;
}
.bar-children.open { grid-template-rows: minmax(0, 1fr); }
.bar-children-inner {
    min-height: 0;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    /* padding (not margin) so the spacing is *inside* the clipped box
       and animates with the row height. Margins would jump instantly. */
    padding-top: var(--space-2);
    padding-bottom: var(--space-1);
}

/* ---- loading state ----
   Two mechanisms:
     .v         — primary data text. Hidden during load, fades in on `.is-loaded`.
                  No skeleton box (text widths vary too much to size cleanly).
     .canvas    — wrapper around empty SVG containers. Shows a shimmering
                  placeholder via `::before` while loading; fades out on `.is-loaded`.
   Per-section transition-delay gives a quiet top-to-bottom stagger. */

@keyframes sk-shimmer {
    0%   { background-position: 200% 0; }
    100% { background-position: -200% 0; }
}

.v { transition: opacity var(--motion-slow) ease; }
body.is-loading .v { opacity: 0; }
/* Auto-refresh fade: brief opacity dip on the values while we swap in fresh
   data. Faster than the initial load fade so it feels like a refresh, not a
   reload. Fade-back-in uses the default .v transition. */
body.is-refreshing .v { opacity: 0; transition: opacity var(--motion-base) ease; }

/* The hero number is the focal point, so give it a centered shimmer block
   instead of leaving it empty. Sized as fixed px so it doesn't try to track
   the (very wide) inline-flex font-driven box. */
.hero-number { position: relative; }
.hero-number::before {
    content: "";
    position: absolute;
    left: 50%;
    top: 50%;
    width: 230px;
    height: 4.5rem;
    transform: translate(-50%, -50%);
    background-color: var(--faint);
    background-image: linear-gradient(90deg,
        transparent 0%,
        rgba(255, 255, 255, 0.04) 50%,
        transparent 100%);
    background-size: 200% 100%;
    background-repeat: no-repeat;
    border-radius: 10px;
    animation: sk-shimmer 1.8s linear infinite;
    transition: opacity var(--motion-slow) ease;
    pointer-events: none;
}
body.is-loaded .hero-number::before {
    opacity: 0;
    animation: none;
}

.canvas {
    position: relative;
    border-radius: 6px;
}
.canvas::before {
    content: "";
    position: absolute;
    inset: 0;
    background-color: var(--faint);
    background-image: linear-gradient(90deg,
        transparent 0%,
        rgba(255, 255, 255, 0.04) 50%,
        transparent 100%);
    background-size: 200% 100%;
    background-repeat: no-repeat;
    border-radius: inherit;
    animation: sk-shimmer 1.8s linear infinite;
    transition: opacity var(--motion-slow) ease;
    z-index: 1;
    pointer-events: none;
}
/* Heights chosen to match what each chart actually renders to, so the panel
   stays the same size when the skeleton is replaced by real content.
   - heatmap: SVG is 103px + 0.5rem padding ≈ 111px (a touch more if the
     scrollbar shows on desktop).
   - hours: SVG is viewBox 240×90 stretched to 100% width, so height tracks
     width via aspect-ratio.
   - spatial: capped at 60vh by .spatial svg max-height.
   - bars: variable depending on folder count; overshoot a little. */
.canvas-heatmap    { min-height: 120px; }
.canvas-cumulative { min-height: 105px; }
.canvas-hours      { aspect-ratio: 240 / 90; }
.canvas-spatial    { min-height: 60vh; }
.canvas-bars       { min-height: 320px; }

body.is-loaded .canvas::before {
    opacity: 0;
    animation: none;
}

/* gentle top-to-bottom stagger when content arrives */
body.is-loaded main > section:nth-child(2) .v,
body.is-loaded main > section:nth-child(2) .canvas::before { transition-delay: 50ms; }
body.is-loaded main > section:nth-child(3) .v,
body.is-loaded main > section:nth-child(3) .canvas::before { transition-delay: 100ms; }
body.is-loaded main > section:nth-child(4) .v,
body.is-loaded main > section:nth-child(4) .canvas::before { transition-delay: 150ms; }
body.is-loaded main > section:nth-child(5) .v,
body.is-loaded main > section:nth-child(5) .canvas::before { transition-delay: 200ms; }
body.is-loaded main > section:nth-child(6) .v,
body.is-loaded main > section:nth-child(6) .canvas::before { transition-delay: 250ms; }
body.is-loaded main > section:nth-child(7) .v,
body.is-loaded main > section:nth-child(7) .canvas::before { transition-delay: 300ms; }

@media (prefers-reduced-motion: reduce) {
    /* Collapse motion tokens to 0ms — every transition that resolves
       through --motion-* becomes instant. Keyframe animations need
       to be killed separately since they don't go through tokens. */
    :root {
        --motion-fast: 0ms;
        --motion-base: 0ms;
        --motion-slow: 0ms;
    }
    .v, .canvas::before, .hero-number::before, .loading-dots span {
        animation: none;
    }
}

.footer {
    text-align: center;
    font-size: var(--text-1);
    color: var(--muted);
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.03em;
    transition: opacity var(--motion-slow) ease;
}
body.is-loading .footer { opacity: 0; }
.footer .sync-link,
.footer .logout,
.footer .footer-link {
    /* Press feedback: underline thickens from dotted→solid and color
       brightens to fg on :active, returning over --motion-base.
       Picked from /playground.html bake-off (variant C); link-native
       feedback that respects the visual category. */
    transition: color var(--motion-base) ease-out,
                border-bottom-color var(--motion-base) ease-out,
                border-bottom-style var(--motion-base) ease-out;
    -webkit-tap-highlight-color: transparent;
    /* Skip the ~50-100ms iOS Safari double-tap-zoom detection delay;
       :active activates immediately. */
    touch-action: manipulation;
}
.footer .sync-link,
.footer .footer-link {
    cursor: pointer;
    border-bottom: 1px dotted var(--faint);
}
/* Public showcase links sit inline in the footer next to "updated …".
   They inherit the footer's muted tone so they read as part of the line
   rather than a separate call-to-action. */
.footer .footer-link { color: inherit; }
.footer .footer-sep { margin: 0 var(--space-2); opacity: 0.6; }
/* No dangling separator before the links if the timestamp hasn't rendered. */
#footer:empty + .footer-sep { display: none; }
.footer .sync-link:focus,
.footer .logout:focus,
.footer .footer-link:focus { outline: none; }
.footer .sync-link:focus-visible,
.footer .logout:focus-visible,
.footer .footer-link:focus-visible { outline: 1px dotted var(--muted); outline-offset: 2px; }
@media (hover: hover) {
    .footer .sync-link:not(.is-syncing):hover,
    .footer .logout:hover,
    .footer .footer-link:hover { color: var(--fg); }
}
.footer .sync-link:not(.is-syncing):active,
.footer .logout:active,
.footer .footer-link:active {
    transition: none;  /* instant brighten on tap-down */
    color: var(--fg);
    border-bottom-style: solid;
    border-bottom-color: var(--fg);
}
.footer .sync-link.is-syncing {
    cursor: default;
    border-bottom-color: transparent;
    opacity: 0.7;
}

/* ─── text link — link-shaped element with press feedback ──────
   Reusable utility for anywhere we want the same visual + press
   behavior as the footer sync-link without depending on a .footer
   ancestor (e.g. playground back-links, in-page navigation). */
.text-link {
    color: var(--muted);
    text-decoration: none;
    border-bottom: 1px dotted var(--faint);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
    transition: color var(--motion-base) ease-out,
                border-bottom-color var(--motion-base) ease-out,
                border-bottom-style var(--motion-base) ease-out;
}
.text-link:focus { outline: none; }
.text-link:focus-visible { outline: 1px dotted var(--muted); outline-offset: 2px; }
@media (hover: hover) {
    .text-link:hover { color: var(--fg); }
}
.text-link:active {
    transition: none;
    color: var(--fg);
    border-bottom-style: solid;
    border-bottom-color: var(--fg);
}

/* ---- login ---- */
.login {
    min-height: 100vh;
    justify-content: center;
    padding-top: var(--space-6);
    padding-bottom: var(--space-8);
}
.login-card {
    background: var(--surface);
    border-radius: 1.25rem;
    padding: var(--space-7) var(--space-6) var(--space-6);
    text-align: center;
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}
.login-title {
    font-size: var(--text-8);
    font-weight: var(--weight-light);
    letter-spacing: -0.04em;
    color: var(--accent);
    line-height: 1;
}
.login-tagline {
    font-size: var(--text-6);
    color: var(--muted);
    margin-top: calc(-1 * var(--space-2));
    margin-bottom: var(--space-3);
}
.login-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}
.login-form input + input {
    /* Small visual gap-on-the-edge so two stacked inputs don't read as one */
    margin-top: var(--space-1);
}
.login-form input[type="email"],
.login-form input[type="text"] {
    font: inherit;
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--faint);
    border-radius: var(--radius-md);
    background: var(--bg);
    color: var(--fg);
    text-align: center;
}
.login-form input[type="email"]:focus,
.login-form input[type="text"]:focus {
    outline: none;
    border-color: var(--heat-2);
}
.login-form button {
    font: inherit;
    font-weight: var(--weight-medium);
    padding: var(--space-3) var(--space-4);
    border: none;
    border-radius: var(--radius-md);
    background: var(--accent);
    color: var(--bg);
    cursor: pointer;
    transition: opacity var(--motion-fast) ease;
}
.login-form button:disabled { opacity: 0.5; cursor: default; }
.login-status {
    font-size: var(--text-5);
    color: var(--muted);
    margin-top: var(--space-1);
}
.login-status[data-kind="ok"]   { color: var(--heat-3); }
.login-status[data-kind="warn"] { color: #b85c3a; }
.login-private {
    font-size: var(--text-5);
    color: var(--muted);
    margin-top: calc(-1 * var(--space-3));
}
.login-aux {
    text-align: center;
    font-size: var(--text-5);
    color: var(--muted);
}
.login-public { margin-top: var(--space-5); }
.login-request { margin-top: calc(-1 * var(--space-4)); }
.login-request-form[hidden] { display: none; }

.text-link {
    color: var(--accent);
    text-decoration: underline;
    text-underline-offset: 2px;
}
.footer .logout {
    color: var(--muted);
    text-decoration: none;
    border-bottom: 1px dotted var(--faint);
    margin-left: 1.5em;
    cursor: pointer;
}
/* hover/active rules live in the unified block near .sync-link above */

.google-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-3);
    font: inherit;
    font-weight: var(--weight-medium);
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--faint);
    border-radius: var(--radius-md);
    background: var(--bg);
    color: var(--fg);
    text-decoration: none;
    cursor: pointer;
    transition: border-color var(--motion-fast) ease, background var(--motion-fast) ease;
}
.google-btn:hover { border-color: var(--muted); }
.google-btn:active { background: var(--surface); }
.google-icon { width: 1.1em; height: 1.1em; }
.login-divider {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    color: var(--muted);
    font-size: var(--text-3);
    letter-spacing: 0.16em;
    text-transform: uppercase;
    margin: var(--space-1) 0;
}
.login-divider::before,
.login-divider::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--faint);
}

.onboarding-steps {
    text-align: left;
    padding-left: var(--space-5);
    color: var(--fg);
    font-size: var(--text-6);
    line-height: 1.5;
    margin: var(--space-2) 0 var(--space-3);
}
.onboarding-steps li { margin-bottom: var(--space-2); }
#code {
    text-transform: uppercase;
    letter-spacing: 0.2em;
    font-variant-numeric: tabular-nums;
}
.loading-dots {
    display: flex;
    gap: var(--space-2);
    justify-content: center;
    align-items: center;
    margin: var(--space-2) 0 var(--space-4);
    height: 12px;
}
.loading-dots span {
    width: 6px; height: 6px; border-radius: 50%;
    background: var(--heat-3);
    opacity: 0.3;
    animation: loading-dot 1.2s ease-in-out infinite;
}
.loading-dots span:nth-child(2) { animation-delay: 0.15s; }
.loading-dots span:nth-child(3) { animation-delay: 0.3s; }
@keyframes loading-dot {
    0%, 100% { opacity: 0.3; transform: translateY(0); }
    50%      { opacity: 1;   transform: translateY(-3px); }
}

/* ─── public-page intro modal ────────────────────────── */
/* Shown once per browser (localStorage). One quiet card on a dimmed
   paper backdrop; entrance/exit is a plain opacity fade. */
/* The base rule's display:flex silently defeats the UA's [hidden] →
   display:none (author rules beat UA rules), which left an invisible
   full-screen layer eating every tap. Re-assert hidden explicitly. */
.intro-overlay[hidden] { display: none; }
.intro-overlay {
    position: fixed;
    inset: 0;
    z-index: 10;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-5);
    background: color-mix(in srgb, var(--bg) 78%, transparent);
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
    animation: intro-in var(--motion-base) ease;
}
.intro-overlay.intro-closing { opacity: 0; transition: opacity var(--motion-base) ease; }
@keyframes intro-in { from { opacity: 0; } }
.intro-card {
    background: var(--surface);
    border-radius: var(--radius-lg);
    padding: var(--space-5);
    max-width: 22rem;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.10);
}
.intro-text {
    font-size: var(--text-5);
    line-height: 1.55;
    color: var(--fg);
    margin: var(--space-3) 0 var(--space-4);
}
.intro-dismiss {
    font: inherit;
    font-weight: var(--weight-medium);
    padding: var(--space-2) var(--space-4);
    border: none;
    border-radius: var(--radius-md);
    background: var(--accent);
    color: var(--bg);
    cursor: pointer;
    touch-action: manipulation;
    opacity: 1;
    transition: opacity 350ms ease;
}
.intro-dismiss:active { opacity: 0.7; transition: opacity 0s; }
@media (prefers-reduced-motion: reduce) {
    .intro-overlay { animation: none; }
    .intro-overlay.intro-closing { transition: none; }
}
