/* Awareness Score App app. Colours and type follow brainfogremedy.com (styles.css). Built for
   adults 65+ on phones: 18 px body text, buttons at least 56 px tall and every tap target at least 48 px, high contrast, colour
   never carries meaning on its own, portrait only. */
:root {
  /* ---------- design tokens, version 2 (8 Nov 2026, Mufy: the redesign's stage 1, foundations) ----------
     Colour roles for the light theme. Stage 5's dark theme redefines the --color-* roles only. The --game-* colours,
     --color-picture-ground and --color-symbol-ground are the same in every theme: games and pictures keep their colours.
     Every text pair here passes WCAG AA for its use (ratios checked 8 Nov 2026). */
  --color-bg: #FBF7F1;                         /* the page: cream */
  --color-surface: #FFFFFF;                    /* cards, text boxes, sheets, menus */
  --color-surface-variant: #F3ECDF;            /* soft boxes on the page: signposts, notes */
  --color-text: #2A2A26;
  --color-text-muted: #5E5E56;
  --color-outline: #E8DFD0;                    /* card edges, hairlines and dividers */
  --color-outline-strong: #958E81;             /* the edge of every control: buttons with an edge, answers, text boxes, lists, a switch when off */
  --color-primary: #1F5F5B;                    /* teal */
  --color-on-primary: #FFFFFF;
  --color-primary-strong: #143F3C;             /* headings and emphasised words */
  --color-primary-container: #E4F0EE;          /* picked answers, soft cards */
  --color-on-primary-container: #143F3C;
  --color-primary-container-pressed: #D5E8E5;
  --color-press-layer: rgba(31, 95, 91, 0.08);
  --color-link-underline: rgba(31, 95, 91, 0.35);
  --color-on-primary-layer: rgba(255, 255, 255, 0.15);
  --color-on-primary-outline: rgba(255, 255, 255, 0.7);
  --color-on-primary-ring: rgba(255, 255, 255, 0.35);
  --color-accent: #E1723F;                     /* orange: marks, rings and edges only, never words */
  --color-accent-strong: #BC572A;              /* the main button */
  --color-on-accent-strong: #FFFFFF;
  --color-accent-strong-pressed: #A8481C;
  --color-accent-text: #A8481C;                /* orange words on a light ground */
  --color-accent-container: #FBE7DB;
  --color-on-accent-container: #2A2A26;
  --color-success: #1E7A46;
  --color-on-success: #FFFFFF;
  --color-success-container: #E3F1E8;
  --color-on-success-container: #14532F;
  --color-warning: #7A4F00;
  --color-warning-container: #FCEFD4;
  --color-on-warning-container: #7A4F00;
  --color-error: #9B2C1D;
  --color-on-error: #FFFFFF;
  --color-error-container: #FBE9E5;
  --color-on-error-container: #7F2418;
  --color-disabled: #E9E2D5;                   /* (9 Nov 2026, stage 3) a button that can't be used yet: a flat pale grey */
  --color-on-disabled: #5E5E56;                /* its words: 5.1:1 (it was white on #CFC6B8, 1.69:1) */
  --color-inverse-surface: #2A2A26;            /* the notice line */
  --color-on-inverse-surface: #FFFFFF;
  --color-focus: #143F3C;                      /* the focus ring */
  --color-focus-on-primary: #FFFFFF;           /* the focus ring on the teal bar */
  --color-scrim: rgba(30, 26, 14, 0.55);
  --color-scrim-strong: rgba(30, 26, 14, 0.9);
  --color-on-scrim: #FFFFFF;
  --color-veil: rgba(251, 247, 241, 0.95);
  --color-switch-knob: #FFFFFF;
  --color-picture-ground: #FFFFFF;             /* pictures and the app's mark sit on white */
  --color-symbol-ground: #FBF7F1;              /* the cream disc inside a personality symbol */
  --color-chart-line: var(--color-primary);
  --color-chart-grid: var(--color-outline);
  --color-chart-label: var(--color-text-muted);
  --color-chart-value: var(--color-text);
  --color-chart-ring: var(--color-surface);
  /* (9 Nov 2026) Blossom's ground: its shadow (drawn at 13%), its sleepy z's and its sparkles. The drawing itself keeps its own
     colours, as a picture does (js/blossom.js) */
  --color-blossom-shadow: #143F3C;
  --color-blossom-z: #1F5F5B;
  --color-blossom-sparkle: #F2B84B;
  --color-blossom-sparkle-2: #E1723F;          /* (9 Nov 2026) every other sparkle of the tap's twirl: the flower's orange */
  /* (9 Nov 2026, Mufy: the redesign's stage 3, components and icons) */
  --color-accent-strong-hover: #AF4F25;        /* the main button under a mouse pointer: white on it 5.3:1 */
  --color-error-edge: #C0705F;                 /* the edge of a button that deletes: 3.4:1 on the page, 3.7:1 on white */
  --color-skeleton: #E9E1D3;                   /* the still grey shapes while a screen's content comes */
  /* (9 Nov 2026, Mufy: the redesign's stage 5) roles of their own, at today's colours, so the dark theme can change them: the top
     bar (a deeper teal in dark; its words stay white), the start screen's orange drawings, a backdrop for the quiz pictures (none
     in light; a soft cream tile in dark, as they were drawn for a light page), the paywall's phone frame, and the envelope's halo,
     shadow and marks (a paper object: in dark only its halo and its shadow change) */
  --color-bar: #1F5F5B;                        /* the top bar */
  --color-on-bar: #FFFFFF;                     /* its title, arrow and buttons, the account button's ring and initial */
  --color-bar-strong: #143F3C;                 /* the account initial's disc */
  --color-art-orange: #C25A2B;                 /* the orange in the start screen's three drawings (js/plans.js INTRO_*) */
  --color-picture-backdrop: transparent;       /* behind the quiz pictures (.ask-pic img, .ask-zoom img) */
  --picture-backdrop-radius: 0px;              /* its corners */
  --color-device-frame: #143F3C;               /* the paywall's phone frame (.phone-shot) */
  --color-env-halo-0: #F6D5BD;                 /* the envelope's warm halo (main.js ENV_ART), its three stops */
  --color-env-halo-1: #F9E1D0;
  --color-env-halo-2: #FBE7DB;
  --color-env-shadow: #3A2E18;                 /* its shadow on the "table" */
  --color-env-ink: #143F3C;                    /* the four points flying into it, with a white halo */
  --color-env-ink-halo: #FFFFFF;
  --color-env-q: #FFFFFF;                      /* the "?" on the wax seal */
  /* (9 Nov 2026, Mufy: the redesign's stage 5, "L2", Faint) the background (index.html .backdrop): a warm sand sky round its
     source, broad light beams turning about it, a glow at it and a wash to nearly white at the bottom; Mufy's values */
  --color-backdrop-sky-0: #F3D6A8;             /* the sky at the source */
  --color-backdrop-sky-1: #F6E2C4;             /* at 12 percent of its reach */
  --color-backdrop-sky-2: #F5E8D6;             /* at 38 percent */
  --color-backdrop-sky-3: #F2E6D5;             /* at the window's farthest corner */
  --color-backdrop-beam: rgb(255 251 242 / 0.85);        /* a beam's brightest line */
  --color-backdrop-beam-clear: rgb(255 251 242 / 0);
  --backdrop-beams-opacity: 0.5;               /* Faint */
  --color-backdrop-glow-0: rgb(255 249 232 / 0.95);      /* the glow at the source, 60 px and 130 px out */
  --color-backdrop-glow-1: rgb(255 249 232 / 0.4);
  --color-backdrop-glow-2: rgb(255 249 232 / 0);
  --backdrop-glow-opacity: 0.6;                /* Faint */
  --color-backdrop-wash-clear: rgb(255 253 249 / 0);     /* the wash: clear down to a quarter of the window, */
  --color-backdrop-wash: rgb(255 253 249 / 0.95);        /* then to nearly white at the bottom */
  /* (9 Nov 2026, Mufy: the redesign's stage 5, "E. Rays that stay") the score's golden rays and the glow at their heart (main.js
     scoreRays), and a halo in the page's colour behind the words in their reach */
  --color-fx-ray: rgb(250 210 140 / 0.95);
  --color-fx-ray-light: rgb(255 236 196 / 0.9);
  --color-fx-ray-clear: rgb(248 200 120 / 0);
  --color-fx-core: rgb(255 250 235 / 0.95);
  --color-fx-core-mid: rgb(255 236 196 / 0.6);
  --color-fx-core-clear: rgb(255 236 196 / 0);
  --color-score-halo: rgb(255 255 255 / 0.9);
  /* (9 Nov 2026, stage 5 fix) grounds that stay light in dark: the personality's strength chips (their words and edges are the
     personality's own colour, made for a light ground: 4.55:1 to 11.6:1 on the cream for all 24) and the screen of the paywall's
     phone behind its picture (a light screenshot; never redefined) */
  --color-pers-chip: #FFFFFF;
  --color-device-screen: #FBF7F1;
  --focus-ring-width: 3px;
  --focus-ring: var(--focus-ring-width) solid var(--color-focus);
  --focus-ring-on-dark: var(--focus-ring-width) solid var(--color-focus-on-primary);   /* on the teal bar, over a photo */
  --focus-ring-offset: 2px;
  --icon-size-s: 1.111rem;                     /* 20 px (23.3 px with Larger text): in buttons and lines */
  --icon-size-m: 1.333rem;                     /* 24 px (28 px): rows, menus, the notice line */
  --icon-size-l: 1.778rem;                     /* 32 px (37.3 px): above an error or offline screen's heading */
  --dialog-width: 400px;
  --bottom-bar-height: 0px;                    /* stage 4's bottom bar sets it; the notice line sits above it */
  /* (9 Nov 2026, Mufy: the redesign's stage 4) the bottom bar: Material's navigation bar in the app's colours. Stage 5 redefines
     the --color-* roles these point to; the bar itself needs nothing more */
  --color-tabbar: var(--color-surface);
  --color-tabbar-edge: var(--color-outline);
  --color-tabbar-indicator: var(--color-primary-container);   /* the pill behind the active icon */
  --color-tabbar-active: var(--color-on-primary-container);   /* the active tab's icon and label: 11.5:1 on white, 9.9:1 on the pill */
  --color-tabbar-inactive: var(--color-text-muted);           /* the others: 6.4:1 on white */
  --tabbar-pill-w: 64px;
  --tabbar-pill-h: 32px;
  /* the labels: caption size, but never wider than a quarter of the bar can hold ("My results" at 360 px with Larger text) */
  --tabbar-label-size: min(var(--font-size-caption), calc((min(100vw, 560px) - 16px) / 20.8));
  /* the bar's height above the phone's bottom inset: 72.7 px (74.3 px at 360 px with Larger text, 76.1 px wider) */
  --tabbar-height: calc(var(--space-2) + var(--tabbar-pill-h) + var(--space-1) + var(--tabbar-label-size) * var(--line-height-caption) + var(--space-2));
  /* Material Symbols "check" and "error" (Copyright Google LLC, Apache License 2.0) as masks: the switch's tick, the error
     line's mark. Only their shape is used; the colour comes from the rule that uses them (made by stage3 make_icons.py) */
  --mask-check: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%20-960%20960%20960'%3E%3Cpath%20d='m382-354%20339-339q12-12%2028-12t28%2012q12%2012%2012%2028.5T777-636L410-268q-12%2012-28%2012t-28-12L182-440q-12-12-11.5-28.5T183-497q12-12%2028.5-12t28.5%2012l142%20143Z'/%3E%3C/svg%3E");
  --mask-error: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%20-960%20960%20960'%3E%3Cpath%20d='M480-280q17%200%2028.5-11.5T520-320q0-17-11.5-28.5T480-360q-17%200-28.5%2011.5T440-320q0%2017%2011.5%2028.5T480-280Zm0-160q17%200%2028.5-11.5T520-480v-160q0-17-11.5-28.5T480-680q-17%200-28.5%2011.5T440-640v160q0%2017%2011.5%2028.5T480-440Zm0%20360q-83%200-156-31.5T197-197q-54-54-85.5-127T80-480q0-83%2031.5-156T197-763q54-54%20127-85.5T480-880q83%200%20156%2031.5T763-763q54%2054%2085.5%20127T880-480q0%2083-31.5%20156T763-197q-54%2054-127%2085.5T480-80Zm0-80q134%200%20227-93t93-227q0-134-93-227t-227-93q-134%200-227%2093t-93%20227q0%20134%2093%20227t227%2093Zm0-320Z'/%3E%3C/svg%3E");
  /* the games' own colours: fixed, so nothing a game measures changes with the look */
  --game-block: #1F5F5B;                       /* Grid Span's plain look */
  --game-block-lit: #E1723F;
  --game-block-lit-glow: rgba(225, 114, 63, 0.35);
  --game-reaction-sky: #143F3C;
  --game-reaction-ink: #FFE08A;                /* the times, 9.0:1 on the sky */
  --game-reaction-ink-soft: #E4F0EE;
  --game-reaction-text: #FFFFFF;
  --game-right: #1E7A46;
  --game-not-right: #8A8478;
  --game-flash-right: rgba(46, 160, 90, 0.34);
  --game-flash-wrong: rgba(214, 69, 56, 0.3);
  --game-board: #F3ECDF;                       /* Grid Span's plain board */
  --game-num: #FFFFFF;                         /* Grid Span's number badge */
  --game-num-ink: #143F3C;                     /* its number and ring */
  --game-pad: #1F5F5B;                         /* the answer pads */
  --game-pad-ink: #FFFFFF;
  --game-pad-alt: #FFFFFF;                     /* the other answer pad (white, with a --game-pad edge) */
  --game-pad-alt-ink: #143F3C;
  /* (9 Nov 2026, stage 3) Grid Span's dots for the taps taken and Symbol Match's key: the colours they had (the teal, white and
     the hairline), now the games' own, so a dark theme never changes them */
  --game-dot: #1F5F5B;
  --game-dot-ground: #FFFFFF;
  --game-key: #FFFFFF;
  --game-key-edge: #E8DFD0;
  /* (9 Nov 2026, fix) and what sits on the key: its row numbers, the hint row's ground and its ring (today's teal, pale orange and
     orange), so the whole key keeps one look when stage 5 redefines the --color-* roles */
  --game-key-ink: #1F5F5B;
  --game-key-hint: #FBE7DB;
  --game-key-hint-ring: #E1723F;
  /* type: the phone's own fonts, no downloads; sizes in rem, so Larger text (the 21 px root) scales them all */
  --font-heading: Georgia, "Times New Roman", Times, serif;
  --font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-size-caption: 0.85rem;                /* 15.3 px (17.9 px with Larger text) */
  --font-size-body: 1rem;                      /* 18 px (21 px) */
  --font-size-lead: 1.1rem;                    /* 19.8 px (23.1 px) */
  --font-size-title: 1.25rem;                  /* 22.5 px (26.3 px) */
  --font-size-headline: 1.5rem;                /* 27 px (31.5 px) */
  --font-size-display-s: 2rem;                 /* numbers and words to read across a room */
  --font-size-display-m: 3rem;
  --font-size-display-l: 4.2rem;               /* the Awareness Score */
  --line-height-caption: 1.35;
  --line-height-body: 1.5;
  --line-height-lead: 1.4;
  --line-height-title: 1.3;
  --line-height-headline: 1.25;
  --line-height-display: 1;
  --font-weight-regular: 400;
  --font-weight-medium: 500;
  --font-weight-semibold: 600;
  --font-weight-bold: 700;
  /* spacing and touch targets */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  --target-min: 48px;
  --target-comfortable: 56px;
  /* corners */
  --radius-s: 8px;
  --radius-m: 12px;
  --radius-l: 16px;
  --radius-full: 999px;
  /* elevation: resting, floating, over everything */
  --elevation-1: 0 1px 2px rgba(30, 26, 14, 0.06), 0 2px 10px rgba(30, 26, 14, 0.07);
  --elevation-2: 0 8px 28px rgba(30, 26, 14, 0.16);
  --elevation-3: 0 12px 40px rgba(30, 26, 14, 0.28);
  --glow-cta: 0 0 0 5px rgba(188, 87, 42, 0.22), 0 0 20px 6px rgba(188, 87, 42, 0.3);   /* the main button's glow */
  /* motion (30 Oct 2026): short and calm, see "how the app feels" further down */
  --duration-fast: 120ms;
  --duration-base: 200ms;
  --duration-slow: 320ms;
  --ease-out: cubic-bezier(0.22, 0.61, 0.36, 1);
  --ease-in: cubic-bezier(0.55, 0.06, 0.68, 0.19);
  --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
  --ease-emphasised: cubic-bezier(0.05, 0.7, 0.1, 1);
  --duration-screen: 1000ms;                   /* (9 Nov 2026, Mufy: "use one second slide everywhere") a screen change (main.js show) */
  --screen-shift: 30px;                        /* how far a screen slides */
  /* the version 1 names, kept for js/ (the games' own styles, the confetti) and any page that still uses them: each is the
     version 2 token it became. app.css itself uses only the names above. */
  --bg: var(--color-bg);
  --bg-alt: var(--color-surface-variant);
  --surface: var(--color-surface);
  --primary: var(--color-primary);
  --primary-dark: var(--color-primary-strong);
  --primary-tint: var(--color-primary-container);
  --accent: var(--color-accent);
  --accent-dark: var(--color-accent-strong);
  --accent-tint: var(--color-accent-container);
  --text: var(--color-text);
  --text-muted: var(--color-text-muted);
  --border: var(--color-outline);
  --radius: var(--radius-l);
  --shadow: var(--elevation-1);
  --t-fast: var(--duration-fast);
  --t-base: var(--duration-base);
  --t-slow: var(--duration-slow);
  font-size: 18px;
  color-scheme: light;
}
/* (9 Nov 2026, Mufy: the redesign's stage 5) the dark theme: tokens only. It follows the phone (Settings' Appearance: Phone
   setting, no data-theme), or Settings' Dark (data-theme="dark"); Light (data-theme="light") keeps the light values. While a game
   is on screen (html.in-game, main.js stoppable) the light values stay, so a game looks and measures the same in either theme.
   On screen only: a page printed in dark prints as the light theme. Keep the two blocks the same.
   The design: Material's dark roles on the app's colours. The page a deep teal ink, cards one lighter step (shadows read poorly
   on dark, so the lighter surface carries the elevation), teal turns light (words, edges, fills) with dark ink on teal fills,
   the orange main button stays exactly as it is (white on it 4.63:1). Every text pair at least 4.5:1 and every control edge
   3:1 (measured 9 Nov 2026). Never here, so the same in both: the --game-* colours, --color-picture-ground and
   --color-symbol-ground, the orange marks, the main button, the top bar's white words, layers and rings, and the aliases
   (--color-chart-*, --color-tabbar*, the version 1 names), which follow what they point to */
@media screen and (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]):not(.in-game) {
    --color-bg: #101B1A;
    --color-surface: #1F2E2C;
    --color-surface-variant: #283836;
    --color-text: #ECE9E2;
    --color-text-muted: #B4BEBA;
    --color-outline: #344543;
    --color-outline-strong: #7F918D;
    --color-primary: #7FCBC1;
    --color-on-primary: #06302C;
    --color-primary-strong: #A9DED6;
    --color-primary-container: #21403D;
    --color-on-primary-container: #CDEEE9;
    --color-primary-container-pressed: #2A524E;
    --color-press-layer: rgba(127, 203, 193, 0.12);
    --color-link-underline: rgba(127, 203, 193, 0.45);
    --color-accent-text: #F2A47E;
    --color-accent-container: #3D291E;
    --color-on-accent-container: #F3E5DB;
    --color-success: #74CF9B;
    --color-on-success: #06301A;
    --color-success-container: #183628;
    --color-on-success-container: #BFEBD1;
    --color-warning: #E9BD67;
    --color-warning-container: #3B2F14;
    --color-on-warning-container: #F3DAA6;
    --color-error: #F4A191;
    --color-on-error: #3D0C05;
    --color-error-container: #40201A;
    --color-on-error-container: #F8CCC2;
    --color-disabled: #2E3C3A;
    --color-on-disabled: #A6B0AC;
    --color-inverse-surface: #E7E4DD;
    --color-on-inverse-surface: #1C2321;
    --color-focus: #A9DED6;
    --color-scrim: rgba(0, 0, 0, 0.6);
    --color-scrim-strong: rgba(0, 0, 0, 0.9);
    --color-veil: rgba(16, 27, 26, 0.95);
    --color-switch-knob: #06302C;
    --color-blossom-shadow: #000000;
    --color-blossom-z: #7FCBC1;
    --color-error-edge: #D98C7B;
    --color-skeleton: #2C3A38;
    --color-bar: #17433F;
    --color-bar-strong: #0D2C29;
    --color-art-orange: #EE9A70;
    --color-picture-backdrop: #EEE6D8;
    --picture-backdrop-radius: var(--radius-l);
    --color-device-frame: #2E5652;
    --color-env-halo-0: rgb(246 213 189 / 0.3);
    --color-env-halo-1: rgb(249 225 208 / 0.18);
    --color-env-halo-2: rgb(251 231 219 / 0);
    --color-env-shadow: #000000;
    --elevation-1: 0 1px 2px rgba(0, 0, 0, 0.45), 0 2px 10px rgba(0, 0, 0, 0.3);
    --elevation-2: 0 8px 28px rgba(0, 0, 0, 0.5);
    --elevation-3: 0 12px 40px rgba(0, 0, 0, 0.6);
    /* the background: a deep teal ink sky, a touch lighter at the source, faint pale teal beams and glow, the wash to the page
       colour; the score's rays golden at about a sixth of the strength, so the light number and "/ 100" stay clear (stage 5 fix:
       a warmer gold at about a fifth, which stays brighter after the burst, score-fx-stay-dark) */
    --color-backdrop-sky-0: #18302D;
    --color-backdrop-sky-1: #152A28;
    --color-backdrop-sky-2: #122422;
    --color-backdrop-sky-3: #101E1D;
    --color-backdrop-beam: rgb(196 232 224 / 0.85);
    --color-backdrop-beam-clear: rgb(196 232 224 / 0);
    --backdrop-beams-opacity: 0.1;
    --color-backdrop-glow-0: rgb(150 214 204 / 0.6);
    --color-backdrop-glow-1: rgb(150 214 204 / 0.25);
    --color-backdrop-glow-2: rgb(150 214 204 / 0);
    --backdrop-glow-opacity: 0.1;
    --color-backdrop-wash-clear: rgb(16 27 26 / 0);
    --color-backdrop-wash: rgb(16 27 26 / 0.95);
    --color-fx-ray: rgb(255 196 110 / 0.18);
    --color-fx-ray-light: rgb(255 226 170 / 0.15);
    --color-fx-core: rgb(255 236 196 / 0.2);
    --color-fx-core-mid: rgb(255 236 196 / 0.1);
    --color-score-halo: rgb(16 27 26 / 0.9);
    --color-pers-chip: #FBF7F1;
    color-scheme: dark;
  }
}
@media screen {
  :root[data-theme="dark"]:not(.in-game) {
    --color-bg: #101B1A;
    --color-surface: #1F2E2C;
    --color-surface-variant: #283836;
    --color-text: #ECE9E2;
    --color-text-muted: #B4BEBA;
    --color-outline: #344543;
    --color-outline-strong: #7F918D;
    --color-primary: #7FCBC1;
    --color-on-primary: #06302C;
    --color-primary-strong: #A9DED6;
    --color-primary-container: #21403D;
    --color-on-primary-container: #CDEEE9;
    --color-primary-container-pressed: #2A524E;
    --color-press-layer: rgba(127, 203, 193, 0.12);
    --color-link-underline: rgba(127, 203, 193, 0.45);
    --color-accent-text: #F2A47E;
    --color-accent-container: #3D291E;
    --color-on-accent-container: #F3E5DB;
    --color-success: #74CF9B;
    --color-on-success: #06301A;
    --color-success-container: #183628;
    --color-on-success-container: #BFEBD1;
    --color-warning: #E9BD67;
    --color-warning-container: #3B2F14;
    --color-on-warning-container: #F3DAA6;
    --color-error: #F4A191;
    --color-on-error: #3D0C05;
    --color-error-container: #40201A;
    --color-on-error-container: #F8CCC2;
    --color-disabled: #2E3C3A;
    --color-on-disabled: #A6B0AC;
    --color-inverse-surface: #E7E4DD;
    --color-on-inverse-surface: #1C2321;
    --color-focus: #A9DED6;
    --color-scrim: rgba(0, 0, 0, 0.6);
    --color-scrim-strong: rgba(0, 0, 0, 0.9);
    --color-veil: rgba(16, 27, 26, 0.95);
    --color-switch-knob: #06302C;
    --color-blossom-shadow: #000000;
    --color-blossom-z: #7FCBC1;
    --color-error-edge: #D98C7B;
    --color-skeleton: #2C3A38;
    --color-bar: #17433F;
    --color-bar-strong: #0D2C29;
    --color-art-orange: #EE9A70;
    --color-picture-backdrop: #EEE6D8;
    --picture-backdrop-radius: var(--radius-l);
    --color-device-frame: #2E5652;
    --color-env-halo-0: rgb(246 213 189 / 0.3);
    --color-env-halo-1: rgb(249 225 208 / 0.18);
    --color-env-halo-2: rgb(251 231 219 / 0);
    --color-env-shadow: #000000;
    --elevation-1: 0 1px 2px rgba(0, 0, 0, 0.45), 0 2px 10px rgba(0, 0, 0, 0.3);
    --elevation-2: 0 8px 28px rgba(0, 0, 0, 0.5);
    --elevation-3: 0 12px 40px rgba(0, 0, 0, 0.6);
    /* the background: a deep teal ink sky, a touch lighter at the source, faint pale teal beams and glow, the wash to the page
       colour; the score's rays golden at about a sixth of the strength, so the light number and "/ 100" stay clear (stage 5 fix:
       a warmer gold at about a fifth, which stays brighter after the burst, score-fx-stay-dark) */
    --color-backdrop-sky-0: #18302D;
    --color-backdrop-sky-1: #152A28;
    --color-backdrop-sky-2: #122422;
    --color-backdrop-sky-3: #101E1D;
    --color-backdrop-beam: rgb(196 232 224 / 0.85);
    --color-backdrop-beam-clear: rgb(196 232 224 / 0);
    --backdrop-beams-opacity: 0.1;
    --color-backdrop-glow-0: rgb(150 214 204 / 0.6);
    --color-backdrop-glow-1: rgb(150 214 204 / 0.25);
    --color-backdrop-glow-2: rgb(150 214 204 / 0);
    --backdrop-glow-opacity: 0.1;
    --color-backdrop-wash-clear: rgb(16 27 26 / 0);
    --color-backdrop-wash: rgb(16 27 26 / 0.95);
    --color-fx-ray: rgb(255 196 110 / 0.18);
    --color-fx-ray-light: rgb(255 226 170 / 0.15);
    --color-fx-core: rgb(255 236 196 / 0.2);
    --color-fx-core-mid: rgb(255 236 196 / 0.1);
    --color-score-halo: rgb(16 27 26 / 0.9);
    --color-pers-chip: #FBF7F1;
    color-scheme: dark;
  }
}
html.big-text { font-size: 21px; }
* { box-sizing: border-box; }
/* (9 Nov 2026, Mufy: the redesign's stage 5) the page's colour on html alone: body stays transparent, so index.html's background
   (.backdrop, z-index -1) shows through it; every other page looks as before (html's colour fills the window) */
html, body { margin: 0; color: var(--color-text); }
html { background: var(--color-bg); }
body {
  font-family: var(--font-body);
  line-height: 1.5;
  -webkit-text-size-adjust: 100%;
  touch-action: manipulation;
  min-height: 100vh;
  min-height: 100dvh;
}
h1, h2, h3 { font-family: var(--font-heading); font-weight: 600; color: var(--color-primary-strong); line-height: 1.25; }
h1 { font-size: var(--font-size-headline); margin: 0.4em 0 0.5em; }
h2 { font-size: var(--font-size-title); margin: 1.2em 0 0.4em; }
h3 { font-size: var(--font-size-lead); margin: 0 0 0.2em; }
h1:focus { outline: none; }   /* focused after each screen change, for screen readers */
p { margin: 0 0 0.8em; }
a { color: var(--color-primary); }
/* (9 Nov 2026, stage 3) one visible ring for the keyboard, wherever a control has no ring of its own. :where() gives it no
   weight at all, so every control's own ring and every "outline: none" wins; tabindex="-1" (headings and status lines
   focused by script) is left out */
:where(a[href], button, input, select, textarea, summary, [tabindex]:not([tabindex="-1"])):where(:focus-visible) {
  outline: var(--focus-ring); outline-offset: var(--focus-ring-offset);
}

/* (9 Nov 2026, Mufy: the redesign's stage 5) the top bar and everything on it take --color-bar and --color-on-bar, not the primary
   roles, which turn light teal with dark ink in dark: the bar is a deeper teal there, its words, arrow and rings stay white */
.bar {
  position: sticky; top: 0; z-index: 5;
  display: flex; align-items: center; gap: 12px;
  padding: calc(10px + env(safe-area-inset-top)) 16px 10px;
  background: var(--color-bar); color: var(--color-on-bar);
}
/* (9 Nov 2026, fix) a heading scrolled to (Settings' Groups, Your plan, Your invites) or a control reached with the keyboard stops
   below the sticky bar (68 px), never under it (WCAG 2.4.11). Stage 4's bottom bar adds its own scroll-padding-bottom */
html { scroll-padding-top: calc(76px + env(safe-area-inset-top)); }
/* the app's small mark: option 04, the focus rings (icons/mark.svg), on white */
.bar__mark {
  width: 34px; height: 34px; border-radius: var(--radius-s); background: url("icons/mark.svg") center / 26px 26px no-repeat; background-color: var(--color-picture-ground);
  flex: none;
}
/* (9 Nov 2026, stage 3) its arrow is js/icons.js's arrow_back (24 px whatever the text size), in the middle; the font size
   stays for the legal pages' text arrows */
.bar__back {
  position: relative; display: grid; place-items: center; width: 44px; height: 44px; border: 0; border-radius: var(--radius-m);
  background: var(--color-on-primary-layer); color: var(--color-on-bar); font-size: 24px; cursor: pointer; flex: none;
}
.bar__back:focus-visible { outline: var(--focus-ring-on-dark); outline-offset: 2px; }   /* (9 Nov 2026, stage 3) white on the teal bar */
/* (8 Nov 2026, stage 1) the tap area reaches 48 px, 2 px past the button on each side; the bar keeps its height */
.bar__back::before { content: ""; position: absolute; inset: -2px; }
/* the title takes the room between the ← arrow (or the mark) and the account button, which is on
   every screen now (20 Oct 2026, Mufy): up to two lines, then "…", so neither button is pushed */
.bar__title {
  flex: 1 1 auto; min-width: 0; font-weight: 600; font-size: var(--font-size-body); overflow-wrap: break-word;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden;
}
/* a button on the right of the bar (home's Settings, 19 Oct 2026; not used since home's account
   button came, 20 Oct 2026, but header() still offers it) */
.bar__button {
  margin-left: auto; min-height: 48px; padding: 0 16px; border: 1px solid var(--color-on-primary-outline); border-radius: var(--radius-m);
  background: var(--color-on-primary-layer); color: var(--color-on-bar); font: inherit; font-weight: 600; font-size: var(--font-size-body); cursor: pointer;
}
.bar__button:focus-visible { outline: var(--focus-ring-on-dark); outline-offset: 2px; }
/* the account button and its menu (20 Oct 2026, Mufy; every screen's bar): the Google picture, or the initial on a
   dark teal circle, 48 px round with a white edge; the menu opens under it, rows 56 px tall. The
   see-through layer behind the menu takes a tap outside it, which only closes the menu. */
