@import url('https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;1,6..72,400;1,6..72,500&display=swap');

/* ───────────────────────────────────────────────────────────
   Tuck — Heritage Kitchen design tokens
   Source of truth: docs/design-brief.md §4–6 + Theme/Color+Tuck.swift
   Colour-system rev (2026-06): tiers re-derived for WCAG, cream
   resolved, toCheck removed, accentText + affirmative formalised,
   Increase-Contrast variants added. See docs/color-verification.md
   for the measured contrast matrix.

   Accessibility-foundations rev (2026-06-05): (1) semantic text roles
   confirmed (--ink primary / --ink-soft secondary; --ink-faint is never
   text); (2) every --t-* type token is now a scalable Dynamic-Type style
   via --type-scale; (3) per-component VoiceOver contracts live in the kit
   + docs/accessibility-contracts.md.
   ─────────────────────────────────────────────────────────────── */

:root {
    color-scheme: light dark;

    /* ── Surfaces ───────────────────────────────────────────── */
    --paper:           #F2E8D5;   /* primary background · canonical paper cream (brand book) */
    --paper-elevated:  #FBF6E9;   /* cards, sheets, toasts · also accentText (light) */
    --paper-recessed:  #E5D8BC;   /* pressed states, pills, input bg */

    /* ── Text / ink — the SEMANTIC TEXT ROLES ────────────────────
       --ink = PRIMARY text · --ink-soft = SECONDARY text. These are the
       only two tokens that may carry readable text or an essential glyph;
       both clear WCAG AA on every surface in this file (primary clears
       AAA). --ink-faint is NOT a text role — decoration only. Colour
       stays reserved for the urgency scale; no warm-tan ink is a text
       colour anywhere. (Primary resolves to the darkest ink we ship,
       #1A0F05, which is darker than the brief's #1F1814 floor.) */
    --ink:             #1A0F05;   /* PRIMARY text · darkest ink · 15.5:1 on paper (AAA) */
    --ink-soft:        #6B5A3F;   /* SECONDARY text · 5.5:1 paper · 5.7:1 elevated · 4.7:1 recessed (all AA) */
    --ink-faint:       #9C8B6E;   /* DECORATION ONLY — hairlines, 25% watermark, halo. NEVER text/glyph (2.7:1). */

    /* ── Brand accent (burnt sienna) ────────────────────────── */
    --accent:          #B4502A;   /* fill only · resolved canonical (chosen over brand-book #B85A2E) */
    --accent-soft:     #F4E2D0;
    --accent-text:     #FBF6E9;   /* text/glyphs sitting ON the accent fill · 4.7:1 on #B4502A */
    --affirmative:     var(--accent);  /* success = warm sienna, NEVER green (green is the fresh tier) */

    /* ── Urgency tiers — the ONLY colour-coded information ─────
       Separated by HUE (green→amber→red), not luminance: on cream all
       three sit in a ~5 L* band, so each clears 4.5:1 but can't be told
       apart by brightness. Colour never stands alone — the days-left
       numeral and the urgency phrase always carry the meaning. Held to
       the full 4.5:1 normal-text bar everywhere (they render at 26px in
       the list numeral, not just 22pt in the answer block). The hero
       "answer" phrase uses THESE tokens directly — no softer copies. */
    --fresh:           #57692E;   /* 4+ days · forest olive · 5.0:1 on paper */
    --dusk:            #8A5612;   /* 2–3 days · deep amber-ochre · 5.1:1 on paper */
    --urgent:          #A6361C;   /* ≤1 day / today · dried chili · 5.5:1 on paper */

    /* Increase-Contrast variants (isDarkerSystemColorsEnabled).
       Same warm hue families, deepened to clear ≥7:1 on paper. */
    --fresh-contrast:  #42501D;   /* 7.2:1 on paper */
    --dusk-contrast:   #6A410C;   /* 7.3:1 on paper */
    --urgent-contrast: #882814;   /* sign-off margin above 7:1 (was #8C2A14, 7.0:1) */

    /* ── Structural ─────────────────────────────────────────── */
    --divider:         #D8C9A8;
    --row-divider:     rgba(26, 15, 5, 0.05);   /* glance-row hairline · mode-aware (flips pale in dark, like --fold-crease) */
    --scrim:           rgba(15, 11, 8, 0.36);

    /* ── Folded-paper / dog-ear — the hero tag's fold geometry + colours.
       Tokenised (rev 2026-06-08) out of the screens' inline magic numbers:
       the 42px corner notch, the crease hairline, the folded-back flap (the
       paper UNDERSIDE = recessed paper), and the dark-mode inset edge-light.
       The tag carries NO border and NO drop shadow — the dog-ear fold (a hard
       figure/ground break at the corner) + baked grain separate it from the
       page, NOT a luminance step (the elevated/paper delta is the kit's
       intentional ~1.1:1 whisper, shared by every card). In dark mode an
       INSET edge-light (top highlight + bottom shade) lifts the raised edge —
       a material highlight, not a shadow. See docs/color-verification.md. */
    --fold-notch:      42px;                      /* corner fold size (the dog-ear) */
    --fold-flap:       #E5D8BC;                   /* folded-back flap face = paper underside (light) */
    --fold-crease:     rgba(26, 15, 5, 0.16);     /* crease hairline along the fold (light) */
    --tag-edge-light:  transparent;               /* light mode: no inset edge-light needed */
    --tag-edge-shade:  transparent;

    /* ── Paper grain — the 3–5% noise overlaid on paper surfaces, now a
       reusable, MODE-AWARE token (was inlined in .paper-grain::after). Light
       = dark specks; dark mode overrides it to light specks (see below).
       Applied DIRECTLY as a background-image (the alpha is baked in — no
       blend mode), exactly as the screens' tag does. The hero tag + flap
       bake it in; .paper-grain reads it below. */
    --grain: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.12  0 0 0 0 0.10  0 0 0 0 0.08  0 0 0 0.06 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");

    /* ── Toast chrome (fixed-role; never flips with mode) ────── */
    --toast-bg:        #1F1A14;
    --toast-text:      #FBF6E9;
    --toast-accent:    #E6B07A;

    /* ── Type — SF Pro family. iOS uses real SF Pro;            */
    /*   web falls back to system stack then Inter / Nunito.    */
    --sf-display: -apple-system, BlinkMacSystemFont, "SF Pro Display",
                  "Inter", "Helvetica Neue", system-ui, sans-serif;
    --sf-text:    -apple-system, BlinkMacSystemFont, "SF Pro Text",
                  "Inter", "Helvetica Neue", system-ui, sans-serif;
    --sf-rounded: ui-rounded, -apple-system, "SF Pro Rounded",
                  "Nunito", system-ui, sans-serif;

    /* ── Serif DISPLAY role — TWO-TIER type system (rev 2026-06-08) ──
       The kit is SF Pro for ALL UI + body. ONE deliberate exception: the
       canonical HeroAnswer paper-tag, whose dish NAME uses this embedded
       serif display face (the locked art direction sourced from the
       screens' RefinedTag). Scope is HARD — serif is confined to the hero
       tag: the dish name in this DISPLAY role, plus the tag's "Saved …"
       caption in italic. The tag's urgency PHRASE stays sans, and nothing
       else in the kit uses serif. Newsreader (loaded above, opsz 6..72,
       weights 400/500 + italics) is the ONE embedded font; this supersedes
       the old "SF Pro only · never embed a font" rule. See README "Type". */
    --serif-display: "Newsreader", Georgia, "Times New Roman", "Iowan Old Style", serif;

    /* ── Dynamic Type ────────────────────────────────────
       Every --t-* token below is a SCALABLE text style, not a fixed size.
       Its px value is the .large (default) baseline; --type-scale
       multiplies it so a single knob reflows all text the way iOS Dynamic
       Type does. 1 = .large. Raise toward the accessibility sizes to
       prove reflow: ≈1.35 = AX1 … ≈2.0 = AX5 (largest accessibility size).
       Components are built to reflow with no truncation / clipping /
       overlap across this whole range. (Production iOS applies
       UIFontMetrics' damped per-style curve; this linear multiplier is
       the upper-bound used to verify reflow.) Drive it globally:
         document.documentElement.style.setProperty('--type-scale', 1.6)
       The kit mirrors this via TuckTokens.setTypeScale(1.6). */
    --type-scale: 1;

    /* Type tokens — SF Pro family. Size/line-height = .large baseline × --type-scale. */
    --t-hero-number:        700 calc(56px * var(--type-scale))/calc(60px * var(--type-scale)) var(--sf-rounded);
    --t-screen-title:       600 calc(28px * var(--type-scale))/calc(34px * var(--type-scale)) var(--sf-display);    /* "Tuck" wordmark */
    --t-glance-title:       700 calc(28px * var(--type-scale))/calc(34px * var(--type-scale)) var(--sf-display);
    --t-item-name:          600 calc(22px * var(--type-scale))/calc(28px * var(--type-scale)) var(--sf-display);
    /* RETIRED 2026-06-08: --t-answer-dish was the SANS hero dish of the old
       HeroAnswer, now superseded by the serif paper-tag. Kept only as a
       reference value; the canonical hero dish uses --t-hero-dish-serif. */
    --t-answer-dish:        600 calc(34px * var(--type-scale))/calc(40px * var(--type-scale)) var(--sf-display);
    /* Canonical hero dish — SERIF DISPLAY role. Base is the .large size for a
       short name; the component steps the BASE down by name-length tier
       (≤20ch → 38/42, ≤30ch → 33/37, longer → 30/34) and wraps T.dt() around
       it, so the step-down is a refinement ON TOP of Dynamic-Type scaling,
       not instead of it. The token below is the short-name (.large) base. */
    --t-hero-dish-serif:    500 calc(38px * var(--type-scale))/calc(42px * var(--type-scale)) var(--serif-display);
    /* Hero urgency phrase — stays SF Pro (sans), coloured by the urgency tier. */
    --t-answer-phrase:      600 calc(18px * var(--type-scale))/calc(22px * var(--type-scale)) var(--sf-text);
    /* Hero "Saved …" caption — serif ITALIC (part of the locked hero look).
       Colour is --ink-soft (NOT --ink-faint): the screens drew it in faint
       tan, but the kit forbids ink-faint as text — see color-verification. */
    --t-hero-caption:       italic 400 calc(14.5px * var(--type-scale))/calc(18px * var(--type-scale)) var(--serif-display);
    --t-detail-title:       600 calc(32px * var(--type-scale))/calc(38px * var(--type-scale)) var(--sf-display);
    --t-detail-big-day:     700 calc(32px * var(--type-scale))/calc(38px * var(--type-scale)) var(--sf-rounded);
    --t-glance-row-name:    600 calc(19px * var(--type-scale))/calc(24px * var(--type-scale)) var(--sf-display);    /* list rows */
    --t-glance-row-number:  700 calc(28px * var(--type-scale))/calc(30px * var(--type-scale)) var(--sf-rounded);
    --t-row-number-label:   400 calc(11px * var(--type-scale))/calc(14px * var(--type-scale)) var(--sf-text);
    --t-body:               400 calc(17px * var(--type-scale))/calc(24px * var(--type-scale)) var(--sf-text);
    --t-body-emph:          500 calc(17px * var(--type-scale))/calc(24px * var(--type-scale)) var(--sf-text);
    --t-caption:            400 calc(13px * var(--type-scale))/calc(18px * var(--type-scale)) var(--sf-text);
    --t-eyebrow:            700 calc(11px * var(--type-scale))/calc(14px * var(--type-scale)) var(--sf-text);       /* small-caps · 1.4 tracking */
    --t-settings-header:    700 calc(11px * var(--type-scale))/calc(14px * var(--type-scale)) var(--sf-text);

    /* Spacing scale — multiples only (4 / 8 / 12 / 16 / 20 / 24 / 32 / 48 / 64) */
    --space-1:   4px;
    --space-2:   8px;
    --space-3:  12px;
    --space-4:  16px;
    --space-5:  20px;   /* default screen horizontal padding */
    --space-6:  24px;
    --space-7:  32px;
    --space-8:  48px;
    --space-9:  64px;

    /* Radii */
    --radius-card:  12px;   /* the warm middle — not 8, not 16 */
    --radius-sheet: 24px;   /* bottom-sheet top corners */
    --radius-pill:  999px;
    --radius-app-icon: 22.37%;  /* iOS squircle visual approx */
    --radius-toast: 16px;

    /* Touch targets */
    --touch-min:    44px;
    --touch-mic:    72px;   /* the one big exception */

    /* Hairline */
    --hairline: 1px;

    /* Shadow (rare — used only for capture-toast & sheet) */
    --shadow-toast:    0 18px 24px -8px rgba(0, 0, 0, 0.28);
    --shadow-sheet:    0 -8px 32px rgba(15, 11, 8, 0.10);

    /* Motion — springs, never linear */
    --ease-spring:     cubic-bezier(0.22, 1, 0.36, 1);   /* approximates spring(0.4, 0.85) */
    --ease-out:        cubic-bezier(0.20, 0.80, 0.30, 1);
    --motion-tap:      120ms;
    --motion-swipe:    250ms;
    --motion-save:     400ms;
    --motion-genie:    700ms;
    --stagger-list:    40ms;
}

