/* Self-hosted fonts: the UI face and all five manuscript faces. The app never loads
   fonts from Google servers. A face downloads only when text uses it.
   See docs/design-system.md section 5. */
@import url('./fonts/fonts.css');

/* =============================================================================
   Ink Familiar design system (repo: writingfriend)
   -----------------------------------------------------------------------------
   Theme: "Familiar". Read docs/design-system.md for the rationale.

   Two rules carry the whole system:
     1. Ink is the writer's. The familiar color is Saga's. The familiar color
        marks everything the AI says or proposes, and nothing else.
        The writer's color is ink blue. The familiar color is violet,
        sampled from the purple Saga sprites.
     2. The familiar sits beside the page, never on it. AI marks go in the
        page margin, not on the words.

   LAYERS
     Primitive palette  --wf-p-*     Raw hex values. Components never use them.
     Semantic tokens    --wf-*       Roles. Themes redefine only these.
     Components         .wf-*        Consume semantic tokens only.

   THEMES (set data-theme on <html>)
     Light family: light (default), cream
     Dark family:  dark (default), navy
     No data-theme: the OS picks light or dark through prefers-color-scheme.
     Light and dark use light-dark() in one block. color-scheme selects the half.

   CONTENTS
     01  Primitive palette
     02  Semantic color tokens: light + dark
     03  Theme: cream
     04  Theme: navy
     05  Type tokens
     06  Manuscript preference tokens
     07  Space, radius, border, elevation, z, motion, layout
     08  Mascot tokens
     09  Reset and base
     10  Type utilities
     11  Buttons            .wf-btn
     12  Fields             .wf-field .wf-input .wf-textarea .wf-select .wf-check .wf-switch
     13  Tabs               .wf-tabs
     14  Panels and cards   .wf-panel .wf-card
     15  The familiar       .wf-familiar .wf-voice
     16  Chat               .wf-msg
     17  Critique cards     .wf-critique
     18  Entities           .wf-entity .wf-hovercard .wf-fact
     19  Badges, kbd        .wf-badge .wf-kbd .wf-saved
     19b Banner             .wf-banner
     19c Dialog             .wf-dialog
     20  Toasts, tooltips   .wf-toast .wf-tooltip
     21  Sidebar            .wf-sidebar .wf-chapter
     22  Manuscript page    .wf-page .wf-ms
     23  App shell          .wf-app .wf-topbar
     23b Auth pages         .wf-auth .wf-auth-card
     23c Focus save state   .wf-focus-status
     23d Find bar           .wf-findbar
     23e Settings page      .wf-settings
     23f History diff       .wf-diff .wf-versions
     24  Motivation         .wf-streak .wf-briefing .wf-capture
     25  Utilities (.wf-divider .wf-icon .wf-grow), accessibility, reduced motion, forced colors
   ============================================================================ */

/* -- 01  Primitive palette ---------------------------------------------------
   Named materials. Only sections 02 to 04 may reference these.
   -------------------------------------------------------------------------- */
:root {
  /* Paper: light neutrals with a faint green-grey cast. Not cream. */
  --wf-p-paper-0: #ffffff;
  --wf-p-paper-50: #fbfbf8;
  --wf-p-paper-100: #f0f0eb;
  --wf-p-paper-150: #eaeae4;
  --wf-p-paper-200: #e6e6e0;
  --wf-p-paper-250: #e4e4de;
  --wf-p-paper-300: #d9d9d2;
  --wf-p-paper-400: #c2c2ba;

  /* Graphite: text and control lines on paper */
  --wf-p-graphite-400: #9a9ca3;
  --wf-p-graphite-500: #7a7c84;
  --wf-p-graphite-600: #5c5f69;
  --wf-p-graphite-700: #474a55;
  --wf-p-graphite-800: #262831;
  --wf-p-graphite-900: #1d1f27;

  /* Dusk: dark surfaces, a cool blue-grey */
  --wf-p-dusk-950: #16181d;
  --wf-p-dusk-925: #181a1f;
  --wf-p-dusk-900: #1c1f25;
  --wf-p-dusk-850: #22252c;
  --wf-p-dusk-800: #2a2e36;
  --wf-p-dusk-750: #30343d;
  --wf-p-dusk-700: #424651;
  --wf-p-dusk-600: #6a6d75;
  --wf-p-dusk-500: #7c818e;

  /* Bone: light text on dusk. Warm, never pure white. */
  --wf-p-bone-100: #e6e2d8;
  --wf-p-bone-200: #dad6cb;
  --wf-p-bone-300: #c0bcb2;
  --wf-p-bone-400: #a5a197;

  /* Iron-gall blue: the writer's ink and the writer's actions */
  --wf-p-ink-50: #e3e7f3;
  --wf-p-ink-100: #dfe4f1;
  --wf-p-ink-200: #cbd5ee;
  --wf-p-ink-300: #b4c3f0;
  --wf-p-ink-350: #aebeee;
  --wf-p-ink-400: #c3d0f5;
  --wf-p-ink-700: #2e3d6b;
  --wf-p-ink-800: #24315a;
  --wf-p-ink-900: #2e3651;
  --wf-p-ink-925: #3a4670;
  --wf-p-ink-950: #161c30;

  /* Violet: sampled from the purple Saga sprites (#7048a8 mid, #a080d0,
     #b098e0, #c0a8f0, #c8b0f0 light, #502880 deep). The familiar color. */
  --wf-p-violet-50: #efe9fb;
  --wf-p-violet-75: #ebe4f0;
  --wf-p-violet-200: #c8b0f0;
  --wf-p-violet-250: #c0a8f0;
  --wf-p-violet-300: #b098e0;
  --wf-p-violet-400: #a080d0;
  --wf-p-violet-500: #7048a8;
  --wf-p-violet-600: #5a3490;
  --wf-p-violet-650: #54308a;
  --wf-p-violet-900: #3a3468;
  --wf-p-violet-950: #2d2645;

  /* Madder: concern and danger. A dyer's red, not an alarm red. */
  --wf-p-madder-50: #f7e3df;
  --wf-p-madder-100: #f2dcd2;
  --wf-p-madder-300: #f0a397;
  --wf-p-madder-350: #f4ab9e;
  --wf-p-madder-400: #e07a6d;
  --wf-p-madder-450: #e5877a;
  --wf-p-madder-500: #b4463d;
  --wf-p-madder-550: #ae4438;
  --wf-p-madder-600: #983530;
  --wf-p-madder-650: #943127;
  --wf-p-madder-700: #a12c2c;
  --wf-p-madder-750: #9a2a24;
  --wf-p-madder-800: #eb8272;
  --wf-p-madder-850: #f09080;
  --wf-p-madder-900: #3b2624;
  --wf-p-madder-925: #43304a;
  --wf-p-madder-950: #2a0f0b;

  /* Moss: saved, done, healthy */
  --wf-p-moss-50: #e1ebdb;
  --wf-p-moss-100: #dfe5cc;
  --wf-p-moss-300: #a7d09a;
  --wf-p-moss-350: #b4d6a3;
  --wf-p-moss-700: #38612f;
  --wf-p-moss-750: #375c2e;
  --wf-p-moss-900: #222f20;
  --wf-p-moss-925: #27404a;

  /* Cream and navy: one pair of materials, used inverted by two themes */
  --wf-p-cream-50: #fbf7eb;
  --wf-p-cream-100: #f6f0df;
  --wf-p-cream-150: #f2ecda;
  --wf-p-cream-175: #ede6d2;
  --wf-p-cream-200: #eee6d0;
  --wf-p-cream-250: #eae2cb;
  --wf-p-cream-300: #e8dfc7;
  --wf-p-cream-350: #e4dbc1;
  --wf-p-cream-400: #dcdde0;
  --wf-p-cream-500: #d2cbb8;
  --wf-p-cream-600: #beb8a7;
  --wf-p-cream-line: #ddd3b8;
  --wf-p-cream-line-strong: #c9bd9c;

  --wf-p-navy-300: #8492b5;
  --wf-p-navy-400: #6f7892;
  --wf-p-navy-500: #525d78;
  --wf-p-navy-600: #3d4968;
  --wf-p-navy-650: #2f4474;
  --wf-p-navy-700: #293c68;
  --wf-p-navy-750: #22335c;
  --wf-p-navy-800: #1b2a4e;
  --wf-p-navy-850: #172340;
  --wf-p-navy-900: #131d36;
  --wf-p-navy-950: #121b33;
  --wf-p-navy-line: #2c3e68;
  --wf-p-navy-line-strong: #3b5185;
}

/* -- 02  Semantic color tokens: light + dark -------------------------------
   Each value is light-dark(<light theme>, <dark theme>).
   The family is chosen by color-scheme:
     no data-theme      light dark (the OS decides)
     light, cream       light
     dark, navy         dark
   Components use only the tokens in this block.
   -------------------------------------------------------------------------- */
:root {
  color-scheme: light dark;
}
:root[data-theme='light'],
:root[data-theme='cream'] {
  color-scheme: light;
}
:root[data-theme='dark'],
:root[data-theme='navy'] {
  color-scheme: dark;
}

:root {
  /* Surfaces. Nesting order: app > chrome > raised. The page is its own surface. */
  --wf-bg-app: light-dark(var(--wf-p-paper-200), var(--wf-p-dusk-950));
  --wf-bg-chrome: light-dark(var(--wf-p-paper-100), var(--wf-p-dusk-900));
  --wf-bg-page: light-dark(var(--wf-p-paper-50), var(--wf-p-dusk-850));
  --wf-bg-raised: light-dark(var(--wf-p-paper-0), var(--wf-p-dusk-800));
  --wf-bg-sunken: light-dark(var(--wf-p-paper-150), var(--wf-p-dusk-925));
  --wf-bg-hover: light-dark(var(--wf-p-paper-250), var(--wf-p-dusk-750));
  --wf-bg-selected: light-dark(var(--wf-p-ink-100), var(--wf-p-ink-900));
  --wf-bg-scrim: light-dark(rgb(29 31 39 / 0.32), rgb(0 0 0 / 0.55));

  /* Text. text, muted and subtle pass 4.5:1 on every surface above. */
  --wf-text: light-dark(var(--wf-p-graphite-900), var(--wf-p-bone-100));
  --wf-text-muted: light-dark(var(--wf-p-graphite-700), var(--wf-p-bone-300));
  --wf-text-subtle: light-dark(var(--wf-p-graphite-600), var(--wf-p-bone-400));
  --wf-text-disabled: light-dark(var(--wf-p-graphite-400), var(--wf-p-dusk-600));
  --wf-text-page: light-dark(var(--wf-p-graphite-800), var(--wf-p-bone-200));

  /* Lines. Only --wf-line-control may bound a control (3:1). */
  --wf-line: light-dark(var(--wf-p-paper-300), var(--wf-p-dusk-750));
  --wf-line-strong: light-dark(var(--wf-p-paper-400), var(--wf-p-dusk-700));
  --wf-line-control: light-dark(var(--wf-p-graphite-500), var(--wf-p-dusk-500));

  /* Ink: the writer's primary action, links, selection */
  --wf-accent: light-dark(var(--wf-p-ink-700), var(--wf-p-ink-350));
  --wf-accent-hover: light-dark(var(--wf-p-ink-800), var(--wf-p-ink-400));
  --wf-accent-text: light-dark(var(--wf-p-ink-700), var(--wf-p-ink-300));
  --wf-accent-soft: light-dark(var(--wf-p-ink-50), var(--wf-p-ink-900));
  --wf-on-accent: light-dark(var(--wf-p-paper-0), var(--wf-p-ink-950));
  --wf-text-selection: light-dark(var(--wf-p-ink-200), var(--wf-p-ink-925));

  /* Familiar: Saga's color. Use for AI voice, AI marks and AI celebration only. */
  --wf-familiar: light-dark(var(--wf-p-violet-500), var(--wf-p-violet-400));
  --wf-familiar-text: light-dark(var(--wf-p-violet-600), var(--wf-p-violet-250));
  --wf-familiar-soft: light-dark(var(--wf-p-violet-50), var(--wf-p-violet-950));

  /* Madder: a concern (inconsistency) and destructive actions */
  --wf-concern: light-dark(var(--wf-p-madder-500), var(--wf-p-madder-400));
  --wf-concern-text: light-dark(var(--wf-p-madder-600), var(--wf-p-madder-300));
  --wf-concern-soft: light-dark(var(--wf-p-madder-50), var(--wf-p-madder-900));
  --wf-danger: light-dark(var(--wf-p-madder-700), var(--wf-p-madder-800));
  --wf-on-danger: light-dark(var(--wf-p-paper-0), var(--wf-p-madder-950));

  /* Moss: saved, done */
  --wf-ok-text: light-dark(var(--wf-p-moss-700), var(--wf-p-moss-300));
  --wf-ok-soft: light-dark(var(--wf-p-moss-50), var(--wf-p-moss-900));

  /* Focus: one ring everywhere */
  --wf-focus: var(--wf-accent);

  /* Shadow colors. Shadows in section 07 compose from these. */
  --wf-shadow-c1: light-dark(rgb(29 31 39 / 0.06), rgb(0 0 0 / 0.35));
  --wf-shadow-c2: light-dark(rgb(29 31 39 / 0.12), rgb(0 0 0 / 0.5));
}