.acct { position: relative; z-index: 1; margin-left: auto; flex: none; }
.acct__btn {
  position: relative; display: grid; place-items: center; width: 48px; height: 48px; padding: 0; overflow: hidden;
  border: 2px solid var(--color-on-bar); border-radius: 50%; background: var(--color-bar-strong); color: var(--color-on-bar);
  font: 700 1.15rem/1 var(--font-body); cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.acct__btn:focus-visible { outline: var(--focus-ring-on-dark); outline-offset: 3px; }
.acct__btn[aria-expanded="true"] { box-shadow: 0 0 0 4px var(--color-on-primary-ring); }
.acct__pic { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0; }
.acct__btn.has-pic .acct__pic { opacity: 1; }
.acct__btn.has-pic .acct__initial { visibility: hidden; }
.acct__scrim { position: fixed; inset: 0; z-index: -1; background: transparent; }
.acct__pop {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 1; width: 260px; max-width: calc(100vw - 32px);
  padding: 6px 0; background: var(--color-surface); color: var(--color-text); border: 1px solid var(--color-outline);
  border-radius: var(--radius-m); box-shadow: var(--elevation-2);   /* (9 Nov 2026, stage 3) a menu's corners */
}
/* (9 Nov 2026, stage 3) "Signed in as" and the email: a label in a 260 px menu, caption size */
.acct__who { margin: 0; padding: 8px 16px 10px; color: var(--color-text-muted); font-size: var(--font-size-caption); overflow-wrap: anywhere; border-bottom: 1px solid var(--color-outline); }
.acct__menu { display: grid; }
.acct__item {
  display: flex; align-items: center; gap: var(--space-4); width: 100%; min-height: 56px; margin: 0; padding: 10px 16px; border: 0; border-radius: 0;
  background: none; color: var(--color-primary-strong); font: 600 var(--font-size-body)/1.25 var(--font-body); text-align: left; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.acct__item:focus { outline: none; }
.acct__item:hover, .acct__item:focus-visible { background: var(--color-primary-container); }
.acct__item:focus-visible { outline: var(--focus-ring); outline-offset: -3px; }
.acct__item--out { border-top: 1px solid var(--color-outline); }
/* (9 Nov 2026, stage 3) each item's icon (js/icons.js), teal, before its words; the letter jump reads the words */
.acct__item > .icon { color: var(--color-primary); }
.acct__label { min-width: 0; }

.screen { max-width: 560px; margin: 0 auto; padding: 16px 16px calc(40px + env(safe-area-inset-bottom)); }
.card {
  background: var(--color-surface); border: 1px solid var(--color-outline); border-radius: var(--radius-l);
  box-shadow: var(--elevation-1); padding: 18px; margin: 14px 0;
}
.card--soft { background: var(--color-primary-container); box-shadow: none; border-color: transparent; }
.card h1 { margin-top: 0; }
.signpost { background: var(--color-surface-variant); border-radius: var(--radius-m); padding: 12px 14px; font-size: var(--font-size-body); }
/* (20 Oct 2026) lines shown only while offline (main.js offlineLine), and the rankings' "As of" line */
.offline-line[hidden], .rankings__asof[hidden] { display: none; }
.rankings__asof { margin: 4px 0 8px; }
/* (9 Nov 2026, stage 3) an offline line in a soft box: the wifi_off icon (js/icons.js) before its words */
.signpost.offline-line:not([hidden]) { display: flex; align-items: flex-start; gap: var(--space-3); }
.offline-line > .icon { margin-top: 0.1em; color: var(--color-text-muted); }
.offline-line__text { min-width: 0; }
/* switches that wait for the connection while offline (main.js needsNet); a button that waits has the disabled look (below,
   since 9 Nov 2026) */
.toggle input[data-net-off], .toggle input[data-net-off] + span { opacity: 0.6; }

.btn {
  display: flex; align-items: center; justify-content: center; gap: var(--space-2); width: 100%;
  min-height: 56px; margin: 10px 0 0; padding: 12px 18px;
  border: 2px solid transparent; border-radius: var(--radius-m);
  font: 600 var(--font-size-body)/1.25 var(--font-body); text-align: center; text-decoration: none;
  cursor: pointer; user-select: none; -webkit-user-select: none; -webkit-tap-highlight-color: transparent;
}
.btn > .icon { width: var(--icon-size-s); height: var(--icon-size-s); }
.btn__label { min-width: 0; text-wrap: balance; }
.btn:focus-visible { outline: var(--focus-ring); outline-offset: var(--focus-ring-offset); }
/* (9 Nov 2026, the redesign's stage 3) one hierarchy: filled orange for the screen's main action; outlined teal for every
   other action (secondary and quiet are one look now); text for Not now, Cancel, Skip and Close. Two filled extras: teal for
   Change the look (Mufy, 8 Nov 2026) and red for a dialog's yes that deletes or leaves.
   The darker site orange (--color-accent-strong, #BC572A): white text on it is 4.6:1, AA at any size */
.btn--primary { background: var(--color-accent-strong); color: var(--color-on-accent-strong); font-weight: 700; }
.btn--primary:active { background: var(--color-accent-strong-pressed); }
.btn--teal { background: var(--color-primary); color: var(--color-on-primary); }
.btn--teal:active { background: var(--color-primary-strong); }
.btn--destructive { background: var(--color-error); color: var(--color-on-error); font-weight: 700; }
.btn--destructive:active { background: var(--color-on-error-container); }
.btn--secondary, .btn--quiet { background: var(--color-surface); color: var(--color-primary); border-color: var(--color-primary); }
.btn--text { background: transparent; color: var(--color-primary); }
.btn--choice { background: var(--color-surface); color: var(--color-text); border-color: var(--color-outline-strong); font-weight: 500; }
.btn--choice.is-picked, .btn--num.is-picked { border-color: var(--color-primary); background: var(--color-primary-container); color: var(--color-primary-strong); font-weight: 700; }
.link { background: none; border: 0; color: var(--color-primary); font: inherit; text-decoration: underline; cursor: pointer; padding: 12px 4px; min-height: 48px; }
/* (30 Oct 2026, Mufy) a press shows at once, in colour (text contrast stays above 6:1 on each); on 30 Oct Mufy chose
   colour only, and on 31 Oct 2026 (21:15 IST, decision C) the press below as well */
/* (9 Nov 2026, stage 3) a hover colour only where there is a mouse (a phone never keeps one); pressed is the deeper
   --color-primary-container-pressed (teal words on it 5.8:1). Star Trails' "Practise again" (st-ov-again) keeps its own
   night colours */
@media (hover: hover) {
  .btn--primary:hover:not(:disabled) { background: var(--color-accent-strong-hover); }
  .btn--secondary:hover:not(:disabled), .btn--text:hover:not(:disabled),
  .btn--choice:hover:not(:disabled):not(.is-picked) { background: var(--color-primary-container); }
  .btn--quiet:hover:not(:disabled):not(.st-ov-again) { background: var(--color-primary-container); }
}
.btn--secondary:active, .btn--quiet:active, .btn--text:active, .btn--choice:active, .result-list__row:active { background: var(--color-primary-container-pressed); }
/* (9 Nov 2026, stage 3) can't be used yet (or waits for the connection: main.js needsNet): readable, 5.1:1, and plainly not a
   live button. A busy button (ui.js setBusy) keeps its own colours */
.btn:disabled:not(.is-busy) { background: var(--color-disabled); color: var(--color-on-disabled); border-color: var(--color-disabled); cursor: default; }
.btn--text:disabled:not(.is-busy) { background: transparent; border-color: transparent; }
.btn.is-busy { cursor: progress; }
/* (9 Nov 2026, fix) inline-block, so the ring is the same inside a .link button (This week's question's "Show everyone's picks")
   as inside a .btn, with a little room before the words there (a .btn's gap spaces it) */
.btn__spinner { flex: none; display: inline-block; width: 1.1em; height: 1.1em; vertical-align: -0.2em; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; }
:not(.btn) > .btn__spinner { margin-right: 0.4em; }
@keyframes btn-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .btn__spinner { animation: btn-spin 900ms linear infinite; }
}
.link:active { color: var(--color-primary-strong); }
/* (1 Nov 2026, Mufy) a link inside a sentence ("turn it on" in the plan screen's timeline): no padding of its own */
.link--inline { padding: 0; min-height: 0; vertical-align: baseline; }
.link--inline[disabled] { cursor: default; opacity: 0.6; }
/* (31 Oct 2026, Mufy's decision C) the press: a button or chip held shrinks to 98.5 percent, 120 ms each way, on the
   app's own screens only (main.screen). Every game draws into its own host in place of the screen, never inside it (main.js
   stoppable), so no game button, Reaction's tap surface, the Stop button or the Stop question ever moves: nothing a
   game measures changes. The games' own press (.btn--answer.is-pressed) stays as it is. Never with less motion. */
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .screen .btn, html:not(.less-motion) .screen .chip { transition: transform var(--duration-fast) var(--ease-out); }
  html:not(.less-motion) .screen .btn:active, html:not(.less-motion) .screen .chip:active { transform: scale(0.985); }
}
/* ---------- icons (9 Nov 2026, the redesign's stage 3: js/icons.js, Material Symbols) ----------
   Drawn in the text's colour, sized in rem so Larger text makes them grow (the bar's Back stays 24 px) */
.icon { display: inline-block; flex: none; width: var(--icon-size-m); height: var(--icon-size-m); fill: currentColor; vertical-align: middle; }
.icon--s { width: var(--icon-size-s); height: var(--icon-size-s); }
.icon--m { width: var(--icon-size-m); height: var(--icon-size-m); }
.icon--l { width: var(--icon-size-l); height: var(--icon-size-l); }
.icon--px24 { width: 24px; height: 24px; }
.icon--trail { width: 0.9em; height: 0.9em; margin-left: 0.2em; vertical-align: -0.1em; }
.icon-pair { display: inline-grid; vertical-align: middle; }
.icon-pair > .icon { grid-area: 1 / 1; }
.icon-pair__on, [aria-pressed="true"] .icon-pair__off { visibility: hidden; }
[aria-pressed="true"] .icon-pair__on { visibility: visible; }
/* in a button the gap spaces the icon and the words (the quiz answers' and the start screen's ticks too) */
.btn > .ask-tick { margin-right: 0; }
.btn > .icon--trail { margin-left: 0; }
.qnote__tick { display: inline-grid; }   /* its check icon (js/icons.js), as the chips' .chip__tick */
/* an icon before the words of a line (a member's "Checked in today", "Your status is posted.", "Liked by ..."). (9 Nov 2026, fix)
   The line is a row, so the icon sits on the middle of its words (inline it sat 2.5 px low) */
.gmember__today, .gnotes__said, .gnote__liked { display: flex; align-items: center; gap: 0.25em; }
.gmember__today { justify-content: flex-end; }

/* (9 Nov 2026, fix) no gap between its lines: the links are 48 px tall already, so a link that wraps (home's About, since Settings
   left the foot) stays with the others instead of floating 70 px under them */
.foot { display: flex; gap: 0 20px; justify-content: center; flex-wrap: wrap; margin-top: 18px; font-size: var(--font-size-body); }

.q { margin: 18px 0; }
.q__text { font-weight: 600; margin-bottom: 4px; }
.q__opts { display: grid; grid-template-columns: 1fr 1fr; gap: 0 10px; }

.scale__opts { display: grid; grid-template-columns: repeat(6, 1fr); gap: 8px; margin-top: 10px; }
.btn--num { min-height: 56px; margin: 0; padding: 0; background: var(--color-surface); color: var(--color-text); border-color: var(--color-outline-strong); font-size: var(--font-size-lead); }
.scale__end { color: var(--color-text-muted); margin: 10px 0 0; }
.scale__end--high { text-align: right; }

.toggle { display: flex; align-items: center; gap: 12px; min-height: 56px; font-weight: 600; }
.toggle input { width: 28px; height: 28px; accent-color: var(--color-primary); }

/* results */
.results { margin: 10px 0; }
.result { background: var(--color-surface); border: 1px solid var(--color-outline); border-radius: var(--radius-l); padding: 14px 16px; margin: 10px 0; }
.result__main { font-weight: 600; margin: 0.2em 0; }
.result__change { margin: 0.6em 0 0.2em; }
.result__what { color: var(--color-text-muted); font-size: var(--font-size-body); margin: 0.2em 0 0; }
.plan-card .plan-card__note { font-size: var(--font-size-body); margin-top: 10px; }
.table-wrap { overflow-x: auto; }
.history { width: 100%; border-collapse: collapse; font-size: var(--font-size-body); }
.history .link { padding: 4px 0; text-align: left; }
.history th, .history td { padding: 10px 6px; border-bottom: 1px solid var(--color-outline); text-align: left; }
.history tbody tr { cursor: pointer; }

/* games: full screen, no scrolling while playing */
.game {
  max-width: 560px; margin: 0 auto; padding: calc(12px + env(safe-area-inset-top)) 16px calc(16px + env(safe-area-inset-bottom));
  min-height: 100vh; min-height: 100dvh; display: flex; flex-direction: column;
  user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;
}
.game-note { min-height: 2.8em; text-align: center; font-weight: 600; color: var(--color-primary-strong); margin: 8px 0; }
.game-note.is-good::before { content: "\2713  "; }
.game-note.is-miss::before { content: "\2192  "; }
.timebar { height: 8px; background: var(--color-outline); border-radius: var(--radius-full); overflow: hidden; margin-bottom: 12px; }
.timebar span { display: block; height: 100%; background: var(--color-primary); transform-origin: left center; }
.timebar--steps span { transition: transform 0.2s linear; }
.btn--answer { background: var(--game-pad); color: var(--game-pad-ink); font-size: var(--font-size-title); min-height: 72px; margin: 0; }
.btn--answer.btn--alt { background: var(--game-pad-alt); color: var(--game-pad-alt-ink); border-color: var(--game-pad); }
.btn--answer.is-pressed { filter: brightness(0.85); }

/* Symbol Match */
.sm-key { background: var(--game-key); border: 2px solid var(--game-key-edge); border-radius: var(--radius-l); padding: 8px 10px; }
.sm-row { display: grid; grid-template-columns: 44px repeat(3, 1fr); align-items: center; gap: 6px; padding: 4px 0; }
.sm-row + .sm-row { border-top: 1px solid var(--game-key-edge); }   /* (9 Nov 2026, fix) the key's own colours */
.sm-num { font: 700 1.4rem var(--font-heading); color: var(--game-key-ink); text-align: center; }
.sm-glyph { display: grid; place-items: center; }
.sm-glyph svg { width: 100%; max-width: 64px; height: auto; aspect-ratio: 1; }
.sm-target { flex: 1; display: grid; place-items: center; min-height: 140px; }
.sm-target svg { width: 120px; height: 120px; }
.sm-pad { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }

/* Picture Memory */
.pm-stage { flex: 1; display: grid; place-items: center; min-height: 260px; }
.pm-pic { width: min(78vw, 340px); height: min(78vw, 340px); object-fit: contain; border-radius: var(--radius-m); background: var(--color-picture-ground); visibility: hidden; }
.pm-buttons { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.pm-buttons[hidden] { display: none; }

/* Grid Span */
.gs-board { position: relative; width: min(92vw, 440px); aspect-ratio: 1; margin: 8px auto; background: var(--game-board); border-radius: var(--radius-l); }
.gs-block {
  position: absolute; width: 19%; aspect-ratio: 1; transform: translate(-50%, -50%);
  border: 0; border-radius: 12px; background: var(--game-block); cursor: pointer; -webkit-tap-highlight-color: transparent;
  transition: background 0.08s linear, box-shadow 0.08s linear;
}
.gs-block.is-lit { background: var(--game-block-lit); box-shadow: 0 0 0 6px var(--game-block-lit-glow); }
.gs-board.is-watching .gs-block { cursor: default; }
/* a soft morning sky around the board, kin to Star Trails' night (20 Oct 2026, Mufy): only the page
   around it changes. The board, its blocks, their colours, sizes and places, and the timings are as
   before, so spans stay comparable with earlier check-ins. The shadow sits outside the board and
   takes no room; max-width only lets the sky reach the sides of a wide screen (the board's own
   width doesn't depend on it). */
.game--grid {
  max-width: none; background-color: var(--color-bg);
  background-image: radial-gradient(70% 40% at 85% 0%, rgba(251, 231, 219, 0.9) 0%, rgba(251, 231, 219, 0) 100%),
    linear-gradient(180deg, #DCEBE7 0%, #EAF2EF 45%, var(--color-bg) 100%);
}
.game--grid .gs-board { box-shadow: 0 8px 28px rgba(20, 63, 60, 0.13), 0 0 0 1px rgba(20, 63, 60, 0.06); }

/* right or wrong (24 Oct 2026, Mufy): engine.js answerFeedback, after a Picture Memory answer or a
   Grid Span sequence. One soft flash over the whole screen, green or red, in and out within 450 ms,
   with a large tick or cross in the middle (the shape says it too, for anyone who can't tell red from
   green). Above the game, below the "turn your phone upright" note, and it never takes a tap.
   Reduced motion: a still, lighter tint for as long, with no fade. */
.answer-flash { position: fixed; inset: 0; z-index: 60; display: grid; place-items: center; pointer-events: none; opacity: 0; }
.answer-flash[hidden] { display: none; }
.answer-flash__tint { position: absolute; inset: 0; }
.answer-flash.is-right .answer-flash__tint { background: var(--game-flash-right); }
.answer-flash.is-wrong .answer-flash__tint { background: var(--game-flash-wrong); }
.answer-flash__mark { position: relative; }
.answer-flash__mark svg { display: block; width: 112px; height: 112px; filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.22)); }
.answer-flash.is-on { animation: answer-flash 0.45s ease-out forwards; }
@keyframes answer-flash { 0% { opacity: 0; } 12% { opacity: 1; } 45% { opacity: 1; } 100% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .answer-flash.is-on { animation: none; opacity: 1; }
  .answer-flash.is-on .answer-flash__tint { opacity: 0.7; }
}
/* (30 Oct 2026) the same with Settings' Less motion (main.js: html.less-motion; :where adds no weight, so
   these behave exactly as the lines above) */
:where(html.less-motion) .answer-flash.is-on { animation: none; opacity: 1; }
:where(html.less-motion) .answer-flash.is-on .answer-flash__tint { opacity: 0.7; }

/* Reaction */
.game--reaction { padding: 0; max-width: none; }
/* (20 Oct 2026, Mufy) a calm evening sky: a soft glow at the top and darker corners, kin to Star
   Trails. The middle, where the numbers appear, keeps the same flat colour as before (the glow fades
   out by a third of the way down, the corners start darkening well outside the numbers and the
   lines under them), so the numbers' size, colour and contrast are unchanged and times stay
   comparable. Nothing in it moves. */
.rt-area {
  flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
  min-height: 100vh; min-height: 100dvh; background: var(--game-reaction-sky); color: var(--game-reaction-text); cursor: pointer; outline: none;
  touch-action: none;
  background-image: radial-gradient(90% 33% at 80% 0%, rgba(128, 196, 186, 0.2) 0%, rgba(128, 196, 186, 0) 100%),
    radial-gradient(circle farthest-corner at 50% 50%, var(--game-reaction-sky) 0%, var(--game-reaction-sky) 42%, #0F3532 72%, #0A2A28 100%);
}
.rt-counter { font: 700 4.5rem/1 var(--font-body); font-variant-numeric: tabular-nums; min-height: 1em; color: var(--game-reaction-ink); }
.rt-note { color: var(--game-reaction-text); max-width: 22em; padding: 0 16px; }

.rotate-note { display: none; }
/* touch phones only (same query as SIDEWAYS in engine.js, which voids a game turned sideways);
   a short desktop browser window isn't blocked */
@media (orientation: landscape) and (max-height: 520px) and (pointer: coarse) {
  .rotate-note {
    display: grid; place-items: center; position: fixed; inset: 0; z-index: 99;
    background: var(--color-primary-strong); color: var(--color-on-primary); font: 600 var(--font-size-title) var(--font-body); text-align: center; padding: 24px;
  }
}
@media (max-width: 360px) {
  .sm-target svg { width: 100px; height: 100px; }
}

/* ---------- v2 (18 Oct 2026): accounts, scores, graph ---------- */
.lead { font-size: var(--font-size-lead); text-wrap: pretty; }
/* (9 Nov 2026, stage 3) body size: instructions and privacy facts on every screen; the muted colour keeps it secondary.
   (9 Nov 2026, fix) text-wrap: pretty, so a line seldom ends with one word alone ("it." under First name) */
.fine { color: var(--color-text-muted); font-size: var(--font-size-body); text-wrap: pretty; }
.eyebrow { color: var(--color-text-muted); font-weight: 600; margin: 0.6em 0 0.2em; }
.or { text-align: center; color: var(--color-text-muted); margin: 14px 0 6px; }
.google-btn { display: flex; justify-content: center; min-height: 44px; margin: 14px 0 4px; }
/* (display: flex above would otherwise beat the hidden attribute) */
.google-btn[hidden], .or[hidden] { display: none; }
/* (polish, 31 Oct 2026, Mufy) the Google Play edition's sign-up: Google first, the email form and "or with your email" folded
   until "Use my email instead" (or by themselves when Google's button can't come) */
.auth--folded .auth-form, .auth--folded .or { display: none; }
.auth-email-link { display: block; margin: 6px auto 0; }
.auth-email-link[hidden], .google-hint[hidden] { display: none; }
.google-hint { margin: 14px 0 0; }
.auth-form { display: grid; gap: 6px; margin: 8px 0; }
.auth-form label, .time-label { font-weight: 600; margin-top: 8px; }
.auth-form input, input[type="time"] {
  font: 500 var(--font-size-body) var(--font-body); min-height: 52px; padding: 10px 14px;
  border: 2px solid var(--color-outline-strong); border-radius: var(--radius-m); background: var(--color-surface); color: var(--color-text); width: 100%;
}
.auth-form input:focus, input[type="time"]:focus { outline: var(--focus-ring); outline-offset: 1px; border-color: var(--color-primary); }
/* Send feedback's message box (21 Oct 2026) */
.feedback-form__text {
  font: 500 var(--font-size-body)/1.45 var(--font-body); min-height: 9em; padding: 10px 14px; resize: vertical;
  border: 2px solid var(--color-outline-strong); border-radius: var(--radius-m); background: var(--color-surface); color: var(--color-text); width: 100%;
}
.feedback-form__text:focus { outline: var(--focus-ring); outline-offset: 1px; border-color: var(--color-primary); }
.input--big { font-size: var(--font-size-headline) !important; letter-spacing: 0.08em; }
.form-error { color: var(--color-error); font-weight: 600; margin: 6px 0 0; }
/* (9 Nov 2026, stage 3) Material's "error" mark before the words (the words say it too) */
.form-error:not([hidden]) { display: flex; align-items: flex-start; gap: var(--space-2); }
.form-error::before {
  content: ""; flex: none; width: 1.2em; height: 1.2em; margin-top: 0.15em; background: currentColor;
  -webkit-mask: var(--mask-error) center / contain no-repeat; mask: var(--mask-error) center / contain no-repeat;
}
.q__opts--one { grid-template-columns: 1fr; }

/* a game's points, and the Awareness Score itself (the one hero number on the screen) */
.points { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; }
.points__n { font: 700 var(--font-size-display-m)/1 var(--font-body); color: var(--color-primary-strong); }
.points__of { font: 500 var(--font-size-body) var(--font-body); color: var(--color-text-muted); }
.hero { display: flex; align-items: baseline; gap: 8px; margin: 4px 0 8px; }
.hero__n { font: 700 var(--font-size-display-l)/1 var(--font-body); color: var(--color-primary-strong); font-variant-numeric: tabular-nums; }
.hero__of { font: 500 var(--font-size-lead) var(--font-body); color: var(--color-text-muted); }
.hero--small .hero__n { font-size: var(--font-size-display-m); }
/* (8 Nov 2026, stage 1) numbers that change keep their width: scores, points, counters and times */
.points__n, .result__pts, .result-list__score, .chart__tip strong, .chart__svg text, .history, .report-table,
.insights__value strong, .group-card__today, .note-form__count, .rec__status, .ask-step__text, .intro__step { font-variant-numeric: tabular-nums; }
.left-line { font-weight: 700; color: var(--color-primary-strong); background: var(--color-primary-container); border-radius: var(--radius-m); padding: 10px 14px; }
.notes { background: var(--color-surface-variant); border-radius: var(--radius-m); padding: 10px 14px 10px 30px; margin: 10px 0; }
p.notes { padding-left: 14px; }
.notes li { margin: 4px 0; }
.result__head { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
.result__pts { font: 700 var(--font-size-headline) var(--font-body); color: var(--color-primary-strong); }

/* extra-game chips: the tick and the outline show the state, not colour alone */
.chips { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px; }
/* (9 Nov 2026, stage 3) Material's filter chip: words at body size, a hover colour with a mouse, a press colour, the ring for
   the keyboard. The tick is js/icons.js's check and keeps its place while hidden, so a chip never changes width when ticked */
.chip {
  display: inline-flex; align-items: center; gap: var(--space-2); min-height: var(--target-min); padding: 8px 16px 8px 12px;
  border: 2px solid var(--color-outline-strong); border-radius: var(--radius-full); background: var(--color-surface); color: var(--color-text);
  font: 600 var(--font-size-body) var(--font-body); cursor: pointer;
}
.chip:focus-visible { outline: var(--focus-ring); outline-offset: var(--focus-ring-offset); }
@media (hover: hover) { .chip:hover:not(.is-on) { background: var(--color-primary-container); } }
.chip__tick { display: inline-grid; visibility: hidden; font-weight: 800; }
.chip.is-on { border-color: var(--color-primary); background: var(--color-primary-container); color: var(--color-primary-strong); }
.chip.is-on .chip__tick { visibility: visible; }
.chip:active, .chip.is-on:active { background: var(--color-primary-container-pressed); }
.chip small { font-weight: 500; color: var(--color-text-muted); }

.result-list { list-style: none; margin: 10px 0; padding: 0; }
.result-list li + li { border-top: 1px solid var(--color-outline); }
.result-list__row {
  width: 100%; display: flex; justify-content: space-between; align-items: center; gap: 12px;
  min-height: 56px; padding: 10px 4px; background: none; border: 0; font: inherit; color: var(--color-text); cursor: pointer; text-align: left;
}
.result-list__score { margin-left: auto; font: 700 var(--font-size-headline) var(--font-body); color: var(--color-primary-strong); }
/* (9 Nov 2026, stage 3) each check-in's row ends in a chevron (js/icons.js), after its score */
.result-list__row > .icon { color: var(--color-text-muted); }
/* (9 Nov 2026, stage 3) red words and a red edge (--color-error-edge: 3.4:1 on the page, 3.7:1 on white) */
.btn--danger { color: var(--color-error); border-color: var(--color-error-edge); }

/* charts (graph.js) */
.chart { position: relative; width: 100%; margin: 6px 0 4px; }
.chart__svg { display: block; width: 100%; height: auto; touch-action: pan-y; }
.chart__svg:focus-visible { outline: var(--focus-ring); outline-offset: 2px; border-radius: var(--radius-s); }
.chart__tip {
  position: absolute; pointer-events: none; background: var(--color-surface); border: 1px solid var(--color-outline);
  border-radius: var(--radius-s); padding: 4px 8px; box-shadow: var(--elevation-1); font-size: var(--font-size-caption); white-space: nowrap;
}
.chart__tip strong { font-size: var(--font-size-lead); color: var(--color-text); }
.chart__tip span { color: var(--color-text-muted); }
/* (8 Nov 2026, stage 1) the chart's colours from the tokens (graph.js keeps the same colours as attributes, underneath) */
.chart__grid { stroke: var(--color-chart-grid); }
.chart__axis { fill: var(--color-chart-label); }
.chart__line, .chart__ring, .chart__ping { stroke: var(--color-chart-line); }
.chart__dot { fill: var(--color-chart-line); stroke: var(--color-chart-ring); }
.chart__value { fill: var(--color-chart-value); }
.chart__cross { stroke: var(--color-chart-label); }

/* ---------- sharing (19 Oct 2026) ---------- */
.share { margin: 0.75rem 0 1rem; }
.share-row { display: flex; flex-wrap: wrap; gap: 0.5rem; }
/* (9 Nov 2026, stage 3) 13rem: two side by side only where both fit their icon and words on one line (else one above the other) */
.share-row .btn { flex: 1 1 13rem; margin: 0; }
.share--one { margin: 0.25rem 0 0; }
/* small buttons are still at least 48 px tall, with 18 px labels (19 Oct 2026); one rule since 8 Nov 2026 */
.btn--small { min-height: 48px; width: auto; display: inline-flex; padding: 0.5rem 0.9rem; font-size: var(--font-size-body); }
.result__share { margin: 0; }
.share-panel__lead { margin: 0 0 0.5rem; font-weight: 600; }
.share-panel__buttons { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.share-panel__buttons .btn { flex: 1 1 13rem; margin: 0; text-align: center; text-decoration: none; }   /* (9 Nov 2026, stage 3) as .share-row */
.share-panel__text { width: 100%; margin-top: 0.6rem; font: inherit; padding: 0.5rem; border-radius: var(--radius-s); border: 1px solid var(--color-outline); }
/* The share popup (22 Oct 2026, Mufy): the choices open over the dimmed screen instead of under the
   button, where people didn't see them and tapped the button again. Centred on a wide screen, a
   sheet from the bottom on a phone; the box scrolls when it is taller than the screen (Larger text
   on a small phone). Its own colours and type, so the card it was opened from doesn't change them. */
.share-pop {
  width: min(520px, calc(100% - 32px)); max-width: none; height: fit-content; max-height: calc(100% - 32px);
  margin: auto; padding: 0; border: 0; border-radius: var(--radius-l); overflow: hidden;
  background: var(--color-surface); color: var(--color-text); box-shadow: var(--elevation-3);
  font: 400 var(--font-size-body)/1.5 var(--font-body); text-align: left;
}
.share-pop[open] { display: flex; flex-direction: column; }
/* (review, 22 Oct 2026) a browser without <dialog> would show it, empty, under every share button */
.share-pop:not([open]) { display: none; }
.share-pop::backdrop { background: var(--color-scrim); }
.share-pop__box { flex: 0 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 18px; }
/* (9 Nov 2026, stage 3) in the dialogs' head (.dlg__head), beside its Close icon, which spaces it from what follows */
.share-pop__title { margin: 0; font-size: var(--font-size-title); overflow-wrap: break-word; }
.share-pop .share-panel__close { display: flex; width: 100%; min-height: 56px; margin: 14px 0 0; }
.share-pop .share-panel__buttons { margin-bottom: 12px; }
/* the share's picture, small, and "Change the look" under the choices (23 Oct 2026, Mufy; "Another look" until 3 Nov 2026) */
.share-pic-box { margin: 0 0 12px; }
.share-pic { display: block; width: min(220px, 56vw, 30vh); height: auto; aspect-ratio: 1 / 1; margin: 0 auto; border-radius: var(--radius-m); border: 1px solid var(--color-outline); background: var(--color-surface); }
.share-pop .share-panel__look { display: flex; margin: 0 auto 12px; }
@media (max-width: 600px) {
  .share-pop { width: 100%; max-height: calc(100% - 24px); margin: auto 0 0; border-radius: var(--radius-l) var(--radius-l) 0 0; }
  .share-pop__box { padding: 18px 16px calc(16px + env(safe-area-inset-bottom)); }
  /* the link and the picture: one under the other, the whole width */
  .share-pop .share-panel__buttons .btn:not(.btn--small) { flex-basis: 100%; }
}
/* The share card (3 Nov 2026, Mufy): the picture to share on the screen, at a size it can be read, its buttons
   under it and "Change the look" under them (js/share.js shareCard). Its square is there from the start, so
   nothing under it moves when the picture comes; smaller on a game's result. The score screen's card waits
   while the envelope is sealed (below, with the envelope) and rises in once (the motion gate, with the milestone's). */
.share-card { margin: 1rem 0 1.25rem; }
.share-card[hidden] { display: none; }
.share-card__title { margin: 0 0 0.25rem; }
.share-card--small .share-card__title { font-size: var(--font-size-lead); }
.share-card__line { margin: 0 0 0.25rem; }
.share-card__pic-box { margin: 0.6rem 0 12px; }
.share-card__pic { display: block; width: min(300px, 100%, 40vh); height: auto; aspect-ratio: 1 / 1; margin: 0 auto; border-radius: var(--radius-l); border: 1px solid var(--color-outline); background: var(--color-surface-variant); }
.share-card--small .share-card__pic { width: min(220px, 62vw, 32vh); }
/* (review, 3 Nov 2026) the card's own button only: the popup opened from a card is inside it, and keeps its own margins */
.share-card > .share-panel__look { display: flex; margin: 10px auto 0; }
.share-card__thanks { margin: 6px 0 0; text-align: center; }
.share-card__thanks:empty { margin: 0; }

/* ---------- the app offer, the reminder card, shared pages (19 Oct 2026) ---------- */
.install-card { display: grid; gap: 0.6rem; }
.install-card__lead { margin: 0; }
.install-card .btn { margin: 0; }
.reminder-card { border-color: var(--color-primary); }
.reminder-card h2 { margin-top: 0; }   /* (9 Nov 2026, fix) as the other cards' headings: the card's own padding is the space above it */
.reminder-card .btn { margin-top: 0.5rem; }
.share__note { margin: 0.4rem 0 0; }
.result--highlight { border: 2px solid var(--color-primary); }
.try-card { margin-top: 1.5rem; display: grid; gap: 0.6rem; }
.try-card h2, .try-card p { margin: 0; }
.share-settings { margin: 1.25rem 0; }
.share-name { margin: 0.5rem 0 0.75rem; }

/* for a heading screen readers need but the design shows another way */
.sr-only { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* the short notice line (e.g. "The game is still running"), the app's one snackbar (9 Nov 2026, stage 3: Material's
   snackbar). Across the bottom with 16 px each side (at most 528 px, centred), above stage 4's bottom bar
   (--bottom-bar-height), the text at body size, an icon before it when main.js notice() gives one. It never takes a tap,
   so a button under it still works */
.notice {
  position: fixed; z-index: 50; left: 16px; right: 16px; bottom: calc(16px + env(safe-area-inset-bottom) + var(--bottom-bar-height));
  display: flex; align-items: center; gap: var(--space-3); max-width: 528px; min-height: 48px; margin: 0 auto; padding: 12px 16px;
  border-radius: var(--radius-s); background: var(--color-inverse-surface); color: var(--color-on-inverse-surface);
  font: 400 var(--font-size-body)/1.4 var(--font-body); box-shadow: var(--elevation-2); pointer-events: none;
}
.notice__text { min-width: 0; }
.notice[hidden] { display: none; }

/* sign-up consent in Europe (awarenessscore.com, 19 Oct 2026): two boxes, big enough to tap */
.consent { border: 1px solid var(--color-outline); border-radius: var(--radius-m); background: var(--color-surface); padding: 14px 16px; margin: 16px 0; }
.consent__row { display: flex; gap: 12px; align-items: flex-start; min-height: 48px; padding: 8px 0; cursor: pointer; line-height: 1.45; }
.consent__row input { width: 26px; height: 26px; flex: none; margin: 2px 0 0; accent-color: var(--color-primary); }
.consent .fine { margin: 8px 0 0; }

/* ---------- this week, milestones, the report (19 Oct 2026) ---------- */
/* this week: under the check card's button. Filled circles are the days with a check-in (the
   circles' label says which); today's has a ring. The check-ins since the first go on their own
   line under the circles, so they aren't read as this week's (19 Oct 2026). Three lines on a
   phone, two on a wide screen. The circles shrink to fit a narrow phone, with 18 px letters. */
.week {
  display: grid; grid-template-columns: 1fr; grid-template-areas: "label" "days" "total";
  align-items: center; gap: 8px 12px; margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--color-outline);
}
.week__label { grid-area: label; font-weight: 600; }
.week__days { grid-area: days; display: grid; grid-template-columns: repeat(7, minmax(0, 2.2rem)); gap: 6px; }
.week__day {
  display: grid; place-items: center; aspect-ratio: 1; border-radius: 50%;
  border: 2px solid var(--color-outline); background: var(--color-surface); color: var(--color-text-muted); font: 700 var(--font-size-body)/1 var(--font-body);
}
.week__day.is-on { background: var(--color-primary); border-color: var(--color-primary); color: var(--color-on-primary); }
.week__day.is-today { outline: 2px solid var(--color-primary-strong); outline-offset: 2px; }
.week__total { grid-area: total; justify-self: start; text-align: left; color: var(--color-text-muted); font-size: var(--font-size-body); }
@media (min-width: 600px) {
  .week { grid-template-columns: auto 1fr; grid-template-areas: "label days" "total total"; }
}
/* "Share my week", under the row from 3 days a week (20 Oct 2026) */
.share--week { margin: 10px 0 0; }
.share--week > .btn { margin: 0; }

/* ---------- groups (23 Oct 2026, Mufy; checking in together in pairs until then) ---------- */
/* home: one card a group ("4 of 6 checked in today"), the invite card for an own group with only
   the person in it, and the one-time line after joining */
.group-card h2, .group-invite h2 { margin-top: 0; }
.group-card__today { font-weight: 600; color: var(--color-primary-strong); }
.group-card .btn { margin-top: 6px; }
.group-invite { border-color: var(--color-primary); }
/* (later on 23 Oct 2026, Mufy) "Join Mufy's group?" for someone already in a group, at the top of home */
.group-ask { border-color: var(--color-primary); }
.group-invite .share { margin: 0.5rem 0 0; }
.group-invite .share > .btn { margin: 0; }
/* (23 Oct 2026, Mufy) "Invite people to Sharma family": a group the person started, with only them in it */
.group-card--new .share { margin: 0.5rem 0 0; }
.group-card--new .share > .btn { margin: 0; }
.join-line { font-weight: 600; }
/* the board: "3 of 6 have checked in today", then each member: the name, on the right whether
   they've checked in today; under the name this week's circles and the count; then Send
   Notification (or the words once sent) and, for the owner, Remove. A long name takes the line. */
.board__today { font: 700 var(--font-size-title)/1.3 var(--font-heading); color: var(--color-primary-strong); margin: 4px 0 10px; }
.board__status:focus { outline: none; }
.board .share { margin: 0.25rem 0 0.75rem; }
.board .share > .btn { margin: 0; }
.gmembers h2, .granks h2, .grecent h2, .gtools h2 { margin-bottom: 0.3em; }
.gmembers__list { list-style: none; margin: 6px 0 0; padding: 0; }
.gmember {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "name today" "feeling feeling" "week week" "remind remind" "tools tools";
  align-items: baseline; gap: 2px 12px; padding: 12px 0;
}
.gmember + .gmember { border-top: 1px solid var(--color-outline); }
.gmember.is-you { border-left: 4px solid var(--color-primary); padding-left: 10px; }
.gmember__name { grid-area: name; font-weight: 700; overflow-wrap: anywhere; }
.gmember__role { font-weight: 500; color: var(--color-text-muted); font-size: var(--font-size-caption); }
.gmember__today { grid-area: today; text-align: right; font-weight: 600; color: var(--color-text-muted); }
.gmember__today.is-done { color: var(--color-primary-strong); }
.gmember__week { grid-area: week; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; margin-top: 4px; }
.gmember__days { color: var(--color-text-muted); font-size: var(--font-size-caption); }
.gmember__remind { grid-area: remind; justify-self: start; max-width: 100%; overflow-wrap: anywhere; margin: 8px 0 0; }
/* (23 Oct 2026) "Send a message" sits in a box with its popup (share.js): the box takes the button's place */
.board .share--nudge { margin: 8px 0 0; }
.board .share--nudge > .btn { margin: 0; max-width: 100%; overflow-wrap: anywhere; }
.gmember__sent { grid-area: remind; color: var(--color-text-muted); margin-top: 6px; }
.gmember__sent:focus { outline: none; }
.gmember__remove { grid-area: tools; justify-self: start; margin: 6px 0 0; }
/* (30 Oct 2026, Mufy) Remove sits apart: 16 px below the row's other buttons, quiet, in the darker orange
   (--color-accent-text, #A8481C: 5.4:1 on the page; --color-accent-strong would be 4.3:1) */
.gmember__remove { margin-top: 16px; }
.gmember__remove.btn--quiet { color: var(--color-accent-text); border-color: var(--color-outline-strong); }   /* (9 Nov 2026, stage 3) a plain edge */
.gmember__tools { grid-area: tools; display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; }
.gmember__tools .btn { margin: 0; }
.gmember--pending .gmember__today { color: var(--color-accent-text); }
/* this week's seven circles for a member, small: filled for a day with a check-in */
.gweek { display: inline-grid; grid-template-columns: repeat(7, 1.5rem); gap: 4px; }
.gweek__day {
  display: grid; place-items: center; aspect-ratio: 1; border-radius: 50%;
  border: 2px solid var(--color-outline); background: var(--color-surface); color: var(--color-text-muted); font: 700 var(--font-size-caption)/1 var(--font-body);
}
.gweek__day.is-on { background: var(--color-primary); border-color: var(--color-primary); color: var(--color-on-primary); }
/* the group's game of the day: today or yesterday, then the rows (.ranks, as the public top 10) */
.granks .period { margin: 6px 0 4px; }
.granks .eyebrow { margin-top: 0.4em; }
.grecent__list { margin: 0 0 8px; padding-left: 1.2em; }
.grecent__list li { margin: 4px 0; }
/* Run your group: the name, approval, how many may join, a new link, handing over */
.gtools { margin-top: 10px; padding-top: 6px; border-top: 1px solid var(--color-outline); }
.gsize { margin: 6px 0 10px; }
.gsize .btn { margin: 6px 0 0; }
.ghand { display: grid; gap: 6px; margin-top: 10px; }
.ghand label { font-weight: 600; }
.select {
  font: 500 var(--font-size-body) var(--font-body); min-height: 52px; padding: 10px 14px; width: 100%;
  border: 2px solid var(--color-outline-strong); border-radius: var(--radius-m); background: var(--color-surface); color: var(--color-text);
}
.select:focus { outline: var(--focus-ring); outline-offset: 1px; border-color: var(--color-primary); }
.gleave { margin-top: 14px; }
.board__fine { margin-top: 14px; }
/* Settings > Groups: each group's name and size, Open and Leave, its two switches; then the code box */
.groups-settings { margin: 1.25rem 0; }
.groups-list { list-style: none; margin: 6px 0 0; padding: 0; }
.groups-list__row { padding: 12px 0; }
.groups-list__row + .groups-list__row { border-top: 1px solid var(--color-outline); }
.groups-list__head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 2px 12px; }
.groups-list__name { font-weight: 700; overflow-wrap: anywhere; }
.groups-list__name .fine { font-weight: 500; }
.groups-list__tools { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; }
.groups-list__tools .btn { margin: 0; }
.toggle--small { min-height: 48px; font-weight: 500; font-size: var(--font-size-body); }
.groups-join { margin-top: 10px; }
.groups-join input { text-transform: uppercase; letter-spacing: 0.08em; }
.groups-join input::placeholder { text-transform: none; letter-spacing: normal; }   /* (8 Nov 2026) the hint as written */
/* (23 Oct 2026, Mufy) "Start a new group" above the code box, or the line at 5 groups */
.groups-start, .groups-start__full { margin-top: 14px; }
/* the share popup's group question (23 Oct 2026): one row a group, the remembered one marked */
.share-panel__groups { margin: 0 0 10px; }
.share-panel__groups .btn small { font-weight: 500; color: var(--color-text-muted); }

/* a milestone, on the score screen */
.milestone-card { display: grid; gap: 0.6rem; }
.milestone-card__text { margin: 0; font-weight: 700; color: var(--color-primary-strong); }
/* (30 Oct 2026, Mufy) the app's orange spark before the words, in the heading type */
.milestone-card__text { display: flex; align-items: center; gap: 10px; font: 700 var(--font-size-lead)/1.35 var(--font-heading); }
.milestone-card__spark { flex: none; display: block; width: 28px; height: 28px; }
.milestone-card__spark svg { display: block; width: 100%; height: 100%; }
.milestone-card .share { margin: 0; }
.milestone-card .share > .btn { margin: 0; }

/* the three questions, on a check-in's page */
.result__context { margin: 0.2em 0 0.8em; }

/* ---------- home's asks and what the check-ins show (20 Oct 2026) ---------- */
/* the Google Play review card: like the reminder card */
.review-card { border-color: var(--color-primary); }
/* each answer on its own line, the average in bold under it (no colour needed) */
.insights__list { list-style: none; margin: 0 0 10px; padding: 0; }
.insights__row { display: grid; gap: 2px; padding: 10px 0; border-top: 1px solid var(--color-outline); }
.insights__label { font-weight: 600; }
.insights__value { color: var(--color-text-muted); }
.insights__value strong { font-size: var(--font-size-title); color: var(--color-primary-strong); }
.insights .fine { margin: 0; }
/* a game's best so far, under its graph on See each game */
.best { margin: 8px 0 0; font-weight: 600; }
/* personal bests under a game's points (20 Oct 2026, Mufy): "Your best: N points"; a new best in a
   soft orange box with its words (they say it, so the colour isn't needed). The confetti canvas
   covers the screen for about 1.5 s and lets every tap through. */
.pb { margin: 2px 0 8px; font-weight: 600; color: var(--color-text-muted); }
.pb--new { margin: 8px 0 4px; padding: 10px 14px; border-left: 4px solid var(--color-accent); border-radius: var(--radius-m); background: var(--color-accent-container); color: var(--color-text); }
.pb--new p { margin: 0; }
.pb__title { font: 700 var(--font-size-lead)/1.3 var(--font-heading); color: var(--color-primary-strong); }
.pb__practice { margin: 4px 0 8px; }
.confetti { position: fixed; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 30; }

/* ---------- the game of the day and its rankings (20 Oct 2026) ---------- */
.gotd-card h2 { margin-top: 0; }
.gotd-note { margin: 10px 0 12px; }
.rankings { margin: 16px 0 6px; }
/* the person's own place, read out when it comes */
.rankings__me { font-weight: 700; color: var(--color-primary-strong); background: var(--color-primary-container); border-radius: var(--radius-m); padding: 10px 14px; margin: 8px 0; }
.rankings__me[hidden] { display: none; }
.rankings__me:focus { outline: none; }
.rankings__count { margin: 6px 0; }
.rankings__other { display: block; margin: 6px 0 0; }
/* "Report a name" (fixes, 20 Oct 2026): there only while the board names someone else */
.rankings__report { display: block; margin: 0; color: var(--color-text-muted); }
.rankings__report[hidden] { display: none; }
/* one row a person: place, name (or "A player"), points. The person's own row says "You" and has a
   border, so it doesn't rely on colour. A name too long for the line takes the line, and the
   points go under it on the right (a narrow phone with Larger text) */
.ranks { list-style: none; margin: 4px 0 8px; padding: 0; }
.ranks__row { display: flex; align-items: baseline; gap: 12px; padding: 10px 8px; border-bottom: 1px solid var(--color-outline); }
.ranks__place { flex: none; min-width: 1.4em; font-weight: 700; color: var(--color-text-muted); text-align: right; font-variant-numeric: tabular-nums; }
.ranks__main { flex: 1 1 0; min-width: 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 12px; }
.ranks__name { flex: 0 1 auto; min-width: 0; font-weight: 600; overflow-wrap: anywhere; }
.ranks__pts { flex: none; margin-left: auto; font-weight: 700; color: var(--color-primary-strong); white-space: nowrap; font-variant-numeric: tabular-nums; }
.ranks__row.is-you { border: 2px solid var(--color-primary); border-radius: var(--radius-m); background: var(--color-primary-container); }
.ranks__row.is-you .ranks__name { font-weight: 800; color: var(--color-primary-strong); }
/* "Show your first name on the board?" the first time in a top 10 */
.rank-ask { border-color: var(--color-primary); }
.rank-ask h2 { margin-top: 0; }
.rank-switch { margin: 14px 0 0; }
/* (.auth-form's display: grid would otherwise beat the hidden attribute) */
.rank-switch .auth-form[hidden] { display: none; }
/* "Report a name" (20 Oct 2026): two questions as rows of radio buttons, each row 56 px tall, and a
   box; a long name takes the line. The thanks once sent, at the top of the board. Settings' names
   hidden with it. */
.name-report { display: grid; gap: 6px; margin: 8px 0; }
.name-report__q { border: 1px solid var(--color-outline); border-radius: var(--radius-m); background: var(--color-surface); padding: 6px 14px 8px; margin: 10px 0 4px; min-width: 0; }
.name-report__q legend { padding: 0 6px; margin-left: -6px; }
.choice-row { display: flex; align-items: center; gap: 12px; min-height: 56px; padding: 4px 0; cursor: pointer; overflow-wrap: anywhere; }
.choice-row + .choice-row { border-top: 1px solid var(--color-outline); }
.choice-row input { width: 26px; height: 26px; flex: none; margin: 0; accent-color: var(--color-primary); }
.choice-row input:focus-visible { outline: var(--focus-ring); outline-offset: 2px; }
.rankings__said { margin: 8px 0; }
.rankings__said:focus, .hidden-names__line:focus { outline: none; }
.hidden-names { margin: 10px 0 0; }
.hidden-names__line { margin: 0; }

/* the report */
.period { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 4px 0 6px; }
.period .btn { margin: 0; }
.report h1 { margin-top: 0.8em; }
.report__meta span { display: inline-block; margin-right: 1em; }
.report__count { font-weight: 600; }
.report-table { width: 100%; border-collapse: collapse; font-size: var(--font-size-caption); margin: 10px 0 16px; }
.report-table th, .report-table td { padding: 8px 5px; border-bottom: 1px solid var(--color-outline); text-align: left; vertical-align: top; }
.report-table th { font-weight: 700; line-height: 1.25; }
.report-table td { white-space: nowrap; }
/* on screen (a phone has no room for six columns, and a wide screen's table text was small):
   each check-in shows as two lines, the date, time and score, then the three answers with their
   names; a check-in without the questions has no answers line (19 Oct 2026). Printing always
   gets the table. */
@media screen {
  .report-table { font-size: var(--font-size-body); }
  .report-table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .report-table, .report-table tbody { display: block; }
  .report-table tr { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 12px; padding: 10px 0; border-bottom: 1px solid var(--color-outline); }
  .report-table td { padding: 0; border: 0; }
  .report-table .rt-score { order: 1; margin-left: auto; font-weight: 700; font-size: var(--font-size-lead); color: var(--color-primary-strong); }
  .report-table tr::after { content: ""; order: 2; flex-basis: 100%; }
  .report-table .rt-answer { order: 3; color: var(--color-text-muted); white-space: normal; }
  .report-table .rt-answer::before { content: attr(data-label) ": "; }
  .report-table tr.rt-none .rt-answer, .report-table tr.rt-none::after { display: none; }
}

/* ---------- notes and voice notes, the clear-headed rating, the game of the day share (23 Oct 2026, Mufy) ---------- */
/* the third question's five answers: two a row, the last across both */
.q__opts--clear > :last-child { grid-column: 1 / -1; }
/* "Clear today" on a member's row, only where they show it to this group */
.gmember__feeling { grid-area: feeling; color: var(--color-primary-strong); font-weight: 600; font-size: var(--font-size-body); }
/* the board: the one line at the top, then "Today in the group": one note a row (who, when, the words,
   Play with the length, Delete, Report); yesterday's folded away */
.gnotes__add { margin: 0 0 6px; }
.gnotes__line { padding-left: 0; }
.gnotes h2 { margin-bottom: 0.3em; }
.gnotes__list { list-style: none; margin: 6px 0 0; padding: 0; }
.gnote { padding: 10px 0; }
.gnote + .gnote { border-top: 1px solid var(--color-outline); }
.gnote.is-you { border-left: 4px solid var(--color-primary); padding-left: 10px; }
.gnote__head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.gnote__name { font-weight: 700; overflow-wrap: anywhere; }
.gnote__time { color: var(--color-text-muted); font-size: var(--font-size-caption); white-space: nowrap; }
.gnote__text { margin: 2px 0 0; overflow-wrap: anywhere; }
.gnote__tools { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; }
.gnote__tools[hidden] { display: none; }
.gnote__tools .btn { margin: 0; }
.gnote__report { margin: 6px 0 0; }
.gnotes__yday { margin-top: 10px; }
.gnotes__yday summary { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 48px; font-weight: 600; cursor: pointer; list-style: none; }
/* the form: the words, the recorder (Record, then Stop counting down; the playback, Record again,
   Remove), Post */
.note-form { margin: 6px 0 10px; }
.note-form__count { margin: 0; min-height: 1.4em; }
.note-form__groups { margin: 0 0 6px; }
.note-form__groups .btn small { font-weight: 500; color: var(--color-text-muted); }
.note-form__who { margin: 4px 0 0; }
.rec { margin: 6px 0 0; }
.rec__row { display: flex; flex-wrap: wrap; gap: 8px; }
.rec__row .btn { margin: 0; }
.rec__row .btn[hidden] { display: none; }
.rec__btn[aria-pressed="true"] { border-color: var(--color-accent-text); color: var(--color-accent-text); font-variant-numeric: tabular-nums; }
.rec__play { margin: 8px 0 0; }
.rec__play[hidden] { display: none; }
.rec__play audio { display: block; width: 100%; }
.rec__status { margin: 6px 0 0; }
/* "Leave a note for your group?", under the Awareness Score */
.note-card { border-color: var(--color-primary); }
.note-card h2 { margin-top: 0; }
.note-card .note-form { margin-bottom: 0; }
.note-card__done { margin: 6px 0; font-weight: 600; color: var(--color-primary-strong); }
.note-card__done:focus { outline: none; }
.note-card__chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 4px; }
.note-card__chips .btn { margin: 0; }
/* "Share my result" on today's rankings, under the person's own place */
.rankings__share { margin: 0 0 8px; }
.rankings__share .share { margin: 0; }
/* the report's "How you felt" column: an older check-in's "Unusual day: Yes" carries its own name */
.report-table .rt-plain::before { content: none; }