@media (prefers-color-scheme: dark) {
    :root {
        --paper:          #15110C;
        --paper-elevated: #1F1A14;
        --paper-recessed: #0E0B07;
        --ink:            #F0E5CF;
        --ink-soft:       #A39B8B;
        --ink-faint:      #7A7468;   /* decoration only — never text */
        --accent:         #D97852;   /* lifted sienna fill for the dark background */
        --accent-soft:    #3D2A1E;
        --accent-text:    #15110C;   /* near-black ON the lifted accent · 6.1:1 (cream would fail ~3.3:1) */
        --affirmative:    var(--accent);
        /* Dark mode has luminance headroom → tiers form an ordered ramp
           that also reads in greyscale: urgent lightest, fresh darkest. */
        --fresh:          #8DA15C;   /* 6.7:1 on paper */
        --dusk:           #D29B4C;   /* 7.7:1 on paper */
        --urgent:         #E58A66;   /* 7.4:1 on paper */
        --fresh-contrast:  #A9BE78;  /* 9.3:1 on paper */
        --dusk-contrast:   #E6B468;  /* 10.0:1 on paper */
        --urgent-contrast: #F2A285;  /* 9.3:1 on paper */
        --divider:        #2A241B;
        --row-divider:    rgba(240, 229, 207, 0.07);  /* pale glance-row hairline for dark paper */
        --scrim:          rgba(0, 0, 0, 0.50);
        /* Dog-ear fold (dark): underside is the darker recessed paper; the
           crease lifts to a pale hairline; an INSET edge-light (top highlight
           + bottom shade) raises the tag off the page — material highlight,
           not a drop shadow. Grain flips to light specks (below). */
        --fold-flap:      #0E0B07;
        --fold-crease:    rgba(240, 229, 207, 0.22);
        --tag-edge-light: rgba(240, 229, 207, 0.07);
        --tag-edge-shade: rgba(0, 0, 0, 0.30);
        --grain: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.92  0 0 0 0 0.84  0 0 0 0 0.72  0 0 0 0.05 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
    }
}