/* -- 03  Theme: cream (light family) ----------------------------------------
   Cream paper, navy ink. The inverse of navy.
   -------------------------------------------------------------------------- */
:root[data-theme='cream'] {
  --wf-bg-app: var(--wf-p-cream-300);
  --wf-bg-chrome: var(--wf-p-cream-200);
  --wf-bg-page: var(--wf-p-cream-100);
  --wf-bg-raised: var(--wf-p-cream-50);
  --wf-bg-sunken: var(--wf-p-cream-250);
  --wf-bg-hover: var(--wf-p-cream-350);
  --wf-bg-selected: var(--wf-p-cream-400);
  --wf-bg-scrim: rgb(27 42 78 / 0.3);

  --wf-text: var(--wf-p-navy-800);
  --wf-text-muted: var(--wf-p-navy-600);
  --wf-text-subtle: var(--wf-p-navy-500);
  --wf-text-disabled: var(--wf-p-graphite-400);
  --wf-text-page: var(--wf-p-navy-800);

  --wf-line: var(--wf-p-cream-line);
  --wf-line-strong: var(--wf-p-cream-line-strong);
  --wf-line-control: var(--wf-p-navy-400);

  --wf-accent: var(--wf-p-navy-800);
  --wf-accent-hover: var(--wf-p-navy-700);
  --wf-accent-text: var(--wf-p-navy-800);
  --wf-accent-soft: var(--wf-p-cream-400);
  --wf-on-accent: var(--wf-p-cream-100);
  --wf-text-selection: var(--wf-p-ink-200);

  --wf-familiar: var(--wf-p-violet-500);
  --wf-familiar-text: var(--wf-p-violet-650);
  --wf-familiar-soft: var(--wf-p-violet-75);

  --wf-concern: var(--wf-p-madder-550);
  --wf-concern-text: var(--wf-p-madder-650);
  --wf-concern-soft: var(--wf-p-madder-100);
  --wf-danger: var(--wf-p-madder-750);
  --wf-on-danger: var(--wf-p-cream-50);

  --wf-ok-text: var(--wf-p-moss-750);
  --wf-ok-soft: var(--wf-p-moss-100);

  --wf-shadow-c1: rgb(27 42 78 / 0.06);
  --wf-shadow-c2: rgb(27 42 78 / 0.12);
}

/* -- 04  Theme: navy (dark family) ------------------------------------------
   Navy paper, cream ink. The inverse of cream.
   -------------------------------------------------------------------------- */
:root[data-theme='navy'] {
  --wf-bg-app: var(--wf-p-navy-900);
  --wf-bg-chrome: var(--wf-p-navy-850);
  --wf-bg-page: var(--wf-p-navy-800);
  --wf-bg-raised: var(--wf-p-navy-750);
  --wf-bg-sunken: var(--wf-p-navy-950);
  --wf-bg-hover: var(--wf-p-navy-700);
  --wf-bg-selected: var(--wf-p-navy-650);
  --wf-bg-scrim: rgb(8 12 24 / 0.6);

  --wf-text: var(--wf-p-cream-150);
  --wf-text-muted: var(--wf-p-cream-500);
  --wf-text-subtle: var(--wf-p-cream-600);
  --wf-text-disabled: var(--wf-p-navy-400);
  --wf-text-page: var(--wf-p-cream-175);

  --wf-line: var(--wf-p-navy-line);
  --wf-line-strong: var(--wf-p-navy-line-strong);
  --wf-line-control: var(--wf-p-navy-300);

  --wf-accent: var(--wf-p-cream-150);
  --wf-accent-hover: var(--wf-p-cream-50);
  --wf-accent-text: var(--wf-p-cream-150);
  --wf-accent-soft: var(--wf-p-navy-650);
  --wf-on-accent: var(--wf-p-navy-800);
  --wf-text-selection: var(--wf-p-navy-650);

  --wf-familiar: var(--wf-p-violet-300);
  --wf-familiar-text: var(--wf-p-violet-200);
  --wf-familiar-soft: var(--wf-p-violet-900);

  --wf-concern: var(--wf-p-madder-450);
  --wf-concern-text: var(--wf-p-madder-350);
  --wf-concern-soft: var(--wf-p-madder-925);
  --wf-danger: var(--wf-p-madder-850);
  --wf-on-danger: var(--wf-p-madder-950);

  --wf-ok-text: var(--wf-p-moss-350);
  --wf-ok-soft: var(--wf-p-moss-925);

  --wf-shadow-c1: rgb(5 9 20 / 0.35);
  --wf-shadow-c2: rgb(5 9 20 / 0.55);
}

/* -- 05  Type tokens ----------------------------------------------------------
   UI face: Atkinson Hyperlegible Next. Manuscript faces: section 06.
   The UI scale follows the classic typographic scale (Bringhurst):
   11 12 14 16 18 21 24 36 48. Sizes are rem so browser zoom works.
   -------------------------------------------------------------------------- */
:root {
  --wf-font-ui:
    'Atkinson Hyperlegible Next', ui-sans-serif, -apple-system, 'Segoe UI', Roboto, sans-serif;

  /* Manuscript faces the writer can pick */
  --wf-font-literata: 'Literata', 'Iowan Old Style', Georgia, serif;
  --wf-font-source-serif: 'Source Serif 4', 'Iowan Old Style', Georgia, serif;
  --wf-font-alegreya: 'Alegreya', 'Palatino Linotype', Palatino, serif;
  --wf-font-atkinson: var(--wf-font-ui);
  --wf-font-courier: 'Courier Prime', 'Courier New', Courier, monospace;

  --wf-text-2xs: 0.6875rem; /* 11px: fine print, counts in chips         */
  --wf-text-xs: 0.75rem; /*    12px: meta, badges, captions             */
  --wf-text-sm: 0.875rem; /*   14px: DEFAULT UI text                    */
  --wf-text-md: 1rem; /*       16px: chat and critique prose, inputs   */
  --wf-text-lg: 1.125rem; /*   18px: panel titles, card titles          */
  --wf-text-xl: 1.3125rem; /*  21px: section headings                   */
  --wf-text-2xl: 1.5rem; /*    24px: screen titles                      */
  --wf-text-3xl: 2.25rem; /*   36px: briefing headline                  */
  --wf-text-4xl: 3rem; /*      48px: empty-state display                */

  --wf-weight-regular: 400;
  --wf-weight-medium: 500;
  --wf-weight-semibold: 600;
  --wf-weight-bold: 700;

  --wf-leading-tight: 1.2;
  --wf-leading-snug: 1.35;
  --wf-leading-normal: 1.5;
  --wf-leading-relaxed: 1.6;

  --wf-track-tight: -0.012em;
  --wf-track-normal: 0;
  --wf-track-wide: 0.01em;
}

/* -- 06  Manuscript preference tokens ---------------------------------------
   The writer picks the face and size apart from the theme.
     data-ms-font on <html>: literata (default) | source-serif | alegreya | atkinson | courier
     data-ms-size on <html>: s | m (default) | l | xl
   The app may also set --wf-ms-size inline for a free value.
   Each face sets its own leading and measure, because x-height and width differ.
   -------------------------------------------------------------------------- */
:root {
  --wf-ms-font: var(--wf-font-literata);
  --wf-ms-size-s: 1.0625rem; /*  17px */
  --wf-ms-size-m: 1.1875rem; /*  19px */
  --wf-ms-size-l: 1.3125rem; /*  21px */
  --wf-ms-size-xl: 1.5rem; /*    24px */
  --wf-ms-size: var(--wf-ms-size-m);
  --wf-ms-leading: 1.7;
  --wf-ms-measure: 31em; /* about 68 characters of Literata */
  --wf-ms-indent: 1.5em;
  --wf-ms-para-gap: 0;
  --wf-ms-title-size: 2em;
  --wf-ms-title-weight: 500;
  --wf-ms-scene-glyph: '\2042'; /* the asterism. Works for starship and castle alike. */
  --wf-ms-numerals: oldstyle-nums proportional-nums;
}
:root[data-ms-font='source-serif'] {
  --wf-ms-font: var(--wf-font-source-serif);
  --wf-ms-leading: 1.65;
  --wf-ms-measure: 32em;
}
:root[data-ms-font='alegreya'] {
  --wf-ms-font: var(--wf-font-alegreya);
  --wf-ms-leading: 1.6;
  --wf-ms-measure: 33em;
}
:root[data-ms-font='atkinson'] {
  --wf-ms-font: var(--wf-font-atkinson);
  --wf-ms-leading: 1.6;
  --wf-ms-measure: 32em;
  --wf-ms-numerals: lining-nums proportional-nums;
}
:root[data-ms-font='courier'] {
  --wf-ms-font: var(--wf-font-courier);
  --wf-ms-leading: 1.75;
  --wf-ms-measure: 62ch;
  --wf-ms-indent: 5ch;
  --wf-ms-title-weight: 700;
  --wf-ms-numerals: normal;
}
:root[data-ms-size='s'] {
  --wf-ms-size: var(--wf-ms-size-s);
}
:root[data-ms-size='l'] {
  --wf-ms-size: var(--wf-ms-size-l);
}
:root[data-ms-size='xl'] {
  --wf-ms-size: var(--wf-ms-size-xl);
}
:root[data-ms-paragraphs='block'] {
  --wf-ms-indent: 0;
  --wf-ms-para-gap: 0.9em;
}