/* ---------- waves and claps, the group chain, a photo with a note (23 Oct 2026, Mufy) ---------- */
/* the board: "Today: 3 waves, 2 claps" under the top line; on your own row who waved or clapped; on the
   others' rows the two small buttons, filled while sent; the chain above "Who's in" */
.board__counts { margin: -6px 0 10px; }
.board__counts[hidden] { display: none; }
.gmember { grid-template-areas: "name today" "feeling feeling" "week week" "from from" "react react" "remind remind" "tools tools"; }
.gmember__from { grid-area: from; margin-top: 4px; color: var(--color-primary-strong); font-weight: 600; font-size: var(--font-size-body); }
.gmember__from-line { display: block; }
.gmember__react { grid-area: react; display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.gmember__react .btn { margin: 0; }
.btn--react { background: var(--color-surface); color: var(--color-primary); border-color: var(--color-outline-strong); }
.btn--react[aria-pressed="true"] { background: var(--color-primary-container); border-color: var(--color-primary); color: var(--color-primary-strong); font-weight: 700; }
.gchain { margin: 12px 0 2px; }
.gchain__line { margin: 0; font: 700 var(--font-size-lead)/1.3 var(--font-heading); color: var(--color-primary-strong); }
.gchain__best { margin: 2px 0 0; }
/* home's card: the chain's short tail under "4 of 6 checked in today" */
.group-card__chain { margin: -8px 0 8px; color: var(--color-primary-strong); font-weight: 600; }
/* "Add a photo" in the note form: the file input is kept off-screen (its label is the button; the
   keyboard reaches the input itself, and the button shows its focus), then the preview with Remove */
.photo { margin: 8px 0 0; }
.photo__row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.photo__row .btn { margin: 0; }
.photo__row .btn[hidden] { display: none; }
/* (".note-form" in front and min-height: 0, or ".auth-form input" above wins: 52 px tall, full width, 15 px past the screen; fixes, 23 Oct 2026) */
.note-form .note-form__file { position: absolute; width: 1px; height: 1px; min-height: 0; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.note-form__file:focus-visible + .note-form__photo-btn { outline: var(--focus-ring); outline-offset: 2px; }
.note-form__photo-btn.is-off { background: var(--color-disabled); color: var(--color-on-disabled); border-color: var(--color-disabled); pointer-events: none; cursor: default; }   /* (9 Nov 2026, stage 3) */
.note-form__preview { margin: 8px 0 0; }
.note-form__preview[hidden] { display: none; }
.note-form__preview-img { display: block; max-width: 160px; max-height: 160px; width: auto; height: auto; border-radius: var(--radius-s); background: var(--color-surface-variant); }
.photo__status { margin: 6px 0 0; }
/* a note's photo on the board: the thumbnail (a button) and the overlay over the dimmed page */
.gnote__photo { display: block; width: auto; max-width: 100%; margin: 6px 0 0; padding: 0; border: 0; border-radius: var(--radius-s); background: none; cursor: pointer; line-height: 0; }
.gnote__photo:focus-visible { outline: var(--focus-ring); outline-offset: 3px; }
.gnote__thumb { display: block; max-width: 100%; height: auto; border-radius: var(--radius-s); background: var(--color-surface-variant); }
.gnote__nophoto { margin: 6px 0 0; }
.photo-pop {
  position: fixed; inset: 0; width: 100%; max-width: 100%; height: 100%; max-height: 100%;
  margin: 0; padding: 0; border: 0; background: var(--color-scrim-strong); color: var(--color-on-scrim);
}
.photo-pop[open] { display: flex; flex-direction: column; }
.photo-pop:not([open]) { display: none; }
.photo-pop::backdrop { background: var(--color-scrim-strong); }
.photo-pop__pic { flex: 1 1 auto; min-height: 0; display: flex; align-items: center; justify-content: center; padding: 12px; }
.photo-pop__img { max-width: 100%; max-height: 100%; width: auto; height: auto; object-fit: contain; border-radius: var(--radius-s); }
.photo-pop__close { flex: 0 0 auto; width: calc(100% - 32px); max-width: 420px; margin: 0 auto calc(12px + env(safe-area-inset-bottom)); }

/* ---------- the note on the check-in's questions screen (23 Oct 2026, Mufy) ---------- */
/* below the three questions, all of it optional: the words, Record, Add a photo, then "Shared with:" and
   a chip per group (ticked: the tick and the picked colours; a tap unticks it), "(replaces today's note)"
   on one that has a note today; once a note has gone from here today, the one line "Change today's note" */
.qnote { margin: 22px 0 6px; padding-top: 12px; border-top: 1px solid var(--color-outline); }
.qnote__h { margin: 0; }
.qnote__with { margin: 10px 0 0; font-weight: 600; }
.qnote__chips { display: flex; flex-wrap: wrap; gap: 8px; }
.qnote__chips .btn { margin: 0; }
.qnote__chip { max-width: 100%; justify-content: flex-start; text-align: left; }
.qnote__label { min-width: 0; overflow-wrap: anywhere; }
.qnote__chip small { font-weight: 500; color: var(--color-text-muted); }
.qnote__chip[aria-pressed="false"] .qnote__tick { display: none; }
.qnote-slot > .qnote__open { padding-left: 0; margin-top: 10px; }
/* the score screen's quiet line when a group refused it (and, offline, which groups it waits for) */
.qnote-said:empty { display: none; }
.qnote-said p { margin: 8px 0; }

/* Printing: only the report (on the report screen), black on white, 12 pt; on any other screen
   the page prints as it is, without the bar and the buttons */
@media print {
  @page { margin: 15mm; }
  :root, :root.big-text { font-size: 12pt; }
  html, body { background: #fff; color: #000; }
  .bar, .no-print, .notice, .rotate-note, .share, .share-card, .btn, .boot, .confetti, .backdrop, .score-fx { display: none !important; }   /* (9 Nov 2026, stage 5) the background and the score's rays too */
  .screen { max-width: none; margin: 0; padding: 0; }
  .report, .report * { color: #000 !important; }
  .report h1 { font-size: 18pt; margin: 0 0 6pt; }
  .report p { margin: 0 0 6pt; }
  .table-wrap { overflow: visible; }
  .report-table { font-size: 10.5pt; }
  .report-table th, .report-table td { border: 1px solid #000; padding: 4pt 5pt; }
  .report-table thead { display: table-header-group; }
  .report-table tr, .report .chart { break-inside: avoid; page-break-inside: avoid; }
  .report .chart { margin: 8pt 0; }
  .report .chart__svg { max-height: 75mm; }
  .report .chart__tip { display: none; }
  .report .chart__svg polyline { stroke: #000; }
  .report .chart__svg circle { fill: #000; }
  .report .chart__svg line { stroke: #888; }
  .report .chart__svg text { fill: #000; }
}

/* (23 Oct 2026, Mufy) Run your group: one block per setting, an on/off switch for approval, "Saved" inside the block */
.gblock { padding: 12px 0; border-top: 1px solid var(--color-outline); }
.gtools > h2 + .gblock { border-top: 0; padding-top: 4px; }
.gblock h3 { margin: 0 0 6px; font-size: var(--font-size-lead); }
.gblock .share-name { margin: 0; }
.gblock .gsize { margin: 4px 0 0; }
.gblock__saved { color: var(--color-primary); font-weight: 600; margin: 8px 0 0; }
.glink { word-break: break-all; margin: 0 0 8px; }
.switch-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 56px; font-weight: 600; cursor: pointer; }
/* (9 Nov 2026, stage 3) Material's switch: off, a hollow track with a small dark knob; on, the teal track with a large white
   knob holding a tick, so the state never rests on colour alone. 52 x 32 px; the row around it is the target. The knob's
   centre is 14 px from the track's inner left edge when off and 34 px when on, pressed or not; the tick sits on the on
   knob's centre */
.switch {
  appearance: none; -webkit-appearance: none; flex: none; position: relative; width: 52px; height: 32px; margin: 0;
  border: 2px solid var(--color-outline-strong); border-radius: var(--radius-full); background: var(--color-surface-variant);
  cursor: pointer; transition: background-color 0.15s, border-color 0.15s;
}
.switch::after {
  content: ""; position: absolute; top: 50%; left: 6px; width: 16px; height: 16px; margin-top: -8px; border-radius: 50%;
  background: var(--color-text-muted); transition: left 0.15s, width 0.15s, height 0.15s, margin 0.15s, background-color 0.15s;
}
.switch:checked { background: var(--color-primary); border-color: var(--color-primary); }
.switch:checked::after { left: 22px; width: 24px; height: 24px; margin-top: -12px; background: var(--color-switch-knob); }
.switch:checked::before {
  content: ""; position: absolute; z-index: 1; top: 50%; left: 26px; width: 16px; height: 16px; margin-top: -8px;
  background: var(--color-primary);
  -webkit-mask: var(--mask-check) center / contain no-repeat; mask: var(--mask-check) center / contain no-repeat;
}
.switch:active::after { left: 0; width: 28px; height: 28px; margin-top: -14px; }
.switch:checked:active::after { left: 20px; }
.switch:focus-visible { outline: var(--focus-ring); outline-offset: 2px; }
.switch[data-net-off], .switch:disabled { opacity: 0.6; cursor: default; }
/* (30 Oct 2026) the switch's knob doesn't slide with reduced motion or Less motion */
@media (prefers-reduced-motion: reduce) {
  .switch, .switch::after { transition: none; }
}
:where(html.less-motion) .switch, :where(html.less-motion) .switch::after { transition: none; }

/* ---------- a person's picture (23 Oct 2026, Mufy) ---------- */
/* By every member's name on a group's board (never the public top 10): their picture, else their initial
   on the teal circle as the account button has it (no first name: the plain circle), about 32 px and
   centred on the name's line. A picture is a button that opens it larger, its tap area reaching 8 px
   past the circle (48 px); while it loads, or if it didn't, it is only the circle. Settings > Your
   picture shows the person's own at 72 px. */
.avatar {
  position: relative; flex: none; display: inline-grid; place-items: center; width: 32px; height: 32px; margin: 0; padding: 0;
  border: 0; border-radius: 50%; background: var(--color-primary-strong); color: var(--color-on-primary); font: 700 0.85rem/1 var(--font-body); vertical-align: middle;
}
.avatar__pic { position: absolute; inset: 0; width: 100%; height: 100%; border-radius: 50%; object-fit: cover; opacity: 0; }
.avatar.has-pic .avatar__pic { opacity: 1; }
.avatar.has-pic .avatar__initial { visibility: hidden; }
.avatar--big { width: 72px; height: 72px; font-size: 1.9rem; }
.avatar--btn { cursor: pointer; -webkit-tap-highlight-color: transparent; }
.avatar--btn::before { content: ""; position: absolute; inset: -8px; border-radius: 50%; }
.avatar--btn:not(.has-pic) { pointer-events: none; cursor: default; }
.avatar--btn:focus-visible { outline: var(--focus-ring); outline-offset: 2px; }
/* the board: Who's in (with the owner's approval list), the group's game of the day, Recently, Today in the group */
.gmember__who { grid-area: name; display: flex; align-items: center; gap: 10px; min-width: 0; }
.gmember__who .gmember__name { min-width: 0; }
.gmember__today { align-self: center; }
.granks .ranks__row { align-items: center; }
.grecent__list { list-style: none; padding-left: 0; }
.grecent__list li.grecent__row { display: flex; align-items: center; gap: 10px; margin: 6px 0; }
.gnote__head { align-items: center; }
.gnote__who { display: flex; align-items: center; gap: 10px; min-width: 0; }
/* a member's picture opened from the board: large over the dimmed board, then the white panel with the
   first name, Report this picture (someone else's) and its reasons, and Close; it scrolls on a short screen */
.pic-pop[open] { display: block; overflow-y: auto; overscroll-behavior: contain; }
.pic-pop__box {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; min-height: 100%;
  padding: calc(16px + env(safe-area-inset-top)) 16px calc(16px + env(safe-area-inset-bottom));
}
.pic-pop__pic { display: flex; justify-content: center; }
.pic-pop__img { display: block; width: min(84vw, 400px, 50vh); height: min(84vw, 400px, 50vh); object-fit: cover; border-radius: var(--radius-m); background: var(--color-primary-strong); }
.pic-pop__panel { width: 100%; max-width: 420px; padding: 12px 16px 14px; border-radius: var(--radius-l); background: var(--color-surface); color: var(--color-text); }
.pic-pop__name { margin: 0; font: 700 var(--font-size-title)/1.3 var(--font-heading); color: var(--color-primary-strong); overflow-wrap: anywhere; }
.pic-pop__line { margin: 4px 0 0; }
.pic-pop__report-btn { margin: 10px 0 0; }
.pic-pop__report-btn[hidden] { display: none; }
.pic-pop__report { margin: 4px 0 0; }
.pic-pop__close { margin-top: 12px; }
/* Settings > Your picture: the person's own, large, and what it is; the buttons (file inputs kept
   off-screen, their labels the buttons, as the status form's photo); the switch */
.picture-settings { margin: 1.25rem 0; }
.picture-settings__head { display: flex; align-items: center; gap: 14px; margin: 6px 0 4px; }
.picture-settings__what { margin: 0; min-width: 0; }
.picture-settings__row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.picture-settings__row .btn { margin: 0; }
.picture-settings__file { position: absolute; width: 1px; height: 1px; min-height: 0; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.picture-settings__file:focus-visible + .picture-settings__btn { outline: var(--focus-ring); outline-offset: 2px; }
/* (9 Nov 2026, stage 3) the disabled look of every button, readable */
.picture-settings__file:disabled + .picture-settings__btn, .picture-settings__btn.is-off { background: var(--color-disabled); color: var(--color-on-disabled); border-color: var(--color-disabled); pointer-events: none; cursor: default; }
.picture-settings__said { margin: 8px 0 0; }
.picture-settings__said:focus { outline: none; }
.picture-settings__switch { margin-top: 4px; }
.picture-settings__switch .fine { margin: 0; }
.picture-settings__blocked { margin: 8px 0 0; }

/* ---------- blocking and reporting in groups, the update card (24 Oct 2026, Mufy) ---------- */
/* Every member's name and picture on a board opens their sheet: the name is a button that looks like the
   name it was, with a soft underline so it reads as one; the picture (or the initial) opens it too, with
   or without a picture (the name is the button a keyboard and a screen reader use) */
.avatar--btn.avatar--always:not(.has-pic) { pointer-events: auto; cursor: pointer; }
.member-link {
  position: relative; display: inline; max-width: 100%; margin: 0; padding: 2px 0; border: 0; border-radius: var(--radius-s); background: none;
  color: inherit; font-family: inherit; font-size: inherit; font-style: inherit; line-height: inherit; text-align: left; cursor: pointer;
  overflow-wrap: anywhere; -webkit-tap-highlight-color: transparent;
  text-decoration: underline; text-decoration-color: var(--color-link-underline); text-decoration-thickness: 2px; text-underline-offset: 4px;
}
/* (8 Nov 2026, stage 1) the name's tap area reaches 48 px without moving the line */
.member-link::before { content: ""; position: absolute; left: 50%; top: 50%; width: max(100%, 48px); height: max(100%, 48px); transform: translate(-50%, -50%); }
/* (the weight is the name's own: .gnote__name and .ranks__name set theirs; these take their line's) */
.gmember__link, .grecent__name { font-weight: inherit; }
.member-link:focus-visible { outline: var(--focus-ring); outline-offset: 2px; }
/* Who's in: "Blocked" in fine text under a blocked member's name; "More" after Wave and Clap */
.gmember__blocked { display: block; font-weight: 500; }
.gmember__more { min-width: 0; }
/* the sheet: the initial, large, when there is no picture; Report and Block under the name, one a line;
   what the sheet says (the thanks, a refusal) above Close. (9 Nov 2026, Mufy: the redesign's stage 5) the initial's ring is --color-on-scrim:
   white on the dimmed photo view in both themes */
.pic-pop__initial {
  display: grid; place-items: center; width: 128px; height: 128px; border: 3px solid var(--color-on-scrim); border-radius: 50%;
  background: var(--color-primary-strong); color: var(--color-on-primary); font: 700 3.2rem/1 var(--font-body);
}
.pic-pop__acts { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; margin-top: 10px; }
.pic-pop__acts[hidden] { display: none; }
.pic-pop__acts .btn { margin: 0; max-width: 100%; overflow-wrap: anywhere; text-align: left; }
.pic-pop__acts .btn[hidden] { display: none; }
.pic-pop__said { margin: 10px 0 0; font-weight: 600; color: var(--color-primary-strong); }
.pic-pop__said[hidden] { display: none; }
.pic-pop__said:focus { outline: none; }
/* "Report this group": a quiet link at the very bottom of the board, its two reasons in its place */
.greport { margin-top: 6px; }
.greport__open { padding-left: 0; color: var(--color-text-muted); }
.greport__said { margin: 10px 0 0; }
.greport__said:focus { outline: none; }
/* Settings > Groups: the line while the account's group features are suspended; Blocked people */
.groups-suspended { margin: 10px 0; }
.blocked-people { margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--color-outline); }
.blocked-people h3 { margin: 0 0 4px; }
.blocked-list { list-style: none; margin: 6px 0 0; padding: 0; }
.blocked-list__row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 6px 12px; padding: 8px 0; }
.blocked-list__row + .blocked-list__row { border-top: 1px solid var(--color-outline); }
.blocked-list__name { font-weight: 700; min-width: 0; overflow-wrap: anywhere; }
.blocked-list__row .btn { margin: 0; }
/* the Google Play app's update card: like the review card */
.update-card { border-color: var(--color-primary); }
.update-card h2 { margin-top: 0; }

/* ---------- the app builder's block (24 Oct 2026, Mufy): the run, the named game of the day, records,
   today's facts under the score, "From your groups" with likes, the family's check-in news ---------- */
/* home's week: the run's line under the circles ("12 days running"), the longest run in the small print
   under the count; a rest day's circle has a soft dashed ring (its words are in the row's label) */
.week__run { grid-area: run; justify-self: start; font-weight: 700; color: var(--color-primary-strong); }
.week__best { grid-area: best; justify-self: start; color: var(--color-text-muted); font-size: var(--font-size-body); }
.week.week--run { grid-template-areas: "label" "days" "run" "total"; }
.week.week--best { grid-template-areas: "label" "days" "total" "best"; }
.week.week--run.week--best { grid-template-areas: "label" "days" "run" "total" "best"; }
@media (min-width: 600px) {
  .week.week--run { grid-template-areas: "label days" "run run" "total total"; }
  .week.week--best { grid-template-areas: "label days" "total total" "best best"; }
  .week.week--run.week--best { grid-template-areas: "label days" "run run" "total total" "best best"; }
}
.week__day.is-rest { border-style: dashed; border-color: var(--color-primary); background: var(--color-primary-container); color: var(--color-primary-strong); }
/* the check-in card: "Welcome back." after a run has ended; the day's game of the day (7 Nov 2026, Mufy: the line naming
   the day's look went, and its rule with it) */
.welcome-back { margin: 0 0 0.4em; font: 700 var(--font-size-lead)/1.3 var(--font-heading); color: var(--color-primary-strong); }
.check-day { margin: -0.4em 0 0.8em; color: var(--color-text-muted); }
/* a record in the game's own units, under its points */
.record { margin: 4px 0 6px; font-weight: 700; color: var(--color-primary-strong); }
/* home's "From your groups": the statuses as on a board, a heart to like them, "More" with Don't show and
   Report; who liked your own; "See all" under them */
.home-statuses h2 { margin-top: 0; }
.home-statuses h2:focus { outline: none; }
.hstatus__list { margin-top: 2px; }
.hstatus__in { margin: 0; }
.hstatus__group { padding: 6px 0; min-height: 48px; text-align: left; overflow-wrap: anywhere; }
.hstatus__menu { margin-top: 8px; }
.hstatus__all { margin-top: 8px; }
.gnote__liked { margin: 6px 0 0; color: var(--color-primary-strong); font-weight: 600; }
.btn--like__heart { margin-right: 0; }   /* (9 Nov 2026, stage 3) the button's gap spaces it */
.btn--like[aria-pressed="true"] .btn--like__heart { color: var(--color-accent-strong); }
/* a group's board: tomorrow's game of the day (7 Nov 2026, Mufy: no line naming the day's look) */
.granks__next { margin: 4px 0 0; }
/* a notification about the person who runs the group opens the board at their Clap or Send Notification */
.btn.is-ready { box-shadow: 0 0 0 4px var(--color-accent-container); border-color: var(--color-accent-strong); }

/* ---------- the day's look, the cues and "See what fooled you" (24 Oct 2026, Mufy; games builder) ----------
   Grid Span: each day's scene (grid.js puts data-scene on the board; engine.js dayLook). The board,
   the blocks' size, the rules for their places and the timings are as before; only the picture
   changes. Each block is still the same square to tap: the thing drawn on it (a stone, a window ...)
   is its ::before, which never takes a tap, so a rounded shape doesn't shrink what can be tapped.
   Every scene draws its things dark on a mid-tone ground and lights them the same warm yellow with the
   same glow, so a lit block stands out as clearly on every day. Measured in headless Chrome (contrast
   ratios of mean colours): lit against unlit 4.7:1 to 7.7:1 (the plain look's orange against its teal:
   2.35:1), unlit against the ground at least 3.1:1, and lit against the ground 1.4:1 to 1.8:1, lighter,
   with the glow round it. No scene has a landmark of its own: the grounds are fine, even textures. */
.gs-board[data-scene] { --gs-lit: radial-gradient(circle at 42% 38%, #FFF8D8 0%, #FFE27A 40%, #FFC83A 100%); --gs-glow: 0 0 0 5px rgba(255, 214, 90, 0.6), 0 0 22px 6px rgba(255, 190, 40, 0.65); }
.gs-board[data-scene] .gs-block, .gs-board[data-scene] .gs-block.is-lit { background: transparent; box-shadow: none; }
.gs-board[data-scene] .gs-block::before { content: ""; position: absolute; inset: 0; pointer-events: none; transition: background 0.08s linear, box-shadow 0.08s linear; }
.gs-board[data-scene] .gs-block.is-lit::before { background: var(--gs-lit); box-shadow: var(--gs-glow); }
/* stones on a lawn */
.gs-board[data-scene="stones"], .twq-card__back[data-scene="stones"] {
  background-color: #A9CE88;
  background-image: repeating-linear-gradient(118deg, rgba(70, 120, 45, 0.13) 0 2px, rgba(70, 120, 45, 0) 2px 10px),
    radial-gradient(130% 100% at 30% 15%, #BCDDA0 0%, #A9CE88 55%, #98C276 100%);
}
.gs-board[data-scene="stones"] .gs-block::before { border-radius: 47% 53% 42% 58% / 56% 44% 56% 44%; background: radial-gradient(circle at 36% 30%, #6E7682 0%, #535A66 50%, #3F4551 100%); box-shadow: 0 4px 0 rgba(52, 86, 38, 0.35); }
/* windows in a house: a pale wall, dark panes; a lit window keeps its frame and bars */
.gs-board[data-scene="windows"], .twq-card__back[data-scene="windows"] {
  background-color: #CDB38C;
  background-image: radial-gradient(rgba(110, 70, 30, 0.12) 1px, rgba(110, 70, 30, 0) 1.6px), linear-gradient(180deg, #D4BB94 0%, #C6AA81 100%);
  background-size: 9px 9px, auto;
}
.gs-board[data-scene="windows"] .gs-block::before {
  border-radius: 8px;
  background: linear-gradient(90deg, rgba(0, 0, 0, 0) calc(50% - 2px), #F6EEDF calc(50% - 2px), #F6EEDF calc(50% + 2px), rgba(0, 0, 0, 0) calc(50% + 2px)),
    linear-gradient(0deg, rgba(0, 0, 0, 0) calc(50% - 2px), #F6EEDF calc(50% - 2px), #F6EEDF calc(50% + 2px), rgba(0, 0, 0, 0) calc(50% + 2px)),
    linear-gradient(160deg, #34446A 0%, #222D4A 100%);
  box-shadow: inset 0 0 0 4px #F6EEDF, 0 3px 0 rgba(100, 70, 35, 0.3);
}
.gs-board[data-scene="windows"] .gs-block.is-lit::before {
  background: linear-gradient(90deg, rgba(0, 0, 0, 0) calc(50% - 2px), #F6EEDF calc(50% - 2px), #F6EEDF calc(50% + 2px), rgba(0, 0, 0, 0) calc(50% + 2px)),
    linear-gradient(0deg, rgba(0, 0, 0, 0) calc(50% - 2px), #F6EEDF calc(50% - 2px), #F6EEDF calc(50% + 2px), rgba(0, 0, 0, 0) calc(50% + 2px)), var(--gs-lit);
  box-shadow: inset 0 0 0 4px #F6EEDF, var(--gs-glow);
}
/* lily pads on a pond: each pad has its notch (the water's colour) */
.gs-board[data-scene="pads"], .twq-card__back[data-scene="pads"] {
  --water: #8DC5DB;
  background-color: var(--water);
  background-image: repeating-linear-gradient(172deg, rgba(255, 255, 255, 0.13) 0 2px, rgba(255, 255, 255, 0) 2px 15px),
    radial-gradient(130% 100% at 30% 15%, #A2D2E4 0%, #8DC5DB 55%, #7CB8D0 100%);
}
.gs-board[data-scene="pads"] .gs-block::before { border-radius: 50%; background: conic-gradient(from 130deg, var(--water) 0deg 26deg, rgba(0, 0, 0, 0) 26deg), radial-gradient(circle at 40% 35%, #356F3E 0%, #285E33 55%, #1F4D29 100%); }
.gs-board[data-scene="pads"] .gs-block.is-lit::before { background: conic-gradient(from 130deg, var(--water) 0deg 26deg, rgba(0, 0, 0, 0) 26deg), var(--gs-lit); }
/* rock pools on the sand: a rim of rock round each pool */
.gs-board[data-scene="pools"], .twq-card__back[data-scene="pools"] {
  background-color: #D9C08E;
  background-image: radial-gradient(rgba(140, 100, 50, 0.22) 1px, rgba(140, 100, 50, 0) 1.7px), radial-gradient(rgba(255, 255, 255, 0.3) 1px, rgba(255, 255, 255, 0) 1.7px),
    linear-gradient(180deg, #DEC799 0%, #D2B783 100%);
  background-size: 11px 11px, 13px 13px, auto;
  background-position: 0 0, 5px 7px, 0 0;
}
.gs-board[data-scene="pools"] .gs-block::before { border-radius: 52% 48% 55% 45% / 47% 55% 45% 53%; background: radial-gradient(circle at 45% 40%, #3F7E98 0%, #255B71 55%, #1C4657 100%); box-shadow: inset 0 0 0 5px #8C8577, 0 3px 0 rgba(120, 90, 50, 0.3); }
.gs-board[data-scene="pools"] .gs-block.is-lit::before { box-shadow: inset 0 0 0 5px #8C8577, var(--gs-glow); }
/* lanterns in the snow at dusk: dark caps top and bottom stay when a lantern lights */
.gs-board[data-scene="lanterns"], .twq-card__back[data-scene="lanterns"] {
  background-color: #B4C3D3;
  background-image: radial-gradient(rgba(255, 255, 255, 0.75) 1.2px, rgba(255, 255, 255, 0) 1.8px), linear-gradient(180deg, #AEBED0 0%, #C2CFDC 100%);
  background-size: 14px 14px, auto;
}
.gs-board[data-scene="lanterns"] .gs-block::before {
  border-radius: 34% 34% 40% 40% / 30% 30% 44% 44%;
  background: linear-gradient(180deg, #2A2232 0 13%, rgba(0, 0, 0, 0) 13% 87%, #2A2232 87%), repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.07) 0 3px, rgba(255, 255, 255, 0) 3px 12px), #503D5C;
  box-shadow: 0 3px 0 rgba(60, 80, 110, 0.25);
}
.gs-board[data-scene="lanterns"] .gs-block.is-lit::before { background: linear-gradient(180deg, #2A2232 0 13%, rgba(0, 0, 0, 0) 13% 87%, #2A2232 87%), var(--gs-lit); }
/* apples on a tree: a stem on each */
.gs-board[data-scene="apples"], .twq-card__back[data-scene="apples"] {
  background-color: #A0CB80;
  background-image: radial-gradient(7px 3.5px at 50% 50%, rgba(52, 100, 40, 0.13) 0 99%, rgba(52, 100, 40, 0) 100%),
    radial-gradient(5px 2.5px at 50% 50%, rgba(255, 255, 255, 0.14) 0 99%, rgba(255, 255, 255, 0) 100%),
    radial-gradient(130% 100% at 30% 15%, #B4D898 0%, #A0CB80 55%, #8EBE6E 100%);
  background-size: 17px 14px, 23px 19px, auto;
  background-position: 0 0, 9px 5px, 0 0;
}
.gs-board[data-scene="apples"] .gs-block::before { border-radius: 50% 50% 46% 46% / 54% 54% 46% 46%; background: radial-gradient(circle at 34% 30%, #BC4247 0%, #8F2429 50%, #6C171C 100%); box-shadow: 0 3px 0 rgba(50, 80, 35, 0.3); }
.gs-board[data-scene="apples"] .gs-block::after { content: ""; position: absolute; left: 47%; top: -5%; width: 7%; height: 16%; border-radius: 3px; background: #5A3A1F; transform: rotate(10deg); pointer-events: none; }
/* leaves on a path: a pale vein along each leaf */
.gs-board[data-scene="leaves"], .twq-card__back[data-scene="leaves"] {
  background-color: #C3B8A4;
  background-image: radial-gradient(rgba(100, 85, 65, 0.24) 1px, rgba(100, 85, 65, 0) 1.8px), radial-gradient(rgba(255, 255, 255, 0.32) 1px, rgba(255, 255, 255, 0) 1.8px),
    linear-gradient(180deg, #CABFAC 0%, #BBAF99 100%);
  background-size: 10px 10px, 12px 12px, auto;
  background-position: 0 0, 4px 6px, 0 0;
}
.gs-board[data-scene="leaves"] .gs-block::before {
  border-radius: 8% 78% 8% 78%;
  background: linear-gradient(45deg, rgba(0, 0, 0, 0) calc(50% - 1.5px), rgba(255, 236, 214, 0.55) calc(50% - 1.5px), rgba(255, 236, 214, 0.55) calc(50% + 1.5px), rgba(0, 0, 0, 0) calc(50% + 1.5px)),
    radial-gradient(circle at 40% 40%, #9A4A22 0%, #7A3518 60%, #5E2810 100%);
  box-shadow: 0 3px 0 rgba(90, 75, 55, 0.3);
}
.gs-board[data-scene="leaves"] .gs-block.is-lit::before { background: linear-gradient(45deg, rgba(0, 0, 0, 0) calc(50% - 1.5px), rgba(160, 110, 40, 0.45) calc(50% - 1.5px), rgba(160, 110, 40, 0.45) calc(50% + 1.5px), rgba(0, 0, 0, 0) calc(50% + 1.5px)), var(--gs-lit); }
@media (prefers-reduced-motion: reduce) {
  .gs-board[data-scene] .gs-block::before { transition: none; }
}
:where(html.less-motion) .gs-board[data-scene] .gs-block::before { transition: none; }   /* (30 Oct 2026, Less motion) */
/* Reaction: the day's cue in place of the counter (reaction.js), drawn in a square twice the numbers'
   size centred where the numbers are (the drawing itself is about as big as "250"); the counter keeps
   its height, so the line under it doesn't move */
.rt-counter--cue { position: relative; }
.rt-cue { position: absolute; left: 50%; top: 50%; width: 2em; height: 2em; transform: translate(-50%, -50%); pointer-events: none; }
.rt-cue svg { display: block; width: 100%; height: 100%; }
/* "See what fooled you" after the picture questions (memory.js fooledYou): each look-alike next to the
   picture first seen, on white, with what changed under them */
.fooled { margin: 14px 0 0; }
.fooled__panel { margin-top: 10px; }
.fooled__panel[hidden] { display: none; }
.fooled__intro, .fooled__none { margin: 6px 0 4px; }
.fooled__pair { background: var(--color-surface); border: 1px solid var(--color-outline); border-radius: var(--radius-m); padding: 12px; margin: 10px 0; }
.fooled__pics { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.fooled__fig { margin: 0; text-align: center; }
.fooled__pic { display: block; width: 100%; aspect-ratio: 1; object-fit: contain; background: var(--color-picture-ground); border: 1px solid var(--color-outline); border-radius: var(--radius-m); }
.fooled__fig figcaption { margin-top: 4px; color: var(--color-text-muted); font-size: var(--font-size-caption); }
.fooled__line { margin: 10px 0 0; font-weight: 600; }

/* ---------- how the app feels (30 Oct 2026, Mufy: "make all the changes to improve how the user feels") ----------
   Calm and short: 120 to 320 ms (the chart's ring 400 ms; a screen change 1 s, Mufy's choice on 9 Nov 2026), transform and
   opacity only (and the tick's stroke),
   nothing loops, bounces or flashes. Every animation here is opt-in: it runs only when the phone doesn't ask
   for reduced motion and Settings' "Less motion" is off (main.js puts less-motion on <html>; engine.js
   lessMotion answers the same for the ones made in script). Otherwise everything is in its end state at once. */

/* Settings: the line under "Less motion" */
.toggle__line { margin: -10px 0 8px 40px; }

/* (9 Nov 2026, Mufy: "use one second slide everywhere"; the redesign's stage 4) a screen change (main.js show, when the bar's title
   changes; the quiz's screens too, plans.js quizShow): the new screen comes in 30 px from the right as it fades in, from the left
   after Back (is-back), while a still copy of the old one (.screen-exit, fixed where it was, inert, hidden from screen readers)
   moves 30 px the other way as it fades out, both over 1 s. Only with motion; #app clips sideways meanwhile (is-moving), so the
   page never scrolls sideways. The copy's layer is under the bottom bar (z 4) and the top bar (z 5), over the new screen, which
   still takes every tap (the copy takes none). Its own animations are off, so nothing in it starts again. It was a 200 ms fade
   with an 8 px rise */
@keyframes screen-from-right { from { opacity: 0; transform: translateX(var(--screen-shift)); } }
@keyframes screen-from-left { from { opacity: 0; transform: translateX(calc(-1 * var(--screen-shift))); } }
@keyframes screen-to-left { to { opacity: 0; transform: translateX(calc(-1 * var(--screen-shift))); } }
@keyframes screen-to-right { to { opacity: 0; transform: translateX(var(--screen-shift)); } }
#app.is-moving { overflow-x: clip; }
.screen-exit { position: fixed; inset: 0; z-index: 3; overflow: hidden; pointer-events: none; }
.screen-exit__page { position: absolute; margin: 0; max-width: none; pointer-events: none; }
.screen-exit__page *, .screen-exit__page *::before, .screen-exit__page *::after { animation: none !important; transition: none !important; }
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .screen.is-entering { animation: screen-from-right var(--duration-screen) var(--ease-out) backwards; will-change: transform, opacity; }
  html:not(.less-motion) .screen.is-entering.is-back { animation-name: screen-from-left; }
  html:not(.less-motion) .screen-exit .screen-exit__page { animation: screen-to-left var(--duration-screen) var(--ease-out) forwards; will-change: transform, opacity; }
  html:not(.less-motion) .screen-exit.is-back .screen-exit__page { animation-name: screen-to-right; }
}

/* the short notice line at the bottom comes up gently (no exit animation) */
@keyframes notice-in { from { opacity: 0; transform: translateY(8px); } }   /* (9 Nov 2026, stage 3) no centring transform now */
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .notice { animation: notice-in var(--duration-fast) var(--ease-out) backwards; }
}

/* a circle filling in (main.js dayCircles and stepDots): today's on home and on the check-in's last screen, and
   the dot of a game just done. The circle shows empty, then a disc with its letter grows from the middle over
   it (320 ms); the disc stays, the same as a filled circle. */
.week__day, .steps__dot { position: relative; }
@keyframes day-fill { from { opacity: 0; transform: scale(0.4); } }
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .week__day.is-on.is-new { background: var(--color-surface); border-color: var(--color-outline); color: var(--color-text-muted); }
  html:not(.less-motion) .steps__dot.is-on.is-new { background: var(--color-surface); }
  html:not(.less-motion) .week__day.is-new::before, html:not(.less-motion) .steps__dot.is-new::before {
    content: attr(data-l); position: absolute; inset: -2px; display: grid; place-items: center; border-radius: 50%;
    background: var(--color-primary); color: var(--color-on-primary); animation: day-fill var(--duration-slow) var(--ease-out) var(--fill-at, 200ms) backwards;
  }
}

/* "Today's check-in is done." on the check-in's last screen (main.js doneBand): the teal circle grows in (0 to
   240 ms), its tick draws (160 to 480 ms), the words rise in (200 to 400 ms), today's circle fills (360 to 680 ms) */
.done-band { --fill-at: 360ms; display: grid; grid-template-columns: minmax(0, 1fr); justify-items: center; gap: 6px; margin: 4px 0 16px; padding: 18px 14px 16px; border-radius: var(--radius-l); background: var(--color-primary-container); text-align: center; }
.done-band__mark { display: block; width: 56px; height: 56px; }
.done-band__mark svg { display: block; width: 100%; height: 100%; }
.done-band__title { margin: 4px 0 0; font: 700 var(--font-size-title)/1.3 var(--font-heading); color: var(--color-primary-strong); }
.done-band__line { margin: 0; font-weight: 600; color: var(--color-primary-strong); }
.week__days.week--mini { grid-area: auto; grid-template-columns: repeat(7, minmax(0, 1.9rem)); gap: 5px; justify-content: center; width: 100%; margin-top: 6px; }
.week--mini .week__day { font-size: var(--font-size-caption); }
.done-band__tick { stroke-dasharray: 100; stroke-dashoffset: 0; }
@keyframes done-grow { from { opacity: 0; transform: scale(0.6); } }
@keyframes done-tick { from { stroke-dashoffset: 100; } }
@keyframes done-rise { from { opacity: 0; transform: translateY(6px); } }
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .done-band.is-new .done-band__mark { animation: done-grow 240ms var(--ease-out) backwards; }
  html:not(.less-motion) .done-band.is-new .done-band__tick { animation: done-tick var(--duration-slow) var(--ease-out) 160ms backwards; }
  html:not(.less-motion) .done-band.is-new .done-band__title, html:not(.less-motion) .done-band.is-new .done-band__line { animation: done-rise var(--duration-base) var(--ease-out) var(--duration-base) backwards; }
  /* (31 Oct 2026, Mufy's B) "See my Awareness Score" ("See my Awareness Score and Personality" with a personality to show,
     7 Nov 2026) rises in after the band (200 ms, 6 px, from 700 ms; main.js gameResult);
     the button is in the DOM and takes focus and taps from the first frame */
  html:not(.less-motion) .result__next.is-new { animation: done-rise var(--duration-base) var(--ease-out) 700ms backwards; }
}

/* between games (main.js stepDots, gameIntro): the four games' dots above "2 more to go"; "Game 2 of 4" above a
   game's heading; the tick by "Pictures done" */
.left-line .steps { display: flex; gap: 8px; margin: 2px 0 8px; }
.left-line__text { display: block; }
.left-line--steps { background: none; padding: 0 0 2px; }
.left-line--steps .steps { margin: 0; }
.steps__dot { display: block; width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--color-primary); background: var(--color-surface); }
.steps__dot.is-on { background: var(--color-primary); }
.steps__dot.is-half { border-style: dashed; background: var(--color-primary-container); }
.intro__step { margin-bottom: 0; }
.intro__step + h1 { margin-top: 0.1em; }
.h--tick { display: flex; align-items: center; gap: 10px; }
.h-tick { flex: none; display: block; width: 1.4em; height: 1.4em; }
.h-tick svg { display: block; width: 100%; height: 100%; }

/* Wave, Clap and Like: the hand or heart pops once when tapped (posts.js tapPop) */
/* (9 Nov 2026, stage 3) no margins: the button's gap spaces them; the heart is js/icons.js's iconPair, two icons in one grid cell */
.btn--react__icon { display: inline-block; margin-right: 0; }
.btn--like__heart { display: inline-grid; }
.btn--react[aria-busy="true"] { cursor: progress; }
@keyframes react-pop { 50% { transform: scale(1.18); } }
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .is-pop { animation: react-pop 240ms var(--ease-out); }
}
/* "✓ Your status is posted." at the top of the board's notes, until the board is left (posts.js) */
.gnotes__said { margin: 0 0 2px; font-weight: 600; color: var(--color-primary-strong); }
.gnotes__said:focus { outline: none; }

/* the score screen: "Today's games" over the four rows; the newest point's ring (graph.js markLast) */
.results__h { margin-bottom: 0; }
.chart__ping { opacity: 0; pointer-events: none; transform-box: fill-box; transform-origin: center; }
@keyframes chart-ping { from { opacity: 0.6; transform: scale(0.6); } to { opacity: 0; transform: scale(1.4); } }
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .chart__ping { animation: chart-ping 400ms var(--ease-out); }
}
/* a milestone rises in, a moment after the score screen (main.js milestoneCard) */
@keyframes card-rise { from { opacity: 0; transform: translateY(6px); } }
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .milestone-card.is-new { animation: card-rise 240ms var(--ease-out) var(--duration-fast) backwards; }
  /* (3 Nov 2026, Mufy) the score screen's share card, once, as the envelope's ceremony ends (main.js scoreScreen) */
  html:not(.less-motion) .share-card.is-new { animation: card-rise var(--duration-slow) var(--ease-out) backwards; }
}

/* ---------- The Awareness Score's envelope, "Open when ready" (31 Oct 2026, Mufy; bigger and centred, 7 Nov 2026) ----------
   (7 Nov 2026, Mufy: "make the reveal envelope take up more space and look better and move it below to the center of the
   screen so the user does not miss it") A sealed paper letter, about two thirds of the phone's width (--env-w: 64vw, at most
   280 px), in the middle of the screen: while sealed the hero is a stage from its own top to the bottom of the visible
   screen (--env-stage, measured by main.js envStage at the drawing and again when the screen's size changes), with the
   envelope and its words in the middle of it; the app bar and the date stay at the top and everything under the hero waits
   below the fold. The picture is three layers (main.js ENV_ART): the halo and the shadow, the lined inside with the inside
   flap, and the front: the pocket's flaps, the closed flap with its teal edge and the orange wax seal with a white "?". The
   points and the number pass between the inside and the front, so they drop into the pocket and the number comes up out
   of it. The words under it: "Tap to Open" over "and View your Awareness Score and Personality" (or "and View your Score"
   when this check-in's screen shows no personality): one name for the button, the same words Mufy chose.
   It stirs three times (a 5 px lift and a 2.5 degree tilt, 600 ms each, at 0.7, 2.3 and 3.9 s; its shadow narrows as it
   lifts) and is then still: every self-started motion is over by 4.6 s and the stir class is taken off on a timer. The
   words rise in at 1.3 s. It never opens by itself. The tap: the seal lifts away, the flap folds up, the lined inside flap
   stands up; the four games' points fly in one at a time from their own places up and to the right, large then smaller,
   and drop into the pocket (STEP_ORDER, digits only); the orange spark opens over the mouth with a ring as the last goes
   in, and the envelope sags; a held beat; the number rises out of the pocket, counting up (main.js countUp), and the
   envelope sinks away beneath it; "/ 100"; then the stage folds away (min-height and the number's padding, 450 ms) so the
   number glides up to its place under the date, centred, and today's lines rise in under it. About 4.4 s from the tap; a
   second tap on the hero ends it at once (not inside 400 ms: a double tap). The same whatever the score: nothing here
   reads the number. No sound. One short buzz at the tap (engine.js), no other. Never a tremble (4 to 8 Hz is the band of
   a tremor). Under Less motion or the phone's reduced motion the envelope is still, the words are there at once, and the
   tap puts the number in its place under the date with nothing in flight. Drawn again: no envelope, the number centred
   under the date, as the ceremony ends. Every rule is scoped to .hero--score (the date line's own rule, .score-date, is
   just after this block): My results (.hero--small) and the shared pages (r.js) have heroes of their own. */
.hero--score { position: relative; justify-content: center; --env-w: min(64vw, 280px); --env-sink: calc(var(--env-w) * 0.19); }
.hero--score ~ .result__change { text-align: center; text-wrap: pretty; }
.hero--score.is-sealed, .hero--score.is-opening { min-height: var(--env-stage, 22rem); }
.hero--score.is-sealed { align-items: stretch; }
.hero--score.is-sealed .hero__n, .hero--score.is-sealed .hero__of { display: none; }
/* (main.js sealedHero measures the end state's height with this for a moment at the tap) */
.hero--score.is-measuring { min-height: 0 !important; padding-top: 0 !important; }
/* the number and "/ 100" pass between the envelope's inside (under them) and its front (over them, z-index 2) */
.hero--score.is-opening .hero__n, .hero--score.is-opening .hero__of { position: relative; z-index: 1; }
.hero--score.is-sealed ~ .result__change { visibility: hidden; }   /* space kept until the number */
.hero--score.is-sealed ~ .result__change--welcome { visibility: visible; }   /* the first check-in's welcome line says nothing about the number */
/* (3 Nov 2026, Mufy) the share card shows the score: never before the number is whole (main.js shows it at the end, sealedHero's done) */
.hero--score.is-sealed ~ .share-card, .hero--score.is-opening ~ .share-card { display: none; }
/* the button fills the stage. Its padding (above and below, the same, so the envelope stays in the middle) keeps room
   over the envelope for the inside flap when it stands open, so on a short stage (a small phone with Larger text) the
   flap never reaches the date: on a tall one it changes nothing */
.env {
  position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center;
  width: 100%; min-height: 0; margin: 0; padding: calc(var(--env-w) * 0.24) 0; border: 0; border-radius: 0; background: none;
  color: var(--color-primary-strong); font: inherit; text-align: center; cursor: pointer;
  -webkit-appearance: none; appearance: none; -webkit-tap-highlight-color: transparent;
}
.env:focus { outline: none; }
/* the whole stage takes the tap; the focus ring goes round the envelope and its words */
.env__group { display: flex; flex-direction: column; align-items: center; gap: 1.15rem; max-width: 100%; padding: 12px 12px 14px; border-radius: 22px; }
.env:focus-visible .env__group { outline: var(--focus-ring); outline-offset: 2px; }
.env__art { position: relative; display: block; flex: none; width: var(--env-w); height: calc(var(--env-w) * 170 / 240); }   /* the viewBox's shape, without aspect-ratio (older iPhones) */
.env__art svg { position: absolute; inset: 0; display: block; width: 100%; height: 100%; overflow: visible; }
.env__front { z-index: 2; }
.env:active .env__front { filter: brightness(0.97); }   /* a press shows in colour only (30 Oct 2026, Mufy) */
.env__inside { transform-box: fill-box; transform-origin: 50% 100%; transform: scaleY(0); }
.env__flap { transform-box: fill-box; transform-origin: 50% 0; }
.env__seal-g, .env__shadow { transform-box: fill-box; transform-origin: 50% 50%; }
.env__q { font: 700 25px var(--font-heading); fill: var(--color-env-q); }
/* (9 Nov 2026, Mufy: the redesign's stage 5) the envelope is a paper object and keeps its paper in dark; only its warm halo and
   its shadow on the "table" follow the theme. CSS wins over ENV_ART's own attributes, so main.js keeps the picture as it is */
.env__base stop:nth-of-type(1) { stop-color: var(--color-env-halo-0); }
.env__base stop:nth-of-type(2) { stop-color: var(--color-env-halo-1); }
.env__base stop:nth-of-type(3) { stop-color: var(--color-env-halo-2); }
.env__shadow ellipse { fill: var(--color-env-shadow); }
.env__hint { display: flex; flex-direction: column; align-items: center; gap: 0.2rem; max-width: 19rem; }
.env__cta { font: 700 var(--font-size-headline)/1.2 var(--font-heading); color: var(--color-primary-strong); }
.env__sub { font: 600 var(--font-size-lead)/1.35 var(--font-body); color: var(--color-text); text-wrap: balance; }
.hero--score.is-opening .env { position: absolute; inset: 0; pointer-events: none; }
/* each point sits at the envelope's mouth (--env-mx, --env-my: measured by main.js at the tap) and flies there from its
   own place up and to the right (--sx, --sy), large at first and smaller as it goes, then drops into the pocket
   (--env-sink), behind the front; when the last one is in, the orange spark opens over the mouth with a ring */
.env-pt { position: absolute; left: var(--env-mx, 50%); top: var(--env-my, 40%); z-index: 1; font: 700 var(--font-size-lead)/1 var(--font-body); color: var(--color-env-ink); font-variant-numeric: tabular-nums; white-space: nowrap; text-shadow: 0 0 6px var(--color-env-ink-halo), 0 0 2px var(--color-env-ink-halo); transform: translate(-50%, -50%); opacity: 0; pointer-events: none; }
.env-burst { position: absolute; left: var(--env-mx, 50%); top: var(--env-my, 40%); width: 3rem; height: 3rem; margin: -1.5rem 0 0 -1.5rem; z-index: 3; opacity: 0; pointer-events: none; }
.env-burst svg { display: block; width: 100%; height: 100%; }
.env-ring { position: absolute; left: var(--env-mx, 50%); top: var(--env-my, 40%); width: 3.6rem; height: 3.6rem; margin: -1.8rem 0 0 -1.8rem; z-index: 3; border: 2px solid var(--color-accent); border-radius: 50%; opacity: 0; pointer-events: none; }
@keyframes env-stir {   /* one stir in a 1600 ms cycle: 600 ms of movement, then still */
  0% { transform: none; animation-timing-function: var(--ease-out); }
  15% { transform: translateY(-5px) rotate(-2.5deg); animation-timing-function: ease-in-out; }
  30% { transform: translateY(1px) rotate(0.4deg); animation-timing-function: var(--ease-out); }
  37.5%, 100% { transform: none; }
}
@keyframes env-shadow {   /* the shadow on the table narrows and pales as the envelope lifts, in step with env-stir */
  0% { transform: none; opacity: 1; animation-timing-function: var(--ease-out); }
  15% { transform: scale(0.9); opacity: 0.65; animation-timing-function: ease-in-out; }
  30% { transform: scale(1.01); opacity: 1; animation-timing-function: var(--ease-out); }
  37.5%, 100% { transform: none; opacity: 1; }
}
@keyframes env-fold { to { transform: scaleY(0); } }
@keyframes env-unfold { to { transform: scaleY(1); } }
@keyframes env-seal-off { to { opacity: 0; transform: scale(1.08); } }
@keyframes env-pt {   /* appears large at its own place, a gentle arc to the mouth, shrinking, then down into the pocket */
  0% { opacity: 0; transform: translate(calc(var(--sx, 8rem) - 50%), calc(var(--sy, 0px) - 50%)) scale(2.5); animation-timing-function: var(--ease-out); }
  16% { opacity: 1; transform: translate(calc(var(--sx, 8rem) - 50%), calc(var(--sy, 0px) - 50%)) scale(2.2); animation-timing-function: ease-in-out; }
  62% { opacity: 1; transform: translate(calc(var(--sx, 8rem) * 0.38 - 50%), calc(var(--sy, 0px) * 0.38 - 50% - 8px)) scale(1.3); animation-timing-function: var(--ease-in); }
  84% { opacity: 1; transform: translate(-50%, -50%) scale(0.8); animation-timing-function: ease-in; }
  94% { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, calc(var(--env-sink, 40px) - 50%)) scale(0.6); }
}
@keyframes env-burst { 0% { opacity: 0; transform: scale(0.2) rotate(-45deg); } 40% { opacity: 1; transform: scale(1.3) rotate(8deg); } 75% { opacity: 1; transform: scale(1) rotate(0deg); } 100% { opacity: 0; transform: scale(0.85); } }
@keyframes env-ring { 0% { opacity: 0; transform: scale(0.5); } 12% { opacity: 0.8; transform: scale(0.6); } 100% { opacity: 0; transform: scale(1.25); } }
@keyframes env-sag { 50% { transform: translateY(4px); } }
@keyframes env-gone { to { opacity: 0; } }
@keyframes env-away { to { opacity: 0; transform: translateY(16px); } }   /* the envelope sinks away beneath the rising number */
@keyframes env-out { 0% { opacity: 0; transform: translateY(var(--env-rise, 18px)) scale(0.92); } 25% { opacity: 1; } }   /* up out of the pocket: whole at once, the front hides what is still inside */
@keyframes hero-in { from { opacity: 0; } }
@keyframes env-land {   /* the stage folds away: the hero to its own height, the number up to its place under the date */
  from { min-height: var(--env-stage, 22rem); padding-top: var(--env-pad, 0px); }
  to { min-height: var(--env-land, 0px); padding-top: 0px; }
}
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .env.is-restless .env__back, html:not(.less-motion) .env.is-restless .env__front { animation: env-stir 1600ms 700ms 3 both; }
  html:not(.less-motion) .env.is-restless .env__shadow { animation: env-shadow 1600ms 700ms 3 both; }
  html:not(.less-motion) .env.is-restless .env__hint { animation: done-rise var(--duration-base) var(--ease-out) 1300ms backwards; }
  html:not(.less-motion) .hero--score.is-opening .env__hint { animation: env-gone var(--duration-fast) linear forwards; }
  html:not(.less-motion) .hero--score.is-opening .env__seal-g { animation: env-seal-off 160ms var(--ease-out) forwards; }
  html:not(.less-motion) .hero--score.is-opening .env__flap { animation: env-fold 180ms var(--ease-in) forwards; }
  html:not(.less-motion) .hero--score.is-opening .env__inside { animation: env-unfold 220ms var(--ease-out) 170ms forwards; }
  html:not(.less-motion) .hero--score.is-opening .env-pt { animation: env-pt 750ms backwards; }   /* delays and the length inline, one per point (main.js sealedHero) */
  html:not(.less-motion) .hero--score.is-opening .env-burst { animation: env-burst 650ms var(--ease-out) var(--env-settle-at, 2750ms) both; }
  html:not(.less-motion) .hero--score.is-opening .env-ring { animation: env-ring 600ms var(--ease-out) var(--env-settle-at, 2750ms) both; }
  html:not(.less-motion) .hero--score.is-opening .env__art > svg { animation: env-sag 240ms ease-in-out var(--env-settle-at, 2750ms), env-away var(--duration-base) var(--ease-in) var(--env-gone-at, 3550ms) forwards; }
  html:not(.less-motion) .hero--score.is-opening .hero__n { animation: env-out 650ms var(--ease-emphasised) var(--env-out-at, 3350ms) backwards; }   /* Material's emphasised decelerate: the one slow, shaped beat */
  html:not(.less-motion) .hero--score.is-opening .hero__of { animation: hero-in var(--duration-base) var(--ease-out) var(--env-of-at, 3800ms) backwards; }
  html:not(.less-motion) .hero--score.is-opening { animation: env-land 450ms var(--ease-standard) var(--env-land-at, 3950ms) both; }
  html:not(.less-motion) .hero--score.is-opening ~ .result__change { animation: done-rise 260ms var(--ease-out) var(--env-lines-at, 4150ms) backwards; }
  html:not(.less-motion) .hero--score.is-opening ~ .result__change--welcome { animation: none; }
}

/* (7 Nov 2026) the score screen's date line (main.js scoreScreen, .score-date), centred over the centred score */
.score-date { text-align: center; text-wrap: pretty; }

/* the first screens: the app's face above the sign-in heading; About you's Delete my account, a quiet grey link
   24 px under Sign out; the reminder question under "You're in Asha's group." */
.auth-face { display: block; width: 72px; height: 72px; margin: 6px auto 2px; border-radius: var(--radius-l); }
.about__delete { display: block; margin: 24px auto 0; color: var(--color-text-muted); }
.about__delete:active { color: var(--color-text); }
.join-ask { margin-top: 0.4em; }

/* ---------- inside the games (30 Oct 2026, Mufy: "improve game experience where possible") ----------
   The same rules as above: calm, short, transform and opacity only, every animation opt-in (none with
   reduced motion or Less motion); nothing here changes when a stimulus appears or how a tap is timed. */

/* a way out of a game (main.js gameStop): "✕ Stop" at the top left of the game, inside the safe area, and the
   room it takes above each game's top line (none in Reaction, whose top is the dark sky) */
.game-stop {
  position: fixed; z-index: 58; top: calc(8px + env(safe-area-inset-top)); left: max(calc(8px + env(safe-area-inset-left)), calc(50% - 272px));
  display: inline-flex; align-items: center; gap: 6px; min-width: 48px; min-height: 48px; margin: 0; padding: 0 14px;
  border: 2px solid var(--color-primary); border-radius: var(--radius-m); background: var(--color-surface); color: var(--color-primary-strong);
  font: 700 var(--font-size-body)/1 var(--font-body); cursor: pointer; -webkit-tap-highlight-color: transparent; box-shadow: var(--elevation-1);
}
.game-stop:active { background: var(--color-primary-container); }
.game-stop:focus-visible { outline: var(--focus-ring); outline-offset: 2px; }
.game-stop > .icon { width: var(--icon-size-s); height: var(--icon-size-s); }   /* (9 Nov 2026, stage 3) the close icon */
/* (fixes, 30 Oct 2026) gone once the game has finished (its end card), its room kept, so nothing under the card moves */
.game-stop[hidden] { display: none; }
html.has-game-stop { --stop-pad: 52px; }
html.has-game-stop .game:not(.game--reaction) { padding-top: calc(12px + var(--stop-pad) + env(safe-area-inset-top)); }
/* the question after Stop: over the game, which runs on underneath */
.stop-ask { position: fixed; inset: 0; z-index: 70; display: grid; place-items: center; padding: 16px; background: var(--color-scrim); }
.stop-ask__box { width: 100%; max-width: 400px; padding: 20px 18px 18px; border-radius: var(--radius-l); background: var(--color-surface); box-shadow: var(--elevation-3); text-align: center; }
.stop-ask__q { margin: 0 0 6px; font: 700 var(--font-size-lead)/1.4 var(--font-heading); color: var(--color-primary-strong); }
/* a game's start screen after Stop: "You stopped Grid Span. It starts again when you tap Start." */
.intro__stopped { margin: 4px 0 12px; padding: 10px 14px; border-radius: var(--radius-m); background: var(--color-primary-container); color: var(--color-primary-strong); font-weight: 600; }

/* the end of a game (engine.js endMoment): a white card in the middle that never takes a tap */
.end-moment { position: fixed; inset: 0; z-index: 45; display: grid; place-items: center; padding: 16px; pointer-events: none; }
.end-moment__card {
  margin: 0; max-width: 22em; padding: 16px 22px; border-radius: var(--radius-l); background: var(--color-surface); color: var(--color-primary-strong);
  font: 700 var(--font-size-title)/1.35 var(--font-heading); text-align: center; box-shadow: var(--elevation-2);
}
/* the end of a first-time practice (engine.js practiceDone): over the game, with room for the Stop button */
.pd {
  position: fixed; inset: 0; z-index: 40; display: grid; place-items: center; overflow-y: auto;
  padding: calc(16px + var(--stop-pad, 0px) + env(safe-area-inset-top)) 16px calc(16px + env(safe-area-inset-bottom));
  background: var(--color-veil);
}
.pd__box { width: 100%; max-width: 420px; padding: 20px 18px 18px; border-radius: var(--radius-l); background: var(--color-surface); border: 1px solid var(--color-outline); box-shadow: var(--elevation-2); text-align: center; }
.pd__title { margin: 0 0 6px; }
.pd__line { margin: 0 0 4px; }
@keyframes end-in { from { opacity: 0; transform: translateY(6px); } }
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .end-moment__card, html:not(.less-motion) .pd__box { animation: end-in var(--duration-base) var(--ease-out) backwards; }
}

/* Grid Span: the board in the middle of the screen with its line right above it; "Watch" with an eye; the page
   around the board dims a little while the blocks light up; a dot for each tap taken; 1 and 2 on the blocks
   when a missed practice is shown again */
.game--grid { justify-content: center; }
.game--grid .gs-note, .game--grid .gs-dots, .game--grid .gs-board { position: relative; z-index: 1; }
.game--grid .gs-note { margin-bottom: 2px; }
.gs-eye { display: inline-block; width: 22px; height: 22px; margin-right: 6px; vertical-align: -5px; }
.gs-eye svg { display: block; width: 100%; height: 100%; }
.gs-dots { display: flex; justify-content: center; gap: 8px; height: 14px; margin: 0 0 4px; visibility: hidden; }
.gs-dots.is-on { visibility: visible; }
.gs-dot { width: 14px; height: 14px; border-radius: 50%; border: 2px solid var(--game-dot); background: var(--game-dot-ground); }
.gs-dot.is-on { background: var(--game-dot); }
.gs-num {
  position: relative; z-index: 1; display: inline-grid; place-items: center; width: 1.7em; height: 1.7em; border-radius: 50%;
  background: var(--game-num); color: var(--game-num-ink); font: 800 var(--font-size-lead)/1 var(--font-body); box-shadow: 0 0 0 2px var(--game-num-ink);
}
.game--grid::before { content: ""; position: fixed; inset: 0; background: rgba(20, 63, 60, 0.08); opacity: 0; pointer-events: none; }
.game--grid.is-watching::before { opacity: 1; }
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .game--grid::before { transition: opacity var(--duration-base) var(--ease-out); }
}

/* Reaction: the tick in the counter's place after a tap in the practice. (2 Nov 2026, Mufy) In the scored round each tap's
   time flies from the counter's place to a list at the top right (js/games/reaction.js showTime): "4 of 10" heads it, then
   ten rows, each the time and a bar on a fixed 0 to 600 ms scale, all the same colour; the empty rows show what is left.
   The list never takes a tap (a tap anywhere still counts) and stays clear of Stop, at the top left. */
.rt-area { position: relative; }
.rt-times {
  position: absolute; z-index: 2; top: calc(16px + env(safe-area-inset-top)); right: calc(14px + env(safe-area-inset-right));
  width: 140px; display: grid; gap: 6px; pointer-events: none;
}
.rt-count {
  margin: 0; text-align: right; white-space: nowrap; pointer-events: none;
  color: var(--game-reaction-ink-soft); opacity: 0.9; font-size: 0.85rem; font-weight: 600; font-variant-numeric: tabular-nums;
}
.rt-times__rows { display: grid; gap: 5px; }
.rt-time { display: grid; grid-template-columns: 50px minmax(0, 1fr); align-items: center; gap: 7px; height: 12px; }
.rt-time__t { text-align: right; font: 700 12px/1 var(--font-body); font-variant-numeric: tabular-nums; color: var(--game-reaction-ink); white-space: nowrap; }
.rt-time__t small { margin-left: 2px; font-size: 10px; font-weight: 600; color: var(--game-reaction-ink-soft); opacity: 0.85; }
.rt-time__track { position: relative; height: 6px; border-radius: 3px; background: rgba(228, 240, 238, 0.13); overflow: hidden; }
.rt-time__bar { position: absolute; top: 0; bottom: 0; left: 0; width: 0; border-radius: 3px; background: var(--game-reaction-ink); opacity: 0.88; }
.rt-fly { position: absolute; z-index: 3; pointer-events: none; font: 700 4.5rem/1 var(--font-body); font-variant-numeric: tabular-nums; color: var(--game-reaction-ink); white-space: nowrap; transform: translate(-50%, -50%); }
@media (prefers-reduced-motion: no-preference) { html:not(.less-motion) .rt-time.is-in .rt-time__bar { transition: width 320ms cubic-bezier(0.2, 0.7, 0.2, 1); } }
.rt-counter { position: relative; }
.rt-tick { position: absolute; left: 50%; top: 50%; width: 72px; height: 72px; transform: translate(-50%, -50%); pointer-events: none; }
.rt-tick svg { display: block; width: 100%; height: 100%; }

/* Picture Memory: "Picture 3 of 12" above the picture; = and ≠ on the answer buttons; the practice's look-alike pair */
.pm-count { min-height: 1.5em; margin: -4px 0 4px; text-align: center; color: var(--color-text-muted); font-size: var(--font-size-body); font-weight: 600; font-variant-numeric: tabular-nums; }
.pm-buttons .btn--answer { flex-direction: column; gap: 2px; }
.pm-sym { font-size: var(--font-size-headline); line-height: 1; font-weight: 700; }
.pm-pair { width: 100%; max-width: 420px; text-align: center; }
.pm-pair__line { margin: 10px 0 0; font-weight: 600; color: var(--color-primary-strong); }

/* Symbol Match: the right row lit after a wrong practice answer; a coloured edge on the button just pressed
   (green when right, grey when not, for 0.3 s); the Start button in the symbol's place */
.sm-row { border-radius: var(--radius-s); }
.sm-row.is-hint { background: var(--game-key-hint); box-shadow: 0 0 0 3px var(--game-key-hint-ring); }   /* (9 Nov 2026, fix) the key's own */
.sm-pad .btn--answer.is-right { box-shadow: 0 0 0 3px var(--game-right); }
.sm-pad .btn--answer.is-wrong { box-shadow: 0 0 0 3px var(--game-not-right); }
.sm-target .sm-start { width: auto; min-width: 180px; margin: 0; }

/* ---------- This week's question (30 Oct 2026, Mufy; js/questions.js) ----------
   The weekly group game: its block on the board (under Who's in), the pick and guess screens, Play it on a call,
   home's line and the score screen's card. Answers are big and wholly tappable, their words at least 18 px. The
   moving parts (a tile lifting, the card turning, a face dropping onto an answer, a flower opening) follow the
   rule above: only without reduced motion and Less motion; otherwise each is in its end state at once (the
   card then fades in 200 ms). The flowers row and the hedge are one inline SVG sized in px, so Larger text never
   widens them. */
.twq { margin: 14px 0 4px; }
.twq__h { margin-bottom: 0.3em; }
.twq__h:focus { outline: none; }
.twq__q { margin: 4px 0 8px; font: 700 var(--font-size-title)/1.3 var(--font-heading); color: var(--color-primary-strong); }
h1.twq__q { font-size: var(--font-size-headline); margin-top: 0.1em; }
.twq__q--small { font-size: var(--font-size-lead); }
.twq-round__label { margin: 0.2em 0 0; }
.twq-round--earlier { margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--color-outline); }
.twq-lastweek { margin: 4px 0 12px; padding: 10px 14px; border-radius: var(--radius-m); background: var(--color-primary-container); color: var(--color-primary-strong); font-weight: 600; }
.twq-knew { margin: 4px 0; font-weight: 600; color: var(--color-primary-strong); }
.twq-faces__h { margin: 10px 0 2px; font-weight: 600; }
.twq-showall { display: block; }
.twq-earlier { margin: 12px 0 0; }
.twq-earlier > summary { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 48px; font-weight: 600; color: var(--color-primary-strong); cursor: pointer; list-style: none; }
/* (9 Nov 2026, stage 3) yesterday's statuses and the earlier questions fold as "How can I cancel?" does, with its chevron
   (display: flex hides the browser's own marker), turned while open */
.gnotes__yday summary::-webkit-details-marker, .twq-earlier > summary::-webkit-details-marker { display: none; }
.gnotes__yday summary::after, .twq-earlier > summary::after {
  content: ""; flex: none; width: 0.55em; height: 0.55em; margin-top: -0.2em;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; transform: rotate(45deg);
}
.gnotes__yday[open] summary::after, .twq-earlier[open] > summary::after { margin-top: 0.25em; transform: rotate(-135deg); }
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .gnotes__yday summary::after, html:not(.less-motion) .twq-earlier > summary::after { transition: transform var(--duration-fast) var(--ease-out); }
}
/* the flowers row and its line: centred, flat, never a button */
.twq-flowers { display: flex; justify-content: center; margin: 2px 0 0; pointer-events: none; }
.twq-bed { display: block; max-width: 100%; height: auto; }
.twq-flowers__line { margin: 2px 0 10px; text-align: center; }
.twq-flower__head { transform-box: fill-box; transform-origin: center; }
@keyframes twq-open { from { opacity: 0; transform: scale(0.3); } }
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .twq-flower.is-new .twq-flower__head { animation: twq-open var(--duration-slow) var(--ease-out) backwards; }
}
/* "Your pick is in": the one flower beside its line */
.twq-in { margin: 14px 0 6px; font: 700 var(--font-size-lead)/1.4 var(--font-heading); color: var(--color-primary-strong); }
.twq-in:focus { outline: none; }
.twq-one { display: flex; align-items: center; gap: 10px; margin: 0 0 8px; }
.twq-one__flower, .twq-one__svg { display: block; flex: none; }
.twq-one__line { margin: 0; }
/* faces to tap ("New picks to guess", "Now guess theirs"): 40 px, the first name under each, "New" for a newcomer */
.twq-faces { display: flex; flex-wrap: wrap; gap: 8px 10px; margin: 6px 0 10px; }
.twq-face {
  display: inline-flex; flex-direction: column; align-items: center; gap: 2px; min-width: 56px; min-height: 56px; margin: 0; padding: 4px 6px;
  border: 0; border-radius: var(--radius-m); background: none; color: var(--color-text); font: 600 var(--font-size-caption)/1.2 var(--font-body); cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.twq-face:focus-visible { outline: var(--focus-ring); outline-offset: 2px; }
.twq-face .avatar { width: 40px; height: 40px; font-size: 1rem; }
.twq-face__name { max-width: 6.5em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.twq-new { padding: 0 6px; border-radius: var(--radius-s); background: var(--color-primary-container); color: var(--color-primary-strong); font-size: var(--font-size-caption); font-weight: 700; }
.twq-more { margin: 0; align-self: center; }
/* a face on an answer: 36 px with a white ring, on the answer's corner; the viewer's own says "You" */
.twq-tface { display: inline-flex; flex-direction: column; align-items: center; }
.twq-tface .avatar { width: 36px; height: 36px; font-size: 0.9rem; box-shadow: 0 0 0 2px var(--color-surface); }
.twq-you { margin-top: 1px; padding: 0 5px; border-radius: var(--radius-s); background: var(--color-primary); color: var(--color-on-primary); font: 700 var(--font-size-caption)/1.4 var(--font-body); }
@keyframes twq-drop { from { opacity: 0; transform: translateY(-14px); } }
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .twq-tface.is-dropping { animation: twq-drop 240ms var(--ease-out) backwards; }
}
/* the answers, 2 a row (an odd last one in the middle): the whole answer is the button */
.twq-tiles { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin: 12px 0; }
.twq-tiles--odd .twq-tile:last-child { grid-column: 1 / -1; justify-self: center; width: calc(50% - 6px); }
.twq-tile {
  position: relative; display: flex; flex-direction: column; align-items: center; justify-content: flex-start; gap: 6px; min-height: 56px; margin: 0; padding: 10px 8px 12px;
  border: 2px solid var(--color-outline-strong); border-radius: var(--radius-l); background: var(--color-surface); color: var(--color-text); font: 700 var(--font-size-body)/1.25 var(--font-body); text-align: center;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.twq-tile:focus-visible { outline: var(--focus-ring); outline-offset: 2px; }
.twq-tile[disabled] { cursor: default; color: var(--color-text); }
.twq-tile__pic { display: block; width: 100%; max-width: 150px; aspect-ratio: 1; border-radius: var(--radius-m); object-fit: contain; }
.twq-tiles--guess .twq-tile__pic { max-width: 112px; }
.twq-tile__word { font-size: var(--font-size-body); overflow-wrap: anywhere; }
.twq-tile__faces { position: absolute; top: 6px; left: 6px; display: flex; gap: 4px; }
.twq-tile__faces:empty { display: none; }
.twq-tile.is-mine, .twq-tile.is-theirs { border-color: var(--color-primary); }
/* lifted: a 3 px teal edge (still), raised 6 px with a shadow where motion is welcome */
.twq-tile.is-lifted { border-color: var(--color-primary); box-shadow: 0 0 0 3px var(--color-primary); }
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .twq-tile { transition: transform var(--duration-fast) var(--ease-out), box-shadow var(--duration-fast) var(--ease-out); }
  html:not(.less-motion) .twq-tile.is-lifted { transform: translateY(-6px); box-shadow: var(--elevation-2), 0 0 0 3px var(--color-primary); }
}
/* (review, 30 Oct 2026) high contrast modes drop the ring (a box-shadow): a lifted answer keeps a system-colour outline */
@media (forced-colors: active) {
  .twq-tile.is-lifted { outline: 3px solid Highlight; outline-offset: 2px; }
}
/* the pick screen: Yes, this one / Choose again under the answers, then the steps */
.twq-confirm[hidden] { display: none; }
.twq-confirm .btn:first-child { margin-top: 0; }
.twq-will h2:focus { outline: none; }
.twq-offer { margin-top: 14px; }
/* the small answers in the block, once picked: each picture at 56 px with its word, and the faces seen on it */
.twq-mini { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin: 8px 0 10px; padding: 0; list-style: none; }
.twq-mini__tile { display: flex; flex-direction: column; gap: 4px; padding: 6px; border: 1px solid var(--color-outline); border-radius: var(--radius-m); background: var(--color-surface); }
.twq-mini__tile.is-mine { border: 2px solid var(--color-primary); padding: 5px; }
.twq-mini__top { display: flex; align-items: center; gap: 8px; }
.twq-mini__pic { flex: none; width: 56px; height: 56px; border-radius: var(--radius-s); object-fit: contain; }
.twq-mini__word { font-size: var(--font-size-caption); font-weight: 600; line-height: 1.25; overflow-wrap: anywhere; }
.twq-mini__faces { display: flex; flex-wrap: wrap; gap: 4px; }
/* the guess screen: the person large, the question small, the card, the reveal */
.twq-who { display: flex; align-items: center; gap: 14px; margin: 4px 0 6px; }
.twq-who__face .avatar { width: 96px; height: 96px; font-size: 2.4rem; }
.twq-who__name { margin: 0; font: 700 var(--font-size-title)/1.3 var(--font-heading); color: var(--color-primary-strong); overflow-wrap: anywhere; }
.twq-guess__q { margin-top: 0.2em; }
.twq-guess__h { margin: 0.1em 0 0.3em; }
.twq-think { margin: 0 0 4px; font-weight: 600; }
.twq-seen { margin: 0 0 4px; }
.twq-just { display: block; margin: 0 auto; }
.twq-reveal { margin: 0 0 4px; text-align: center; }
.twq-reveal:focus { outline: none; }
.twq-reveal__line { margin: 0 0 4px; font: 700 var(--font-size-lead)/1.4 var(--font-heading); color: var(--color-primary-strong); }
.twq-same { margin: 0 0 4px; font-weight: 700; color: var(--color-primary-strong); }
.twq-too { margin: 4px 0; }
.twq-starter { margin: 12px 0 0; padding: 12px 14px; border-radius: var(--radius-m); background: var(--color-surface-variant); }
.twq-starter p { margin: 0 0 6px; }
.twq-starter .btn { margin: 0; }
/* Turn the card: the back shows today's look (Grid Span's scene, above), the front the picker's answer. It turns
   over on its Y axis in 500 ms; with less motion it fades (200 ms) and nothing moves. */
.twq-card { position: relative; width: 150px; height: 172px; margin: 8px auto 10px; perspective: 700px; }
.twq-card__inner { position: relative; width: 100%; height: 100%; transform-style: preserve-3d; }
.twq-card__back, .twq-card__front { position: absolute; inset: 0; border-radius: var(--radius-l); -webkit-backface-visibility: hidden; backface-visibility: hidden; }
.twq-card__back { background-color: var(--color-surface-variant); border: 1px solid var(--color-outline); box-shadow: var(--elevation-1); }
.twq-card__front {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; padding: 4px;
  transform: rotateY(180deg); background: var(--color-surface); border: 2px solid var(--color-primary); box-shadow: var(--elevation-1);
}
.twq-card__pic { display: block; width: 120px; height: 120px; object-fit: contain; }
.twq-card__word { font-weight: 700; font-size: var(--font-size-caption); line-height: 1.2; text-align: center; overflow-wrap: anywhere; }
.twq-card__mark { position: absolute; top: -14px; right: -14px; width: 64px; height: 64px; }
.twq-card__mark svg { display: block; width: 100%; height: 100%; }
.twq-card.is-turned .twq-card__inner { transform: rotateY(180deg); }
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .twq-card__inner { transition: transform 500ms var(--ease-out); }
}
@media (prefers-reduced-motion: reduce) {
  .twq-card.is-turned .twq-card__inner, .twq-card__front { transform: none; }
  .twq-card__back, .twq-card__front { transition: opacity 200ms linear; }
  .twq-card__front, .twq-card.is-turned .twq-card__back { opacity: 0; }
  .twq-card.is-turned .twq-card__front { opacity: 1; }
}
:where(html.less-motion) .twq-card.is-turned .twq-card__inner, :where(html.less-motion) .twq-card__front { transform: none; }
:where(html.less-motion) .twq-card__back, :where(html.less-motion) .twq-card__front { transition: opacity 200ms linear; }
:where(html.less-motion) .twq-card__front, :where(html.less-motion) .twq-card.is-turned .twq-card__back { opacity: 0; }
:where(html.less-motion) .twq-card.is-turned .twq-card__front { opacity: 1; }
/* Play it on a call: very large words to read out, the faces only after "Show the picks" */
.twq-call__q { font-size: var(--font-size-display-s); line-height: 1.25; }
.twq-call__words { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin: 14px 0; }
.twq-call__word {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; min-height: 104px; padding: 12px 8px;
  border: 2px solid var(--color-outline); border-radius: var(--radius-l); background: var(--color-surface); text-align: center;
}
.twq-call__word:last-child:nth-child(odd) { grid-column: 1 / -1; }
.twq-call__text { font: 700 var(--font-size-headline)/1.25 var(--font-heading); color: var(--color-primary-strong); overflow-wrap: anywhere; }
.twq-call__faces { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; }
.twq-call__faces[hidden] { display: none; }
.twq-call__ask { font: 700 var(--font-size-title)/1.4 var(--font-heading); color: var(--color-primary-strong); }
.twq-nface { display: inline-flex; flex-direction: column; align-items: center; font-size: var(--font-size-caption); font-weight: 600; }
.twq-nface .avatar { width: 36px; height: 36px; }
/* To ask cards: the 💬 on a member's row, its cards under the row, and the list on Play it on a call */
.twq-ask-mark { font-size: var(--font-size-lead); }
.twq-ask-cards { grid-column: 1 / -1; margin-top: 8px; padding: 4px 12px; border-radius: var(--radius-m); background: var(--color-surface-variant); }
.twq-ask-cards[hidden], .twq-ask-mark[hidden] { display: none; }
.twq-asklist { margin: 6px 0; padding: 0; list-style: none; }
.twq-askrow { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; padding: 10px 0; }
.twq-askrow + .twq-askrow { border-top: 1px solid var(--color-outline); }
.twq-askrow__face .avatar { width: 40px; height: 40px; font-size: 1rem; }
.twq-askrow__q { flex: 1 1 12em; margin: 0; }
.twq-askrow__tools { display: flex; flex-wrap: wrap; gap: 8px; flex-basis: 100%; }
.twq-askrow__tools .btn { margin: 0; }
.twq-lovely { margin: 0; font-weight: 700; color: var(--color-primary-strong); }
.twq-lovely:focus { outline: none; }
/* home's line on a group's card, and the score screen's card */
.group-card__question { display: block; padding-left: 0; text-align: left; font-weight: 600; }
.twq-score__line { font-weight: 600; }
.twq-score .btn { margin-top: 6px; }
/* (1 Nov 2026, Mufy: "it is buried" inside the group) home's card for This week's question, right after the check-in
   card: the heading, the question in large type, one quiet button (the check-in keeps home's orange one) */
.twq-home h2 { margin-top: 0; }
.twq-home__q { margin: 4px 0 10px; font: 600 var(--font-size-title)/1.35 var(--font-heading); color: var(--color-primary-strong); }
.twq-home__line { margin: 0 0 4px; font-weight: 600; }
.twq-home .btn { margin-top: 6px; }
/* version 2 (released with it): stories, asks and likes. A story is drawn as a status is (posts.js storyRow); the
   story form is the status form's pieces (posts.js storyForm); the asks line sits under the small answers. */
.twq-why h2:focus { outline: none; }
.twq-why .twq-starter, .twq-asks .twq-starter { margin: 6px 0 10px; font-weight: 600; }
.story-form__field[hidden], .story-form__post[hidden] { display: none; }
.story-form .rec__row .btn[hidden] { display: none; }
.twq-told { margin: 10px 0 4px; font-weight: 600; color: var(--color-primary-strong); }
.twq-told:focus { outline: none; }
.twq-asks { margin: 6px 0 10px; padding: 10px 14px; border-radius: var(--radius-m); background: var(--color-primary-container); }
.twq-asks__line { margin: 0 0 6px; font-weight: 600; color: var(--color-primary-strong); }
.twq-asks__tools { display: flex; flex-wrap: wrap; gap: 8px; }
.twq-asks__tools .btn { margin: 0; }
.twq-asks .story-form { margin-bottom: 0; }
/* "Ask Asha about their pick" under a face to guess, and "Ask Ravi why" under a reveal */
.twq-facebox { display: inline-flex; flex-direction: column; align-items: center; gap: 2px; max-width: 9.5em; }
.twq-askwhy { margin: 0; min-height: 48px; padding: 4px 8px; font-size: var(--font-size-caption); line-height: 1.2; white-space: normal; text-align: center; }
.twq-stories__h { margin: 14px 0 0; font-size: var(--font-size-lead); }
.twq-stories { margin-top: 2px; }
.twq-reveal__why { margin-top: 10px; text-align: left; }
.twq-reveal__why:empty { display: none; }

/* ---------- Plans in the Google Play app (30 Oct 2026, Mufy; js/plans.js) ----------
   The introduction, the paywall's screens, the No plan screen, Settings' Your plan and the cover warning. Sizes in rem
   (Larger text makes them grow); a price never breaks inside its number; no line here moves unless the phone allows
   motion and Settings' Less motion is off (the chevron of "How can I cancel?" is the only thing that turns). */
.plans .amount, .plan-settings .amount { white-space: nowrap; }
.intro-list { list-style: none; margin: 14px 0 16px; padding: 0; display: grid; gap: 12px; font-size: var(--font-size-lead); }
.intro-list li { margin: 0; padding: 12px 14px; border-radius: var(--radius-m); background: var(--color-surface); border: 1px solid var(--color-outline); }
.offer__lead { font-size: var(--font-size-lead); color: var(--color-text-muted); text-align: center; margin: 10px 0 0; }
.offer__lead + h1 { text-align: center; margin-top: 0.15em; }
/* (9 Nov 2026, stage 5 fix) its screen stays the light app's cream under the screenshot, so no dark band shows below it in dark */
.phone-shot {
  margin: 16px auto 14px; width: 62vw; max-width: 240px; aspect-ratio: 1 / 2; overflow: hidden;
  border: 6px solid var(--color-device-frame); border-radius: 28px; background: var(--color-device-screen); box-shadow: var(--elevation-2);
}
.phone-shot img { display: block; width: 100%; height: auto; }
.tick-line { display: flex; align-items: center; justify-content: center; gap: 4px; margin: 0 0 4px; font-weight: 600; }
.tick-line__tick { color: var(--color-primary); font-weight: 700; font-size: var(--font-size-title); line-height: 1; }
/* (9 Nov 2026, stage 3) one above the other, the main button first: side by side they squeezed to two lines at 360 px */
.remind__buttons, .followup__actions { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-3); margin-top: 14px; }
.remind__buttons .btn, .followup__actions .btn { margin: 0; }
.followup__actions { margin-bottom: var(--space-3); }   /* (9 Nov 2026, fix) room above "Plans renew until you cancel." */
/* (polish, 31 Oct 2026, Mufy) the one plan screen: the two plans in rows to compare (the yearly saving on its row's top edge),
   a short timeline for the chosen one, and "Start my free trial now" above the fold at 360 x 700. Sizes are the app's own
   (1rem, 0.95rem, 0.875rem), so Larger text makes them grow; nothing here moves. */
.plans--choose h1 { margin: 0 0 0.3em; }
.plan-rows { display: grid; gap: 12px; min-width: 0; margin: 14px 0 0; padding: 0; border: 0; }
.plan-row {
  position: relative; display: flex; align-items: center; gap: 12px; min-height: 56px; padding: 10px 14px;
  border: 2px solid var(--color-outline-strong); border-radius: var(--radius-m); background: var(--color-surface); cursor: pointer;
}
.plan-row input { flex: none; width: 24px; height: 24px; margin: 0; accent-color: var(--color-primary); }
.plan-row.is-chosen { border-color: var(--color-primary); background: var(--color-primary-container); }
.plan-row:focus-within { outline: var(--focus-ring); outline-offset: 2px; }
.plan-row--only { cursor: default; }
/* (8 Nov 2026) the price column starts as wide as its price and takes the room the name leaves, so with Larger text
   the yearly price stays beside the name and "about ... a week" wraps under it in two even lines (the row was four
   lines, and "Start my free trial now" went below the fold at 360 x 700); a price too long for the room still drops below */
.plan-row__text { flex: 1 1 auto; min-width: 0; display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; column-gap: 8px; }
.plan-row__left, .plan-row__right { display: flex; flex-direction: column; min-width: 0; }
.plan-row__right { flex-grow: 1; width: min-content; margin-left: auto; text-align: right; }
.plan-row__name { color: var(--color-primary-strong); }
.plan-row__trial { font-size: var(--font-size-caption); font-weight: 600; color: var(--color-primary-strong); }
.plan-row__price { font-weight: 700; white-space: nowrap; }
.plan-row__sub { font-size: var(--font-size-caption); color: var(--color-text-muted); text-wrap: balance; }
.plan-badge { display: inline-block; padding: 2px 10px; border-radius: var(--radius-full); background: var(--color-primary); color: var(--color-on-primary); font: 700 var(--font-size-caption)/1.5 var(--font-body); white-space: nowrap; }
.plan-row__badge { position: absolute; top: 0; right: 12px; transform: translateY(-50%); }
.plan-option__badge { margin-left: 8px; vertical-align: 0.1em; }
/* the timeline: a dot for each day joined by a line (in em, so Larger text keeps them together), the day in bold with its
   words on the same line. (9 Nov 2026, fix) 4 px less above it and between its days (10 px and 4 px, from 14 and 8): at body
   size it had grown by 11 px, and "Start my free trial now" ended 1 px below the fold at 360 x 700 with Larger text; and no
   one-word last line ("obligation.") */
.timeline { list-style: none; margin: 10px 0 0; padding: 0 0 0 1.6em; font-size: var(--font-size-body); }   /* (9 Nov 2026, stage 3) body: what someone agrees to */
.timeline__item { position: relative; margin: 0 0 4px; text-wrap: pretty; }
.timeline__item:last-child { margin-bottom: 0; }
.timeline__item::before {
  content: ""; position: absolute; left: calc(-1.525em + 1px); top: 0.4em; width: 0.75em; height: 0.75em; border-radius: 50%;
  background: var(--color-surface); border: 2px solid var(--color-primary); box-sizing: border-box;
}
.timeline__item:first-child::before { background: var(--color-primary); }
.timeline__item:not(:last-child)::after {
  content: ""; position: absolute; left: -1.15em; top: 1.15em; bottom: calc(-4px - 0.4em); width: 2px; background: var(--color-primary); opacity: 0.35;
}
.timeline__when { color: var(--color-primary-strong); margin-right: 0.3em; }
.plans--choose .plans__cta { margin-top: 14px; }
.cancel-help--short > p { margin: 0 14px 12px; }
.centre { text-align: center; }
.plans .fine.centre { margin: 8px 0 0; }
/* (review, 31 Oct 2026) the plan screen's needs line, below the fold, apart from the agree line above it */
.plans--choose .fine.centre + .fine:not(.centre) { margin-top: 0.8em; }
.cancel-help { margin: 12px 0; border: 1px solid var(--color-outline); border-radius: var(--radius-m); background: var(--color-surface); }
.cancel-help summary {
  display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 48px; padding: 0 14px;
  color: var(--color-primary); font-weight: 600; cursor: pointer; list-style: none;
}
.cancel-help summary::-webkit-details-marker { display: none; }
.cancel-help summary::after {
  content: ""; flex: none; width: 0.55em; height: 0.55em; margin-top: -0.2em;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; transform: rotate(45deg);
}
.cancel-help[open] summary::after { margin-top: 0.25em; transform: rotate(-135deg); }
.cancel-help summary:focus-visible { outline: var(--focus-ring); outline-offset: 2px; border-radius: var(--radius-m); }
.cancel-help > p { margin: 0 14px 4px; }
.cancel-help > .btn { width: calc(100% - 28px); margin: 8px 14px 14px; }
/* (fix stage, 30 Oct 2026) on the buying screens the steps show with no tap: the same box, its heading in place of the summary */
.cancel-help--open > h2 { margin: 0; padding: 12px 14px 6px; font-size: var(--font-size-body); line-height: 1.4; color: var(--color-primary-strong); }
.followup__charge { margin: 12px 0 0; font-weight: 600; color: var(--color-primary-strong); }
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .cancel-help summary::after { transition: transform var(--duration-fast) var(--ease-out); }
}
.plan-choice { display: grid; gap: 10px; min-width: 0; margin: 14px 0 0; padding: 0; border: 0; }
.plan-option {
  display: flex; align-items: flex-start; gap: 12px; min-height: 56px; padding: 12px 14px;
  border: 2px solid var(--color-outline-strong); border-radius: var(--radius-m); background: var(--color-surface); cursor: pointer;
}
.plan-option input { flex: none; width: 24px; height: 24px; margin: 1px 0 0; accent-color: var(--color-primary); }
.plan-option.is-chosen { border-color: var(--color-primary); background: var(--color-primary-container); }
.plan-option:focus-within { outline: var(--focus-ring); outline-offset: 2px; }
/* (review, 31 Oct 2026) the ring for the keyboard only: a tap focuses the radio too (browsers without :has keep the ring) */
@supports selector(:has(a)) {
  .plan-row:focus-within, .plan-option:focus-within { outline: none; }
  .plan-row:has(input:focus-visible), .plan-option:has(input:focus-visible) { outline: var(--focus-ring); outline-offset: 2px; }
}
.plan-option__title { display: block; color: var(--color-primary-strong); }
.plan-option__sub { font-size: var(--font-size-caption); color: var(--color-text-muted); white-space: nowrap; }
.under18 { margin: 10px 0 4px; padding: 12px 14px; border-radius: var(--radius-m); background: var(--color-surface-variant); }
.under18 p { margin: 0; }
.under18 .btn { margin-top: 10px; }
.cover-warning { background: var(--color-surface-variant); border-color: transparent; box-shadow: none; }
.cover-warning p { font-weight: 600; }
.join-ask h2 { margin-top: 0; }
.plans__links { display: flex; flex-wrap: wrap; justify-content: center; gap: 0 20px; margin-top: 18px; }
.plans__delete { display: block; margin: 8px auto 0; color: var(--color-text-muted); }
.plans__delete:active { color: var(--color-text); }
.plans__foot { margin-top: 10px; }
.plan-settings__people { font-weight: 600; }

/* ---------- Settings, Your invites (2 Nov 2026, Mufy; js/plans.js invitesSection) ----------
   In both editions, for every signed-in account: the switch "Cover the people who join from my links" (the groups' .switch-row
   and .switch), its question in the page, From and To side by side (one under the other on a narrow phone), four quick choices
   as chips, and the numbers as a table of label and number, the four "where they are now" rows set in a little. Nothing moves.
   (2 Nov 2026, Mufy) The dates, the chips and the table are on the account menu's Referral stats now (statsScreen), in the
   same .invites section. */
.invites [hidden] { display: none !important; }
.invites__line { margin: -4px 0 8px; }
.invites__ask { margin: 4px 0 12px; padding: 12px 14px; border: 1px solid var(--color-outline); border-radius: var(--radius-m); background: var(--color-surface); }
.invites__ask p { margin: 0 0 2px; font-weight: 600; }
.invites__ask p:focus { outline: none; }
.invites__dates { display: grid; grid-template-columns: 1fr 1fr; gap: 0 12px; margin-top: 10px; }
.invites__dates > div { min-width: 0; }
.invites__dates label { display: block; font-weight: 600; margin: 8px 0 4px; }
.invites__date {
  display: block; width: 100%; min-width: 0; min-height: 52px; padding: 8px 10px;
  font: 500 var(--font-size-body) var(--font-body); border: 2px solid var(--color-outline-strong); border-radius: var(--radius-m); background: var(--color-surface); color: var(--color-text);
}
.invites__date:focus { outline: var(--focus-ring); outline-offset: 1px; border-color: var(--color-primary); }
.invites__quick { display: flex; flex-wrap: wrap; gap: 8px; margin: 12px 0 6px; }
.invites__quick .btn { flex: 1 1 auto; margin: 0; }
.invites__status:empty { margin: 0; }
.invites__table { width: 100%; border-collapse: collapse; margin: 6px 0 10px; font-variant-numeric: tabular-nums; }
.invites__table th, .invites__table td { padding: 9px 4px; border-bottom: 1px solid var(--color-outline); text-align: left; vertical-align: baseline; }
.invites__table th { font-weight: 500; }
.invites__table td { text-align: right; font-weight: 700; white-space: nowrap; padding-left: 12px; }
.invites__table .invites__sub th { padding-left: 20px; }
.invites__table .invites__group th { padding-top: 16px; font-weight: 700; color: var(--color-primary-strong); }
@media (max-width: 359px) {
  .invites__dates { grid-template-columns: 1fr; }
}

/* ---------- the four questions before the paywall (1 Nov 2026, Mufy; js/plans.js intro, ask, recap) ----------
   In place of the introduction: the start screen, four question screens and the recap, on the plans screens' own classes
   (auth-face, q__opts, btn--choice is-picked, signpost, intro-list, fine) plus these. The progress row is the check-in's own
   dots (hidden from screen readers; "Question 2 of 4" beside them), the picked answer carries a tick, the line for it sits in
   a status box that keeps the tallest line's height once an answer is picked (so Next moves only once), Read aloud and
   Larger text sit side by side on the start screen, and the recap is the intro's card list a little tighter. Sizes in rem,
   so Larger text makes them grow. Nothing here moves but the picture: plans.js's zoom (3 Nov 2026), never with less
   motion. */
/* (3 Nov 2026) seven dots: where "Question n of 7" doesn't fit beside them (Larger text at 360 px, or a 320 px phone),
   it moves under them whole instead of splitting */
.ask-step { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; margin: 2px 0 0; }
.ask-step .steps { display: flex; gap: 8px; }
.ask-step .steps__dot { width: 18px; height: 18px; }
.ask-step__text { color: var(--color-text-muted); font-weight: 600; margin: 0; white-space: nowrap; }
/* (2 Nov 2026, Mufy) the tick is a drawn SVG (it was the text "✓"), the same size; its stroke draws in when new (below) */
.ask-tick { flex: none; display: inline-block; width: 1.1em; height: 1.1em; color: var(--color-primary); margin-right: 6px; }
.ask-tick svg { display: block; width: 100%; height: 100%; }
.ask-tick path { stroke-dasharray: 100; stroke-dashoffset: 0; }
.ask-status { display: flow-root; }
.ask-line { margin: 12px 0 0; }
.ask-tools { display: flex; flex-wrap: wrap; gap: 0 10px; }
.ask-tools .btn--quiet[aria-pressed="true"] { border-color: var(--color-primary); background: var(--color-primary-container); color: var(--color-primary-strong); font-weight: 700; }
.ask-tools .btn--quiet[aria-pressed="true"] > svg.icon { display: none; }   /* (9 Nov 2026, fix) Larger text's text_increase: the tick takes its place */
/* (4 Nov 2026, Mufy) the start screen's title and "How [word] are you?" (js/plans.js fitIntro and typeWord): both span the text
   width, their sizes set on the page; "How" and "are you?" in the title's colour, the word in the app's teal on a teal band as
   wide as the longest word. The band has a fixed height and line and always holds an invisible character, so it sits the same
   way with no letters as with some (an empty one has no text to line up on, and would sit higher, then drop with the first
   letter). The cursor is still while letters come and go, blinks while a word stays, and fades 2 s after the last word. */
/* (5 Nov 2026, Mufy: design G) the title is "Welcome" (or "Welcome back"), centred at the question's size (fitIntro), no longer
   across the width */
.intro-title { white-space: nowrap; line-height: 1.15; margin: 0.35em 0 0.15em; text-align: center; }
/* (5 Nov 2026, Mufy: design G) under the question: "Get your Awareness Score", then a dial, a clock and the four games' squares,
   each beside its line (js/plans.js INTRO_DIAL, INTRO_CLOCK, INTRO_GAMES), and memory, focus and speed as tags under the last */
.intro-get { font-size: var(--font-size-lead); margin: 0 0 0.35em; }
.intro-get strong { color: var(--color-primary-strong); }
.intro-points { list-style: none; margin: 0 0 0.8em; padding: 0; }
.intro-points > li { display: grid; grid-template-columns: 1.7em minmax(0, 1fr); gap: 0.55em; align-items: start; margin: 0.35em 0; }
.intro-points__icon { color: var(--color-primary); }   /* (9 Nov 2026, Mufy: the redesign's stage 5) the drawings' teal (currentColor) */
.intro-points__icon svg { display: block; width: 1.7em; height: 1.7em; }
/* (9 Nov 2026, fix) a little less room round the tags' words (0.5em, from 0.75em) and between them, so the three stay on one row
   at 360 px with Larger text, as at every other size */
.intro-skills { display: flex; flex-wrap: wrap; gap: 0.35em; margin: 0.35em 0 0; }
.intro-skills > span { display: inline-block; padding: 0.18em 0.5em; border-radius: var(--radius-full); background: var(--color-primary-container); color: var(--color-primary-strong); font-weight: 600; font-size: var(--font-size-body); }
.intro-q { font-family: var(--font-heading); font-weight: 600; font-size: var(--font-size-headline); line-height: 1.2; color: var(--color-primary-strong); margin: 0.2em 0 0.55em; }
.intro-q__line { display: flex; align-items: baseline; gap: 0.25em; white-space: nowrap; }
.intro-word {
  flex: 1 1 auto; display: inline-block; height: 1.35em; line-height: 1.35em; padding: 0 0.22em; border-radius: 0.28em;
  background: var(--color-primary-container); color: var(--color-primary); text-align: center; white-space: pre;
}
.intro-word::before { content: "\200B"; }
.intro-word__caret {
  display: inline-block; vertical-align: -0.16em; width: 0.07em; min-width: 2px; height: 1em; margin-left: 0.04em;
  background: currentColor; border-radius: 1px; transition: opacity 400ms var(--ease-out);
}
.intro-word__caret.is-blink { animation: intro-caret 1s steps(1, end) infinite; }
.intro-word__caret.is-gone { opacity: 0; animation: none; }
@keyframes intro-caret { 50% { opacity: 0; } }
.ask-recap { margin: 10px 0 12px; gap: 6px; font-size: var(--font-size-body); }
.ask-recap li { display: flex; align-items: flex-start; padding: 6px 12px; }   /* (2 Nov 2026) its tick, then the words */
.ask-recap .ask-tick, .build__list .ask-tick { margin-top: 0.2em; }
.ask-recap strong { color: var(--color-primary-strong); }
/* the picture on each question screen: a 112 px square top right, level with the question's first line, the words wrapping
   round it (the head holds the float, so the answers start below both). It never changes size, so nothing moves when a
   picture loads or changes. Decoration only: no tap, no focus, no long-press "Download image" menu in Chrome. The files are
   WebP with transparent backgrounds, shown with no card behind them (3 Nov 2026, Mufy; a cream card with round corners
   before), so the picture floats on the page; a new one simply takes the old one's place (the zoom below brings it in). */
.ask-head { display: flow-root; }
.ask-head > h1 { margin-top: 0.35em; }
.ask-pic {
  position: relative; float: right; width: 112px; height: 112px; margin: 10px 0 6px 12px;
  pointer-events: none; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;
}
/* (9 Nov 2026, Mufy: the redesign's stage 5) a backdrop behind the picture: none in light (no card, as on 3 Nov 2026); in dark a soft
   cream tile, so the teal outlines and pale shadows drawn for a light page read. The zoom's copy has the same */
.ask-pic img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; background: var(--color-picture-backdrop); border-radius: var(--picture-backdrop-radius); }
/* Larger text (2 Nov 2026, Mufy: "is there a way we can show the images also"): the picture sits above the question, centred
   and a little smaller, so the larger words keep the screen's full width; it still never changes size */
html.big-text .ask-pic { float: none; width: 96px; height: 96px; margin: 4px auto 8px; }
/* (later on 3 Nov 2026, Mufy: "remove the sticky bar") no pinned copy of the picture under the bar any more: the zoom below
   glides into the picture's own spot, even when the page has scrolled it off the screen */
/* (3 Nov 2026, Mufy) the zoom (plans.js picView): a copy of the new picture (.ask-zoom) over a veil of the page's cream
   (.ask-veil) across the whole screen, bar and all. plans.js moves both with transform and opacity only (Web Animations) and
   removes them as the copy lands, while the real picture waits hidden (is-zooming), off the screen or not. Neither takes a
   tap. plans.js adds neither with reduced motion or Less motion. */
.ask-veil { position: fixed; z-index: 20; top: 0; left: 0; right: 0; height: 100vh; background: var(--color-bg); opacity: 0; pointer-events: none; }
.ask-zoom {
  position: fixed; z-index: 21; margin: 0; opacity: 0; will-change: transform, opacity;
  pointer-events: none; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;
}
.ask-zoom img { display: block; width: 100%; height: 100%; background: var(--color-picture-backdrop); border-radius: var(--picture-backdrop-radius); }
.ask-pic.is-zooming img { visibility: hidden; }

/* ---------- the quiz's motion, Martha K.'s card and "Building your plan" (2 Nov 2026, Mufy; js/plans.js) ----------
   Calm and short, transform and opacity (the ticks', the ring's and the graph's strokes are the drawn exceptions, as the
   check-in's tick), nothing repeats and nothing flashes. Every animation sits inside the reduced-motion query with
   html:not(.less-motion), and plans.js adds the classes that start them only when engine.js lessMotion() says motion is fine,
   so with Less motion or the phone's reduced motion everything is in its end state at once. Nothing here is in a game.
   (2 Nov 2026, Mufy: "too fast") every time below was twice the first build's, half the speed, with plans.js in step; then
   (2 Nov 2026, Mufy) Mufy's own times, below, with plans.js in step; "Building your plan" is as it was (its own rules below).
   - Between the quiz's screens (plans.js quizShow): main.js show()'s slide, asked for with header's move (the titles are the
     same), as every screen's (stage 4, 9 Nov 2026; it was a 24 px move of its own, with the old screen simply gone).
   - An answer (and a reminder chip) picked (plans.js fillable): the picked colour fills it from the press, a circle under the
     words reaching the farthest corner (700 ms), in place of the press's own colour; the tick draws in (700 ms; 500 ms on
     "Building your plan").
   - The dot just reached fills as its question opens (the check-in's day-fill, 500 ms from 200 ms); the answer's line rises
     8 px as it fades in (1 s).
   - The recap's cards come in one after another, each rising over 500 ms, then the line under them, "I am ready" (from 2 s,
     rising over 2 s: --rise-ms) and Martha K.'s card (from 3 s, over 500 ms), all from --rise-at set by plans.js recap, each
     card's tick drawing in (--tick-at).
   - The reminder screen's bell swings once (1.5 s) when notifications are allowed there.
   - "Building your plan": the ring round the app's mark fills over 3 seconds (--build-ms), the five lines come in with their
     ticks (each line rising over 480 ms, each tick drawing over 500 ms), the graph's card rises, its line draws and its points
     fade in (--rise-at, --line-at, --dots-at), all still by about 2.8 s. */
.ask-opts .btn--choice { position: relative; isolation: isolate; overflow: hidden; }
@keyframes ask-fill { from { transform: translate(-50%, -50%) scale(0); } }
@keyframes ask-rise { from { opacity: 0; transform: translateY(8px); } }
@keyframes ask-bell { 15% { transform: rotate(12deg); } 35% { transform: rotate(-9deg); } 55% { transform: rotate(5deg); } 75% { transform: rotate(-2deg); } }
@keyframes build-draw { from { stroke-dashoffset: 100; } }
@keyframes build-fade { from { opacity: 0; } }
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .ask-opts .btn--choice.is-filling { background: var(--color-surface); }
  html:not(.less-motion) .ask-opts .btn--choice.is-filling::before {
    content: ""; position: absolute; z-index: -1; left: var(--fill-x, 50%); top: var(--fill-y, 50%);
    width: var(--fill-d, 0px); height: var(--fill-d, 0px); border-radius: 50%; background: var(--color-primary-container);
    transform: translate(-50%, -50%); pointer-events: none; animation: ask-fill 700ms var(--ease-out) backwards;
  }
  html:not(.less-motion) .ask-tick.is-new path { animation: done-tick 700ms var(--ease-out) var(--tick-at, 0ms) backwards; }
  html:not(.less-motion) .ask-step .steps__dot.is-new::before { animation-duration: 500ms; animation-delay: 200ms; }
  html:not(.less-motion) .ask-line.is-new { animation: ask-rise 1000ms var(--ease-out) backwards; }
  html:not(.less-motion) .ask-in.is-new { animation: ask-rise 500ms var(--ease-out) var(--rise-at, 0ms) backwards; }
  html:not(.less-motion) .ask-next.is-new { animation: done-rise var(--rise-ms, 2000ms) var(--ease-out) var(--rise-at, 0ms) backwards; }
  html:not(.less-motion) .ask-bell.is-ringing { animation: ask-bell 1500ms ease-in-out; }
  /* "Building your plan" keeps its own: each line (and the graph's card) rises over 480 ms, each tick draws over 500 ms */
  html:not(.less-motion) .build .ask-in.is-new { animation-duration: 480ms; }
  html:not(.less-motion) .build__list .ask-tick.is-new path { animation-duration: 500ms; }
  html:not(.less-motion) .build.is-moving .build__arc { animation: build-draw var(--build-ms, 3000ms) linear backwards; }
  html:not(.less-motion) .build.is-moving .build__line { animation: build-draw 1150ms var(--ease-out) var(--line-at, 1150ms) backwards; }
  html:not(.less-motion) .build.is-moving .build__dots { animation: build-fade 480ms var(--ease-out) var(--dots-at, 2300ms) backwards; }
}
/* the reminder screen's bell, beside its heading (decoration), hung from its top */
.ask-bellhead { display: flex; align-items: center; gap: 10px; }
.ask-bell { flex: none; display: block; width: 28px; height: 28px; color: var(--color-primary); transform-origin: 50% 8%; }
.ask-bell svg { display: block; width: 100%; height: 100%; }
/* Martha K.'s words on the recap: a soft card, a large faint opening quote mark in its corner, compact (two or three lines at
   360 px) */
.ask-quote { position: relative; margin: 0 0 12px; padding: 10px 14px 10px 42px; border-radius: var(--radius-m); background: var(--color-primary-container); }
.ask-quote::before {
  content: "\201C"; position: absolute; left: 9px; top: 2px; font: 700 var(--font-size-display-m)/1 var(--font-heading); color: var(--color-primary);
  opacity: 0.22; pointer-events: none;
}
.ask-quote__words { margin: 0; }
.ask-quote__words p { margin: 0; font-size: var(--font-size-body); line-height: 1.45; color: var(--color-text); }
.ask-quote__by { margin-top: 4px; font-size: var(--font-size-caption); font-weight: 600; color: var(--color-primary-strong); }
.ask-quote__note { display: block; font-weight: 400; color: var(--color-text-muted); }
/* "Building your plan": the ring round the app's mark, the five lines, then the graph's glimpse in its card */
.build h1 { margin-bottom: 0.3em; }
.build__art { display: flex; justify-content: center; margin: 4px 0 14px; }
.build__art svg { display: block; width: 96px; height: 96px; }
.build__arc, .build__line { stroke-dasharray: 100; stroke-dashoffset: 0; }
.build__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.build__list li { display: flex; align-items: flex-start; margin: 0; font-weight: 600; color: var(--color-primary-strong); }
.build__graph { margin: 16px 0 0; padding: 8px 10px; border: 1px solid var(--color-outline); border-radius: var(--radius-m); background: var(--color-surface); }
.build__graph svg { display: block; width: 100%; height: auto; max-height: 80px; }

/* ---------- the paywall's motion, "Everything in your plan" and the trial's spark (2 Nov 2026, Mufy; js/plans.js, main.js) ----------
   Calm, transform and opacity only, nothing flashes; only the button's glow comes back. Every animation sits inside the
   reduced-motion query with html:not(.less-motion), and the scripts add the classes that start them only when lessMotion() says
   motion is fine, so with Less motion or the phone's reduced motion everything is in its end state at once and nothing is left hidden.
   (2 Nov 2026, Mufy: "too fast") every time below was twice the first build's, half the speed; then (2 Nov 2026, Mufy) Mufy's
   own times, below, with plans.js in step.
   - The plan screen's timeline (plans.js planScreen): the line grows down from Today to the charge day over 2 s, a part between
     each two days (--tl-seg-at, --tl-seg-ms), while each day's dot fills in turn over 1 s (--tl-dot-at: 0, 900 and 1800 ms);
     another plan chosen: it draws again from the start.
   - The chosen plan row sits 2 px higher with a soft shadow (500 ms); the "Save N%" badge pops in once (1 s from 240 ms, a small
     overshoot).
   - The main buying button ("Try for", "Start my free trial now", "Subscribe for"): a soft glow in its own colour 5 seconds after
     the screen opens, in and out over 3 s, on a layer of its own (opacity only), then again 5 s after each glow has ended, while
     the screen is up (plans.js glowRepeat adds is-glowing for each); never while it is busy or disabled, never after a press on it.
   - "Your free trial has started" (main.js sparkNode): the app's orange spark twists in and settles once (1.5 s from 320 ms). */
@keyframes tl-grow { from { transform: scaleY(0); } }
@keyframes badge-pop {
  0% { opacity: 0; transform: translateY(-50%) scale(0.6); }
  70% { opacity: 1; transform: translateY(-50%) scale(1.08); }
  100% { opacity: 1; transform: translateY(-50%) scale(1); }
}
@keyframes cta-glow { 50% { opacity: 1; } }
@keyframes spark-twist {
  0% { opacity: 0; transform: scale(0.2) rotate(-45deg); }
  60% { opacity: 1; transform: scale(1.15) rotate(-5deg); }
  100% { opacity: 1; transform: scale(1) rotate(0deg); }
}
.plans__glow { position: relative; }
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .timeline.is-drawing .timeline__item::before { animation: day-fill 1000ms var(--ease-out) var(--tl-dot-at, 0ms) backwards; }
  html:not(.less-motion) .timeline.is-drawing .timeline__item:not(:last-child)::after {
    transform-origin: 50% 0; animation: tl-grow var(--tl-seg-ms, 1000ms) linear var(--tl-seg-at, 0ms) backwards;
  }
  html:not(.less-motion) .timeline.is-swapped .timeline__item { animation: build-fade 320ms var(--ease-out) backwards; }
  html:not(.less-motion) .plan-row { transition: transform 500ms var(--ease-out), box-shadow 500ms var(--ease-out); }
  html:not(.less-motion) .plan-row.is-chosen:not(.plan-row--only) { transform: translateY(-2px); box-shadow: var(--elevation-2); }
  html:not(.less-motion) .plan-row__badge.is-new { animation: badge-pop 1000ms var(--ease-out) 240ms backwards; }
  html:not(.less-motion) .plans__glow:not([disabled])::after {
    content: ""; position: absolute; inset: -2px; border-radius: inherit; pointer-events: none; opacity: 0;
    box-shadow: var(--glow-cta);
  }
  html:not(.less-motion) .plans__glow.is-glowing:not([disabled])::after { animation: cta-glow 3000ms ease-in-out; }
  html:not(.less-motion) .spark-line__spark.is-new { animation: spark-twist 1500ms var(--ease-out) 320ms backwards; }
}
/* "Everything in your plan" on the first paywall screen, below its button: a small teal icon before each line; the cover line
   (the group icon) first and highlighted when the quiz's friends and family answer was Yes, else last and plain; the earn line,
   when the server shows it, under the cover line in smaller type */
.plan-list { margin: 22px 0 6px; }
.plan-list h2 { margin: 0 0 10px; font-size: var(--font-size-lead); }
.plan-list__items { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.plan-list__item { display: flex; align-items: flex-start; gap: 10px; margin: 0; }
.plan-list__icon { flex: none; display: block; width: 1.3em; height: 1.3em; margin-top: 0.05em; color: var(--color-primary); }
.plan-list__icon svg { display: block; width: 100%; height: 100%; }
.plan-list__cover.is-first { padding: 10px 12px; border-radius: var(--radius-m); background: var(--color-primary-container); font-weight: 600; color: var(--color-primary-strong); }
.plan-list__earn { display: block; margin-top: 4px; font-size: var(--font-size-caption); font-weight: 400; color: var(--color-text-muted); }
/* the app's spark beside "Your free trial has started" (the sign-up screen's note and home's notice line) */
.spark-line { display: flex; align-items: flex-start; gap: 10px; }
.notice.spark-line { align-items: center; }
.spark-line__spark { flex: none; display: block; width: 1.5em; height: 1.5em; }
.spark-line__spark svg { display: block; width: 100%; height: 100%; }

/* The Android install screen (31 Oct 2026, Mufy; js/getapp.js): Install from Google Play and I have the app: open it,
   stacked at full width, 56 px tall, then the browser link (48 px, .link), the account and diagnosis lines, Read aloud
   and Terms and Privacy Policy. Nothing on it moves. Also get-app.html's other screen. */
.getapp__buttons { display: grid; gap: 12px; margin: 20px 0 8px; }
.getapp__buttons .btn { display: flex; align-items: center; justify-content: center; width: 100%; min-height: 56px; margin: 0; text-align: center; text-decoration: none; }
.getapp__lead { font-size: var(--font-size-lead); text-wrap: pretty; }
.getapp__browser { font-size: var(--font-size-body); }
.getapp .centre { margin: 4px 0 8px; }
.getapp a.link { display: inline-block; }
a.bar__back { display: grid; place-items: center; text-decoration: none; }
/* (31 Oct 2026, Mufy) a member's own invite code, shown for typing: "ABCDE 23456" (Settings > Groups, Run your group) */
.gcode { white-space: nowrap; letter-spacing: 0.06em; font-variant-numeric: tabular-nums; font-weight: 600; }

/* (1 Nov 2026, Mufy) the paywall's test mode (bin/plans.php --test on): a pill on every screen's bar, and the
   pretend payment sheet that stands in for Google Play's (js/plans.js testSheet) */
.bar__test {
  flex: none; padding: 3px 9px; border: 1px solid var(--color-on-primary-outline); border-radius: var(--radius-full);
  font-size: var(--font-size-caption); line-height: var(--line-height-caption); font-weight: 700; white-space: nowrap;
}
.testsheet {
  position: fixed; inset: 0; z-index: 60; display: grid; align-items: end; justify-items: center;
  padding: 16px; background: var(--color-scrim);
}
.testsheet__card {
  width: 100%; max-width: 420px; display: grid; gap: 10px; padding: 20px 16px;
  background: var(--color-surface); color: var(--color-text); border-radius: var(--radius-l); box-shadow: var(--elevation-3);
}
/* (9 Nov 2026, stage 3) its heading is the dialogs' title (.dlg__title, in a .dlg__head with the Close icon), a little smaller */
.testsheet__card .dlg__title { font-size: var(--font-size-lead); }
.testsheet__card p { margin: 0 0 6px; }

/* Awareness personalities (6 Nov 2026, Mufy; js/personality.js): a symbol's disc, the shared page (js/r.js: kind
   personality) and the 24 in their four families. The app's own screens can reuse .pers-mini (its
   size set where it is used), .pers-hero, .pers-all, .pers-strengths and .pers-line. Nothing here moves. */
.pers-mini { display: grid; place-items: center; flex: none; width: 48px; height: 48px; border-radius: 50%; }
.pers-mini > span { display: grid; place-items: center; width: 80%; height: 80%; border-radius: 50%; background: var(--color-symbol-ground); }
.pers-mini svg { display: block; width: 78%; height: 78%; }
.pers-hero { display: grid; place-items: center; margin: 8px 0 4px; }
.pers-hero .pers-mini { width: 144px; height: 144px; }
.pers-page .pers-title, .pers-page .pers-which { text-align: center; }
.pers-family { margin: 0 0 10px; text-align: center; color: var(--color-text-muted); font-weight: 600; }
.pers-line { margin: 0 0 12px; text-align: center; font-family: var(--font-heading); font-size: var(--font-size-lead); line-height: 1.35; color: var(--color-primary-strong); }
/* (9 Nov 2026, fix) a little less room round the words (0.5em, from 12 px) and between the strengths, so three fit one row at 412 px
   at body size ("Philosophical" was alone on the second) */
.pers-strengths { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; margin: 0 0 14px; padding: 0; list-style: none; }
/* (9 Nov 2026, stage 5 fix) the chips keep a light ground in dark (--color-pers-chip): their words are the personality's colour */
.pers-strengths li { padding: 3px 0.5em; border: 2px solid; border-radius: var(--radius-full); background: var(--color-pers-chip); font-size: var(--font-size-body); font-weight: 700; }
/* the 24: three a row on a phone, six from 480 px; the sharer's ringed in the app's dark orange, with its name in bold */
.pers-all { display: grid; gap: 16px; margin: 10px 0 6px; }
.pers-fam__name { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; margin: 0 0 8px; font-family: var(--font-body); font-size: var(--font-size-body); font-weight: 700; color: var(--color-primary-strong); }
.pers-fam__dot { flex: none; width: 10px; height: 10px; border-radius: 50%; }
.pers-fam__lead { font-weight: 400; color: var(--color-text-muted); }
.pers-tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px 8px; margin: 0; padding: 0; list-style: none; }
@media (min-width: 480px) { .pers-tiles { grid-template-columns: repeat(6, minmax(0, 1fr)); } }
.pers-tile { display: grid; justify-items: center; gap: 4px; text-align: center; font-size: var(--font-size-caption); line-height: 1.2; }
.pers-tile .pers-mini { width: 56px; height: 56px; }
.pers-tile.is-theirs .pers-mini { box-shadow: 0 0 0 3px var(--color-bg), 0 0 0 6px var(--color-accent-strong); }
.pers-tile.is-theirs .pers-tile__name { font-weight: 700; color: var(--color-primary-strong); }
.pers-find .fine { margin: 0; }
/* (7 Nov 2026, Mufy: "remove ask someone to guess it") the "Guess my personality" page's styles went with it. */

/* ---------- Awareness personalities in the app's own screens (6 Nov 2026, Mufy; main.js "Awareness personalities") ----------
   The teaser before the reveal (the 24 faintly, a line), the sealed card ("Your Awareness personality is ready." / "Tap to
   open": a white seal with the four families' colours round a "?"), the personality opened (the symbol drawing in, then its
   words one after another), the change line, My results' card and See all 24 (which reuses .pers-all above). On the score
   screen it waits while the envelope is sealed, as the share card does, and rises in with it. Every movement is under the
   motion gate at the end: with Settings' Less motion or the phone's reduced motion, everything is simply there. The seal
   breathes twice, gently, then is still (over by 3.7 s: WCAG 2.2.2's five seconds); the opening is over by 1.6 s. Nothing
   here reads which personality it is, and nothing is coloured to mean better or worse. */
.hero--score.is-sealed ~ .pers-moment, .hero--score.is-opening ~ .pers-moment { display: none; }
.pers-moment { margin: 1rem 0 1.25rem; }
.card--check .pers-teaser { margin: 12px 0 2px; }
.pers-teaser { margin: 10px 0 4px; }
.pers-teaser__row { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 6px; max-width: 360px; margin: 0 auto 8px; opacity: 0.4; }
.pers-teaser__row .pers-mini { width: 100%; height: auto; aspect-ratio: 1 / 1; }
.pers-teaser__line { margin: 0; text-align: center; font-size: var(--font-size-body); color: var(--color-text-muted); }
.pers-seal {
  display: flex; align-items: center; gap: 14px; width: 100%; min-height: 88px; margin: 0; padding: 14px 16px;
  border: 2px solid var(--color-primary); border-radius: var(--radius-l); background: var(--color-primary-container);
  color: var(--color-primary-strong); font: inherit; text-align: left; cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.pers-seal:focus-visible { outline: var(--focus-ring); outline-offset: 2px; }
.pers-seal:active { background: var(--color-primary-container-pressed); }   /* a press shows in colour (30 Oct 2026, Mufy) */
.pers-seal__art { flex: none; display: block; width: 60px; height: 60px; }
.pers-seal__art svg { display: block; width: 100%; height: 100%; }
.pers-seal__words { display: grid; gap: 2px; min-width: 0; }
.pers-seal__title { font: 700 var(--font-size-lead)/1.3 var(--font-heading); color: var(--color-primary-strong); }
.pers-seal__hint { font-size: var(--font-size-body); font-weight: 600; color: var(--color-primary); }
/* the personality opened (the score screen's reveal, and its own screen) */
.pers-open { margin: 0 0 0.5rem; }
.pers-hero { position: relative; }
.pers-hero__ring { position: absolute; left: 50%; top: 50%; width: 164px; height: 164px; margin: -82px 0 0 -82px; border: 3px solid var(--color-accent); border-radius: 50%; opacity: 0; pointer-events: none; }
.pers-you { margin: 0.25em 0 0.2em; text-align: center; font-size: var(--font-size-headline); }
.pers-you:focus { outline: none; }   /* focused when it opens, for screen readers */
.pers-portrait { margin: 0 0 14px; }
.pers-pair { margin: 0 0 6px; text-align: center; font-weight: 600; color: var(--color-primary-strong); }
/* (7 Nov 2026, Mufy) the note under it: the personality comes from the average of all the check-ins */
.pers-average { margin: 12px 0 4px; padding: 10px 14px; border-radius: var(--radius-m); background: var(--color-surface-variant); color: var(--color-text); font-size: var(--font-size-body); line-height: 1.45; text-align: center; }
.pers-actions .share-card { margin-top: 0.75rem; }
.pers-honest { margin: 12px 0 0; text-align: center; }
/* "Your Awareness personality is now The Navigator. Before, you were The Spark." */
.pers-change__head { display: flex; align-items: center; gap: 12px; }
.pers-change__head .pers-mini { width: 56px; height: 56px; }
.pers-change__text { margin: 0; font: 700 var(--font-size-lead)/1.35 var(--font-heading); color: var(--color-primary-strong); }
.pers-change .share-card { margin: 0.75rem 0 0.25rem; }   /* (7 Nov 2026, Mufy) its share card with the picture */
.pers-change > .btn { margin-top: 8px; }
/* My results' card: the symbol, "Your Awareness personality" and its name, opening its screen */
.pers-card {
  display: flex; align-items: center; gap: 14px; width: 100%; min-height: 80px; margin: 6px 0 14px; padding: 12px 14px;
  border: 1px solid var(--color-outline); border-radius: var(--radius-l); background: var(--color-surface); box-shadow: var(--elevation-1);
  color: var(--color-text); font: inherit; text-align: left; cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.pers-card:focus-visible { outline: var(--focus-ring); outline-offset: 2px; }
.pers-card:active { background: var(--color-primary-container); }
.pers-card .pers-mini { width: 56px; height: 56px; }
.pers-card__words { display: grid; flex: 1 1 auto; min-width: 0; }
.pers-card__label { font-size: var(--font-size-caption); color: var(--color-text-muted); }
.pers-card__name { font: 700 var(--font-size-title)/1.3 var(--font-heading); color: var(--color-primary-strong); }
.pers-card__go { flex: none; display: grid; color: var(--color-text-muted); }   /* (9 Nov 2026, stage 3) js/icons.js's chevron_right; (fix) muted, as My results' */
/* (7 Nov 2026, Mufy: "make the app show the personality after each checkin ... show the users personality on the home
   page") the score screen's personality on a check-in that neither reveals nor changes it (main.js persDaily): the symbol
   smaller than the reveal's, its words centred as the reveal's, then its share card; home's card once today's check-in is
   done (persHomeShare): "Your Awareness personality", the symbol beside the name and the tagline, then the share card with
   the picture; before today's check-in, My results' small card (persCardButton). Nothing here moves. */
.pers-daily .pers-hero .pers-mini { width: 96px; height: 96px; }
.pers-daily .pers-hero__ring { display: none; }
.pers-daily .pers-you { font-size: var(--font-size-title); }
.pers-card.pers-home { margin: 14px 0; }
.pers-today__h { margin: 0 0 10px; }
.pers-today__head { display: flex; align-items: center; gap: 14px; }
.pers-today__head .pers-mini { width: 64px; height: 64px; }
.pers-today__words { display: grid; gap: 2px; min-width: 0; }
.pers-today__name { margin: 0; font: 700 var(--font-size-title)/1.3 var(--font-heading); color: var(--color-primary-strong); }
.pers-today__line { margin: 0; }
.pers-today .share-card { margin: 0.9rem 0 0.25rem; }
/* See all 24's family headings are h2 there (h3 on the shared page) */
.screen > .pers-all .pers-fam__name { margin-top: 0; }
/* a personality's share picture is taller (js/share.js pictureBox: 1080 x 1350, or 1080 x 1920 "For your Status"), and the
   Status size says under it how to post it */
.share-card__pic-box.is-tall .share-card__pic { width: min(260px, 100%, 36vh); aspect-ratio: 1080 / 1350; }
.share-card__pic-box.is-status .share-card__pic { width: min(200px, 56vw, 32vh); aspect-ratio: 1080 / 1920; }
.share-pic-box.is-tall .share-pic { width: min(190px, 50vw, 28vh); aspect-ratio: 1080 / 1350; }
.share-pic-box.is-status .share-pic { width: min(150px, 42vw, 26vh); aspect-ratio: 1080 / 1920; }
.share-pic__tip { margin: 8px 0 0; text-align: center; }
.share-pic__tip[hidden] { display: none; }
@keyframes pers-pop { 0% { opacity: 0; transform: scale(0.3) rotate(-24deg); } 60% { opacity: 1; transform: scale(1.08) rotate(4deg); } 100% { transform: none; } }
@keyframes pers-draw { from { opacity: 0; transform: scale(0.5) rotate(-40deg); } }
@keyframes pers-ring { 0% { opacity: 0; transform: scale(0.7); } 15% { opacity: 0.8; } 100% { opacity: 0; transform: scale(1.35); } }
@keyframes pers-breathe { 0%, 100% { transform: none; } 50% { transform: scale(1.06); } }
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .pers-moment.is-new { animation: card-rise var(--duration-slow) var(--ease-out) backwards; }
  html:not(.less-motion) .pers-seal__art svg { animation: pers-breathe 1400ms ease-in-out 900ms 2; }
  html:not(.less-motion) .pers-open.is-opening .pers-hero .pers-mini { animation: pers-pop 560ms var(--ease-out) backwards; }
  html:not(.less-motion) .pers-open.is-opening .pers-hero .pers-mini svg { animation: pers-draw 520ms var(--ease-out) 200ms backwards; }
  html:not(.less-motion) .pers-open.is-opening .pers-hero__ring { animation: pers-ring 700ms var(--ease-out) 380ms; }
  html:not(.less-motion) .pers-open.is-opening .pers-step { animation: done-rise var(--duration-slow) var(--ease-out) var(--pers-d, 600ms) backwards; }
}

/* ---------- Awareness personalities in groups (6 Nov 2026, Mufy: phase 2; js/groups.js, main.js persAskBox) ----------
   On a board, a member's symbol and name next to their week circles (wrapping under them on a narrow screen), only where
   they show it to that group; under Who's in, "5 of the 24 are in your group. Who's next?" with Invite, and "Share our
   group's personalities". In the opened personality, the one quiet ask, "Show your Awareness personality to your groups?".
   Nothing here moves, and nothing is coloured to mean better or worse. */
.gmember__pers { display: inline-flex; align-items: center; gap: 6px; min-width: 0; font-size: var(--font-size-caption); font-weight: 600; color: var(--color-primary-strong); }
.gmember__pers .pers-mini { width: 28px; height: 28px; }
.gpers { margin: 4px 0 12px; }
.gpers__line { margin: 0 0 4px; font-weight: 600; color: var(--color-primary-strong); }
.pers-ask { margin: 14px 0 6px; padding: 12px 14px; border: 1px solid var(--color-outline); border-radius: var(--radius-m); background: var(--color-surface); }
.pers-ask__q { margin: 0 0 4px; font-weight: 700; color: var(--color-primary-strong); }
.pers-ask__fine { margin: 0 0 8px; }
.pers-ask__tools { display: flex; flex-wrap: wrap; gap: 8px; }
.pers-ask__tools .btn { margin: 0; }
.pers-ask__said { margin: 6px 0 0; font-weight: 600; color: var(--color-primary-strong); }
.pers-ask__said:empty { display: none; }
.pers-ask__said:focus { outline: none; }
.pers-ask > .pers-ask__said:only-child { margin: 0; }

/* ---------- Blossom, the little guide (9 Nov 2026, Mufy: "build blossom into the app with the props"; "Everyone, with a switch
   to hide it") ----------
   js/blossom.js draws Blossom (an <svg class="blossom bl-<mood> bl-<size>">) and the guide row (.bl-guide: Blossom and its speech
   bubble, .bl-say). The drawing keeps its own colours, as a picture does; the bubble is on the tokens. The moods are classes on the
   drawing: bl-idle, bl-wave, bl-wink, bl-cheer, bl-fly, bl-sleepy, bl-watch, bl-think, bl-proud. Every movement is opt-in, as
   everywhere in the app: it runs only when the phone doesn't ask for reduced motion and Settings' Less motion is off. Otherwise
   Blossom keeps still in its mood's pose. At rest Blossom only breathes, slowly and by 2 px, and blinks; beside a game (watch) it
   nods once and then keeps still, so it never pulls the eye from the game. Settings' Show helper off puts bl-off on <html>. */
.blossom {
  /* its shadow, its z's, its sparkles: tokens since 9 Nov 2026 (fix), so a dark theme redefines them on :root only */
  --bl-shadow: var(--color-blossom-shadow); --bl-z: var(--color-blossom-z); --bl-sparkle: var(--color-blossom-sparkle);
  display: block; flex: none; overflow: visible; width: 72px; height: 81.7px;
}
.blossom.bl-small { width: 56px; height: 63.6px; }
.blossom.bl-big { width: 120px; height: 136.2px; }
html.bl-off .blossom, html.bl-off .bl-guide[data-kind="chatter"] { display: none; }
/* the rig (scratchpad mascots3/rig.css, 8 Nov 2026): each part turns about its own joint, in the drawing's units */
.blossom .bl-armL, .blossom .bl-armR { transform-box: view-box; }
.blossom .bl-armL { transform-origin: -28px 6px; }
.blossom .bl-armR { transform-origin: 28px 6px; }
.blossom .bl-eyes, .blossom .bl-eyeL, .blossom .bl-eyeR { transform-box: fill-box; transform-origin: 50% 50%; }
.blossom .bl-bob { transform-box: view-box; transform-origin: 0px 50px; }
.blossom .bl-fly { transform-box: view-box; transform-origin: 0px 0px; }
.blossom .bl-sleepeyes, .blossom .bl-open, .blossom .bl-o, .blossom .bl-z, .blossom .bl-fx { opacity: 0; }
.blossom .bl-fx path { fill: var(--bl-sparkle); transform-box: fill-box; transform-origin: 50% 50%; }
.blossom .bl-z path { fill: none; stroke: var(--bl-z); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.blossom .bl-shadow { fill: var(--bl-shadow); opacity: 0.13; transform-box: fill-box; transform-origin: 50% 50%; }
/* each mood's face, the same still or moving. (9 Nov 2026, fix) think keeps its smile: at 56 px its eyes' shift can't be seen,
   and the small "o" alone looked startled */
.blossom.bl-cheer .bl-smile, .blossom.bl-proud .bl-smile, .blossom.bl-sleepy .bl-smile { opacity: 0; }
.blossom.bl-cheer .bl-open, .blossom.bl-proud .bl-open, .blossom.bl-cheer .bl-fx, .blossom.bl-proud .bl-fx { opacity: 1; }
.blossom.bl-sleepy .bl-o, .blossom.bl-sleepy .bl-sleepeyes, .blossom.bl-sleepy .bl-z { opacity: 1; }
.blossom.bl-sleepy .bl-eyes { opacity: 0; }
.blossom.bl-think .bl-eyes { transform: translate(2px, -2px); }   /* eyes up to the side */
.blossom.bl-proud .bl-armR { transform: rotate(-24deg); }
.blossom.bl-proud .bl-armL { transform: rotate(24deg); }
/* the poses that stand in for movement: with the phone's reduced motion, and with Settings' Less motion. (9 Nov 2026, fix) cheer's
   is where its movement ends: arms 20 degrees up, on the ground, no sparkles */
@media (prefers-reduced-motion: reduce) {
  .blossom.bl-wave .bl-armR { transform: rotate(-55deg); }
  .blossom.bl-wave.bl-has-prop .bl-armR { transform: rotate(-30deg); }
  .blossom.bl-wink .bl-eyeR { transform: scaleY(0.12); }
  .blossom.bl-wink .bl-bob { transform: rotate(-4deg); }
  .blossom.bl-cheer .bl-armR { transform: rotate(-20deg); }
  .blossom.bl-cheer .bl-armL { transform: rotate(20deg); }
  .blossom.bl-cheer .bl-fx { opacity: 0; }
  .blossom.bl-fly .bl-fly { transform: translateY(-12px) rotate(-4deg); }
  .blossom.bl-fly .bl-armR { transform: rotate(-32deg); }
  .blossom.bl-fly .bl-armL { transform: rotate(32deg); }
  .blossom.bl-fly .bl-shadow { transform: scale(0.6); opacity: 0.07; }
}
:where(html.less-motion) .blossom.bl-wave .bl-armR { transform: rotate(-55deg); }
:where(html.less-motion) .blossom.bl-wave.bl-has-prop .bl-armR { transform: rotate(-30deg); }
:where(html.less-motion) .blossom.bl-wink .bl-eyeR { transform: scaleY(0.12); }
:where(html.less-motion) .blossom.bl-wink .bl-bob { transform: rotate(-4deg); }
:where(html.less-motion) .blossom.bl-cheer .bl-armR { transform: rotate(-20deg); }
:where(html.less-motion) .blossom.bl-cheer .bl-armL { transform: rotate(20deg); }
:where(html.less-motion) .blossom.bl-cheer .bl-fx { opacity: 0; }
:where(html.less-motion) .blossom.bl-fly .bl-fly { transform: translateY(-12px) rotate(-4deg); }
:where(html.less-motion) .blossom.bl-fly .bl-armR { transform: rotate(-32deg); }
:where(html.less-motion) .blossom.bl-fly .bl-armL { transform: rotate(32deg); }
:where(html.less-motion) .blossom.bl-fly .bl-shadow { transform: scale(0.6); opacity: 0.07; }
/* the movements */
@keyframes bl-blink { 0%, 92%, 100% { transform: scaleY(1); } 95% { transform: scaleY(0.12); } 97% { transform: scaleY(1); } }
@keyframes bl-breathe { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-2px); } }
@keyframes bl-shade-breathe { 0%, 100% { transform: scale(1); } 50% { transform: scale(0.96); } }
@keyframes bl-wave { 0%, 100% { transform: rotate(0); } 20% { transform: rotate(-58deg); } 40% { transform: rotate(-26deg); } 60% { transform: rotate(-58deg); } 80% { transform: rotate(-26deg); } }
@keyframes bl-wink { 0%, 25%, 70%, 100% { transform: scaleY(1); } 35%, 60% { transform: scaleY(0.1); } }
@keyframes bl-tilt { 0%, 100% { transform: rotate(0); } 40%, 60% { transform: rotate(-5deg); } }
@keyframes bl-jump { 0%, 100% { transform: translateY(0) scale(1, 1); } 18% { transform: translateY(2px) scale(1.07, 0.9); } 45% { transform: translateY(-14px) scale(0.96, 1.04); } 72% { transform: translateY(0) scale(1.05, 0.95); } }
/* (9 Nov 2026, fix) the arms go up twice in one run and stay a little up at its end (the still pose's angle) */
@keyframes bl-upR { 0%, 50% { transform: rotate(0); } 22.5%, 72.5% { transform: rotate(-52deg); } 100% { transform: rotate(-20deg); } }
@keyframes bl-upL { 0%, 50% { transform: rotate(0); } 22.5%, 72.5% { transform: rotate(52deg); } 100% { transform: rotate(20deg); } }
@keyframes bl-pop { 0% { opacity: 0; transform: scale(0.2) rotate(0); } 35% { opacity: 1; transform: scale(1.15) rotate(25deg); } 100% { opacity: 0; transform: scale(0.8) rotate(45deg); } }
@keyframes bl-shade-jump { 0%, 100% { transform: scale(1); opacity: 0.13; } 18% { transform: scale(1.08); } 45% { transform: scale(0.7); opacity: 0.07; } 72% { transform: scale(1.04); } }
@keyframes bl-float { 0%, 100% { transform: translate(0, -8px) rotate(-3deg); } 50% { transform: translate(3px, -16px) rotate(3deg); } }
@keyframes bl-flapR { from { transform: rotate(-8deg); } to { transform: rotate(-38deg); } }
@keyframes bl-flapL { from { transform: rotate(8deg); } to { transform: rotate(38deg); } }
@keyframes bl-shade-float { 0%, 100% { transform: scale(0.66); opacity: 0.08; } 50% { transform: scale(0.52); opacity: 0.05; } }
@keyframes bl-doze { 0%, 100% { transform: scale(1, 1); } 50% { transform: scale(1.03, 0.97); } }
@keyframes bl-z { 0% { opacity: 0; transform: translate(0, 0); } 20% { opacity: 1; } 100% { opacity: 0; transform: translate(8px, -14px); } }
@keyframes bl-nod { 0%, 100% { transform: none; } 35% { transform: translateY(2px) scale(1.02, 0.96); } 70% { transform: translateY(-1px); } }
@keyframes bl-twinkle { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.3; transform: scale(0.7); } }
@keyframes bl-say-in { from { opacity: 0; transform: translateY(6px) scale(0.96); } }
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .blossom .bl-eyes { animation: bl-blink 5.2s infinite; }
  html:not(.less-motion) .blossom.bl-wink .bl-eyes, html:not(.less-motion) .blossom.bl-think .bl-eyes, html:not(.less-motion) .blossom.bl-sleepy .bl-eyes { animation: none; }
  html:not(.less-motion) .blossom.bl-watch .bl-eyes { animation: none; }   /* (9 Nov 2026, fix) beside a game: no blink either */
  html:not(.less-motion) .blossom.bl-idle .bl-bob, html:not(.less-motion) .blossom.bl-wave .bl-bob, html:not(.less-motion) .blossom.bl-think .bl-bob, html:not(.less-motion) .blossom.bl-proud .bl-bob { animation: bl-breathe 4.8s ease-in-out infinite; }
  html:not(.less-motion) .blossom.bl-idle .bl-shadow, html:not(.less-motion) .blossom.bl-wave .bl-shadow, html:not(.less-motion) .blossom.bl-think .bl-shadow, html:not(.less-motion) .blossom.bl-proud .bl-shadow { animation: bl-shade-breathe 4.8s ease-in-out infinite; }
  html:not(.less-motion) .blossom.bl-wave .bl-armR { animation: bl-wave 1.5s ease-in-out 2; }
  html:not(.less-motion) .blossom.bl-wink .bl-eyeR { animation: bl-wink 1.4s ease-in-out 2; }
  html:not(.less-motion) .blossom.bl-wink .bl-bob { animation: bl-tilt 1.4s ease-in-out 2; }
  html:not(.less-motion) .blossom.bl-cheer .bl-bob { animation: bl-jump 0.9s var(--ease-out) 2; }
  html:not(.less-motion) .blossom.bl-cheer .bl-armR { animation: bl-upR 1.8s var(--ease-out) both; }
  html:not(.less-motion) .blossom.bl-cheer .bl-armL { animation: bl-upL 1.8s var(--ease-out) both; }
  html:not(.less-motion) .blossom.bl-cheer .bl-shadow { animation: bl-shade-jump 0.9s var(--ease-out) 2; }
  html:not(.less-motion) .blossom.bl-cheer .bl-fx path { animation: bl-pop 1.2s ease-out both; }
  html:not(.less-motion) .blossom.bl-cheer .bl-fx path:nth-child(2) { animation-delay: 120ms; }
  html:not(.less-motion) .blossom.bl-cheer .bl-fx path:nth-child(3) { animation-delay: 240ms; }
  html:not(.less-motion) .blossom.bl-cheer .bl-fx path:nth-child(4) { animation-delay: 360ms; }
  html:not(.less-motion) .blossom.bl-fly .bl-fly { animation: bl-float 2.6s ease-in-out infinite; }
  html:not(.less-motion) .blossom.bl-fly .bl-armR { animation: bl-flapR 0.32s ease-in-out infinite alternate; }
  html:not(.less-motion) .blossom.bl-fly .bl-armL { animation: bl-flapL 0.32s ease-in-out infinite alternate; }
  html:not(.less-motion) .blossom.bl-fly .bl-shadow { animation: bl-shade-float 2.6s ease-in-out infinite; }
  html:not(.less-motion) .blossom.bl-sleepy .bl-bob { animation: bl-doze 3.6s ease-in-out infinite; }
  html:not(.less-motion) .blossom.bl-sleepy .bl-z path { animation: bl-z 3s ease-in infinite backwards; }
  html:not(.less-motion) .blossom.bl-sleepy .bl-z path:nth-child(2) { animation-delay: 1s; }
  html:not(.less-motion) .blossom.bl-sleepy .bl-z path:nth-child(3) { animation-delay: 2s; }
  html:not(.less-motion) .blossom.bl-watch .bl-bob { animation: bl-nod 0.7s var(--ease-out) 1; }
  html:not(.less-motion) .blossom.bl-proud .bl-fx path { animation: bl-twinkle 1.8s ease-in-out 3; }
  html:not(.less-motion) .blossom.bl-proud .bl-fx path:nth-child(2) { animation-delay: 0.45s; }
  html:not(.less-motion) .blossom.bl-proud .bl-fx path:nth-child(3) { animation-delay: 0.9s; }
  html:not(.less-motion) .blossom.bl-proud .bl-fx path:nth-child(4) { animation-delay: 1.35s; }
  html:not(.less-motion) .bl-say { animation: bl-say-in var(--duration-slow) var(--ease-out) backwards; }
}
/* the tap (9 Nov 2026, Mufy: "make the helper react with an animation when it is tapped"). js/blossom.js ("the tap") puts bl-tap
   and one of bl-tap1 (a little hop with squash and stretch, and a wink), bl-tap2 (a giggle: a quick wiggle with happy closed eyes
   and an open smile) or bl-tap3 (a twirl: the flower spins once and five sparkles pop out of it, eyes open, smile open) on the
   drawing while a reaction lasts, each under a second. They move layers of their own: bl-react around the body (its joint at the
   feet; the giggle's in the middle of the body), bl-rshade around the shadow, bl-flower and the sparkles of bl-rfx, so the mood's
   own movements carry on inside them and the prop stays in the mitt. The faces below win over the mood's while the class is on,
   still or moving. Measured in Chrome (9 Nov 2026): with every mood and prop the app draws, everything stays inside the drawing's
   box (a mood's opening movement still running is eased into its resting pose first: js/blossom.js settle). With Less motion or
   the phone's reduced motion nothing moves: only the face (js/blossom.js: the wink, then the happy face). The drawing takes the
   tap itself: no grey flash, no text selection or callout, and a pointer only where there is a mouse. */
.blossom {
  --bl-sparkle-2: var(--color-blossom-sparkle-2); --bl-lift: 1;   /* --bl-lift: how far the hop and the twirl lift it (none in the air: fly) */
  -webkit-tap-highlight-color: transparent; touch-action: manipulation;
  user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;
}
@media (hover: hover) { .blossom { cursor: pointer; } }
.blossom .bl-react, .blossom .bl-rshade, .blossom .bl-flower, .blossom .bl-rfx g { transform-box: view-box; }
.blossom .bl-react { transform-origin: 0px 54px; }
.blossom.bl-tap.bl-tap2 .bl-react { transform-origin: 0px 24px; }   /* the giggle rocks about the middle of the body */
.blossom.bl-fly { --bl-lift: 0; }
.blossom.bl-fly .bl-react { transform-origin: 0px -20px; }   /* (in the air: its squash and stretch about its middle) */
.blossom .bl-rshade { transform-origin: 0px 55px; }
.blossom .bl-flower { transform-origin: 0px 0px; }        /* the flower's middle: its layer sits in the flower's own place */
.blossom .bl-rfx g { transform-origin: 0px -48.4px; }     /* the flower's middle again: each sparkle flies out from it */
.blossom .bl-rfx path { fill: var(--bl-sparkle); transform-box: fill-box; transform-origin: 50% 50%; }
.blossom .bl-rfx g:nth-child(even) path { fill: var(--bl-sparkle-2); }
.blossom .bl-joy, .blossom .bl-rfx { opacity: 0; }
/* the faces: 1 the wink (the right eye closed, smiling), 2 happy closed eyes and an open smile, 3 eyes open and an open smile */
.blossom.bl-tap .bl-eyes { opacity: 1; }   /* (a sleepy helper's eyes open for it) */
.blossom.bl-tap .bl-sleepeyes, .blossom.bl-tap .bl-o, .blossom.bl-tap .bl-z { opacity: 0; }
.blossom.bl-tap.bl-tap1 .bl-eyeR, .blossom.bl-tap.bl-tap1 .bl-joyL, .blossom.bl-tap.bl-tap1 .bl-open { opacity: 0; }
.blossom.bl-tap.bl-tap1 .bl-joy, .blossom.bl-tap.bl-tap1 .bl-smile { opacity: 1; }
.blossom.bl-tap.bl-tap2 .bl-eyes, .blossom.bl-tap.bl-tap2 .bl-smile, .blossom.bl-tap.bl-tap3 .bl-smile { opacity: 0; }
.blossom.bl-tap.bl-tap2 .bl-joy, .blossom.bl-tap.bl-tap2 .bl-open, .blossom.bl-tap.bl-tap3 .bl-open { opacity: 1; }
/* the movements: the hop (650 ms), the giggle (700 ms), the twirl (900 ms; its sparkles from 120 ms) */
@keyframes bl-tap-hop {
  0% { transform: translateY(0) scale(1, 1); animation-timing-function: ease-out; }
  14% { transform: translateY(1px) scale(1.05, 0.91); animation-timing-function: ease-out; }
  42% { transform: translateY(calc(var(--bl-lift) * -8px)) scale(0.96, 1.05); animation-timing-function: ease-in-out; }
  54% { transform: translateY(calc(var(--bl-lift) * -9px)) scale(1, 1); animation-timing-function: ease-in; }
  74% { transform: translateY(0) scale(1.04, 0.93); animation-timing-function: ease-out; }
  88% { transform: translateY(0) scale(0.98, 1.02); animation-timing-function: ease-in-out; }
  100% { transform: translateY(0) scale(1, 1); }
}
@keyframes bl-tap-hop-shade { 0%, 100% { transform: scale(1); opacity: 1; } 14% { transform: scale(1.08); opacity: 1; } 48% { transform: scale(0.7); opacity: 0.55; } 74% { transform: scale(1.06); opacity: 1; } }
@keyframes bl-tap-giggle {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  10% { transform: translateY(-2px) rotate(-5deg); }
  24% { transform: translateY(0) rotate(4.5deg); }
  38% { transform: translateY(-2px) rotate(-4.5deg); }
  52% { transform: translateY(0) rotate(3.5deg); }
  66% { transform: translateY(-1.5px) rotate(-2.5deg); }
  80% { transform: translateY(0) rotate(1.2deg); }
}
@keyframes bl-tap-twirl {
  0%, 100% { transform: translateY(0) rotate(0deg) scale(1, 1); }
  14% { transform: translateY(1px) rotate(0deg) scale(1.04, 0.94); }
  38% { transform: translateY(calc(var(--bl-lift) * -6px)) rotate(-3deg) scale(0.98, 1.03); }
  60% { transform: translateY(calc(var(--bl-lift) * -5px)) rotate(2deg) scale(1, 1); }
  84% { transform: translateY(0) rotate(0deg) scale(1.02, 0.97); }
}
@keyframes bl-tap-twirl-shade { 0%, 100% { transform: scale(1); opacity: 1; } 14% { transform: scale(1.05); opacity: 1; } 38%, 60% { transform: scale(0.82); opacity: 0.7; } 84% { transform: scale(1.03); opacity: 1; } }
@keyframes bl-tap-spin {
  0% { transform: rotate(0deg) scale(1); }
  12% { transform: rotate(-14deg) scale(1); }
  55% { transform: rotate(250deg) scale(1.16); }
  86%, 100% { transform: rotate(360deg) scale(1); }
}
@keyframes bl-tap-burst { 0% { opacity: 0; transform: scale(0.3); } 30% { opacity: 1; } 70% { opacity: 1; transform: scale(1); } 100% { opacity: 0; transform: scale(1); } }
@keyframes bl-tap-glint { 0% { transform: rotate(0deg) scale(0.6); } 55% { transform: rotate(50deg) scale(1.1); } 100% { transform: rotate(90deg) scale(0.8); } }
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .blossom.bl-tap .bl-eyes { animation: none; }   /* no blink in the middle of it */
  /* (9 Nov 2026, fix) the face changes with a short fade, not a snap (a big helper's open mouth snapped back at a reaction's end).
     The left closed eye (bl-joyL, under the wink) goes at once as the wink starts, and comes back only once the closed eyes
     have faded, so it never shows over the open eye */
  html:not(.less-motion) .blossom .bl-eyes, html:not(.less-motion) .blossom .bl-eyeR, html:not(.less-motion) .blossom .bl-joy,
  html:not(.less-motion) .blossom .bl-smile, html:not(.less-motion) .blossom .bl-open { transition: opacity var(--duration-fast) ease-out; }
  html:not(.less-motion) .blossom .bl-joyL { transition: opacity 0s linear var(--duration-fast); }
  html:not(.less-motion) .blossom.bl-tap.bl-tap1 .bl-joyL { transition: none; }
  html:not(.less-motion) .blossom.bl-tap.bl-tap1 .bl-react { animation: bl-tap-hop 650ms both; }
  html:not(.less-motion) .blossom.bl-tap.bl-tap1 .bl-rshade { animation: bl-tap-hop-shade 650ms ease-in-out both; }
  html:not(.less-motion) .blossom.bl-tap.bl-tap2 .bl-react { animation: bl-tap-giggle 700ms ease-in-out both; }
  html:not(.less-motion) .blossom.bl-tap.bl-tap3 .bl-react { animation: bl-tap-twirl 900ms ease-in-out both; }
  html:not(.less-motion) .blossom.bl-tap.bl-tap3 .bl-rshade { animation: bl-tap-twirl-shade 900ms ease-in-out both; }
  html:not(.less-motion) .blossom.bl-tap.bl-tap3 .bl-flower { animation: bl-tap-spin 900ms ease-in-out both; }
  html:not(.less-motion) .blossom.bl-tap.bl-tap3 .bl-rfx { opacity: 1; }
  html:not(.less-motion) .blossom.bl-tap.bl-tap3 .bl-rfx g { animation: bl-tap-burst 640ms var(--ease-out) 120ms both; }
  html:not(.less-motion) .blossom.bl-tap.bl-tap3 .bl-rfx path { animation: bl-tap-glint 640ms var(--ease-out) 120ms both; }
  html:not(.less-motion) .blossom.bl-tap.bl-tap3 .bl-rfx g:nth-child(2), html:not(.less-motion) .blossom.bl-tap.bl-tap3 .bl-rfx g:nth-child(2) path { animation-delay: 170ms; }
  html:not(.less-motion) .blossom.bl-tap.bl-tap3 .bl-rfx g:nth-child(3), html:not(.less-motion) .blossom.bl-tap.bl-tap3 .bl-rfx g:nth-child(3) path { animation-delay: 140ms; }
  html:not(.less-motion) .blossom.bl-tap.bl-tap3 .bl-rfx g:nth-child(4), html:not(.less-motion) .blossom.bl-tap.bl-tap3 .bl-rfx g:nth-child(4) path { animation-delay: 190ms; }
  html:not(.less-motion) .blossom.bl-tap.bl-tap3 .bl-rfx g:nth-child(5), html:not(.less-motion) .blossom.bl-tap.bl-tap3 .bl-rfx g:nth-child(5) path { animation-delay: 160ms; }
}
/* the guide row and the speech bubble: Blossom on the left with the bubble beside it, or (big) Blossom above a centred bubble.
   The bubble's corner nearest Blossom is the sharp one. lines (js/blossom.js) keeps room for that many lines of text. */
.bl-guide { display: flex; align-items: flex-end; gap: var(--space-2); margin: 0 0 var(--space-4); }
.bl-guide--big { flex-direction: column; align-items: center; gap: var(--space-3); text-align: center; }
.bl-say {
  --bl-lines: 0; --bl-lh: var(--line-height-lead); --bl-pad: var(--space-3);   /* js/blossom.js sets --bl-lines on the bubble */
  position: relative; flex: 0 1 auto; min-width: 0; margin: 0; padding: var(--bl-pad) var(--space-4);
  background: var(--color-surface); color: var(--color-primary-strong); border: 1px solid var(--color-outline);
  border-radius: var(--radius-l) var(--radius-l) var(--radius-l) var(--radius-s); box-shadow: var(--elevation-1);
  font-size: var(--font-size-body); line-height: var(--bl-lh); overflow-wrap: anywhere; transform-origin: 0% 100%; text-wrap: pretty;
  min-height: calc(var(--bl-lines) * var(--bl-lh) * 1em + 2 * var(--bl-pad) + 2px);
}
/* (9 Nov 2026, stage 3) the small bubble's words at body size now (the questions, Picture Memory); only its padding is smaller */
.bl-guide--small .bl-say { --bl-pad: var(--space-2); padding-inline: var(--space-3); }
.bl-guide--big .bl-say { max-width: 20em; border-radius: var(--radius-l); transform-origin: 50% 0%; text-wrap: balance; }
/* (9 Nov 2026, fix) the big bubble's tail, up to Blossom: a turned square with the bubble's edge on its two upper sides; none on
   a plain note (Blossom off) */
.bl-guide--big .bl-say::before {
  content: ""; position: absolute; left: 50%; top: calc(1px - var(--space-2)); width: var(--space-3); height: var(--space-3);
  margin-left: calc(var(--space-3) / -2); background: var(--color-surface); border: solid var(--color-outline);
  border-width: 1px 0 0 1px; transform: rotate(45deg);
}
.bl-guide--plain .bl-say::before, html.bl-off .bl-say::before { content: none; }
.bl-guide--plain .bl-say, html.bl-off .bl-say { border-radius: var(--radius-l); }

/* ---------- Blossom on the screens (9 Nov 2026, Mufy: "build blossom into the app with the props") ----------
   Where each screen puts the guide row (js/blossom.js .bl-guide, styled above) and what comes with it. The start screen and
   the questions are js/plans.js's; Picture Memory, the screens between the games, the first check-in invitation and the
   check-in done screen are main.js's. */
/* the start screen: Blossom's hello, centred, under Start and above Read aloud (later on 9 Nov 2026, Mufy: "move it below the
   start button") */
.bl-guide.intro-hello { justify-content: center; margin: var(--space-3) 0 var(--space-2); }
/* (9 Nov 2026, fix) a short screen (760 px high or less): the hello's room a little tighter. (Later on 9 Nov 2026, Mufy: "keep
   the welcome icon") The app icon at the top stays as it is, 72 px, on every screen and with Larger text. */
@media (max-height: 760px) {
  .bl-guide.intro-hello { margin: var(--space-2) 0 var(--space-1); }
}
/* a question: under Skip or Next, so it never pushes them down */
.bl-guide.ask-guide { margin: var(--space-3) 0 var(--space-4); }   /* (9 Nov 2026, fix) room above Read aloud */
/* (9 Nov 2026, fix) while the quiz picture's veil is up (js/plans.js zoom), the helper under it takes no tap */
.ask-screen.is-veiled .blossom { pointer-events: none; }
/* "Building your plan": under the graph */
.build .bl-guide { margin: var(--space-4) 0 0; }
/* Picture Memory's 12 pictures: under the picture, centred, never shrinking (main.js keeps the bubble's room for the day's
   longest line, so the picture never moves). (9 Nov 2026, fix) The bubble is one width all through (18em, or the row's room on a
   narrow phone), never each line's own, so Blossom doesn't move sideways when the line changes. */
.game--memory .bl-guide { flex: none; justify-content: center; margin: var(--space-2) 0 0; }
.game--memory .bl-say { display: flex; align-items: center; flex: 0 1 18em; }   /* (a shorter line sits in the middle of the room kept) */
/* between a check-in's games: Blossom under Next, saying what comes next (main.js betweenGames; 9 Nov 2026, fix: no card under
   it any more). "Pictures done": what comes next on a card above Next (main.js nextCard), which goes with Blossom */
.result__next + .bl-guide { margin: var(--space-3) 0 var(--space-5); }
.bl-next { margin: var(--space-3) 0 var(--space-4); padding: var(--space-4); }
.bl-next .eyebrow { margin: 0; }
.bl-next h2 { margin: var(--space-1) 0; }
.bl-next p:last-child { margin: 0; }
html.bl-off .bl-next { display: none; }
/* the first check-in invitation after sign-up (main.js firstInvite) */
.hello-first { text-align: center; }
.hello-first .bl-guide { margin: var(--space-4) 0 var(--space-5); }
/* the check-in done screen (main.js checkinDone): Blossom, the day of the run, three tiles, this week, Open my score */
.cdone { text-align: center; }
.cdone .bl-guide { margin: 0 0 var(--space-3); }
.cdone__day {
  margin: var(--space-2) 0 0; font-size: var(--font-size-display-s); line-height: var(--line-height-display);
  color: var(--color-accent-text); font-family: var(--font-body); font-weight: var(--font-weight-bold);
  font-variant-numeric: lining-nums tabular-nums;   /* (9 Nov 2026, fix) the body font's even figures: Georgia's are old style */
}
.cdone__of { margin: var(--space-1) 0 var(--space-4); color: var(--color-text-muted); }
.cdone__tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-2); margin: 0 0 var(--space-4); padding: 0; list-style: none; }
.cdone__tile {
  display: grid; align-content: center; gap: var(--space-1); padding: var(--space-3) var(--space-1);
  background: var(--color-surface); border: 1px solid var(--color-outline); border-radius: var(--radius-m);
}
.cdone__tile b {
  font-family: var(--font-body); font-size: var(--font-size-lead); font-weight: var(--font-weight-bold);
  line-height: var(--line-height-title); color: var(--color-primary-strong); font-variant-numeric: lining-nums tabular-nums;
}
.cdone__tile span { font-size: var(--font-size-caption); line-height: var(--line-height-caption); color: var(--color-text-muted); }
.cdone .week__days { margin: 0 auto var(--space-2); }
@keyframes cdone-rise { from { opacity: 0; transform: translateY(10px) scale(0.96); } }
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .cdone__tile.is-new { animation: cdone-rise var(--duration-slow) var(--ease-out) backwards; }
  html:not(.less-motion) .cdone__tile.is-new:nth-child(2) { animation-delay: 160ms; }
  html:not(.less-motion) .cdone__tile.is-new:nth-child(3) { animation-delay: 320ms; }
}

/* ---------- components (9 Nov 2026, Mufy: the redesign's stage 3, components and icons; js/ui.js and js/icons.js) ----------
   Material Design 3 is the reference for the parts, the states and the spacing, on the app's own colours. Every colour, size,
   corner and shadow here is a token on :root, so a dark theme only redefines tokens. Every movement is opt-in, as everywhere
   in the app: none with the phone's reduced motion or Settings' Less motion. */

/* list rows and switches (js/ui.js listRow and switchRow): an optional teal icon, the title (semibold) with an optional line
   under it (muted), then a switch, a chevron or a value on the right. At least 56 px tall (48 px compact), the whole row the
   target; its press colour reaches 12 px past the words on each side, so the words stay in line with the screen's other text.
   (9 Nov 2026, fix) Material's rule for rows of more than one line: the icon and the switch stay beside the title's first line
   (each in a box one title line tall, 1.35em), so a long line under the title, or a title that wraps, never leaves them beside
   line 3 or between two lines. The padding keeps a one-line row 56 px tall (48 px compact) with its words in the middle */
.list-row {
  display: flex; align-items: flex-start; gap: var(--space-4); min-height: var(--target-comfortable);
  margin: 0 calc(-1 * var(--space-3)); padding: max(var(--space-2), calc((var(--target-comfortable) - 1.35em) / 2)) var(--space-3);
  border-radius: var(--radius-m); color: var(--color-text); text-align: left; -webkit-tap-highlight-color: transparent;
}
label.list-row, button.list-row, a.list-row { cursor: pointer; }
button.list-row:focus-visible, a.list-row:focus-visible { outline: var(--focus-ring); outline-offset: 0; }   /* inside the screen's edge */
button.list-row { width: calc(100% + 2 * var(--space-3)); border: 0; background: none; font: inherit; }
a.list-row { text-decoration: none; }
.list-row__icon { flex: none; display: grid; place-items: center; height: 1.35em; color: var(--color-primary); }
.list-row__text { flex: 1 1 auto; min-width: 0; display: grid; gap: 2px; }
.list-row__title { font-weight: 600; line-height: 1.35; overflow-wrap: anywhere; }
.list-row__line { display: block; color: var(--color-text-muted); line-height: 1.45; }
.list-row__trail { flex: none; display: flex; align-items: center; gap: var(--space-2); height: 1.35em; margin-left: auto; color: var(--color-text-muted); }
.list-row--compact { min-height: var(--target-min); padding-block: max(var(--space-2), calc((var(--target-min) - 1.35em) / 2)); }
.list-row--compact .list-row__title { font-weight: 500; }
@media (hover: hover) { label.list-row:hover, button.list-row:hover, a.list-row:hover { background: var(--color-primary-container); } }
label.list-row:active, button.list-row:active, a.list-row:active { background: var(--color-primary-container-pressed); }
@supports selector(:has(a)) {
  /* the focus ring goes round the whole row, not the small switch (browsers without :has keep the switch's own ring) */
  .list-row--switch:has(.switch:focus-visible) { outline: var(--focus-ring); outline-offset: 0; }
  .list-row--switch .switch:focus-visible { outline: none; }
}
/* (9 Nov 2026, Mufy: the redesign's stage 5) Settings' Appearance (main.js settings): a row's icon, title and line, then the three
   choices as radio rows lined up under the title (the icon's width and the row's gap), each at least 56 px tall, no hairlines */
.theme-choice > .list-row { padding-bottom: var(--space-1); }
.theme-choice__rows { display: grid; margin: 0 0 var(--space-2) calc(var(--icon-size-m) + var(--space-4)); }
.theme-choice__rows .choice-row + .choice-row { border-top: 0; }

/* while something loads, and when there is nothing yet (js/ui.js skeleton, stateIcon, progressCircle). Skeletons are still
   grey shapes where a screen's content is on its way, with no shimmer (nothing loops: 30 Oct 2026). They fade in after
   300 ms, so a fast answer never flashes them; with reduced motion or Less motion they are simply there. Hidden from screen
   readers: the words that say what is loading stay, and are read */
.skel { display: grid; gap: var(--space-3); margin: var(--space-3) 0; }
.skel__line { display: block; height: 0.9em; border-radius: var(--radius-s); background: var(--color-skeleton); }
.skel__line--title { height: 1.4em; }
.skel__line--w25 { width: 25%; } .skel__line--w40 { width: 40%; } .skel__line--w60 { width: 60%; } .skel__line--w80 { width: 80%; }
.skel__block { display: block; height: var(--target-comfortable); border-radius: var(--radius-m); background: var(--color-skeleton); }
.skel__row { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-2) 0; }
.skel__circle { flex: none; width: 32px; height: 32px; border-radius: 50%; background: var(--color-skeleton); }
.skel__lines { flex: 1 1 auto; display: grid; gap: var(--space-2); }
/* each kind in the shape of what it stands for: a board's members, a top 10's rows, a home card, a card's lines, My results' rows */
.skel--board .skel__row { padding: var(--space-3) 0; }
.skel--board .skel__row + .skel__row, .skel--rows .skel__row + .skel__row { border-top: 1px solid var(--color-outline); }
.skel--ranks, .skel--rows { gap: 0; }
.skel--ranks .skel__row { min-height: var(--target-min); padding: 0 8px; border-bottom: 1px solid var(--color-outline); }
.skel--rows .skel__row { min-height: var(--target-comfortable); }
.skel--card { margin: 14px 0; }   /* a card's own margins: it is a .card too */
.skel--lines { margin: var(--space-2) 0; }
@keyframes skel-in { from { opacity: 0; } }
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .skel { animation: skel-in var(--duration-base) var(--ease-out) 300ms backwards; }
}
/* a share card's square before its picture is drawn (the personality's and the score's, js/share.js pictureBox) */
.share-card__pic:not([src]), .share-pic:not([src]) { background: var(--color-skeleton); }
/* a circle that turns while "Getting the pictures ready..." (still, with a gap, under reduced motion or Less motion) */
.progress {
  display: block; width: 40px; height: 40px; margin: var(--space-6) 0 var(--space-3);
  border: 4px solid var(--color-primary-container); border-top-color: var(--color-primary); border-radius: 50%;
}
@media (prefers-reduced-motion: no-preference) { html:not(.less-motion) .progress { animation: btn-spin 1000ms linear infinite; } }
/* a full-screen error or offline state: its icon in a soft disc above the heading (no Blossom beside a failure) */
.state__icon { display: grid; place-items: center; width: 64px; height: 64px; margin: var(--space-5) 0 0; border-radius: 50%; }
.state__icon--offline { background: var(--color-surface-variant); color: var(--color-text-muted); }
.state__icon--error { background: var(--color-error-container); color: var(--color-on-error-container); }
.state__icon--warning { background: var(--color-warning-container); color: var(--color-on-warning-container); }
.state__icon + h1 { margin-top: var(--space-3); }
/* an empty state (My results with no check-ins): Blossom's line, or the plain note, then the screen's own button */
.state { margin: var(--space-4) 0; }
.state--empty .bl-guide { margin-bottom: 0; }

/* dialogs and sheets: one look (js/ui.js confirmDialog and alertDialog, the share popup, the photo views, the test sheet).
   The dimmed page, a white box with a head (the title, and a 48 px Close icon at the top right), the words, then the actions
   stacked at full width, the yes first. Centred on every screen; the share popup stays a sheet from the bottom on a phone */
.dlg {
  width: min(var(--dialog-width), calc(100% - 32px)); max-width: none; height: fit-content; max-height: calc(100% - 32px);
  margin: auto; padding: 0; border: 0; border-radius: var(--radius-l); overflow: hidden;
  background: var(--color-surface); color: var(--color-text); box-shadow: var(--elevation-3);
  font: 400 var(--font-size-body)/1.5 var(--font-body); text-align: left;
}
.dlg[open] { display: flex; flex-direction: column; }
.dlg:not([open]) { display: none; }
.dlg::backdrop { background: var(--color-scrim); }
.dlg__box { flex: 0 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: var(--space-5); }
.dlg__head { display: flex; align-items: flex-start; gap: var(--space-2); margin: 0 0 var(--space-3); }
.dlg__title { flex: 1 1 auto; min-width: 0; margin: 0; padding-top: 8px; font: 600 var(--font-size-title)/1.3 var(--font-heading); color: var(--color-primary-strong); overflow-wrap: break-word; }
.dlg__head > .icon-btn { margin: -8px -12px 0 auto; }
.dlg__text { margin: 0 0 var(--space-3); }
.dlg__actions { clear: both; display: grid; gap: var(--space-2); margin-top: var(--space-5); }
/* a dialog with no title (alertDialog with only its words): the Close icon floats at the top right and the words start beside it */
@supports selector(:has(a)) {
  .dlg__head:not(:has(.dlg__title:not(:empty))) { float: right; margin: 0 0 var(--space-2) var(--space-2); }
  .dlg__head .dlg__title:empty { display: none; }
}
.dlg__actions > .btn { margin: 0; }
/* the icon button: 48 px round, the icon in the muted text colour; on a photo's dark backdrop, white on a dimmed disc */
.icon-btn {
  position: relative; display: inline-grid; place-items: center; flex: none; width: var(--target-min); height: var(--target-min);
  margin: 0; padding: 0; border: 0; border-radius: var(--radius-full); background: transparent; color: var(--color-text-muted);
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
@media (hover: hover) { .icon-btn:hover { background: var(--color-primary-container); color: var(--color-primary-strong); } }
.icon-btn:active { background: var(--color-primary-container-pressed); }
.icon-btn:focus-visible { outline: var(--focus-ring); outline-offset: 0; }
.icon-btn--on-scrim { background: var(--color-scrim); color: var(--color-on-scrim); }
.icon-btn--on-scrim:focus-visible { outline: var(--focus-ring-on-dark); }
.photo-pop__x { position: absolute; z-index: 1; top: calc(8px + env(safe-area-inset-top)); right: calc(8px + env(safe-area-inset-right)); }
@keyframes dlg-in { from { opacity: 0; transform: scale(0.96); } }
@keyframes sheet-in { from { opacity: 0; transform: translateY(24px); } }
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .dlg[open], html:not(.less-motion) .share-pop[open] { animation: dlg-in var(--duration-base) var(--ease-out); }
}
@media (prefers-reduced-motion: no-preference) and (max-width: 600px) {
  html:not(.less-motion) .share-pop[open] { animation-name: sheet-in; }
}

/* ---------- (9 Nov 2026, Mufy: the redesign's stage 4) the bottom bar and the slide ----------
   The bottom bar (main.js setNav): fixed at the bottom on the four tabs' top screens and the screens opened from them, hidden
   while a task runs. On wider screens it keeps the app's column's width (560 px, centred), with its top corners rounded once
   there is room beside it; never a rail, as the app is one centred column. The page keeps room for it at the bottom (.screen),
   a control reached with the keyboard or a heading scrolled to stops above it (scroll-padding-bottom), and the notice sits 16 px
   above it (--bottom-bar-height, its edge included). Under the top bar (z 5), so the account menu's see-through layer covers it
   and a tap there only closes the menu; over a screen's exit copy (z 3). Every tab is the bar's full height (73 to 76 px) and a
   quarter of its width (at least 88 px). The icons are 24 px whatever the text size, as Material's; the labels keep one weight
   (a bolder active label would be wider): the active tab has its colour, the pill and the filled icon. The slide is with "how the
   app feels" (screen-from-right). */
html.has-tabbar {
  --bottom-bar-height: calc(var(--tabbar-height) + 1px);
  scroll-padding-bottom: calc(var(--bottom-bar-height) + env(safe-area-inset-bottom) + var(--space-2));
}
html.has-tabbar .screen { padding-bottom: calc(40px + env(safe-area-inset-bottom) + var(--bottom-bar-height)); }
.tabbar {
  position: fixed; z-index: 4; left: 0; right: 0; bottom: 0; max-width: 560px; margin: 0 auto;
  padding: 0 env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
  background: var(--color-tabbar); border-top: 1px solid var(--color-tabbar-edge);
}
.tabbar[hidden] { display: none; }
.tabbar__list {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); height: var(--tabbar-height);
  margin: 0; padding: 0 var(--space-1); list-style: none;
}
.tabbar__item { display: flex; min-width: 0; }
.tabbar__tab {
  display: flex; flex-direction: column; align-items: center; justify-content: flex-start; gap: var(--space-1);
  width: 100%; min-width: 0; margin: 0; padding: var(--space-2) 0 0; border: 0; border-radius: var(--radius-m); background: none;
  color: var(--color-tabbar-inactive); font-family: var(--font-body); font-size: var(--tabbar-label-size);
  font-weight: var(--font-weight-semibold); line-height: var(--line-height-caption);
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.tabbar__icon {
  position: relative; display: grid; place-items: center; flex: none;
  width: var(--tabbar-pill-w); height: var(--tabbar-pill-h); border-radius: var(--radius-full);
}
.tabbar__pill { position: absolute; inset: 0; border-radius: inherit; background: var(--color-tabbar-indicator); opacity: 0; }
.tabbar__icon .icon { position: relative; }
.tabbar__on { display: none; }
.tabbar__tab[aria-current="page"] { color: var(--color-tabbar-active); }
.tabbar__tab[aria-current="page"] .tabbar__pill { opacity: 1; }
.tabbar__tab[aria-current="page"] .tabbar__on { display: block; }
.tabbar__tab[aria-current="page"] .tabbar__off { display: none; }
.tabbar__label { max-width: 100%; padding: 0 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tabbar__tab:not([aria-current]):active .tabbar__icon { background: var(--color-press-layer); }
@media (hover: hover) { .tabbar__tab:not([aria-current]):hover .tabbar__icon { background: var(--color-press-layer); } }
.tabbar__tab:focus-visible { outline: var(--focus-ring); outline-offset: -3px; }
/* the active tab's pill grows from its centre (main.js setNav adds is-new when the active tab changes from another one) */
@keyframes tabbar-pill { from { clip-path: inset(0 30% round var(--radius-full)); opacity: 0.5; } to { clip-path: inset(0 0 round var(--radius-full)); opacity: 1; } }
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .tabbar__pill.is-new { animation: tabbar-pill var(--duration-base) var(--ease-standard); }
}
/* (9 Nov 2026, fix) the bar coming on a screen that is already up (the score screen once the envelope has opened: main.js setNav's
   arrive) fades in over 200 ms instead of appearing all at once under someone reading; at once with less motion */
@keyframes tabbar-in { from { opacity: 0; } }
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .tabbar.is-arriving { animation: tabbar-in var(--duration-base) var(--ease-out); }
}
/* once the window is wider than the column: a sheet's top corners and edges (from 561 px, review) */
@media (min-width: 561px) {
  .tabbar {
    border: 1px solid var(--color-tabbar-edge); border-bottom: 0; border-radius: var(--radius-l) var(--radius-l) 0 0;
    box-shadow: var(--elevation-1);
  }
}
@media print {
  .tabbar, .screen-exit { display: none !important; }
  html.has-tabbar .screen { padding: 0; }
}

/* ---------- (9 Nov 2026, Mufy: the redesign's stage 4) the screens ----------
   The Groups tab and home's group lines (js/groups.js groupsTab, homeLines), My results' chart card and Settings' Extra games
   (main.js), the score screen after the envelope, the paywall's screens (js/plans.js) and stage 2's and 3's leftovers. Every
   colour, size, corner and shadow is a token on :root, so the dark theme only redefines tokens. Nothing here moves. */

/* home: one card, "Groups", with a line for each group (a list row: the name, "3 of 4 checked in today", a chevron), the whole
   row the target, opening the group's board in the Groups tab; a hairline between the rows */
.home-groups h2 { margin: 0 0 var(--space-1); }
.home-groups__list { list-style: none; margin: 0; padding: 0; }
.home-groups__item { margin: 0; }
.home-groups__item + .home-groups__item { border-top: 1px solid var(--color-outline); }
.home-gline .list-row__title { color: var(--color-primary-strong); }

/* the Groups tab: a card for each group, the whole card the target (its name in the headings' face, the day's line, the chain's
   tail, the people waiting for approval, a chevron), This week's question's line under it inside the card; then Invite to my
   group, Start a new group and Join with a code, and who sees what */
.gcards { list-style: none; display: grid; gap: var(--space-3); margin: var(--space-4) 0 0; padding: 0; }
.gcards__item {
  margin: 0; background: var(--color-surface); border: 1px solid var(--color-outline); border-radius: var(--radius-l);
  box-shadow: var(--elevation-1);
}
.gcard {
  display: flex; align-items: center; gap: var(--space-3); width: 100%; min-height: 72px; margin: 0; padding: var(--space-4);
  border: 0; border-radius: calc(var(--radius-l) - 1px) calc(var(--radius-l) - 1px) 0 0; background: none;
  color: var(--color-text); font: inherit; text-align: left; cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.gcard:only-child { border-radius: calc(var(--radius-l) - 1px); }
.gcard:focus-visible { outline: var(--focus-ring); outline-offset: -3px; }
@media (hover: hover) { .gcard:hover { background: var(--color-primary-container); } }
.gcard:active { background: var(--color-primary-container-pressed); }
.gcard__text { flex: 1 1 auto; min-width: 0; display: grid; gap: var(--space-1); }
.gcard__name { font: 600 var(--font-size-title)/1.3 var(--font-heading); color: var(--color-primary-strong); overflow-wrap: anywhere; }
.gcard__today { font-weight: 600; color: var(--color-text); }
.gcard__chain { color: var(--color-primary-strong); }
.gcard__pending { color: var(--color-text-muted); }
.gcard__go { flex: none; color: var(--color-text-muted); }
.gcards__item > .group-card__question { margin: calc(-1 * var(--space-2)) var(--space-4) var(--space-1); padding-right: 0; }
.groups-tab-slot > .skel--card { margin-top: var(--space-4); }
.groups-tab__more { display: grid; gap: var(--space-5); margin: var(--space-6) 0 0; }
.groups-tab__more > * { margin: 0; }
.groups-tab__more > .share > .btn { margin: 0; }
/* (9 Nov 2026, fix) Start a new group and Join with a code (js/groups.js startForm, joinForm): each box with its button beside it,
   the button as wide as its word, the label above both and the error line under them, so the two forms weigh less than the
   cards above them (Settings > Groups keeps them as they were). The busy words ("Starting…") widen the button, the box gives way */
.groups-tab__more .groups-start, .groups-tab__more .groups-join { grid-template-columns: minmax(0, 1fr) auto; column-gap: var(--space-2); }
.groups-tab__more .groups-start > label, .groups-tab__more .groups-join > label { grid-column: 1 / -1; }
.groups-tab__more .groups-start > input, .groups-tab__more .groups-join > input { grid-row: 2; grid-column: 1; }
.groups-tab__more .groups-start > .btn, .groups-tab__more .groups-join > .btn { grid-row: 2; grid-column: 2; min-width: 4.5em; margin: 0; }
.groups-tab__more .groups-start > .form-error, .groups-tab__more .groups-join > .form-error { grid-row: 3; grid-column: 1 / -1; }
.groups-tab-slot > .groups-tab__fail { margin: var(--space-4) 0 0; }
.groups-tab__who { margin-top: var(--space-6); }

/* My results: the "Your Awareness Score" card from home (the chart, the practice line, See each game; (9 Nov 2026, fix) Share my
   results under the check-ins' list now, so the newest check-ins show on arrival) */
.results-graph h2 { margin: 0 0 var(--space-2); }
.results-graph > .fine { margin: var(--space-2) 0 0; }
.results-graph > .btn { margin-top: var(--space-2); }

/* Settings: Extra games, from home, a section like Settings' others (its h2, its line, the chips) */
.extras-settings > p { margin: 0; }

/* the score screen after the envelope (main.js scoreScreen): hierarchy and spacing only, the same words. Its parts stay siblings in
   main after the hero (the envelope's rules hide and reveal them with ~), and .hero--score is the only one, so it scopes them;
   the hero's own margins are never touched (main.js envStage counts on its 8 px). The date small and quiet above the score;
   today's lines a size up, centred; the personality as a section of its own under a hairline, its "You're The Gardener" at title
   size; the share card; the action cards 16 px apart; "Today's games" well apart, its four rows in one card with hairlines
   between them (a check-in's page, .hero--small, gets the same card); then the practice line, the chart and the signposts */
.score-date { margin: var(--space-2) 0 0; font-size: var(--font-size-caption); }
.hero--score ~ .result__change { max-width: 32em; margin: var(--space-2) auto 0; font-size: var(--font-size-lead); line-height: 1.45; }
.hero--score ~ .pers-moment { margin: var(--space-6) 0 var(--space-5); }
.hero--score ~ .pers-moment:not(.card) { padding-top: var(--space-5); border-top: 1px solid var(--color-outline); }
.hero--score ~ .pers-moment .pers-you { font-size: var(--font-size-title); }
.hero--score ~ .share-card { margin: var(--space-6) 0 var(--space-5); }
.hero--score ~ .card:not(.pers-moment), .hero--score ~ .twq-score-slot > .card { margin: var(--space-4) 0; }
.hero--score ~ .results__h { margin-top: var(--space-7); }
.hero--score ~ .results, .hero--small ~ .results {
  margin: var(--space-3) 0 var(--space-5); padding: 0 var(--space-4);
  background: var(--color-surface); border: 1px solid var(--color-outline); border-radius: var(--radius-l); box-shadow: var(--elevation-1);
}
.hero--score ~ .results > .result, .hero--small ~ .results > .result { margin: 0; padding: var(--space-4) 0; border: 0; border-radius: 0; background: none; }
.hero--score ~ .results > .result + .result, .hero--small ~ .results > .result + .result { border-top: 1px solid var(--color-outline); }
.hero--score ~ .results .result__head h3, .hero--small ~ .results .result__head h3 { margin: 0; }
.hero--score ~ .results .result__pts, .hero--small ~ .results .result__pts { font-variant-numeric: tabular-nums; }
.hero--score ~ .results .result__main, .hero--small ~ .results .result__main { margin: var(--space-1) 0 0; }
.hero--score ~ .results .share, .hero--small ~ .results .share { margin: var(--space-2) 0 0; }
.hero--score ~ .fine, .hero--score ~ .chart, .hero--score ~ .signpost { margin-top: var(--space-5); }
.hero--score ~ .signpost + .signpost { margin-top: var(--space-3); }

/* the paywall's screens (js/plans.js). The plan screen: nothing above "Start my free trial now" changes (the fold rule: the
   button wholly above the fold at 360 x 700 with Larger text, measured before and after); below it, the line under the button
   as a centred caption, "How can I cancel?" in a soft box, the three fine lines close together under it, the Terms and Privacy
   links, then Read aloud and Sign in in one row (.plans__tools). The follow-up: its two options in the plan rows' look, its two
   buttons 12 px apart, the same box and row */
.plans--choose .plans__cta + .fine.centre { margin: var(--space-2) 0 0; font-size: var(--font-size-caption); }
.plans--choose .cancel-help--short, .plans--followup .cancel-help--short {
  margin: var(--space-5) 0 0; padding: var(--space-4); border: 0; border-radius: var(--radius-l); background: var(--color-surface-variant);
}
.plans--choose .cancel-help--short > h2, .plans--followup .cancel-help--short > h2 { margin: 0 0 var(--space-1); padding: 0; font-size: var(--font-size-body); }
.plans--choose .cancel-help--short > p, .plans--followup .cancel-help--short > p { margin: 0; }
.plans--choose .cancel-help--short ~ .fine { margin: var(--space-2) 0 0; }
.plans--choose .cancel-help--short + .fine { margin-top: var(--space-5); }
.plans--choose .plans__foot, .plans--followup .plans__foot { margin-top: var(--space-5); }
.plans__tools { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 0 var(--space-3); margin-top: var(--space-3); }
.plans__tools > .btn { margin: 0; }
.plans--followup .plan-choice { gap: var(--space-3); }
.plans--followup .plan-option { align-items: center; min-height: 72px; padding: var(--space-4); border-radius: var(--radius-l); }
.plans--followup .plan-option input { margin: 0; }
/* (9 Nov 2026, fix) the title and "Save 80%" side by side where they fit; where they don't (360 px with Larger text), the badge on a
   line of its own at the left edge, 4 px under the title (it sat indented by its own margin, between the title and the price) */
.plans--followup .plan-option__title { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-2); }
.plans--followup .plan-option__badge { margin: 0; }
.plans--followup .followup__actions { margin-top: var(--space-5); }
.plans--followup .followup__actions ~ .fine { margin: 0; }

/* Your answers: the answers as one card, a hairline between them, each tick 8 px before its words (their rise and tick times
   untouched); "Not a diagnosis or a medical test." 16 px under the card. (9 Nov 2026, fix) 12 px inside the card's sides and 8 px
   above and below each answer (it was 16 and 12, and the tick 12 px before its words), so the words keep their width and "I am
   ready" comes back up (at 360 x 700 with Larger text every answer had wrapped one more line) */
.ask-recap { gap: 0; margin: var(--space-3) 0 var(--space-4); padding: 0 var(--space-3); background: var(--color-surface); border: 1px solid var(--color-outline); border-radius: var(--radius-l); }
.ask-recap li { padding: var(--space-2) 0; border: 0; border-radius: 0; background: none; }
.ask-recap li + li { border-top: 1px solid var(--color-outline); }
.ask-recap .ask-tick { margin-right: var(--space-2); }
.ask-recap ~ .fine { margin-top: 0; }
.ask-recap ~ .plans__tools { margin-top: var(--space-2); }

/* Building your plan: the ring centred with room round it, the lines 12 px apart with each tick beside its first line, the
   graph's card, then the helper. On a short screen (to 760 px high) everything a little closer and the ring and the graph
   smaller, so the whole screen fits 360 x 700 with Larger text with no scrolling: it has no button and moves on by itself */
.build__art { margin: var(--space-5) 0; }
.build__list { gap: var(--space-3); }
.build__graph { margin-top: var(--space-5); padding: var(--space-3) var(--space-4); border-radius: var(--radius-l); }
.build .bl-guide { margin-top: var(--space-4); }
@media (max-height: 760px) {
  .build h1 { margin: var(--space-1) 0 0; }
  .build__art { margin: var(--space-2) 0; }
  .build__art svg { width: 56px; height: 56px; }
  .build__list { gap: 6px; }
  .build__graph { margin-top: var(--space-2); padding: var(--space-1) var(--space-4); }
  .build__graph svg { max-height: 44px; }
  .build .bl-guide { margin-top: var(--space-2); }
}

/* the test checkout sheet (js/plans.js testSheet): a sheet from the bottom in the dialogs' look, its top corners round on a
   phone (all four from 600 px, where it stands clear of the bottom), its three choices 12 px apart */
.testsheet { padding: 0; }
.testsheet__card {
  max-width: var(--dialog-width); gap: var(--space-3);
  padding: var(--space-5) var(--space-5) calc(var(--space-5) + env(safe-area-inset-bottom));
  border-radius: var(--radius-l) var(--radius-l) 0 0; box-shadow: var(--elevation-3);
}
.testsheet__card p { margin: 0; }
.testsheet__card .btn { margin: 0; }
@media (min-width: 600px) {
  .testsheet { padding: var(--space-4); }
  .testsheet__card { padding-bottom: var(--space-5); border-radius: var(--radius-l); }
}
/* (9 Nov 2026, fix) on a phone the sheet is the screen's full width (at 412 px the 400 px card left 6 px of scrim each side) */
@media (max-width: 599px) { .testsheet__card { max-width: none; } }

/* the helper in the quiz's progress row (stage 2's leftover): the drawing alone, small, at the row's right end, placed over the row
   so it never adds height or moves Next; only where the row has room beside the dots and "Question n of 7" (321 px, 344 px with
   Larger text, and 40 px for the helper with its gap); none with Show helper off. (9 Nov 2026, fix) One helper on a screen: none
   where the screen has its own helper with words (.ask-guide, under Skip or Next), which today is every question, so it shows
   nowhere for now (Mufy's choice which one the questions keep); without :not(:has(.ask-guide)) it comes back beside that one */
.ask-step { position: relative; }
.blossom.ask-step__bl { position: absolute; right: 0; top: 50%; width: 28px; height: 31.8px; transform: translateY(-50%); display: none; }
@media (min-width: 400px) { html:not(.big-text):not(.bl-off) .plans:not(:has(.ask-guide)) .blossom.ask-step__bl { display: block; } }
@media (min-width: 440px) { html.big-text:not(.bl-off) .plans:not(:has(.ask-guide)) .blossom.ask-step__bl { display: block; } }

/* ---------- the background (9 Nov 2026, Mufy: the redesign's stage 5, "L2", Faint, with slow motion) ----------
   index.html's .backdrop: ONE fixed layer behind #app, under every app screen (signed in or not, the questions and the paywall
   too), never under a game (html.in-game, main.js stoppable, until the next screen) and not on the other pages (only index.html
   has it). A sand sky round a source, broad light beams turning slowly about it, a glow at it, and a wash that lightens to
   nearly white at the bottom (dark: a teal ink sky, faint beams and glow, the wash to the page colour). main.js sets the source
   for each screen (--backdrop-x, --backdrop-y: the welcome's app icon, a question's picture, else the top centre at the bar's
   bottom edge) and the reach from it to the window's farthest corner (--backdrop-r); the values here hold until main.js runs.
   Only the beams move: their square, 2 x --backdrop-r, centred on the source, turns with transform, one 30 degree period every
   18 s (the pattern repeats every 30 degrees, so it is seamless); its round mask is centred on the turning point, so it stays
   put. Nothing animates a gradient or a custom property; no filter, no blur. Still (shown, not turning) with reduced motion or
   Less motion. z-index -1 in the root: under every word and card; body is transparent so it shows (html keeps the page colour).
   Words straight on it keep 4.5:1 on the darkest sky (measured 9 Nov 2026: muted words 4.67:1 on #F3D6A8; in dark 5.14:1 at the
   lightest point). Order of the layers as Mufy's demo: the sky, the beams, the wash, the glow */
.backdrop {
  position: fixed; inset: 0; z-index: -1; overflow: hidden; contain: strict; pointer-events: none;
  --backdrop-x: 50vw; --backdrop-y: calc(68px + env(safe-area-inset-top)); --backdrop-r: 150vmax;
  background: radial-gradient(circle farthest-corner at var(--backdrop-x) var(--backdrop-y), var(--color-backdrop-sky-0) 0%,
    var(--color-backdrop-sky-1) 12%, var(--color-backdrop-sky-2) 38%, var(--color-backdrop-sky-3) 100%);
}
.backdrop__beams {
  position: absolute; left: calc(var(--backdrop-x) - var(--backdrop-r)); top: calc(var(--backdrop-y) - var(--backdrop-r));
  width: calc(2 * var(--backdrop-r)); height: calc(2 * var(--backdrop-r)); opacity: var(--backdrop-beams-opacity);
  background: repeating-conic-gradient(from 0deg, var(--color-backdrop-beam-clear) 0deg, var(--color-backdrop-beam) 9deg,
    var(--color-backdrop-beam-clear) 18deg, var(--color-backdrop-beam-clear) 30deg);
  /* the demo's mask, a circle to the farthest corner from the source: here closest-side of the square, the same radius (black for
     its alpha only) */
  -webkit-mask-image: radial-gradient(circle closest-side, #000 0%, #000 10%, rgb(0 0 0 / 0.75) 45%, rgb(0 0 0 / 0.6) 100%);
          mask-image: radial-gradient(circle closest-side, #000 0%, #000 10%, rgb(0 0 0 / 0.75) 45%, rgb(0 0 0 / 0.6) 100%);
  will-change: transform;
}
.backdrop__wash { position: absolute; inset: 0; background: linear-gradient(180deg, var(--color-backdrop-wash-clear) 0%,
  var(--color-backdrop-wash-clear) 25%, var(--color-backdrop-wash) 100%); }
.backdrop__glow { position: absolute; inset: 0; opacity: var(--backdrop-glow-opacity); background: radial-gradient(circle at
  var(--backdrop-x) var(--backdrop-y), var(--color-backdrop-glow-0) 0px, var(--color-backdrop-glow-1) 60px, var(--color-backdrop-glow-2) 130px); }
html.in-game .backdrop { display: none; }   /* a game keeps its plain play surface */
@keyframes backdrop-turn { to { transform: rotate(30deg); } }
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .backdrop__beams { animation: backdrop-turn 18s linear infinite; }
}

/* (9 Nov 2026, Mufy: the redesign's stage 5, "E. Rays that stay") the score's rays (main.js scoreRays): 16 golden rays about 360 px
   across, centred on the number, from behind it, 6.5 s; a glow 220 px across at their heart, 800 ms. Under every word and card
   (z-index -1 in the root), over the background. The wrapper rides the stage's fold with the number (score-fx-land, the same 450 ms
   curve as env-land); main.screen clips sideways while they show. Transform and opacity only; none with reduced motion or Less
   motion (and main.js adds none then). Golden in both themes; in dark at about a sixth of the strength, so the light number and
   "/ 100" stay clear on the dark sky (measured 9 Nov 2026 at the rays' peak, 0.78 s, when "/ 100" is already whole: light, the
   number 8.13:1 and "/ 100" 4.57:1 on the darkest sky; dark, 6.31:1 and 4.92:1). (Stage 5 fix: in dark a warmer gold that stays
   brighter after the burst, score-fx-stay-dark below.) */
.score-fx { position: absolute; z-index: -1; width: 0; height: 0; pointer-events: none; }
.score-fx__rays, .score-fx__core { position: absolute; left: 0; top: 0; border-radius: 50%; opacity: 0; }
.score-fx__rays {
  width: 360px; height: 360px; margin: -180px 0 0 -180px;
  background: repeating-conic-gradient(from 0deg, var(--color-fx-ray-clear) 0deg, var(--color-fx-ray) 5.5deg, var(--color-fx-ray-light) 6.5deg,
    var(--color-fx-ray-clear) 12deg, var(--color-fx-ray-clear) 22.5deg);
  -webkit-mask-image: radial-gradient(circle, transparent 0%, transparent 12%, #000 22%, rgb(0 0 0 / 0.7) 42%, transparent 70%);
          mask-image: radial-gradient(circle, transparent 0%, transparent 12%, #000 22%, rgb(0 0 0 / 0.7) 42%, transparent 70%);
}
.score-fx__core { width: 220px; height: 220px; margin: -110px 0 0 -110px;
  background: radial-gradient(circle, var(--color-fx-core) 0%, var(--color-fx-core-mid) 35%, var(--color-fx-core-clear) 70%); }
@keyframes score-fx-stay {
  0% { opacity: 0; transform: scale(0.35) rotate(-10deg); }
  12% { opacity: 1; transform: scale(1.1) rotate(0deg); }
  26% { opacity: 0.38; transform: scale(1.25) rotate(5deg); }
  85% { opacity: 0.32; transform: scale(1.25) rotate(40deg); }
  100% { opacity: 0; transform: scale(1.25) rotate(48deg); }
}
@keyframes score-fx-core { 0% { opacity: 0; transform: scale(0.4); } 20% { opacity: 1; transform: scale(1); } 100% { opacity: 0; transform: scale(1.3); } }
@keyframes score-fx-land { from { transform: translateY(var(--env-pad, 0px)); } }
@media (prefers-reduced-motion: no-preference) {
  html:not(.less-motion) .score-fx__rays { animation: score-fx-stay 6500ms linear forwards; }
  html:not(.less-motion) .score-fx__core { animation: score-fx-core 800ms ease-out forwards; }
  html:not(.less-motion) .hero--score.is-opening .score-fx { animation: score-fx-land 450ms var(--ease-standard) var(--fx-land-in, 600ms) both; }
}
.screen--fx { overflow-x: hidden; overflow-x: clip; }   /* clip where it is known: no scroll box, the rays still reach up and down */
/* a halo in the page's colour behind the words in the rays' reach (the demo's white halo on the number): it keeps them clear over a
   ray */
.score-date, .hero--score .hero__n, .hero--score .hero__of, .hero--score ~ .result__change { text-shadow: 0 0 10px var(--color-score-halo); }
/* (9 Nov 2026, stage 5 fix) in dark the rays stay at half their peak after the burst (0.38 and 0.32 of a faint gold left them all
   but gone after the first second). The same moves; only the opacity after 26 percent differs. Measured 9 Nov 2026 on the dark sky
   with no halo counted: "/ 100" 4.82:1 at the peak on the source's colour, the date 4.50:1 while they stay at its lightest point
   (the source, a beam and the glow), the number 6.18:1 */
@keyframes score-fx-stay-dark {
  0% { opacity: 0; transform: scale(0.35) rotate(-10deg); }
  12% { opacity: 1; transform: scale(1.1) rotate(0deg); }
  26% { opacity: 0.5; transform: scale(1.25) rotate(5deg); }
  85% { opacity: 0.45; transform: scale(1.25) rotate(40deg); }
  100% { opacity: 0; transform: scale(1.25) rotate(48deg); }
}
@media screen and (prefers-color-scheme: dark) and (prefers-reduced-motion: no-preference) {
  :root:not([data-theme="light"]):not(.less-motion) .score-fx__rays { animation-name: score-fx-stay-dark; }
}
@media screen and (prefers-reduced-motion: no-preference) {
  :root[data-theme="dark"]:not(.less-motion) .score-fx__rays { animation-name: score-fx-stay-dark; }
}

/* (9 Nov 2026, stage 5 fix) the radios and tick boxes in dark (Settings' Appearance, the plans, a game's "Don't show this again",
   the report forms, the sign-up consent): drawn by the app, as the switches are. The browser's own dark ones are flat grey and
   read as switched off beside the teal switches. Light keeps the phone's own, tinted teal. Each row keeps its sizes, places and
   focus ring. The edge 4.3:1 on a card and 5.3:1 on the page, the teal 6.0:1 on a chosen plan (measured 9 Nov 2026). Where the
   phone forces its own colours (high contrast), its own controls stay. Keep the two blocks the same */
@media screen and (prefers-color-scheme: dark) and (forced-colors: none) {
  :root:not([data-theme="light"]):not(.in-game) :is(.choice-row, .toggle, .consent__row, .plan-row, .plan-option) input:is([type="radio"], [type="checkbox"]):not(.switch) {
    -webkit-appearance: none; appearance: none; position: relative; border: 2px solid var(--color-outline-strong); border-radius: 50%;
    background: var(--color-surface); cursor: pointer;
  }
  :root:not([data-theme="light"]):not(.in-game) :is(.choice-row, .toggle, .consent__row, .plan-row, .plan-option) input[type="checkbox"]:not(.switch) { border-radius: 4px; }
  :root:not([data-theme="light"]):not(.in-game) :is(.choice-row, .toggle, .consent__row, .plan-row, .plan-option) input:is([type="radio"], [type="checkbox"]):not(.switch):checked { border-color: var(--color-primary); }
  :root:not([data-theme="light"]):not(.in-game) :is(.choice-row, .toggle, .consent__row, .plan-row, .plan-option) input[type="checkbox"]:not(.switch):checked { background: var(--color-primary); }
  :root:not([data-theme="light"]):not(.in-game) :is(.choice-row, .toggle, .consent__row, .plan-row, .plan-option) input[type="radio"]:checked::before {
    content: ""; position: absolute; inset: 4px; border-radius: 50%; background: var(--color-primary);
  }
  :root:not([data-theme="light"]):not(.in-game) :is(.choice-row, .toggle, .consent__row, .plan-row, .plan-option) input[type="checkbox"]:not(.switch):checked::before {
    content: ""; position: absolute; inset: 1px; background: var(--color-on-primary);
    -webkit-mask: var(--mask-check) center / contain no-repeat; mask: var(--mask-check) center / contain no-repeat;
  }
  :root:not([data-theme="light"]):not(.in-game) :is(.choice-row, .toggle, .consent__row, .plan-row, .plan-option) input:is([type="radio"], [type="checkbox"]):not(.switch):disabled { opacity: 0.6; cursor: default; }
}
@media screen and (forced-colors: none) {
  :root[data-theme="dark"]:not(.in-game) :is(.choice-row, .toggle, .consent__row, .plan-row, .plan-option) input:is([type="radio"], [type="checkbox"]):not(.switch) {
    -webkit-appearance: none; appearance: none; position: relative; border: 2px solid var(--color-outline-strong); border-radius: 50%;
    background: var(--color-surface); cursor: pointer;
  }
  :root[data-theme="dark"]:not(.in-game) :is(.choice-row, .toggle, .consent__row, .plan-row, .plan-option) input[type="checkbox"]:not(.switch) { border-radius: 4px; }
  :root[data-theme="dark"]:not(.in-game) :is(.choice-row, .toggle, .consent__row, .plan-row, .plan-option) input:is([type="radio"], [type="checkbox"]):not(.switch):checked { border-color: var(--color-primary); }
  :root[data-theme="dark"]:not(.in-game) :is(.choice-row, .toggle, .consent__row, .plan-row, .plan-option) input[type="checkbox"]:not(.switch):checked { background: var(--color-primary); }
  :root[data-theme="dark"]:not(.in-game) :is(.choice-row, .toggle, .consent__row, .plan-row, .plan-option) input[type="radio"]:checked::before {
    content: ""; position: absolute; inset: 4px; border-radius: 50%; background: var(--color-primary);
  }
  :root[data-theme="dark"]:not(.in-game) :is(.choice-row, .toggle, .consent__row, .plan-row, .plan-option) input[type="checkbox"]:not(.switch):checked::before {
    content: ""; position: absolute; inset: 1px; background: var(--color-on-primary);
    -webkit-mask: var(--mask-check) center / contain no-repeat; mask: var(--mask-check) center / contain no-repeat;
  }
  :root[data-theme="dark"]:not(.in-game) :is(.choice-row, .toggle, .consent__row, .plan-row, .plan-option) input:is([type="radio"], [type="checkbox"]):not(.switch):disabled { opacity: 0.6; cursor: default; }
}
