/* =========================================================================
   Sailyn design tokens. The single source of truth.

   THE WORLD: time is the interface.

   Every task, session, commitment and challenge is an extent in a continuous
   time field, drawn at its real size. An hour is visibly four times a quarter
   hour. The category default is a stack of equally weighted cards where a
   two-minute thing and a four-hour thing are the same rectangle, which is a
   picture of a list rather than a picture of a day. This system refuses that.

   Three rules carry the whole world, and every value below serves one of them.

   1. RULES, NOT BOXES. Separation is a hairline. Content never sits on a
      filled card, never has a border on four sides, and never casts a shadow.
      --surface therefore resolves to the same colour as --ground: the token
      still exists so 1,200 call sites keep working, but a "surface" is now a
      region of the page rather than a floating panel.

   2. ONE BAND. --accent is a field, not a tint, and at most one region per
      screen carries it: the current hour, or the next action. Nothing else on
      a screen is saturated. Hierarchy is made of contrast, and there is none
      left if everything is already coloured.

      The exception, written here rather than in the revamp doc because this
      is the file somebody will open to "fix" it. Decision D1 made Progress a
      second alive surface and its charts carry three data hues at once:

        The three data hues are not --accent. Rule 2 governs --accent, which
        remains at most one region per screen on every screen including
        Progress. The data hues are a separate scale, permitted only on
        Progress and in the harbour, and only ever on data. Never on a button,
        a link, a badge, a tab or any chrome. --tide and --gold keep their
        existing meanings unchanged.

      Both scales are at the end of this file, under an attribute rather than
      at :root, so neither can be reached from a screen it does not belong to.

   3. NUMBERS ARE THE VOICE. Duration, time, count and balance are set in one
      tabular face at a size that reads across a desk. The language around
      them stays quiet.

   Colour is authored in oklch, not hex, so lightness is the same perceived
   step everywhere on the wheel and contrast is arithmetic rather than
   eyeballing.

   Token NAMES are unchanged from the previous system on purpose. The app
   shell resolves 1,235 token references; renaming them would be a week of
   churn with no design benefit and a real chance of silent breakage. What
   changed is what every name means.
   ========================================================================= */

/* The brand's two faces, self-hosted (fonts/, SIL Open Font License): Plus
   Jakarta Sans for the wordmark and display headings, Inter for everything
   else. One variable file per face and script range covers every weight the
   product uses, 400, 500 and 600. Latin extended is fetched only by a page
   that draws a character in it, which is Polish, Czech and the rest; swap,
   so text is never held invisible while a face arrives. */