/* -- 07  Space, radius, border, elevation, z, motion, layout ---------------- */
:root {
  /* Space: 4px base */
  --wf-space-1: 2px;
  --wf-space-2: 4px;
  --wf-space-3: 8px;
  --wf-space-4: 12px;
  --wf-space-5: 16px;
  --wf-space-6: 24px;
  --wf-space-7: 32px;
  --wf-space-8: 48px;
  --wf-space-9: 64px;
  --wf-space-10: 96px;

  /* Radius: larger containers get larger radii */
  --wf-radius-xs: 3px; /*  entity chips, kbd, checkboxes */
  --wf-radius-sm: 6px; /*  buttons, inputs               */
  --wf-radius-md: 10px; /* cards, popovers, toasts        */
  --wf-radius-lg: 16px; /* phone sheets, dialogs          */
  --wf-radius-full: 999px; /* badges, switches, streak dots */

  /* Borders */
  --wf-border-w: 1px;
  --wf-border-w-strong: 2px;
  --wf-rule-w: 3px; /* the familiar's rule and margin marks */
  --wf-hairline: var(--wf-border-w) solid var(--wf-line);
  --wf-hairline-strong: var(--wf-border-w) solid var(--wf-line-strong);
  --wf-border-control: var(--wf-border-w) solid var(--wf-line-control);

  /* Elevation. The page is level 0 and never has a shadow. */
  --wf-shadow-1: 0 1px 2px var(--wf-shadow-c1);
  --wf-shadow-2: 0 2px 6px -1px var(--wf-shadow-c1), 0 1px 2px var(--wf-shadow-c1);
  --wf-shadow-3: 0 8px 24px -6px var(--wf-shadow-c2), 0 2px 6px var(--wf-shadow-c1);
  --wf-shadow-4: 0 20px 48px -12px var(--wf-shadow-c2), 0 4px 12px var(--wf-shadow-c1);

  /* Z layers. Use only these. */
  --wf-z-base: 0;
  --wf-z-margin-mark: 1;
  --wf-z-sticky: 10;
  --wf-z-sidebar: 20;
  --wf-z-panel: 30;
  --wf-z-popover: 40;
  --wf-z-scrim: 50;
  --wf-z-sheet: 60;
  --wf-z-dialog: 70;
  --wf-z-toast: 80;
  --wf-z-tooltip: 90;

  /* Motion */
  --wf-dur-instant: 80ms; /*  hover, press                         */
  --wf-dur-quick: 140ms; /*   tooltips, chips, small reveals       */
  --wf-dur-gentle: 240ms; /*  panels, sheets, toasts               */
  --wf-dur-slow: 420ms; /*    familiar state changes               */
  --wf-dur-glow: 1200ms; /*   the one familiar glow per idea       */
  --wf-dur-toast: 6000ms; /*  time a toast stays                   */
  --wf-ease-standard: cubic-bezier(0.2, 0, 0, 1);
  --wf-ease-enter: cubic-bezier(0, 0, 0.2, 1);
  --wf-ease-exit: cubic-bezier(0.4, 0, 1, 1);
  --wf-ease-flutter: cubic-bezier(0.34, 1.4, 0.64, 1);

  /* Layout */
  --wf-topbar-h: 48px;
  --wf-sidebar-w: 264px;
  --wf-panel-w: 368px;
  --wf-form-w: 400px; /* small standalone forms: sign-in, first-run setup */
  --wf-dialog-w: 480px; /* default dialog width */
  --wf-settings-w: 560px; /* settings page column */
  --wf-find-field-min: 8em; /* narrowest find or replace field */
  --wf-page-pad-x: var(--wf-space-8);
  --wf-page-pad-top: var(--wf-space-9);
  --wf-margin-mark-gap: var(--wf-space-5); /* distance from text to a margin mark */
  --wf-hovercard-w: 320px;
  --wf-toast-w: 380px;
  --wf-tooltip-w: 240px;
  --wf-sheet-max-h: 85dvh;

  /* Controls */
  --wf-control-h-sm: 28px;
  --wf-control-h: 36px;
  --wf-control-h-lg: 44px;
  --wf-touch-target: 44px;
  --wf-icon-sm: 16px;
  --wf-icon: 18px;
  --wf-icon-lg: 22px;
  --wf-icon-stroke: 1.5;
  --wf-check-size: 18px;
  --wf-switch-w: 34px;
  --wf-switch-h: 20px;
  --wf-switch-knob: 14px;
  --wf-dot-size: 10px;
  --wf-caret-size: 5px;

  /* Mascot sizes */
  --wf-mascot-xs: 16px; /*  glyph only: inline marks, tab icon      */
  --wf-mascot-sm: 24px; /*  chat avatar                             */
  --wf-mascot-md: 40px; /*  panel perch                             */
  --wf-mascot-lg: 96px; /*  empty states, briefing                  */
  --wf-mascot-xl: 160px; /* onboarding, streak milestone            */

  /* Formatting toolbar */
  --wf-tool-size: 32px;
  --wf-tool-sep-h: 20px;
  --wf-dock-h: 48px; /* phone toolbar docked above the keyboard */
  /* Height of the on-screen keyboard. Chrome fills it when the app sets
     navigator.virtualKeyboard.overlaysContent = true. On iOS the app sets
     it from visualViewport in JS. */
  --wf-keyboard-inset: env(keyboard-inset-height, 0px);

  /* Continuous scroll */
  --wf-chapter-gap: var(--wf-space-10); /* space above each chapter heading */

  /* Focus */
  --wf-focus-w: 2px;
  --wf-focus-offset: 2px;

  /* Device safe areas (PWA on phones) */
  --wf-safe-top: env(safe-area-inset-top, 0px);
  --wf-safe-bottom: env(safe-area-inset-bottom, 0px);
}

@media (max-width: 600px) {
  :root {
    --wf-page-pad-x: var(--wf-space-5);
    --wf-page-pad-top: var(--wf-space-7);
    --wf-ms-size-m: 1.125rem; /* 18px on phones */
    --wf-control-h: var(--wf-touch-target);
    --wf-margin-mark-gap: var(--wf-space-2);
  }
}

/* -- 08  Mascot tokens ---------------------------------------------------------
   Saga is a set of purple PNG sprites in src/assets/saga/. The typed module
   src/assets/saga/index.ts maps each UI state to its sprite URL.
   Sprites have different aspect ratios, so the box is square and the image
   sits on the bottom edge (Saga stands on the panel).
   -------------------------------------------------------------------------- */
:root {
  --wf-mascot-pop: 1.06; /* scale at the top of the idea pop */
  --wf-mascot-bob: 2px; /*  lift while thinking               */
}

/* -- 09  Reset and base ----------------------------------------------------- */
*,
*::before,
*::after {
  box-sizing: border-box;
}

[hidden] {
  display: none !important;
}

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--wf-bg-app);
  color: var(--wf-text);
  font-family: var(--wf-font-ui);
  font-size: var(--wf-text-sm);
  line-height: var(--wf-leading-normal);
  font-weight: var(--wf-weight-regular);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

h1,
h2,
h3,
h4,
p,
ul,
ol,
figure,
blockquote {
  margin: 0;
}

h1,
h2,
h3,
h4 {
  font-weight: var(--wf-weight-semibold);
  line-height: var(--wf-leading-tight);
  letter-spacing: var(--wf-track-tight);
  text-wrap: balance;
}

p {
  text-wrap: pretty;
}

a {
  color: var(--wf-accent-text);
  text-decoration-thickness: var(--wf-border-w);
  text-underline-offset: 0.18em;
}
a:hover {
  text-decoration-thickness: var(--wf-border-w-strong);
}

button,
input,
select,
textarea {
  font: inherit;
  color: inherit;
}

img,
svg {
  display: block;
  max-width: 100%;
}

::selection {
  background: var(--wf-text-selection);
  color: var(--wf-text);
}

:focus-visible {
  outline: var(--wf-focus-w) solid var(--wf-focus);
  outline-offset: var(--wf-focus-offset);
}

hr {
  border: 0;
  border-top: var(--wf-hairline);
  margin: var(--wf-space-5) 0;
}

/* -- 10  Type utilities ----------------------------------------------------- */
.wf-title {
  font-size: var(--wf-text-2xl);
  font-weight: var(--wf-weight-semibold);
  line-height: var(--wf-leading-tight);
}
.wf-heading {
  font-size: var(--wf-text-xl);
  font-weight: var(--wf-weight-semibold);
  line-height: var(--wf-leading-tight);
}
.wf-subheading {
  font-size: var(--wf-text-lg);
  font-weight: var(--wf-weight-semibold);
  line-height: var(--wf-leading-snug);
}
.wf-body {
  font-size: var(--wf-text-md);
  line-height: var(--wf-leading-relaxed);
}
.wf-meta {
  font-size: var(--wf-text-xs);
  color: var(--wf-text-subtle);
  line-height: var(--wf-leading-snug);
}
.wf-muted {
  color: var(--wf-text-muted);
}
.wf-num {
  font-variant-numeric: tabular-nums lining-nums;
}
.wf-label {
  display: block;
  font-size: var(--wf-text-sm);
  font-weight: var(--wf-weight-semibold);
  color: var(--wf-text);
}

/* -- 11  Buttons ---------------------------------------------------------------
   primary   The writer's one main action in a view. Ink fill.
   secondary Outlined.
   quiet     No box at rest. For toolbars and card actions.
   familiar  Asks the familiar for something (Ask, Riff, Check). Familiar-color outline.
   danger    Destructive only.
   -------------------------------------------------------------------------- */
.wf-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--wf-space-2);
  min-height: var(--wf-control-h);
  padding: 0 var(--wf-space-4);
  border: var(--wf-border-w) solid transparent;
  border-radius: var(--wf-radius-sm);
  background: transparent;
  color: var(--wf-text);
  font-size: var(--wf-text-sm);
  font-weight: var(--wf-weight-semibold);
  line-height: var(--wf-leading-tight);
  white-space: nowrap;
  cursor: pointer;
  transition:
    background-color var(--wf-dur-instant) var(--wf-ease-standard),
    border-color var(--wf-dur-instant) var(--wf-ease-standard),
    color var(--wf-dur-instant) var(--wf-ease-standard);
}
.wf-btn svg {
  width: var(--wf-icon);
  height: var(--wf-icon);
  flex: none;
}
.wf-btn:disabled,
.wf-btn[aria-disabled='true'] {
  color: var(--wf-text-disabled);
  cursor: not-allowed;
}

.wf-btn--primary {
  background: var(--wf-accent);
  color: var(--wf-on-accent);
}
.wf-btn--primary:hover:not(:disabled) {
  background: var(--wf-accent-hover);
}
.wf-btn--primary:disabled {
  background: var(--wf-bg-hover);
}

.wf-btn--secondary {
  border: var(--wf-border-control);
  background: var(--wf-bg-raised);
}
.wf-btn--secondary:hover:not(:disabled) {
  background: var(--wf-bg-hover);
}