/* ── Semantic element styles ───────────────────────────────────────
   Apply Tuck typographic intent without inventing class names. */

body {
    background: var(--paper);
    color: var(--ink);
    font: var(--t-body);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

h1 { font: var(--t-glance-title); letter-spacing: -0.4px; color: var(--ink); margin: 0; }
h2 { font: var(--t-screen-title); letter-spacing: -0.3px; color: var(--ink); margin: 0; }
h3 { font: var(--t-item-name);    letter-spacing: -0.2px; color: var(--ink); margin: 0; }
h4 { font: var(--t-body-emph);    color: var(--ink); margin: 0; }
p  { font: var(--t-body); color: var(--ink); margin: 0; }

/* Captions + eyebrows are READ, so they use ink-soft, never ink-faint
   (ink-faint fails contrast as text — it is decoration only). */
small, .caption { font: var(--t-caption); color: var(--ink-soft); }

.eyebrow {
    font: var(--t-eyebrow);
    letter-spacing: 1.4px;
    text-transform: uppercase;
    color: var(--ink-soft);
}

code, kbd, samp {
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
    font-size: 0.9em;
    background: var(--paper-recessed);
    padding: 1px 6px;
    border-radius: 4px;
}

/* Paper grain — apply to any surface via .paper-grain.
   Specced at "3–5% noise, never decorative, skip on text bg
   and inside lock-screen notification card." */
.paper-grain {
    position: relative;
    isolation: isolate;
}
.paper-grain::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 0;
    /* The --grain token is mode-aware (dark specks light / light specks dark)
       with its alpha baked in, so it's applied DIRECTLY — no blend mode. */
    background-image: var(--grain);
    background-size: 220px 220px;
}