@font-face{ font-family:"Inter"; font-style:normal; font-weight:400 600; font-display:swap;
  src:url(/fonts/inter-latin.woff2) format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face{ font-family:"Inter"; font-style:normal; font-weight:400 600; font-display:swap;
  src:url(/fonts/inter-latin-ext.woff2) format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }
@font-face{ font-family:"Plus Jakarta Sans"; font-style:normal; font-weight:400 600; font-display:swap;
  src:url(/fonts/jakarta-latin.woff2) format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face{ font-family:"Plus Jakarta Sans"; font-style:normal; font-weight:400 600; font-display:swap;
  src:url(/fonts/jakarta-latin-ext.woff2) format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }

/* Daybreak's faces (D132), loaded only where a page asks for them: the app.
   Atkinson Hyperlegible Next for everything read and tapped, designed for
   low vision; Atkinson Hyperlegible Mono for numbers that change; Bricolage
   Grotesque for headlines and task names. All SIL Open Font License, files
   and licences in /fonts. */
@font-face{ font-family:"Atkinson Hyperlegible Next"; font-style:normal; font-weight:400; font-display:swap;
  src:url(/fonts/atkinson-latin-400.woff2) format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face{ font-family:"Atkinson Hyperlegible Next"; font-style:normal; font-weight:400; font-display:swap;
  src:url(/fonts/atkinson-latin-ext-400.woff2) format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }
@font-face{ font-family:"Atkinson Hyperlegible Next"; font-style:normal; font-weight:500; font-display:swap;
  src:url(/fonts/atkinson-latin-500.woff2) format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face{ font-family:"Atkinson Hyperlegible Next"; font-style:normal; font-weight:500; font-display:swap;
  src:url(/fonts/atkinson-latin-ext-500.woff2) format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }
@font-face{ font-family:"Atkinson Hyperlegible Next"; font-style:normal; font-weight:700; font-display:swap;
  src:url(/fonts/atkinson-latin-700.woff2) format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face{ font-family:"Atkinson Hyperlegible Next"; font-style:normal; font-weight:700; font-display:swap;
  src:url(/fonts/atkinson-latin-ext-700.woff2) format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }
@font-face{ font-family:"Atkinson Hyperlegible Mono"; font-style:normal; font-weight:400; font-display:swap;
  src:url(/fonts/atkinson-mono-latin-400.woff2) format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face{ font-family:"Atkinson Hyperlegible Mono"; font-style:normal; font-weight:400; font-display:swap;
  src:url(/fonts/atkinson-mono-latin-ext-400.woff2) format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }
@font-face{ font-family:"Atkinson Hyperlegible Mono"; font-style:normal; font-weight:600; font-display:swap;
  src:url(/fonts/atkinson-mono-latin-600.woff2) format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face{ font-family:"Atkinson Hyperlegible Mono"; font-style:normal; font-weight:600; font-display:swap;
  src:url(/fonts/atkinson-mono-latin-ext-600.woff2) format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }
@font-face{ font-family:"Bricolage Grotesque"; font-style:normal; font-weight:200 800; font-display:swap;
  src:url(/fonts/bricolage-latin.woff2) format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face{ font-family:"Bricolage Grotesque"; font-style:normal; font-weight:200 800; font-display:swap;
  src:url(/fonts/bricolage-latin-ext.woff2) format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }

:root{
    /* ---- material ----
       A near-neutral ground with the faintest cool cast, so it reads as a
       lit surface rather than as paper. Warm cream was considered and
       rejected: it is what every calm-productivity product ships, and this
       product is an instrument, not stationery. */

    /* light, the default. The ground is the brand's Off White and the ink
       its Navy taken to reading contrast, so the instrument and the mark are
       one palette (docs/brand/). */
    --ground:      oklch(0.984 0.003 248);
    --ground-deep: oklch(0.962 0.004 248);
    /* Not a panel. Regions separate by rule and by value, never by a fill
       that floats. Kept identical to --ground so every legacy card call site
       flattens automatically instead of needing 800 edits. */
    --surface:       oklch(0.984 0.003 248);
    --surface-2:     oklch(0.957 0.006 248);
    --surface-solid: oklch(1 0 0);

    /* The hairline does all the work the old card borders and shadows did, so
       it has to be genuinely visible rather than a suggestion. */
    --line:        oklch(0.30 0.035 248 / 0.22);
    --line-soft:   oklch(0.30 0.035 248 / 0.11);
    --line-strong: oklch(0.30 0.035 248 / 0.42);

    --ink:         oklch(0.255 0.055 248);
    --ink-soft:    oklch(0.405 0.035 252);
    /* Metadata, timestamps, secondary lines. Measured against --ground rather
       than chosen: this sits at 4.6:1, above the 4.5 floor. Light grey for
       elegance is the failure that makes an interface tiring to read. It is the
       brand's Slate at 5.4:1 here; the Slate swatch itself measures 4.55. */
    --ink-mute:    oklch(0.515 0.035 256);

    /* The signal. One saturated field per screen: the live band, or the next
       action. Never a border, never an icon, never a badge, never a tint on
       something that is merely present.
       Sky-sea (1 October): the sea blue of the icon's water, taken to the
       lightness that holds text contrast both ways, 5.3:1 on --ground and
       5.4:1 under --accent-ink, the same as the teal it replaced. The icon's
       pale ice blue is the dark theme's accent. */
    --accent:      oklch(0.515 0.115 250);
    --accent-deep: oklch(0.435 0.100 252);
    --accent-ink:  oklch(0.99 0 0);
    --accent-wash: oklch(0.515 0.115 250 / 0.10);
    --accent-line: oklch(0.515 0.115 250 / 0.30);
    /* The accent the app ships with, which a theme bought in the shop never
       overrides: the shop's swatch for going back to Sailyn is drawn in it. */
    --accent-brand: oklch(0.515 0.115 250);

    /* Amber is reserved to what has been earned and is never navigation. */
    /* Measured, not chosen: at 0.72 this sat at 2.40:1 on the light ground,
       which is unreadable as text. 0.54 measures 4.94:1. The wash below stays
       light because it is a fill, where contrast does not apply. */
    --gold:        oklch(0.54 0.150 68);
    --gold-wash:   oklch(0.72 0.150 68 / 0.15);

    /* Machine state: sync, hints, streaks. Deliberately desaturated to a
       slate so it recedes behind the signal instead of competing with it.
       It was a cyan, which read as a second accent. Never an action. */
    --tide:        oklch(0.505 0.045 252);
    --tide-deep:   oklch(0.415 0.042 252);
    --tide-wash:   oklch(0.505 0.045 252 / 0.12);

    --good:        oklch(0.52 0.140 150);   /* 4.88:1 on --ground; 0.545 measured 4.37 */
    --warn:        oklch(0.660 0.150 65);
    --danger:      oklch(0.530 0.200 25);
    --bad:         oklch(0.530 0.200 25);
    --bad-wash:    oklch(0.530 0.200 25 / 0.10);

    /* Rest (D130, from the Daybreak brief): a break is drawn in its own hue so
       it reads as a break at a glance, never as focus time. A violet, away from
       the accent's blue. --rest is text and marks, 6.0:1 on --ground and 5.6:1
       on --rest-ground; --rest-ground is the break screen's own ground, which
       is a plane (the screen) rather than a fill under content, and every ink
       holds 4.5:1 on it (--ink-mute 4.95). */
    --rest:        oklch(0.50 0.130 295);
    --rest-ground: oklch(0.958 0.018 295);
    --rest-wash:   oklch(0.50 0.130 295 / 0.12);

    /* ---- depth ----
       Content casts nothing. What used to be a soft blur is now a hairline
       ring, which converts every legacy shadow call site into a rule without
       touching it. Only things that genuinely float above the page, a sheet
       or a dialog, get a real shadow, and those are steps 3 and 4. */
    --shadow-1: 0 0 0 1px oklch(0.32 0.012 255 / 0.16);
    --shadow-2: 0 0 0 1px oklch(0.32 0.012 255 / 0.22);
    --shadow-3: 0 1px 2px oklch(0.20 0.02 258 / 0.10),
                0 12px 28px oklch(0.20 0.02 258 / 0.13);
    --shadow-4: 0 2px 6px oklch(0.20 0.02 258 / 0.12),
                0 30px 70px oklch(0.20 0.02 258 / 0.22);
    /* Legacy names, still referenced by the marketing pages. Pointed at the
       same values so those pages inherit the flattening rather than keeping
       the old soft-card look on their own. */
    /* All three map to the hairline ring, not to the sheet blurs. An earlier
       pass pointed 2 and 3 at --shadow-3/4 and claimed in a comment that the
       marketing pages would "inherit the flattening"; they did the opposite,
       which is why every card on index.html still floated. Sheets and dialogs
       ask for --shadow-3 and --shadow-4 by name. */
    --lift-1: var(--shadow-1);
    --lift-2: var(--shadow-2);
    --lift-3: var(--shadow-2);
    --edge-hi: inset 0 1px 0 oklch(1 0 0 / 0.55);
    --edge-lo: inset 0 -1px 0 oklch(0.32 0.012 255 / 0.07);

    --scrim: oklch(0.19 0.01 258 / 0.42);
    --glass-blur: saturate(1.15) blur(14px);

    /* The material's rest state, moved by script as the surface moves. */
    --lens-angle: 176deg;
    --spec-x: 30%;
    --spec-y: -20%;
    --spec: 1;
    --glass-thick: 0;

    /* ---------------------------------------------------------------------
       RADIUS. An instrument has edges. 22px and 16px read as a soft consumer
       card, which is the look this world exists to refuse. Three steps, all
       crisp, plus a pill kept for chips where a full round is the whole
       affordance.
       --------------------------------------------------------------------- */
    --r-control: 3px;
    --r-card:    0px;
    --r-pill:    999px;
    /* legacy names, same values, so nothing has to be hunted down */
    --radius:    0px;
    --radius-s:  3px;
    --radius-xs: 2px;

    /* ---------------------------------------------------------------------
       SPACE. Seven steps, unchanged. 715 call sites resolve against these and
       the scale was already correct; churning it would buy nothing.
       --------------------------------------------------------------------- */
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 24px;
    --space-6: 32px;
    --space-7: 48px;

    /* ---------------------------------------------------------------------
       THE TIME FIELD. The one genuinely new family. Duration is length, so a
       component that draws a block of work multiplies its minutes by
       --minute-h rather than picking a height that looks right.
       --------------------------------------------------------------------- */
    --hour-h:   64px;                        /* one hour of the day column */
    --minute-h: calc(var(--hour-h) / 60);    /* multiply minutes by this */
    --field-gutter: 52px;                    /* the hour-label column */
    --band-w: 3px;                           /* the live marker's weight */

    /* ---------------------------------------------------------------------
       TYPE SCALE. Six steps in rem. Wider at the top than the old scale: a
       screen title at 32px and a section head at 22px were four points apart
       and read as the same thing.
       --------------------------------------------------------------------- */
    --fs-1: 0.75rem;      /* 12px  metadata, hour labels, tags */
    --fs-2: 0.8125rem;    /* 13px  secondary text, field notes */
    --fs-3: 0.9375rem;    /* 15px  body, the default */
    --fs-4: 1.125rem;     /* 18px  emphasised body, list titles */
    --fs-5: 1.5rem;       /* 24px  section headings */
    --fs-6: 2.5rem;       /* 40px  screen title */

    /* One step above the scale at the display end, for a Persuade
       surface. The app never needs it; a landing hero does. */
    --fs-display: 4.5rem;   /* 72px, marketing hero only */

    /* One step above the scale, for numerics read across a desk rather than
       at reading distance. The session countdown and the reward amount. */
    --fs-numeric: 4rem;

    /* ---------------------------------------------------------------------
       WEIGHT. Three, product-wide, no exceptions.

       The stack resolves variable weights on macOS and iOS. On Windows and
       most Android builds Segoe UI and Roboto snap to the nearest static cut,
       so 520, 560 and 620 all render as 500 or 600. Any weight outside these
       three is invisible to most users, which flattens on the majority of
       devices the hierarchy that reads correctly on the machine it was
       designed on.
       --------------------------------------------------------------------- */
    --fw-body: 400;
    --fw-med:  500;
    --fw-hi:   600;

    /* ---------------------------------------------------------------------
       TYPE FACES.

       --font-num is the new one and it carries the world. Every duration,
       clock time, count and balance is set in it, tabular, so a counting
       number does not jitter and a column of times aligns on the digit.

       --display is the wordmark, and on the public pages the display
       headings: Plus Jakarta Sans, the brand's own face. Inside the app it is
       the wordmark alone, and screen titles are the UI face at 40px with
       tight tracking. --font-ui is Inter, the brand's text face, with the
       platform's own sans behind it while it loads.
       --------------------------------------------------------------------- */
    --font-ui: "Inter", -apple-system, BlinkMacSystemFont, "SF Pro Text",
               "Segoe UI", Roboto, system-ui, sans-serif;
    --font-num: ui-monospace, "SF Mono", "Cascadia Mono", "Roboto Mono",
                Menlo, Consolas, monospace;
    --display: "Plus Jakarta Sans", "Inter", -apple-system, BlinkMacSystemFont,
               "Segoe UI", Roboto, system-ui, sans-serif;

    /* Tracking. Large type needs negative tracking to stop looking loose;
       small caps-ish labels need positive to stay readable. */
    --track-title: -0.022em;
    --track-label: 0.06em;

    /* ---------------------------------------------------------------------
       MOTION. Three durations. Reduced motion collapses all three to zero in
       one place rather than by hunting rules.
       --------------------------------------------------------------------- */
    --dur-fast: 120ms;
    --dur-base: 200ms;
    --dur-slow: 360ms;
    --ease: cubic-bezier(0.2, 0, 0, 1);
    --ease-soft: cubic-bezier(0.32, 0.72, 0, 1);
    --ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);
    --ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);

    /* One focus ring, applied everywhere, on both themes. A keyboard user
       gets a visible ring on all 205 pressable elements or the product is not
       operable without a pointer. */
    --focus-ring:     0 0 0 2px var(--ground), 0 0 0 4px oklch(0.515 0.115 250 / 0.90);
    --focus-ring-bad: 0 0 0 2px var(--ground), 0 0 0 4px oklch(0.530 0.200 25 / 0.90);

    --z-nav:   50;
    --z-scrim: 90;
    --z-sheet: 100;
    --z-overlay: 60;
    --z-reward:  80;

    /* The usable screen, on all four sides.
     *
     * Top and bottom existed. Left and right did not, so a phone held
     * sideways put the notch through the leading edge of every fixed control
     * in the product, and nothing in the stylesheet could say so.
     *
     * --safe-x is the pair, for the common case of horizontal padding that has
     * to clear whichever side the notch is on. Every one of these resolves to
     * 0px on a screen with no inset, so they cost nothing on a laptop. */
    --safe-top: env(safe-area-inset-top, 0px);
    --safe-bottom: env(safe-area-inset-bottom, 0px);
    --safe-left: env(safe-area-inset-left, 0px);
    --safe-right: env(safe-area-inset-right, 0px);
    --safe-x: max(env(safe-area-inset-left, 0px), env(safe-area-inset-right, 0px));

    /* The tallest a thing that floats over the page may be, and still have
     * both of its ends reachable. A dialog centred on the viewport with
     * max-height:88dvh has its top edge at 6% of the screen, which on a phone
     * with a Dynamic Island is underneath it, and the close control is at the
     * top of the dialog. */
    --overlay-max-h: calc(100svh - var(--safe-top) - var(--safe-bottom) - var(--space-6) * 2);

    /* ---- the site outside the app ----
     * The marketing and long-form pages had three container widths between
     * them (1140px, 760px and 74ch) and no agreement about horizontal padding.
     * These are the two measures the whole site uses: one for layout, one for
     * prose, plus the gutter that clears a notch in landscape. */
    --shell-w:  1180px;
    --measure-w: 68ch;
    --gutter: var(--space-5);

    /* The reserved height of the fixed sidebar header on desktop. A length,
       never a colour: a cleanup pass once mapped this onto a colour token, so
       `height: var(--rail-head)` became `height: <a colour>`, which is invalid
       and drops to auto. The header then had no height and the logo sat under
       the first navigation item. */
    --rail-head: 128px;
}

/* Dark is a token swap, not a second stylesheet. Every component is written
   once and both themes resolve it.

   The ground is the icon's night sea (sky-sea, 1 October): a deep navy, dark
   enough that the page is still an unlit surface and the band is the only
   thing emitting. In the light theme the page is a lit one. */
html[data-theme="dark"]{
    --ground:      oklch(0.170 0.030 254);
    --ground-deep: oklch(0.140 0.026 254);
    --surface:       oklch(0.170 0.030 254);
    --surface-2:     oklch(0.212 0.034 254);
    --surface-solid: oklch(0.200 0.032 254);

    --line:        oklch(0.82 0.01 260 / 0.20);
    --line-soft:   oklch(0.82 0.01 260 / 0.10);
    --line-strong: oklch(0.82 0.01 260 / 0.38);

    --ink:         oklch(0.965 0.003 260);
    --ink-soft:    oklch(0.795 0.006 260);
    --ink-mute:    oklch(0.665 0.008 260);

    /* The icon's ice blue, which reads on the night ground and carries navy
       ink at 10.5:1. */
    --accent:      oklch(0.814 0.080 235);
    --accent-deep: oklch(0.720 0.090 240);
    --accent-ink:  oklch(0.200 0.045 248);
    --accent-brand: oklch(0.814 0.080 235);
    --accent-wash: oklch(0.814 0.080 235 / 0.18);
    --accent-line: oklch(0.814 0.080 235 / 0.38);

    --gold:        oklch(0.800 0.140 72);
    --gold-wash:   oklch(0.800 0.140 72 / 0.16);

    --tide:        oklch(0.690 0.040 252);
    --tide-deep:   oklch(0.560 0.038 252);
    --tide-wash:   oklch(0.690 0.040 252 / 0.14);

    --good:        oklch(0.720 0.135 152);
    --warn:        oklch(0.780 0.140 68);
    --danger:      oklch(0.680 0.180 25);
    --bad:         oklch(0.680 0.180 25);
    --bad-wash:    oklch(0.680 0.180 25 / 0.14);

    /* Rest at night: 9.2:1 on its ground, --ink-mute 5.8. */
    --rest:        oklch(0.800 0.090 295);
    --rest-ground: oklch(0.215 0.050 290);
    --rest-wash:   oklch(0.800 0.090 295 / 0.16);

    /* A hairline ring reads on a dark ground where a soft shadow does not, so
       the same substitution holds and the ring simply lightens. */
    --shadow-1: 0 0 0 1px oklch(0.82 0.01 260 / 0.14);
    --shadow-2: 0 0 0 1px oklch(0.82 0.01 260 / 0.20);
    --shadow-3: 0 1px 2px oklch(0 0 0 / 0.44),
                0 12px 28px oklch(0 0 0 / 0.46);
    --shadow-4: 0 2px 6px oklch(0 0 0 / 0.48),
                0 30px 70px oklch(0 0 0 / 0.60);
    --edge-hi: inset 0 1px 0 oklch(1 0 0 / 0.05);
    --edge-lo: inset 0 -1px 0 oklch(0 0 0 / 0.16);

    --scrim: oklch(0.06 0.004 260 / 0.66);

    --lift-1: var(--shadow-1);
    --lift-2: var(--shadow-3);
    --lift-3: var(--shadow-4);

    --focus-ring:     0 0 0 2px var(--ground), 0 0 0 4px oklch(0.814 0.080 235 / 0.95);
    --focus-ring-bad: 0 0 0 2px var(--ground), 0 0 0 4px oklch(0.700 0.180 25 / 0.95);
}

@media (prefers-reduced-motion: reduce){
  :root{ --dur-fast:0ms; --dur-base:0ms; --dur-slow:0ms; }
}

/* Higher contrast on request: the hairline is the only thing separating
   regions in this world, so it is what has to strengthen. */
@media (prefers-contrast: more){
  :root{
    --line:      oklch(0.32 0.012 255 / 0.46);
    --line-soft: oklch(0.32 0.012 255 / 0.26);
    --ink-mute:  oklch(0.42 0.010 260);
  }
  html[data-theme="dark"]{
    --line:      oklch(0.82 0.01 260 / 0.44);
    --line-soft: oklch(0.82 0.01 260 / 0.24);
    --ink-mute:  oklch(0.760 0.008 260);
  }
}

/* Daybreak (D132): the app's palette, type, shape and motion, scoped to
   html[data-ui="daybreak"], which only app.html carries, so the public pages
   keep the values above. The brief's colours converted to oklch and measured
   in both themes; every text pairing holds 4.5:1 or better. Placed before the
   access settings so "more contrast" still wins over it. */
:root[data-ui="daybreak"]{
  --ground:       oklch(0.991 0.010 87.5);
  --surface:      oklch(1 0 0);               /* cards, lists, inputs */
  --surface-2:    oklch(0.953 0.016 86.4);  /* soft buttons, tracks */
  --surface-solid: oklch(1 0 0);
  --ink:          oklch(0.247 0.047 293.7);  /* 15.9:1 */
  --ink-soft:     oklch(0.446 0.046 297.6);  /* 7.5:1 */
  --ink-mute:     oklch(0.495 0.042 299);     /* 4.9:1 on the soft fill and every tint */
  --line:         oklch(0.900 0.024 86);
  --line-soft:    oklch(0.900 0.024 86 / 0.6);
  --line-strong:  oklch(0.800 0.030 84.6);  /* inputs, 1.9:1 */

  /* The filled primary is ink, as the brief's Start and Plan it are. */
  --accent:       oklch(0.247 0.047 293.7);
  --accent-deep:  oklch(0.330 0.050 293.7);
  --accent-ink:   oklch(1 0 0);               /* 16.3:1 */
  --accent-wash:  oklch(0.247 0.047 293.7 / 0.06);
  --accent-line:  oklch(0.247 0.047 293.7 / 0.24);

  /* The four hues. */
  /* Coral, the founder's FF8F70 (21 October, D153): the warm accent against the dark
     purple, still a sunset. It was amber, which became the attention colour
     below, so a shortfall never reads as the Now card. */
  --sun:          oklch(0.764 0.143 36.4);  /* the Now card, lit days */
  --sun-ink:      oklch(0.247 0.047 293.7);   /* 7.3:1 on --sun */
  --sun-text:     oklch(0.535 0.150 36.4);  /* 5.4:1 */
  --sun-tint:     oklch(0.958 0.032 36.4);
  --jade:         oklch(0.641 0.116 176.9);  /* progress bars */
  --jade-text:    oklch(0.473 0.085 177.2);  /* 6.3:1 */
  --jade-tint:    oklch(0.948 0.026 172.9);
  --lilac:        oklch(0.822 0.100 295.1);  /* break screens */
  --lilac-text:   oklch(0.473 0.167 287.9);  /* 7.0:1, links and focus */
  --lilac-tint:   oklch(0.941 0.033 299.1);
  /* Amber since D153, the old sun's measured values: coral took the sun, and
     the attention colour must not look like the Now card. */
  --apricot:      oklch(0.824 0.163 77.8);  /* shortfall bars */
  --apricot-text: oklch(0.522 0.111 73.8);  /* 5.4:1 */
  --apricot-tint: oklch(0.960 0.050 89.8);

  /* The roles the rest of the stylesheet already reads, on the four hues. */
  --gold:         var(--sun-text);
  --gold-wash:    var(--sun-tint);
  --good:         var(--jade-text);
  --tide:         var(--ink-soft);
  --tide-wash:    var(--surface-2);
  --danger:       var(--apricot-text);
  --warn:         var(--apricot);
  --rest:         var(--lilac-text);
  --rest-ground:  var(--lilac-tint);
  --rest-wash:    var(--lilac-tint);
  --focus-ring:   0 0 0 3px oklch(0.473 0.167 287.9 / 0.45);

  /* Rounder is calmer. */
  --sun-ink-wash: oklch(0.247 0.047 293.7 / 0.10);
  --sun-ink-line: oklch(0.247 0.047 293.7 / 0.62);   /* Not now's outline on --sun */
  --sun-face:     oklch(0.991 0.010 87.5);           /* Start's label on --sun-ink, both themes */
  --r-control:    999px;
  --r-circle:     50%;
  --r-card:       24px;
  --r-now:        32px;
  --radius:       24px;
  --radius-s:     16px;
  --radius-xs:    10px;

  --font-ui:   "Atkinson Hyperlegible Next", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  --font-num:  "Atkinson Hyperlegible Mono", ui-monospace, "SF Mono", "Cascadia Mono", monospace;
  --font-head: "Bricolage Grotesque", "Atkinson Hyperlegible Next", system-ui, sans-serif;
  --fw-hi:     700;
  --fw-head:   800;

  /* One curve: rise, never bounce. 120 for a press, 240 for a change. */
  --ease:      cubic-bezier(0.22, 1, 0.36, 1);
  --dur-fast:  120ms;
  --dur-base:  240ms;
  /* The crisis control's red, the only red: 6.2:1 on its own wash, 4.9:1 at the top of the night band. */
  --bad:          oklch(0.455 0.185 27);
  --bad-wash:     oklch(0.455 0.185 27 / 0.10);
}
:root[data-ui="daybreak"][data-theme="dark"]{
  --ground:       oklch(0.223 0.048 289.3);
  --surface:      oklch(0.261 0.055 289.7);
  --surface-2:    oklch(0.197 0.049 286.5);
  --surface-solid: oklch(0.261 0.055 289.7);
  --ink:          oklch(0.968 0.018 300.1);   /* 15.7:1 */
  --ink-soft:     oklch(0.789 0.043 297.6);   /* 8.8:1 */
  --ink-mute:     oklch(0.715 0.048 296.5);   /* 6.1:1 on a card and every tint */
  --line:         oklch(0.360 0.072 290);
  --line-soft:    oklch(0.360 0.072 290 / 0.6);
  --line-strong:  oklch(0.460 0.078 290.7);
  --accent:       oklch(0.968 0.018 300.1);
  --accent-deep:  oklch(0.880 0.030 300.1);
  --accent-ink:   oklch(0.247 0.047 293.7);
  --accent-wash:  oklch(0.968 0.018 300.1 / 0.08);
  --accent-line:  oklch(0.968 0.018 300.1 / 0.30);
  --sun-text:     oklch(0.720 0.120 36.4);   /* 4.6:1 on every dark tint */
  --sun-tint:     oklch(0.305 0.055 36.4);
  --jade-text:    oklch(0.840 0.100 174.5);
  --jade-tint:    oklch(0.328 0.048 182.5);
  --lilac-text:   oklch(0.836 0.091 294.9);
  --lilac-tint:   oklch(0.300 0.079 290);
  --apricot-text: oklch(0.863 0.149 84.7);
  --apricot-tint: oklch(0.308 0.046 86.3);
  --focus-ring:   0 0 0 3px oklch(0.836 0.091 294.9 / 0.55);
  --bad:          oklch(0.800 0.120 25);   /* 6.0:1 on its wash over a card */
  --bad-wash:     oklch(0.800 0.120 25 / 0.14);
}

/* More contrast in Daybreak: text at 7:1 or better and every
   edge at 4:1 or better. Set on the attribute, which applyAccess() and the
   boot block also write when the device itself asks for more contrast. The
   dark block names exactly the same properties, so with one more attribute it
   outranks this one wherever the theme is dark. */
:root[data-ui="daybreak"][data-contrast="more"]{
  --ink:          oklch(0.200 0.045 293.7);
  --ink-soft:     oklch(0.330 0.050 296);
  --ink-mute:     oklch(0.385 0.048 298);
  --sun-text:     oklch(0.430 0.120 36.4);
  --jade-text:    oklch(0.380 0.080 177);
  --lilac-text:   oklch(0.400 0.170 288);
  --apricot-text: oklch(0.420 0.100 70);
  --line:         oklch(0.560 0.030 86);
  --line-soft:    oklch(0.640 0.030 86);
  --line-strong:  oklch(0.480 0.030 86);
  --bad:          oklch(0.420 0.170 27);
  --bad-wash:     oklch(0.420 0.170 27 / 0.10);
}
:root[data-ui="daybreak"][data-contrast="more"][data-theme="dark"]{
  --ink:          oklch(0.985 0.010 300);
  --ink-soft:     oklch(0.900 0.030 298);
  --ink-mute:     oklch(0.850 0.035 297);
  --sun-text:     oklch(0.840 0.090 36.4);
  --jade-text:    oklch(0.880 0.090 174);
  --lilac-text:   oklch(0.880 0.080 295);
  --apricot-text: oklch(0.900 0.130 86);
  --line:         oklch(0.640 0.060 292);
  --line-soft:    oklch(0.560 0.060 292);
  --line-strong:  oklch(0.720 0.050 292);
  --bad:          oklch(0.860 0.090 25);
  --bad-wash:     oklch(0.860 0.090 25 / 0.14);
}

/* The same two things, asked for in Settings rather than in the operating
   system, plus a text size the system has no query for. Three attributes on
   the root element, written by applyAccess() and by the boot block in
   app.html so nothing flashes and no text reflows after first paint.
   -
   Each may only ever add. There is no value here that animates for somebody
   whose device asked for stillness, or that lowers contrast below the base,
   which is why "still" and "more" are the only words either attribute takes:
   a setting that could weaken an accessibility default is a setting that can
   be turned against the person who found it.
   -
   Text size is the root font size and nothing else, because every size in
   this product is a rem off it, so one number moves the whole instrument
   together. What it does not move is the handful of pixel lengths: an hour of
   the day column, the 44px touch floor and the hairline rings. Those are
   physical rather than typographic, and an hour that grew with the text would
   change what the time field means. */
:root[data-motion="still"]{ --dur-fast:0ms; --dur-base:0ms; --dur-slow:0ms; }
:root[data-contrast="more"]{
  --line:      oklch(0.32 0.012 255 / 0.46);
  --line-soft: oklch(0.32 0.012 255 / 0.26);
  --ink-mute:  oklch(0.42 0.010 260);
}
/* Two attributes, so it outranks the base dark palette above it as well as
   the light values on the line before. */
:root[data-contrast="more"][data-theme="dark"]{
  --line:      oklch(0.82 0.01 260 / 0.44);
  --line-soft: oklch(0.82 0.01 260 / 0.24);
  --ink-mute:  oklch(0.760 0.008 260);
}
:root[data-text="large"]{ font-size: 112.5%; }
:root[data-text="larger"]{ font-size: 125%; }


/* =========================================================================
   Numerics. Every time, count and currency. Applied here rather than per
   component so a new counter cannot forget it and jitter as it counts.
   ========================================================================= */

.num, time, [data-num]{
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* =========================================================================
   Spacing utilities.

   108 inline style attributes existed, 42 of them margin-top, which is what
   happens when no rule owns the space between siblings. These let a template
   ask for a step on the scale without reaching for a literal.
   ========================================================================= */

[data-u~="mt-1"]{ margin-top:var(--space-1); }
[data-u~="mt-2"]{ margin-top:var(--space-2); }
[data-u~="mt-3"]{ margin-top:var(--space-3); }
[data-u~="mt-4"]{ margin-top:var(--space-4); }
[data-u~="mt-5"]{ margin-top:var(--space-5); }
[data-u~="mt-6"]{ margin-top:var(--space-6); }
[data-u~="mt-7"]{ margin-top:var(--space-7); }
[data-u~="mb-1"]{ margin-bottom:var(--space-1); }
[data-u~="mb-2"]{ margin-bottom:var(--space-2); }
[data-u~="mb-3"]{ margin-bottom:var(--space-3); }
[data-u~="mb-4"]{ margin-bottom:var(--space-4); }
[data-u~="mb-5"]{ margin-bottom:var(--space-5); }

[data-u~="flex-1"]{ flex:1; }
[data-u~="hidden"]{ display:none; }
[data-u~="center"]{ text-align:center; }
[data-u~="pad-3"]{ height:auto; padding:var(--space-3) var(--space-4); }
[data-u~="bg-page"]{ background:var(--ground); }
[data-u~="fg-system"]{ color:var(--tide); }
[data-u~="fade"]{ transition:opacity var(--dur-slow) var(--ease); }


/* The Story theme is gone.

   It existed to make one tab change register: near-black ground, cyan signal,
   mono numerals, angular cuts. In isolation it worked. In the product it read
   as a monitoring tool bolted onto a warm, calm app, and the seam was the
   most noticeable thing on the screen.

   One visual language, and the scene carries the atmosphere instead. Nothing
   here needed a second palette to be worth looking at.
   ========================================================================= */

/* =========================================================================
   Layout constants. Lengths, not colours, and kept apart from the palette
   for that reason.
   ========================================================================= */

:root {
  /* The reserved height of the fixed sidebar header on desktop.
     This existed as --rail-head and a cleanup pass mapped it onto a colour
     token, so `height: var(--rail-head)` became `height: <a colour>`, which
     is invalid and drops to auto. The header then had no height, the nav had
     no top padding, and the two overlapped: the logo sat under the first
     navigation item. A length and a colour are not interchangeable and the
     rename did not notice. */
  --rail-head: 128px;
}


/* =========================================================================
   THE TWO ALIVE SURFACES. Task 2.1, decision D1.

   Everything above this line is the instrument, and the instrument is most of
   the app. Two surfaces are not it: the harbour, on Now and inside Progress,
   and Progress's own charts. Each declares its palette under an attribute
   rather than adding names to :root, so a value that only means something on
   one surface cannot be reached from anywhere else. That is the whole
   mechanism, and it is what makes this addition additive: no existing token
   changed, and no existing selector can reach a new one.

   data-surface names the material an element is painted in, not where it
   sits. The harbour's is the scene, and per UI-SPEC section 5 it is also the
   one control the painting owns: Sail now is filled --hull "so the button
   reads as a piece of the painting rather than as a control in the accent
   colour". That button sits on the instrument ground, below the scene, and
   still carries the attribute.

   Nothing sets either attribute yet. Task 2.2 draws the harbour behind
   flags.harbour and Task 3.4 draws the charts behind flags.progressMerge, so
   every value below is inert until one of them lands. There is no JS flag on
   a custom property; an attribute nobody writes is the flag.

   test/surfacecheck.js is the gate, and it multiplies the contrast figures
   out rather than trusting the comments beside them.
   ========================================================================= */

/* ---- the harbour ----
   One painted deep-navy scene, on Now and inside Progress, and nowhere else.

   It came back painterly rather than flat vector (addendum 22.1), so these
   are not the colours the scene is drawn with. The plate carries its own
   sunset, its own reflections and its own falloff, and none of that is a
   token. These are the chrome around and over the painting, which is the
   part CSS draws: the field behind the plate, the ink that sits on it, a lit
   window, and the one control filled in the painting's own darkest value.

   Sampled from the approved mockup in docs/ui/ and authored in oklch to match
   by eye, which is UI-SPEC's own instruction and the standing rule that
   colour is never hex here. Every contrast figure is measured against
   --water, never against --ground, because --ground is never behind any of
   it.

   There is no dark-theme block for this scope and there will not be one. The
   harbour is the same painting in both themes: it is the one dark thing in a
   light product, and a dark harbour is what it already is.

   --tide is not in here and is not repurposed. It is machine state on the
   instrument, sync and hints and streaks, and Task 4.4's mechanic borrowing
   the word does not borrow the token. */
[data-surface="harbour"]{
  /* The body of water, and the field the scene sits in while its plate is
     still loading. Sampled from the foreground water. */
  --water:  oklch(0.175 0.030 248);
  /* The far distance: the sky at the top of the plate, which is almost black
     and is the reason the header reads white over it with no scrim at all. */
  --depth:  oklch(0.190 0.040 260);
  /* The darkest painted mass. A token rather than a pixel in the plate
     because UI-SPEC section 5 fills the Sail now button with it. */
  --hull:   oklch(0.165 0.020 252);

  /* A lit window: what has been earned, in the harbour. --gold is the same
     idea on the instrument and keeps its meaning unchanged; this is the one
     the harbour reads, and it is bright because it sits on --water rather
     than on --ground. 13.2:1 there. */
  --lamp:      oklch(0.880 0.095 80);
  /* Its falloff: the bloom at the moment it lights, and the reflection the
     water answers with half a second later. A flat wash behind a flat core,
     the way .reward-glow already does it, never a gradient. */
  --lamp-wash: oklch(0.880 0.095 80 / 0.18);

  /* What is drawn over the painting to be read: the mark and wordmark in the
     header, and the dashed outline of a ghost object. 18.5:1 on --water.
     It is the same value as --accent-ink and cannot be that token: ink on an
     accent field flips to near-black in the dark theme, and ink on the
     harbour has to stay white in both, because the harbour does not flip. */
  --signal:      oklch(0.990 0 0);
  /* Its quiet step: the two-line tagline slot at the top right of Now, which
     has to stay out of the painting's way. 5.3:1, held to the text floor
     rather than the graphic one because that slot sets 8px type. */
  --signal-mute: oklch(0.620 0.042 256);

  /* What coins can buy for the harbour, Task 4.8, decision D26. D14 says
     coins decide how the harbour looks and never what is in it, so these are
     the only colours a coin reaches: the light in a lit window, in two other
     colours, and the houses' paint, in two. Declared here, in the scope, like
     the rest of the harbour's chrome, and measured against --water: each lamp
     is at least 4.5:1 on it, the way --lamp is, and each paint stays darker
     than the sky behind the houses, the way --hull does. */
  --lamp-rose:   oklch(0.860 0.050 15);
  --lamp-green:  oklch(0.880 0.090 145);
  --paint-red:   oklch(0.320 0.080 28);
  --paint-ochre: oklch(0.400 0.075 70);
}

/* ---- the season the harbour is in ----
   Task 17.3, Part 17's 7.3, decision D49.

   Seasons already exist and are already deterministic: SEASONS in
   js/rewards/seasons.js, four of them on a fortnightly cycle from a fixed UTC
   epoch, read through seasonNow(). This is a second layer on that one, not a
   second system: the attribute is written from seasonNow().id and there is no
   parallel list of season names anywhere.

   Two colours move and only two: the body of water and the far sky. That is
   the season a harbour is in, said the way a harbour says it, and it is
   deliberately not the lamp. Decision D14 gives the lamp to coins -- what a
   person bought is what a person bought -- so a season that re-tinted it
   would spend somebody's coins for them every fortnight. --hull does not move
   either, because UI-SPEC section 5 fills the Sail now button with it and a
   primary that changes colour on a schedule is chrome pretending to be
   information.

   Nothing is added and nothing is taken away, which is the file's own rule for
   seasons: it is the same painting in a different light.

   steady has no block, because steady is this palette. "Ordinary weeks pay
   ordinarily" is the season, and an unknown id falls here too.

   Measured against the same floors the base pair is. Every figure in the
   harbour is read on --water, so each season's water was checked against all
   of it: --lamp and the two the shop sells stay above 11.6:1, --signal above
   17.7:1, and --signal-mute, the tight one at 8px type, stays above 5:1 in
   every season (5.21 base, 5.01 at the lightest). --signal on --depth stays
   above 17:1, which is what lets the header sit on the painting with no scrim.
   All eight values are inside sRGB. */
[data-surface="harbour"][data-season="depth"]{
  /* Deep water. Long sessions pay, so the water is deeper and colder and
     gives less back. */
  --water:  oklch(0.150 0.038 246);
  --depth:  oklch(0.165 0.048 258);
}
[data-surface="harbour"][data-season="return"]{
  /* Coming about. The harbour at the turn: a step lighter and further round
     towards the evening, which is the warmest this palette goes. */
  --water:  oklch(0.190 0.032 262);
  --depth:  oklch(0.205 0.040 272);
}
[data-surface="harbour"][data-season="launch"]{
  /* Casting off. The hour before a boat leaves: the lightest of the four and
     turned towards the green of open water. */
  --water:  oklch(0.195 0.034 232);
  --depth:  oklch(0.210 0.042 244);
}

/* ---- the three data hues ----
   Decision D1 promoted these out of harbour-only scope, and only these: the
   founder's call was two alive surfaces rather than the one Part 3 proposed,
   and Progress is the second. Its charts are filled shapes in three hues,
   one per metric, per UI-SPEC section 7: focus time is 1, tasks done is 2,
   the streak is 3, and the weekly bar chart is all of 3, one hue and not
   three. --water, --depth and the rest stay in the harbour, per D1.

   Measured, not sampled. The mockup's three sit at 2.0:1, 3.6:1 and 2.2:1 on
   --ground, and a 2px sparkline stroke needs 3:1 to be a shape somebody can
   see. Each is the lightest its own hue reaches at 3:1 and still inside sRGB,
   which lands them a step apart rather than on one shared lightness; the hue
   and the chroma are the mockup's own. The third was a blue, lighter and
   flatter than the teal accent; since the accent became the sky-sea blue
   (1 October) it is a violet, 50 degrees or more from the accent in both
   themes, because UI-SPEC says they are two tokens and a bar is never drawn
   in the accent.

   These are not text. The positive delta line under focus time and tasks done
   is 12px type, which needs 4.5:1, and no member of this scale reaches that
   without turning the blue into --accent. That line is --good, which this
   product already measured at 4.88:1 for exactly that job, and that is why
   there is no fourth green here. */
[data-surface="progress"]{
  --data-1: oklch(0.660 0.142 70);    /* focus time.  3.04:1 on --ground */
  --data-2: oklch(0.640 0.125 164);   /* tasks done.  3.02:1 */
  --data-3: oklch(0.650 0.131 300);   /* streak, and every weekly bar. 3.07:1 */
  /* The faint wash under a sparkline. UI-SPEC draws one under 2 and 3 only;
     the third exists so a chart that wants it reaches for a token instead of
     inventing an alpha at the call site. */
  --data-1-wash: oklch(0.660 0.142 70 / 0.12);
  --data-2-wash: oklch(0.640 0.125 164 / 0.12);
  --data-3-wash: oklch(0.650 0.131 300 / 0.12);
}

/* Progress is the instrument in dark, so its ground moves and the hues move
   with it. One shared lightness is reachable here because the dark ground is
   far from all three: 0.74 measures 8.2, 8.8 and 8.4 to one.

   A descendant selector rather than a third bare html[data-theme="dark"]
   block, which is what themecheck.js counts and caps at two. */
html[data-theme="dark"] [data-surface="progress"]{
  --data-1: oklch(0.740 0.142 70);
  --data-2: oklch(0.740 0.125 164);
  --data-3: oklch(0.740 0.131 300);
  --data-1-wash: oklch(0.740 0.142 70 / 0.16);
  --data-2-wash: oklch(0.740 0.125 164 / 0.16);
  --data-3-wash: oklch(0.740 0.131 300 / 0.16);
}