.wf-btn--quiet {
  color: var(--wf-text-muted);
}
.wf-btn--quiet:hover:not(:disabled) {
  background: var(--wf-bg-hover);
  color: var(--wf-text);
}

.wf-btn--familiar {
  border: var(--wf-border-w) solid var(--wf-familiar);
  background: var(--wf-familiar-soft);
  color: var(--wf-familiar-text);
}
.wf-btn--familiar:hover:not(:disabled) {
  background: var(--wf-bg-raised);
}

.wf-btn--danger {
  background: var(--wf-danger);
  color: var(--wf-on-danger);
}

.wf-btn--sm {
  min-height: var(--wf-control-h-sm);
  padding: 0 var(--wf-space-3);
  font-size: var(--wf-text-xs);
}
.wf-btn--lg {
  min-height: var(--wf-control-h-lg);
  padding: 0 var(--wf-space-5);
  font-size: var(--wf-text-md);
}
.wf-btn--icon {
  width: var(--wf-control-h);
  padding: 0;
}
.wf-btn--icon.wf-btn--sm {
  width: var(--wf-control-h-sm);
}
.wf-btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--wf-space-2);
}

/* -- 12  Fields --------------------------------------------------------------- */
.wf-field {
  display: grid;
  gap: var(--wf-space-2);
}
.wf-field-hint {
  font-size: var(--wf-text-xs);
  color: var(--wf-text-subtle);
}
.wf-field-error {
  font-size: var(--wf-text-xs);
  color: var(--wf-concern-text);
  font-weight: var(--wf-weight-semibold);
}

.wf-input,
.wf-textarea,
.wf-select {
  width: 100%;
  min-height: var(--wf-control-h);
  padding: var(--wf-space-2) var(--wf-space-4);
  border: var(--wf-border-control);
  border-radius: var(--wf-radius-sm);
  background: var(--wf-bg-sunken);
  color: var(--wf-text);
  font-size: var(--wf-text-md);
  line-height: var(--wf-leading-snug);
  transition: border-color var(--wf-dur-instant) var(--wf-ease-standard);
}
.wf-input::placeholder,
.wf-textarea::placeholder {
  color: var(--wf-text-subtle);
}
.wf-input:hover,
.wf-textarea:hover,
.wf-select:hover {
  border-color: var(--wf-text-muted);
}
.wf-input:focus-visible,
.wf-textarea:focus-visible,
.wf-select:focus-visible {
  outline-offset: 0;
  border-color: var(--wf-focus);
}
.wf-input[aria-invalid='true'] {
  border-color: var(--wf-concern);
}
.wf-textarea {
  min-height: calc(var(--wf-control-h) * 2.5);
  padding-block: var(--wf-space-3);
  resize: vertical;
}
.wf-select {
  appearance: none;
  padding-inline-end: var(--wf-space-7);
  background-image:
    linear-gradient(45deg, transparent 50%, var(--wf-text-muted) 50%),
    linear-gradient(135deg, var(--wf-text-muted) 50%, transparent 50%);
  background-position:
    right var(--wf-space-5) center,
    right var(--wf-space-4) center;
  background-size: var(--wf-caret-size) var(--wf-caret-size);
  background-repeat: no-repeat;
}

.wf-input-group {
  position: relative;
  display: flex;
  align-items: center;
}
.wf-input-group > svg {
  position: absolute;
  inset-inline-start: var(--wf-space-4);
  width: var(--wf-icon-sm);
  height: var(--wf-icon-sm);
  color: var(--wf-text-subtle);
  pointer-events: none;
}
.wf-input-group > .wf-input {
  padding-inline-start: var(--wf-space-7);
}

.wf-check {
  display: inline-flex;
  align-items: center;
  gap: var(--wf-space-3);
  min-height: var(--wf-control-h-sm);
  cursor: pointer;
}
.wf-check input {
  appearance: none;
  margin: 0;
  width: var(--wf-check-size);
  height: var(--wf-check-size);
  border: var(--wf-border-control);
  border-radius: var(--wf-radius-xs);
  background: var(--wf-bg-sunken);
  display: grid;
  place-content: center;
  flex: none;
}
.wf-check input::after {
  content: '';
  width: calc(var(--wf-check-size) / 2.2);
  height: calc(var(--wf-check-size) / 3.8);
  border-left: var(--wf-border-w-strong) solid var(--wf-on-accent);
  border-bottom: var(--wf-border-w-strong) solid var(--wf-on-accent);
  transform: rotate(-45deg) translate(var(--wf-border-w), calc(-1 * var(--wf-border-w)));
  opacity: 0;
}
.wf-check input:checked {
  background: var(--wf-accent);
  border-color: var(--wf-accent);
}
.wf-check input:checked::after {
  opacity: 1;
}

.wf-switch {
  display: inline-flex;
  align-items: center;
  gap: var(--wf-space-3);
  cursor: pointer;
}
.wf-switch input {
  appearance: none;
  margin: 0;
  position: relative;
  width: var(--wf-switch-w);
  height: var(--wf-switch-h);
  border: var(--wf-border-control);
  border-radius: var(--wf-radius-full);
  background: var(--wf-bg-sunken);
  flex: none;
  transition: background-color var(--wf-dur-quick) var(--wf-ease-standard);
}
.wf-switch input::after {
  content: '';
  position: absolute;
  top: 50%;
  left: var(--wf-space-1);
  width: var(--wf-switch-knob);
  height: var(--wf-switch-knob);
  border-radius: var(--wf-radius-full);
  background: var(--wf-text-muted);
  transform: translateY(-50%);
  transition:
    left var(--wf-dur-quick) var(--wf-ease-standard),
    background-color var(--wf-dur-quick) var(--wf-ease-standard);
}
.wf-switch input:checked {
  background: var(--wf-accent);
  border-color: var(--wf-accent);
}
.wf-switch input:checked::after {
  left: calc(100% - var(--wf-switch-knob) - var(--wf-space-1));
  background: var(--wf-on-accent);
}

/* -- 13  Tabs ------------------------------------------------------------------
   Used for the AI panel modes: Chat, Riff, Critique.
   -------------------------------------------------------------------------- */
.wf-tabs {
  display: flex;
  gap: var(--wf-space-1);
  padding: var(--wf-space-1);
  border-radius: var(--wf-radius-sm);
  background: var(--wf-bg-sunken);
}
.wf-tab {
  flex: 1;
  white-space: nowrap;
  min-height: var(--wf-control-h-sm);
  padding: 0 var(--wf-space-3);
  border: 0;
  border-radius: calc(var(--wf-radius-sm) - var(--wf-space-1));
  background: transparent;
  color: var(--wf-text-muted);
  font-size: var(--wf-text-sm);
  font-weight: var(--wf-weight-semibold);
  cursor: pointer;
}
.wf-tab:hover {
  color: var(--wf-text);
}
.wf-tab[aria-selected='true'] {
  background: var(--wf-bg-raised);
  color: var(--wf-text);
  box-shadow: var(--wf-shadow-1);
}

/* -- 14  Panels and cards ------------------------------------------------------
   A panel is a region of chrome: the sidebar, the AI panel, a settings pane.
   A card is one item on a panel. Cards are the only raised thing on chrome.
   -------------------------------------------------------------------------- */
.wf-panel {
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: var(--wf-bg-chrome);
  color: var(--wf-text);
}
.wf-panel-head {
  display: flex;
  align-items: center;
  gap: var(--wf-space-3);
  min-height: var(--wf-topbar-h);
  padding: 0 var(--wf-space-5);
  border-bottom: var(--wf-hairline);
}
.wf-panel-title {
  flex: 1;
  font-size: var(--wf-text-sm);
  font-weight: var(--wf-weight-semibold);
}
.wf-panel-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: var(--wf-space-5);
  display: flex;
  flex-direction: column;
  gap: var(--wf-space-4);
}
.wf-panel-foot {
  padding: var(--wf-space-4) var(--wf-space-5);
  border-top: var(--wf-hairline);
}
.wf-panel-empty {
  display: grid;
  justify-items: center;
  gap: var(--wf-space-4);
  padding: var(--wf-space-8) var(--wf-space-5);
  text-align: center;
  color: var(--wf-text-muted);
}

.wf-card {
  padding: var(--wf-space-5);
  border: var(--wf-hairline);
  border-radius: var(--wf-radius-md);
  background: var(--wf-bg-raised);
  box-shadow: var(--wf-shadow-1);
}
.wf-card-title {
  font-size: var(--wf-text-md);
  font-weight: var(--wf-weight-semibold);
  line-height: var(--wf-leading-snug);
}
.wf-card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--wf-space-2);
  margin-top: var(--wf-space-4);
}

/* -- 15  The familiar ----------------------------------------------------------
   .wf-voice    Anything the familiar says. Carries the familiar rule on its
                start edge. This rule is the familiar's signature.
   .wf-familiar Saga's sprite box. The app sets data-state and picks the file:
                idle        saga_idle
                thinking    saga_thinking
                idea        saga_exclamation
                celebrating saga_success
                concerned   saga_magnifying_glass
                data-pose overrides the file for single moments.
   .wf-perch    The mascot seat at the top of the AI panel. It hangs on the
                inner edge of the panel, facing the page.
   -------------------------------------------------------------------------- */
.wf-voice {
  position: relative;
  padding-inline-start: var(--wf-space-5);
  border-inline-start: var(--wf-rule-w) solid var(--wf-familiar);
}
.wf-voice-name {
  display: flex;
  align-items: center;
  gap: var(--wf-space-2);
  font-size: var(--wf-text-xs);
  font-weight: var(--wf-weight-semibold);
  color: var(--wf-familiar-text);
}

.wf-familiar {
  display: inline-block;
  flex: none;
  width: var(--wf-mascot-md);
  height: var(--wf-mascot-md);
}
.wf-familiar img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center bottom;
  transform-origin: center bottom;
}
.wf-familiar--xs {
  width: var(--wf-mascot-xs);
  height: var(--wf-mascot-xs);
}
.wf-familiar--sm {
  width: var(--wf-mascot-sm);
  height: var(--wf-mascot-sm);
}
.wf-familiar--lg {
  width: var(--wf-mascot-lg);
  height: var(--wf-mascot-lg);
}
.wf-familiar--xl {
  width: var(--wf-mascot-xl);
  height: var(--wf-mascot-xl);
}

/* Motion per state. The sprite carries the pose. CSS adds only a small cue. */
.wf-familiar[data-state='thinking'] img {
  animation: wf-bob var(--wf-dur-glow) var(--wf-ease-standard) infinite alternate;
}
.wf-familiar[data-state='idea'] img,
.wf-familiar[data-state='celebrating'] img,
.wf-familiar[data-state='concerned'] img {
  animation: wf-pop var(--wf-dur-slow) var(--wf-ease-flutter) 1;
}

@keyframes wf-bob {
  to {
    transform: translateY(calc(-1 * var(--wf-mascot-bob)));
  }
}
@keyframes wf-pop {
  50% {
    transform: scale(var(--wf-mascot-pop));
  }
}

.wf-perch {
  display: flex;
  align-items: center;
  gap: var(--wf-space-3);
  padding: var(--wf-space-4) var(--wf-space-5);
  border-bottom: var(--wf-hairline);
}
.wf-perch-text {
  flex: 1;
  min-width: 0;
}
.wf-perch-status {
  font-size: var(--wf-text-xs);
  color: var(--wf-text-subtle);
}

/* -- 16  Chat ------------------------------------------------------------------
   The writer's messages sit plain. The familiar's messages carry its voice.
   Manuscript quotes inside any message use the manuscript face.
   -------------------------------------------------------------------------- */
.wf-thread {
  display: flex;
  flex-direction: column;
  gap: var(--wf-space-5);
}
.wf-msg {
  font-size: var(--wf-text-md);
  line-height: var(--wf-leading-relaxed);
}
.wf-msg p + p {
  margin-top: var(--wf-space-3);
}
.wf-msg--writer {
  align-self: flex-end;
  max-width: 88%;
  padding: var(--wf-space-3) var(--wf-space-4);
  border-radius: var(--wf-radius-md);
  background: var(--wf-bg-hover);
}
.wf-msg--familiar {
  padding-inline-start: var(--wf-space-5);
  border-inline-start: var(--wf-rule-w) solid var(--wf-familiar);
}
.wf-composer {
  display: flex;
  align-items: flex-end;
  gap: var(--wf-space-2);
}
.wf-composer .wf-textarea {
  min-height: var(--wf-control-h-lg);
  font-size: var(--wf-text-md);
}

/* A verbatim quote from the manuscript. Code verified it before display. */
.wf-quote {
  margin: var(--wf-space-3) 0;
  padding: var(--wf-space-2) 0 var(--wf-space-2) var(--wf-space-4);
  border-inline-start: var(--wf-border-w-strong) solid var(--wf-line-strong);
  font-family: var(--wf-ms-font);
  font-size: var(--wf-text-md);
  line-height: var(--wf-leading-relaxed);
  color: var(--wf-text-page);
  font-variant-numeric: var(--wf-ms-numerals);
}
.wf-quote-source {
  display: flex;
  align-items: center;
  gap: var(--wf-space-2);
  margin-top: var(--wf-space-1);
  font-family: var(--wf-font-ui);
  font-size: var(--wf-text-xs);
  color: var(--wf-text-subtle);
}
.wf-quote-source svg {
  width: var(--wf-icon-sm);
  height: var(--wf-icon-sm);
  color: var(--wf-ok-text);
}

/* -- 17  Critique cards ------------------------------------------------------
   One finding per card. The familiar's rule marks the start edge.
   Kinds: default (familiar rule), --concern (madder rule, inconsistency).
   Anatomy: head (kind + location + severity), quote, note, ledger line or
   suggested wording, actions.
   -------------------------------------------------------------------------- */
.wf-critique {
  display: grid;
  gap: var(--wf-space-3);
  padding: var(--wf-space-4) var(--wf-space-5) var(--wf-space-4) var(--wf-space-5);
  border: var(--wf-hairline);
  border-inline-start: var(--wf-rule-w) solid var(--wf-familiar);
  border-radius: 0 var(--wf-radius-md) var(--wf-radius-md) 0;
  background: var(--wf-bg-raised);
  box-shadow: var(--wf-shadow-1);
}
.wf-critique--concern {
  border-inline-start-color: var(--wf-concern);
}
.wf-critique[aria-current='true'] {
  box-shadow: var(--wf-shadow-2);
  border-color: var(--wf-line-strong);
}
.wf-critique-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--wf-space-2) var(--wf-space-3);
}
.wf-critique-kind {
  font-size: var(--wf-text-sm);
  font-weight: var(--wf-weight-semibold);
}
.wf-critique-where {
  margin-inline-start: auto;
  font-size: var(--wf-text-xs);
  color: var(--wf-text-subtle);
}
.wf-critique-note {
  font-size: var(--wf-text-md);
  line-height: var(--wf-leading-relaxed);
}
.wf-critique .wf-quote {
  margin: 0;
}
.wf-ledger-line {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--wf-space-2);
  padding: var(--wf-space-3) var(--wf-space-4);
  border-radius: var(--wf-radius-sm);
  background: var(--wf-bg-sunken);
  font-size: var(--wf-text-sm);
}
.wf-suggestion {
  padding: var(--wf-space-3) var(--wf-space-4);
  border-radius: var(--wf-radius-sm);
  background: var(--wf-familiar-soft);
  font-family: var(--wf-ms-font);
  font-size: var(--wf-text-md);
  line-height: var(--wf-leading-relaxed);
  color: var(--wf-text);
}
.wf-suggestion-label {
  display: block;
  margin-bottom: var(--wf-space-1);
  font-family: var(--wf-font-ui);
  font-size: var(--wf-text-xs);
  font-weight: var(--wf-weight-semibold);
  color: var(--wf-familiar-text);
}
.wf-critique-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--wf-space-2);
}

/* -- 18  Entities ------------------------------------------------------------
   Kind is shown by a shape, not by color, so it survives every theme and
   color blindness:
     character  circle     place    square     rule   triangle
     object     diamond    faction  hexagon    event  bar
   -------------------------------------------------------------------------- */
.wf-entity {
  display: inline-flex;
  align-items: center;
  gap: var(--wf-space-2);
  min-height: var(--wf-control-h-sm);
  padding: 0 var(--wf-space-3);
  border: var(--wf-hairline-strong);
  border-radius: var(--wf-radius-xs);
  background: var(--wf-bg-raised);
  color: var(--wf-text);
  font-size: var(--wf-text-sm);
  font-weight: var(--wf-weight-medium);
  text-decoration: none;
  cursor: pointer;
}
.wf-entity:hover {
  background: var(--wf-bg-hover);
}
.wf-entity::before {
  content: '';
  width: var(--wf-space-3);
  height: var(--wf-space-3);
  flex: none;
  background: var(--wf-text-muted);
}
.wf-entity[data-kind='character']::before {
  border-radius: var(--wf-radius-full);
}
.wf-entity[data-kind='place']::before {
  border-radius: 0;
}
.wf-entity[data-kind='rule']::before {
  clip-path: polygon(50% 0, 100% 100%, 0 100%);
}
.wf-entity[data-kind='object']::before {
  clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
}
.wf-entity[data-kind='faction']::before {
  clip-path: polygon(25% 0, 75% 0, 100% 50%, 75% 100%, 25% 100%, 0 50%);
}
.wf-entity[data-kind='event']::before {
  height: var(--wf-space-2);
}
.wf-entity-count {
  font-size: var(--wf-text-2xs);
  color: var(--wf-text-subtle);
  font-variant-numeric: tabular-nums;
}
.wf-entity--proposed {
  border-style: dashed;
  border-color: var(--wf-familiar);
}

/* Hover card: an entity's state as of the passage under the pointer. */
.wf-hovercard {
  width: var(--wf-hovercard-w);
  max-width: calc(100vw - var(--wf-space-6));
  padding: var(--wf-space-5);
  border: var(--wf-hairline);
  border-radius: var(--wf-radius-md);
  background: var(--wf-bg-raised);
  box-shadow: var(--wf-shadow-3);
  z-index: var(--wf-z-popover);
  font-size: var(--wf-text-sm);
}
.wf-hovercard-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--wf-space-3);
  margin-bottom: var(--wf-space-3);
}
.wf-hovercard-name {
  font-size: var(--wf-text-lg);
  font-weight: var(--wf-weight-semibold);
}
.wf-facts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--wf-space-2) var(--wf-space-4);
  margin: 0;
}
.wf-facts dt {
  color: var(--wf-text-subtle);
}
.wf-facts dd {
  margin: 0;
}
.wf-fact-changed {
  display: block;
  font-size: var(--wf-text-xs);
  color: var(--wf-text-subtle);
}

/* Fact timeline: one attribute across the book */
.wf-fact-track {
  display: flex;
  gap: var(--wf-space-1);
  font-size: var(--wf-text-xs);
}
.wf-fact-span {
  flex: var(--span, 1);
  min-width: 0;
  padding: var(--wf-space-2) var(--wf-space-3);
  border-top: var(--wf-border-w-strong) solid var(--wf-line-control);
  color: var(--wf-text-muted);
}
.wf-fact-span[aria-current='true'] {
  border-top-color: var(--wf-accent);
  color: var(--wf-text);
}

/* -- 19  Badges, kbd, saved state -------------------------------------------- */
.wf-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--wf-space-1);
  min-height: calc(var(--wf-text-xs) * 1.8);
  padding: 0 var(--wf-space-3);
  border-radius: var(--wf-radius-full);
  background: var(--wf-bg-hover);
  color: var(--wf-text-muted);
  font-size: var(--wf-text-xs);
  font-weight: var(--wf-weight-semibold);
  white-space: nowrap;
}
.wf-badge--familiar {
  background: var(--wf-familiar-soft);
  color: var(--wf-familiar-text);
}
.wf-badge--concern {
  background: var(--wf-concern-soft);
  color: var(--wf-concern-text);
}
.wf-badge--ok {
  background: var(--wf-ok-soft);
  color: var(--wf-ok-text);
}
.wf-badge--ink {
  background: var(--wf-accent-soft);
  color: var(--wf-accent-text);
}

.wf-kbd {
  display: inline-block;
  min-width: calc(var(--wf-text-xs) * 1.8);
  padding: 0 var(--wf-space-2);
  border: var(--wf-hairline-strong);
  border-bottom-width: var(--wf-border-w-strong);
  border-radius: var(--wf-radius-xs);
  background: var(--wf-bg-raised);
  color: var(--wf-text-muted);
  font-size: var(--wf-text-xs);
  line-height: var(--wf-leading-normal);
  text-align: center;
}

.wf-saved {
  display: inline-flex;
  align-items: center;
  gap: var(--wf-space-2);
  font-size: var(--wf-text-xs);
  color: var(--wf-text-subtle);
}
.wf-saved::before {
  content: '';
  width: var(--wf-space-3);
  height: var(--wf-space-3);
  border-radius: var(--wf-radius-full);
  background: var(--wf-ok-text);
}
.wf-saved[data-state='saving']::before {
  background: transparent;
  border: var(--wf-border-w-strong) solid var(--wf-text-subtle);
}
.wf-saved[data-state='offline']::before {
  /* Saved on this device, not yet on the server. Not an error: a hollow ring
     in the moss color says "safe here, waiting". */
  background: transparent;
  border: var(--wf-border-w-strong) solid var(--wf-ok-text);
}
.wf-saved[data-state='error'] {
  color: var(--wf-concern-text);
  font-weight: var(--wf-weight-semibold);
}
.wf-saved[data-state='error']::before {
  background: var(--wf-concern);
}

/* -- 19b Banner ----------------------------------------------------------------
   A non-modal notice, inline above the page text. Use it when the writer must
   choose before work continues (a save conflict), but the page stays usable.
   Neutral by default. --concern for problems that need a decision.
   -------------------------------------------------------------------------- */
.wf-banner {
  display: grid;
  gap: var(--wf-space-3);
  margin-bottom: var(--wf-space-6);
  padding: var(--wf-space-4) var(--wf-space-5);
  border-inline-start: var(--wf-rule-w) solid var(--wf-line-strong);
  border-radius: 0 var(--wf-radius-sm) var(--wf-radius-sm) 0;
  background: var(--wf-bg-sunken);
  color: var(--wf-text);
  font-family: var(--wf-font-ui);
  font-size: var(--wf-text-sm);
  line-height: var(--wf-leading-normal);
}
.wf-banner--concern {
  border-inline-start-color: var(--wf-concern);
  background: var(--wf-concern-soft);
}
.wf-banner-title {
  font-weight: var(--wf-weight-semibold);
}
.wf-banner-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--wf-space-2);
}

/* -- 19c Dialog ----------------------------------------------------------------
   For the native <dialog>. showModal() puts it in the top layer, so it needs
   no z-index. Width comes from --wf-dialog-w; --form uses the small form width.
   -------------------------------------------------------------------------- */
.wf-dialog {
  width: min(var(--wf-dialog-w), calc(100vw - 2 * var(--wf-space-5)));
  max-width: none;
  max-height: calc(100dvh - 2 * var(--wf-space-7));
  margin: auto;
  padding: var(--wf-space-6);
  border: var(--wf-hairline);
  border-radius: var(--wf-radius-lg);
  background: var(--wf-bg-raised);
  color: var(--wf-text);
  box-shadow: var(--wf-shadow-4);
  overflow: auto;
}
.wf-dialog--form {
  --wf-dialog-w: var(--wf-form-w);
}
.wf-dialog::backdrop {
  background: var(--wf-bg-scrim);
}
.wf-dialog[open] {
  animation: wf-dialog-in var(--wf-dur-gentle) var(--wf-ease-enter);
}
.wf-dialog-title {
  font-size: var(--wf-text-xl);
  font-weight: var(--wf-weight-semibold);
  line-height: var(--wf-leading-tight);
}
.wf-dialog-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--wf-space-2);
}
@keyframes wf-dialog-in {
  from {
    opacity: 0;
    transform: translateY(var(--wf-space-3));
  }
}

/* -- 20  Toasts, tooltips ------------------------------------------------------ */
.wf-toast-region {
  position: fixed;
  inset-inline-start: 50%;
  bottom: calc(var(--wf-space-6) + var(--wf-safe-bottom));
  z-index: var(--wf-z-toast);
  display: grid;
  gap: var(--wf-space-3);
  width: min(var(--wf-toast-w), calc(100vw - var(--wf-space-6)));
  transform: translateX(-50%);
}
.wf-toast {
  display: flex;
  align-items: center;
  gap: var(--wf-space-4);
  padding: var(--wf-space-3) var(--wf-space-3) var(--wf-space-3) var(--wf-space-5);
  border-radius: var(--wf-radius-md);
  background: var(--wf-text);
  color: var(--wf-bg-raised);
  box-shadow: var(--wf-shadow-4);
  font-size: var(--wf-text-sm);
  animation: wf-toast-in var(--wf-dur-gentle) var(--wf-ease-enter);
}
.wf-toast-msg {
  flex: 1;
}
.wf-toast .wf-btn--quiet {
  color: var(--wf-bg-raised);
}
.wf-toast .wf-btn--quiet:hover {
  background: var(--wf-text-muted);
  color: var(--wf-bg-raised);
}
@keyframes wf-toast-in {
  from {
    opacity: 0;
    transform: translateY(var(--wf-space-3));
  }
}

.wf-tooltip {
  max-width: var(--wf-tooltip-w);
  padding: var(--wf-space-2) var(--wf-space-3);
  border-radius: var(--wf-radius-sm);
  background: var(--wf-text);
  color: var(--wf-bg-raised);
  font-size: var(--wf-text-xs);
  line-height: var(--wf-leading-snug);
  z-index: var(--wf-z-tooltip);
  box-shadow: var(--wf-shadow-2);
}
.wf-tooltip .wf-kbd {
  border-color: var(--wf-text-muted);
  background: transparent;
  color: var(--wf-bg-raised);
}

/* -- 21  Sidebar ---------------------------------------------------------------
   Chapters and scenes. Drag to reorder.
   -------------------------------------------------------------------------- */
.wf-sidebar {
  width: var(--wf-sidebar-w);
  border-inline-end: var(--wf-hairline);
}
.wf-book-title {
  font-size: var(--wf-text-md);
  font-weight: var(--wf-weight-semibold);
  line-height: var(--wf-leading-snug);
}
.wf-chapters {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--wf-space-1);
}
.wf-chapter {
  display: grid;
  grid-template-columns: var(--wf-icon-sm) 1fr auto;
  align-items: center;
  gap: var(--wf-space-3);
  min-height: var(--wf-control-h);
  padding: var(--wf-space-2) var(--wf-space-3);
  border-radius: var(--wf-radius-sm);
  color: var(--wf-text-muted);
  text-decoration: none;
  cursor: pointer;
}
.wf-chapter:hover {
  background: var(--wf-bg-hover);
  color: var(--wf-text);
}
.wf-chapter[aria-current='page'] {
  background: var(--wf-bg-selected);
  color: var(--wf-text);
  font-weight: var(--wf-weight-semibold);
}
.wf-chapter-grip {
  width: var(--wf-icon-sm);
  height: var(--wf-icon-sm);
  color: var(--wf-text-subtle);
  opacity: 0;
  cursor: grab;
}
.wf-chapter:hover .wf-chapter-grip,
.wf-chapter:focus-within .wf-chapter-grip {
  opacity: 1;
}
.wf-chapter-count {
  font-size: var(--wf-text-xs);
  color: var(--wf-text-subtle);
  font-variant-numeric: tabular-nums;
  font-weight: var(--wf-weight-regular);
}
.wf-scenes {
  list-style: none;
  margin: 0 0 var(--wf-space-2);
  padding: 0 0 0 calc(var(--wf-icon-sm) + var(--wf-space-5));
  display: grid;
}
.wf-scene {
  padding: var(--wf-space-1) var(--wf-space-3);
  border-inline-start: var(--wf-hairline);
  font-size: var(--wf-text-xs);
  color: var(--wf-text-subtle);
}
.wf-scene[aria-current='true'] {
  border-inline-start-color: var(--wf-accent);
  color: var(--wf-text);
}
/* Scene jump link: a button that scrolls the page to the scene. One line,
   truncated. It inherits the scene's color, so the current scene reads darker. */
.wf-scene-link {
  display: block;
  width: 100%;
  padding: var(--wf-space-1) var(--wf-space-2);
  border: 0;
  border-radius: var(--wf-radius-xs);
  background: transparent;
  color: inherit;
  font-size: var(--wf-text-xs);
  line-height: var(--wf-leading-snug);
  text-align: start;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
}
.wf-scene-link:hover {
  background: var(--wf-bg-hover);
  color: var(--wf-text);
}
.wf-scene:has(.wf-scene-link) {
  padding-block: 0;
  padding-inline-end: 0;
}
.wf-chapter.is-dragging {
  background: var(--wf-bg-raised);
  box-shadow: var(--wf-shadow-3);
}
.wf-dropline {
  height: var(--wf-border-w-strong);
  background: var(--wf-accent);
  border-radius: var(--wf-radius-full);
}

/* -- 22  Manuscript page -------------------------------------------------------
   The most important surface. Flat, no shadow, no border, no texture.
   Book conventions: indented paragraphs, no indent after a heading or a scene
   break, old-style numerals, ragged right.
   The familiar marks a paragraph from the margin. It never tints the words.
   -------------------------------------------------------------------------- */
.wf-page {
  min-height: 100%;
  background: var(--wf-bg-page);
  padding: var(--wf-page-pad-top) var(--wf-page-pad-x) var(--wf-space-10);
}
.wf-ms {
  max-width: var(--wf-ms-measure);
  margin: 0 auto;
  font-family: var(--wf-ms-font);
  font-size: var(--wf-ms-size);
  line-height: var(--wf-ms-leading);
  color: var(--wf-text-page);
  font-variant-numeric: var(--wf-ms-numerals);
  font-optical-sizing: auto;
  font-kerning: normal;
  font-variant-ligatures: common-ligatures;
  hanging-punctuation: first;
  hyphens: manual;
  caret-color: var(--wf-accent);
}
.wf-ms:focus {
  outline: none;
}
/* Headings inside a chapter. The chapter title is not here: it is chrome
   (.wf-chapter-head), outside the chapter's document. */
.wf-ms h2,
.wf-ms h3 {
  font-family: var(--wf-ms-font);
  font-weight: var(--wf-ms-title-weight);
  line-height: var(--wf-leading-tight);
  letter-spacing: var(--wf-track-tight);
  color: var(--wf-text-page);
  text-wrap: balance;
}
.wf-ms h2 {
  margin: 2em 0 0.75em;
  font-size: 1.45em;
}
.wf-ms h3 {
  margin: 1.75em 0 0.6em;
  font-size: 1.15em;
  font-style: italic;
}
.wf-ms > :first-child {
  margin-top: 0;
}
.wf-ms blockquote {
  margin: 1em 0;
  padding-inline-start: var(--wf-ms-indent);
  border-inline-start: var(--wf-border-w-strong) solid var(--wf-line-strong);
  font-style: italic;
}
.wf-ms blockquote p {
  text-indent: 0;
}
.wf-ms u {
  text-decoration-thickness: var(--wf-border-w);
  text-underline-offset: 0.2em;
}
.wf-ms s {
  text-decoration-thickness: var(--wf-border-w);
  color: var(--wf-text-muted);
}
.wf-ms p {
  position: relative;
  margin: 0;
  text-indent: var(--wf-ms-indent);
  text-wrap: pretty;
}
.wf-ms p + p {
  margin-top: var(--wf-ms-para-gap);
}
.wf-ms h2 + p,
.wf-ms h3 + p,
.wf-ms blockquote + p,
.wf-ms hr + p,
.wf-ms p:first-child {
  text-indent: 0;
}
.wf-ms em {
  font-style: italic;
}
.wf-ms strong {
  font-weight: var(--wf-weight-semibold);
}
/* Scene break: centered asterism, a full blank line above and below */
.wf-ms hr {
  border: 0;
  margin: calc(var(--wf-ms-leading) * 1em) 0;
  text-align: center;
  overflow: visible;
  height: calc(var(--wf-ms-leading) * 1em);
}
.wf-ms hr::after {
  content: var(--wf-ms-scene-glyph);
  color: var(--wf-text-subtle);
  font-size: 1em;
  line-height: var(--wf-ms-leading);
}

/* Entity mention: faint dotted underline. Hover opens the hover card. */
.wf-mention {
  text-decoration: underline dotted var(--wf-line-control);
  text-decoration-thickness: var(--wf-border-w);
  text-underline-offset: 0.25em;
  cursor: help;
}

/* Margin mark: the familiar points at a paragraph from the start margin */
.wf-ms p[data-mark]::before {
  content: '';
  position: absolute;
  top: 0.3em;
  bottom: 0.3em;
  inset-inline-start: calc(-1 * var(--wf-margin-mark-gap) - var(--wf-rule-w));
  width: var(--wf-rule-w);
  border-radius: var(--wf-radius-full);
  background: var(--wf-familiar);
  z-index: var(--wf-z-margin-mark);
}
.wf-ms p[data-mark='concern']::before {
  background: var(--wf-concern);
}
.wf-ms p[data-mark][data-mark-active]::before {
  inset-inline-start: calc(-1 * var(--wf-margin-mark-gap) - var(--wf-rule-w) * 2);
  width: calc(var(--wf-rule-w) * 2);
}

/* Origins view: the writer turns it on to see where text came from.
   This is the only case in which the familiar color touches the words. */
.wf-ms.is-showing-origins [data-origin='suggestion'] {
  text-decoration: underline dashed var(--wf-familiar);
  text-decoration-thickness: var(--wf-border-w);
  text-underline-offset: 0.3em;
}
.wf-ms.is-showing-origins [data-origin='pasted'],
.wf-ms.is-showing-origins [data-origin='imported'] {
  text-decoration: underline solid var(--wf-line-strong);
  text-underline-offset: 0.3em;
}

/* Find and replace hits */
.wf-ms mark,
.wf-find-hit {
  background: var(--wf-text-selection);
  color: inherit;
  border-radius: var(--wf-radius-xs);
}
.wf-find-hit[aria-current='true'] {
  outline: var(--wf-border-w-strong) solid var(--wf-accent);
}

/* -- 22b Chapter on the page: chapter heading ----------------------------------
   .wf-page-chapter and .wf-chapter-head are in use now: one chapter per page,
   with its title as chrome above the editor.
   .wf-chapter-stack and .wf-chapter-ph are for continuous scroll, which is in
   the backlog. They are small and kept so the backlog item starts from them.
   .wf-chapter-stack  The scroll content. overflow-anchor keeps the view still
                      when a placeholder above the viewport becomes an editor.
   .wf-page-chapter  One chapter: a heading plus an editor or a placeholder.
   .wf-chapter-head   Chrome, not manuscript text. It is not in either
                      chapter's document and the caret cannot enter it.
   .wf-chapter-ph     A chapter that is not mounted. Quiet text lines, sized
                      from the last measured height (--wf-ph-h, set inline).
   -------------------------------------------------------------------------- */
.wf-chapter-stack {
  overflow-anchor: auto;
}
.wf-page-chapter {
  max-width: var(--wf-ms-measure);
  margin: 0 auto;
  font-size: var(--wf-ms-size);
}
.wf-page-chapter + .wf-page-chapter {
  margin-top: var(--wf-chapter-gap);
}
.wf-page-chapter .wf-ms {
  font-size: 1em;
}
.wf-chapter-head {
  position: relative;
  display: grid;
  gap: var(--wf-space-2);
  margin-bottom: 1.5em;
  padding-top: var(--wf-space-6);
  border-top: var(--wf-hairline);
  user-select: none;
  overflow-anchor: none;
}
.wf-page-chapter:first-child .wf-chapter-head {
  border-top: 0;
  padding-top: 0;
}
.wf-chapter-num {
  font-family: var(--wf-font-ui);
  font-size: var(--wf-text-sm);
  color: var(--wf-text-subtle);
  font-variant-numeric: tabular-nums;
}
.wf-chapter-title {
  font-family: var(--wf-ms-font);
  font-size: var(--wf-ms-title-size);
  font-weight: var(--wf-ms-title-weight);
  line-height: var(--wf-leading-tight);
  letter-spacing: var(--wf-track-tight);
  color: var(--wf-text-page);
  text-wrap: balance;
}
.wf-chapter-head-actions {
  position: absolute;
  top: var(--wf-space-6);
  inset-inline-end: 0;
  display: flex;
  gap: var(--wf-space-1);
  opacity: 0;
  transition: opacity var(--wf-dur-quick) var(--wf-ease-standard);
}
.wf-page-chapter:first-child .wf-chapter-head-actions {
  top: 0;
}
.wf-chapter-head:hover .wf-chapter-head-actions,
.wf-chapter-head:focus-within .wf-chapter-head-actions {
  opacity: 1;
}

.wf-chapter-ph {
  height: var(--wf-ph-h, calc(var(--wf-ms-leading) * 1em * 30));
  background-image: repeating-linear-gradient(
    to bottom,
    var(--wf-bg-hover) 0,
    var(--wf-bg-hover) 0.5em,
    transparent 0.5em,
    transparent calc(var(--wf-ms-leading) * 1em)
  );
  background-position: 0 calc((var(--wf-ms-leading) * 1em - 0.5em) / 2);
  opacity: 0.7;
  border-radius: var(--wf-radius-xs);
}

/* -- 22c Formatting toolbar ----------------------------------------------------
   A slim row of quiet tools, not a ribbon. It acts on the chapter that holds
   the caret. aria-pressed shows the mark at the caret. Focus mode hides it.
   .wf-formatbar         The row. Sits in the top bar on desktop.
   .wf-formatbar--dock   Phone: fixed above the on-screen keyboard.
   .wf-tool              One toggle or action. Icon only, with a tooltip.
   .wf-tool-sep          A short divider between groups.
   .wf-block-select      Block style: Body, Heading 2, Heading 3, Quote.
   -------------------------------------------------------------------------- */
.wf-formatbar {
  display: flex;
  align-items: center;
  gap: var(--wf-space-1);
  min-width: 0;
}
.wf-tool {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: var(--wf-tool-size);
  height: var(--wf-tool-size);
  padding: 0;
  border: 0;
  border-radius: var(--wf-radius-sm);
  background: transparent;
  color: var(--wf-text-muted);
  font-family: var(--wf-font-ui);
  cursor: pointer;
  transition:
    background-color var(--wf-dur-instant) var(--wf-ease-standard),
    color var(--wf-dur-instant) var(--wf-ease-standard);
}
.wf-tool svg {
  width: var(--wf-icon);
  height: var(--wf-icon);
}
.wf-tool:hover {
  background: var(--wf-bg-hover);
  color: var(--wf-text);
}
.wf-tool[aria-pressed='true'] {
  background: var(--wf-accent-soft);
  color: var(--wf-accent-text);
}
.wf-tool:disabled {
  color: var(--wf-text-disabled);
  background: transparent;
  cursor: not-allowed;
}
.wf-tool--glyph {
  font-family: var(--wf-ms-font);
  font-size: var(--wf-text-lg);
  line-height: var(--wf-leading-tight);
}
.wf-tool-sep {
  flex: none;
  width: var(--wf-border-w);
  height: var(--wf-tool-sep-h);
  margin: 0 var(--wf-space-2);
  background: var(--wf-line);
}
.wf-block-select {
  flex: none;
  min-height: var(--wf-tool-size);
  padding: 0 var(--wf-space-6) 0 var(--wf-space-3);
  border: 0;
  border-radius: var(--wf-radius-sm);
  background-color: transparent;
  color: var(--wf-text);
  font-size: var(--wf-text-sm);
  font-weight: var(--wf-weight-semibold);
  appearance: none;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--wf-text-muted) 50%),
    linear-gradient(135deg, var(--wf-text-muted) 50%, transparent 50%);
  background-position:
    right var(--wf-space-4) center,
    right var(--wf-space-3) center;
  background-size: var(--wf-caret-size) var(--wf-caret-size);
  background-repeat: no-repeat;
  cursor: pointer;
}
.wf-block-select:hover {
  background-color: var(--wf-bg-hover);
}

.wf-formatbar--dock {
  position: fixed;
  inset-inline: 0;
  bottom: var(--wf-keyboard-inset);
  z-index: var(--wf-z-sticky);
  height: var(--wf-dock-h);
  padding: 0 var(--wf-space-2);
  gap: 0;
  background: var(--wf-bg-chrome);
  border-top: var(--wf-hairline);
  overflow-x: auto;
  scrollbar-width: none;
}
.wf-formatbar--dock .wf-tool {
  width: var(--wf-touch-target);
  height: var(--wf-touch-target);
}
.wf-formatbar--dock .wf-block-select {
  min-height: var(--wf-touch-target);
}
.wf-formatbar--dock .wf-tool-sep {
  margin: 0 var(--wf-space-1);
}

/* -- 23  App shell -------------------------------------------------------------
   Desktop: sidebar | page | AI panel. The page takes all spare width.
   Under 1180px the AI panel overlays the page. Under 860px the sidebar becomes
   a drawer. Under 600px both become bottom or side sheets.
   -------------------------------------------------------------------------- */
.wf-app {
  display: grid;
  grid-template-columns: var(--wf-sidebar-w) minmax(0, 1fr) var(--wf-panel-w);
  grid-template-rows: auto minmax(0, 1fr);
  grid-template-areas:
    'side top panel'
    'side page panel';
  height: 100dvh;
  background: var(--wf-bg-app);
}
.wf-app > .wf-sidebar {
  grid-area: side;
}
.wf-app > .wf-topbar {
  grid-area: top;
}
.wf-app > .wf-page-scroll {
  grid-area: page;
  overflow-y: auto;
  background: var(--wf-bg-page);
}
.wf-app > .wf-ai {
  grid-area: panel;
}
.wf-app.is-panel-closed {
  grid-template-columns: var(--wf-sidebar-w) minmax(0, 1fr) 0;
}
.wf-app.is-focus {
  grid-template-columns: 0 minmax(0, 1fr) 0;
}
.wf-app.is-focus > .wf-sidebar,
.wf-app.is-focus > .wf-ai {
  visibility: hidden;
}
/* Focus mode hides the toolbar. The top bar fades out. When the pointer
   reaches the top edge, only the focus tool comes back, to exit. Esc also exits.
   The save state never hides: .wf-focus-status shows it in the corner. */
.wf-app.is-focus .wf-formatbar > :not([data-tool='focus']),
.wf-app.is-focus .wf-topbar-status {
  visibility: hidden;
}
.wf-app.is-focus > .wf-topbar {
  opacity: 0;
  transition: opacity var(--wf-dur-gentle) var(--wf-ease-standard);
}
.wf-app.is-focus > .wf-topbar:hover,
.wf-app.is-focus > .wf-topbar:focus-within {
  opacity: 1;
}

.wf-topbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--wf-space-2) var(--wf-space-3);
  min-height: var(--wf-topbar-h);
  padding: var(--wf-space-2) var(--wf-space-5);
  background: var(--wf-bg-page);
  color: var(--wf-text-muted);
  font-size: var(--wf-text-sm);
}
.wf-topbar-spacer {
  flex: 1;
}
.wf-wordcount {
  display: inline-flex;
  white-space: nowrap;
  gap: var(--wf-space-4);
  font-size: var(--wf-text-xs);
  color: var(--wf-text-subtle);
  font-variant-numeric: tabular-nums;
}
.wf-wordcount b {
  color: var(--wf-text);
  font-weight: var(--wf-weight-semibold);
}

.wf-ai {
  border-inline-start: var(--wf-hairline);
  width: var(--wf-panel-w);
}

@media (max-width: 1180px) {
  .wf-app,
  .wf-app.is-panel-closed {
    grid-template-columns: var(--wf-sidebar-w) minmax(0, 1fr);
    grid-template-areas:
      'side top'
      'side page';
  }
  .wf-app > .wf-ai {
    position: fixed;
    inset-block: 0;
    inset-inline-end: 0;
    z-index: var(--wf-z-panel);
    box-shadow: var(--wf-shadow-4);
  }
  .wf-app.is-panel-closed > .wf-ai {
    display: none;
  }
  .wf-app.is-focus {
    grid-template-columns: 0 minmax(0, 1fr);
  }
}
@media (max-width: 860px) {
  .wf-app,
  .wf-app.is-panel-closed,
  .wf-app.is-focus {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      'top'
      'page';
  }
  .wf-app > .wf-sidebar {
    position: fixed;
    inset-block: 0;
    inset-inline-start: 0;
    z-index: var(--wf-z-sidebar);
    box-shadow: var(--wf-shadow-4);
  }
  .wf-app:not(.is-sidebar-open) > .wf-sidebar {
    display: none;
  }
}
@media (max-width: 600px) {
  .wf-wordcount > span:not(:first-child) {
    display: none;
  }
  .wf-app > .wf-ai {
    inset-block: auto 0;
    inset-inline: 0;
    width: 100%;
    max-height: var(--wf-sheet-max-h);
    border-inline-start: 0;
    border-radius: var(--wf-radius-lg) var(--wf-radius-lg) 0 0;
    padding-bottom: var(--wf-safe-bottom);
    z-index: var(--wf-z-sheet);
  }
}

/* -- 23b Auth pages ----------------------------------------------------------
   Sign-in and other standalone forms. Full viewport height, one centered
   card, a side gutter at phone width. The card fills the width up to
   --wf-form-w.
   -------------------------------------------------------------------------- */
.wf-auth {
  display: grid;
  place-items: center;
  min-height: 100dvh;
  padding: calc(var(--wf-space-5) + var(--wf-safe-top)) var(--wf-space-5)
    calc(var(--wf-space-5) + var(--wf-safe-bottom));
  background: var(--wf-bg-app);
}
.wf-auth-card {
  width: 100%;
  max-width: var(--wf-form-w);
}

/* -- 23c Focus-mode save state -------------------------------------------------
   The save state stays visible in focus mode (spec: editor requirement 10).
   It sits in the top end corner with no box and no shadow, in subtle text,
   so "Saved" is barely there. Offline and error states carry their own
   color and draw the eye only when something needs attention.
   -------------------------------------------------------------------------- */
.wf-focus-status {
  position: fixed;
  top: calc(var(--wf-space-4) + var(--wf-safe-top));
  inset-inline-end: var(--wf-space-5);
  z-index: var(--wf-z-sticky);
  display: flex;
  align-items: center;
  gap: var(--wf-space-2);
  max-width: calc(100vw - 2 * var(--wf-space-5));
  padding: var(--wf-space-1) var(--wf-space-3);
  border-radius: var(--wf-radius-full);
  background: var(--wf-bg-page);
}

/* -- 23d Find bar ----------------------------------------------------------------
   Replaces the formatting tools in the top bar while it is open.
   Order: find field, match case, whole word, count, previous, next,
   [replace field, Replace, Replace all], show-replace or close.
   Two groups. When the top bar is too narrow for both (about 860-1180 px with
   the sidebar open), the replace group wraps to a second row.
   -------------------------------------------------------------------------- */
.wf-findbar {
  display: flex;
  /* Grow into the spare room, but ask for at least one full find row, so the
     top bar wraps the status and chapter tools before the find row breaks. */
  flex: 1 1 0;
  min-width: min(100%, 26em);
  flex-wrap: wrap;
  align-items: center;
  gap: var(--wf-space-2) var(--wf-space-3);
}
.wf-findbar-group {
  display: flex;
  flex: 1 1 22em;
  align-items: center;
  gap: var(--wf-space-1);
  min-width: 0;
}
.wf-findbar-group--replace {
  flex-basis: 20em;
}
.wf-findbar-field {
  flex: 1 1 auto;
  width: auto;
  min-width: var(--wf-find-field-min);
  min-height: var(--wf-tool-size);
  padding-block: 0;
  font-size: var(--wf-text-sm);
}
.wf-findbar-count {
  flex: none;
  min-width: 7ch;
  padding-inline: var(--wf-space-2);
  font-size: var(--wf-text-xs);
  color: var(--wf-text-subtle);
  text-align: center;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.wf-findbar-count[data-empty='true'] {
  color: var(--wf-concern-text);
}

/* -- 23e Settings page ---------------------------------------------------------- */
.wf-settings {
  display: grid;
  gap: var(--wf-space-7);
  width: 100%;
  max-width: var(--wf-settings-w);
  margin-inline: auto;
  padding-block: var(--wf-space-7) var(--wf-space-9);
}
.wf-settings-section {
  display: grid;
  gap: var(--wf-space-4);
  padding-top: var(--wf-space-6);
  border-top: var(--wf-hairline);
}

/* -- 23f History diff ----------------------------------------------------------
   Compares a snapshot with the chapter now. Words: <ins class="wf-diff-ins">
   is only in the chapter now, <del class="wf-diff-del"> is only in the old
   version. Each color pairs with a line style, so the diff reads without
   color. Blocks: .wf-diff-row[data-status] with a rule on the start edge.
     changed  ink rule        added, split   moss rule
     removed, merged  madder rule   moved   dashed subtle rule
   -------------------------------------------------------------------------- */
.wf-diff {
  display: grid;
  gap: var(--wf-space-4);
  margin: 0;
  padding: 0;
  list-style: none;
}
.wf-diff-row {
  display: grid;
  gap: var(--wf-space-2);
  padding-inline-start: var(--wf-space-4);
  border-inline-start: var(--wf-rule-w) solid var(--wf-line-strong);
}
.wf-diff-row[data-status='changed'] {
  border-inline-start-color: var(--wf-accent);
}
.wf-diff-row[data-status='added'],
.wf-diff-row[data-status='split'] {
  border-inline-start-color: var(--wf-ok-text);
}
.wf-diff-row[data-status='removed'],
.wf-diff-row[data-status='merged'] {
  border-inline-start-color: var(--wf-concern);
}
.wf-diff-row[data-status='moved'] {
  border-inline-start-style: dashed;
  border-inline-start-color: var(--wf-text-subtle);
}
.wf-diff-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--wf-space-2);
}
.wf-diff-text {
  margin: 0;
  font-family: var(--wf-ms-font);
  font-size: var(--wf-text-md);
  line-height: var(--wf-leading-relaxed);
  color: var(--wf-text-page);
  font-variant-numeric: var(--wf-ms-numerals);
}
.wf-diff-ins {
  background: var(--wf-ok-soft);
  color: var(--wf-ok-text);
  text-decoration: underline;
  text-decoration-thickness: var(--wf-border-w);
  text-underline-offset: 0.2em;
  border-radius: var(--wf-radius-xs);
}
.wf-diff-del {
  background: var(--wf-concern-soft);
  color: var(--wf-concern-text);
  text-decoration: line-through;
  text-decoration-thickness: var(--wf-border-w);
  border-radius: var(--wf-radius-xs);
}
.wf-diff-same {
  padding: var(--wf-space-1) var(--wf-space-4);
  font-size: var(--wf-text-xs);
  color: var(--wf-text-subtle);
}
/* Snapshot list in the history dialog */
.wf-versions {
  display: grid;
  gap: var(--wf-space-1);
  margin: 0;
  padding: 0;
  list-style: none;
}
.wf-version {
  display: flex;
  align-items: center;
  gap: var(--wf-space-3);
  padding: var(--wf-space-3) 0;
  border-bottom: var(--wf-hairline);
}

/* -- 24  Motivation ------------------------------------------------------------
   Streaks forgive. A frozen day is a hollow ring, not a gap.
   -------------------------------------------------------------------------- */
.wf-streak {
  display: grid;
  gap: var(--wf-space-3);
}
.wf-streak-count {
  font-size: var(--wf-text-2xl);
  font-weight: var(--wf-weight-semibold);
  line-height: var(--wf-leading-tight);
  font-variant-numeric: tabular-nums;
}
.wf-streak-days {
  display: flex;
  flex-wrap: wrap;
  gap: var(--wf-space-2);
  list-style: none;
  margin: 0;
  padding: 0;
}
.wf-day {
  width: var(--wf-dot-size);
  height: var(--wf-dot-size);
  border-radius: var(--wf-radius-full);
  border: var(--wf-border-w) solid var(--wf-line-control);
}
.wf-day[data-state='done'] {
  background: var(--wf-familiar);
  border-color: var(--wf-familiar);
}
.wf-day[data-state='frozen'] {
  border: var(--wf-border-w-strong) solid var(--wf-accent);
}
.wf-day[data-state='today'] {
  outline: var(--wf-border-w) solid var(--wf-text);
  outline-offset: var(--wf-space-1);
}

.wf-briefing {
  display: grid;
  gap: var(--wf-space-5);
  max-width: var(--wf-ms-measure);
  margin: 0 auto;
}
.wf-briefing-title {
  font-family: var(--wf-ms-font);
  font-size: var(--wf-text-3xl);
  font-weight: var(--wf-ms-title-weight);
  line-height: var(--wf-leading-tight);
  letter-spacing: var(--wf-track-tight);
}
.wf-briefing-list {
  display: grid;
  gap: var(--wf-space-4);
  padding-inline-start: var(--wf-space-6);
  font-size: var(--wf-text-md);
  line-height: var(--wf-leading-relaxed);
}

.wf-capture {
  position: fixed;
  inset-inline-end: var(--wf-space-5);
  bottom: calc(var(--wf-space-5) + var(--wf-safe-bottom));
  z-index: var(--wf-z-sticky);
  min-width: var(--wf-touch-target);
  min-height: var(--wf-touch-target);
  border-radius: var(--wf-radius-full);
  box-shadow: var(--wf-shadow-3);
}

/* -- 25  Utilities, accessibility, reduced motion, forced colors ------------- */

/* Divider with an optional centred label, for example "or" between two
   sign-in methods. Without a label it is a plain hairline. */
.wf-divider {
  display: flex;
  align-items: center;
  gap: var(--wf-space-3);
  margin: 0;
  font-size: var(--wf-text-xs);
  color: var(--wf-text-subtle);
}
.wf-divider::before,
.wf-divider::after {
  content: '';
  flex: 1;
  border-top: var(--wf-hairline);
}
.wf-divider:empty {
  gap: 0;
}

/* Lucide icons: the design-system stroke width */
.wf-icon {
  stroke-width: var(--wf-icon-stroke);
}

/* A flex child that takes the spare room and may shrink, and that breaks a
   long word (an email, a long title) instead of overflowing. */
.wf-grow {
  flex: 1 1 auto;
  min-width: 0;
  overflow-wrap: anywhere;
}

.wf-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
.wf-stack {
  display: grid;
  gap: var(--wf-space-4);
}
.wf-row {
  display: flex;
  align-items: center;
  gap: var(--wf-space-3);
}

* {
  scrollbar-width: thin;
  scrollbar-color: var(--wf-line-strong) transparent;
}

@media (pointer: coarse) {
  .wf-btn,
  .wf-chapter,
  .wf-entity,
  .wf-tab {
    min-height: var(--wf-touch-target);
  }
  .wf-chapter-grip {
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
  }
}

@media (forced-colors: active) {
  .wf-voice,
  .wf-msg--familiar,
  .wf-critique {
    border-inline-start-color: Highlight;
  }
  .wf-ms p[data-mark]::before,
  .wf-day[data-state='done'] {
    background: Highlight;
  }
  .wf-entity::before {
    background: CanvasText;
  }
  .wf-btn--primary,
  .wf-btn--danger {
    border-color: ButtonText;
  }
}
