/**
 * Design tokens, light and dark (framework core).
 *
 * Three layers, in this order:
 *   1. :root                       light values, the baseline
 *   2. @media (prefers-color-scheme: dark)   the system preference
 *   3. :root[data-theme="light|dark"]        an explicit choice, which wins
 *
 * Layer 3 must come last and must set BOTH directions, or a visitor whose system
 * is dark cannot choose light: the media query would keep overriding them.
 *
 * A fork rebrands by overriding the --brand-* values in its own stylesheet. The
 * semantic tokens below are derived from them, so a single accent change carries
 * through both themes.
 *
 * Every foreground/background pair here is measured against WCAG 2.1 AA
 * (>= 4.5:1 body text, >= 3.0:1 large text and UI). tests/contrast.test.mjs
 * re-measures them, so a rebrand that breaks contrast fails the test run rather
 * than shipping.
 */

:root {
  color-scheme: light dark;

  /* --- brand seed: Madevisible Trattoria Demo (2026-10-15) ----------------------
     Terracotta as the primary action colour on cream, a warm apricot on dark
     surfaces, olive as the secondary partner, espresso ink for headlines.
     Contrast measured with tests/contrast.test.mjs (6 of 6 pass). */
  --brand-accent:        #9E3B1B;   /* terracotta, 6.8:1 with white button text */
  --brand-accent-dark:   #F0956A;   /* warm apricot, 7.8:1 on dark surfaces    */
  --brand-accent-2:      #4F5A2B;   /* olive, 7.2:1 on cream                    */
  --brand-ink:           #2B1E17;   /* espresso ink                              */
  --brand-surface:       #FFFBF5;

  /* --- surfaces ------------------------------------------------------------ */
  --bg:          #FAF5EC;
  --bg-2:        #F3EADB;
  --surface:     var(--brand-surface);
  --surface-2:   #F7F0E4;
  /* Alias the existing branded secondary-button colours in either theme. */
  --btn-2-bg: var(--surface-2);
  --btn-2-text: var(--text);
  --btn-2-line: var(--line-strong);
  /* --line is decorative (card dividers, hairlines) and is not held to the 3:1
     rule. --line-strong is the one that bounds a UI component (input, select,
     checkbox) and therefore must meet WCAG 1.4.11 non-text contrast. Using one
     token for both is what quietly makes form fields fail. */
  --line:        #E6D9C6;
  --line-soft:   #EFE5D6;
  --line-strong: #8C7A6B;

  /* --- text ---------------------------------------------------------------- */
  --text:        #2B1E17;   /* 16.7:1 on --surface  */
  --text-2:      #5B4A3F;   /* 7.4:1  on --surface  */
  --text-3:      #6E5D51;   /* 5.7:1 on --surface, 5.0:1 on --panel-a, above the AA floor */

  /* --- accent -------------------------------------------------------------- */
  --accent:      var(--brand-accent);
  --accent-2:    var(--brand-accent-2);
  --accent-text: var(--brand-accent);      /* accent used AS text on a surface */
  --accent-soft: rgba(158, 59, 27, .09);   /* tinted pill background, red       */
  --on-accent:   #FFFFFF;                  /* text on a filled accent surface  */
  --panel-a:     #EFE3D0;                  /* image placeholder gradient       */
  --panel-b:     #F7F0E4;

  /* Chevron for styled <select> fields. An SVG in a background-image cannot
     inherit currentColor, so the ink is baked into the data URI and must be
     re-declared per theme, or a dark theme gets an invisible dark arrow.
     Ink mirrors --text-2 (#5B4A3F): measured 7.4:1 on --surface #FFFFFF at
     rest, well above 3:1 (WCAG 1.4.11). Changing --text-2 does NOT update
     this URI; keep in sync. */
  --select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M4 6l4 4 4-4' stroke='%235B4A3F' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");

  /* --- status -------------------------------------------------------------- */
  --error:       #B3261E;  --error-bg:   #FDF0EF;
  --success:     #1F6B4F;  --success-bg: #EDF8F3;
  --warning:     #8A5A00;  --warning-bg: #FFF6E6;
  --info:        #1F5F80;  --info-bg:    #EDF6FA;

  /* --- elevation: tinted, never pure black --------------------------------- */
  --shadow-1: 0 1px 3px rgba(43, 30, 23, .07), 0 1px 2px rgba(43, 30, 23, .05);
  --shadow-2: 0 4px 12px rgba(43, 30, 23, .10), 0 2px 4px rgba(43, 30, 23, .07);
  --shadow-3: 0 8px 24px rgba(43, 30, 23, .14), 0 4px 8px rgba(43, 30, 23, .09);
  --shadow:   var(--shadow-2);

  /* --- geometry + type ----------------------------------------------------- */
  --radius-sm: 6px;  --radius-md: 10px;  --radius-lg: 14px;  --radius: var(--radius-md);
  --maxw: 1200px;
  --font-heading: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
  --font-body:    system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

/* --- dark values, applied to both the system preference and the explicit
       choice. Kept in one custom property block via @media + attribute so the
       numbers exist exactly once. ------------------------------------------- */
@media (prefers-color-scheme: dark) {
  :root {
    --bg:        #17110D;
    --bg-2:      #1B140F;
    --surface:   #1E1611;
    --surface-2: #271D16;
    --line:      #3D3026;
    --line-soft: #30251D;
    --line-strong: #8C7A6B;

    --text:      #FBF3E8;   /* 15.0:1 on --surface */
    --text-2:    #DCCDBC;   /* 9.6:1  on --surface */
    --text-3:    #B9A898;   /* 5.6:1  on --surface */

    --accent:      var(--brand-accent-dark);
    --accent-2:    #C9D19A;
    --accent-text: var(--brand-accent-dark);   /* 5.4:1+ on --surface and --bg */
    --accent-soft: rgba(240, 149, 106, .10);
    --on-accent:   #17110D;                    /* dark ink on the bright red accent, 4.82:1 */
    --panel-a:     #2A2019;
    --panel-b:     #1E1611;

    /* Ink mirrors dark --text-2 (#DCCDBC): measured 10.12:1 on --surface
       #1E1611 and 10.61:1 on --error-bg #2A1614, both >= 3:1. */
    --select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M4 6l4 4 4-4' stroke='%23DCCDBC' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");

    --error:   #F2857D;  --error-bg:   #2A1614;
    --success: #6FD3A8;  --success-bg: #10241C;
    --warning: #E8B45C;  --warning-bg: #2A2013;
    --info:    #7FC3E4;  --info-bg:    #12242E;

    /* Shadows carry almost no weight on dark surfaces; a border does the work. */
    --shadow-1: 0 1px 3px rgba(0, 0, 0, .40);
    --shadow-2: 0 4px 12px rgba(0, 0, 0, .45);
    --shadow-3: 0 8px 24px rgba(0, 0, 0, .55);
  }
}

/* Explicit choice wins over the system preference, in BOTH directions. */
:root[data-theme="dark"] {
  --bg:        #17110D;
  --bg-2:      #1B140F;
  --surface:   #1E1611;
  --surface-2: #271D16;
  --line:      #3D3026;
  --line-soft: #30251D;
  --line-strong: #8C7A6B;

  --text:      #FBF3E8;
  --text-2:    #DCCDBC;
  --text-3:    #B9A898;

  --accent:      var(--brand-accent-dark);
  --accent-2:    #C9D19A;
  --accent-text: var(--brand-accent-dark);
  --accent-soft: rgba(240, 149, 106, .10);
  --on-accent:   #17110D;
  --panel-a:     #2A2019;
  --panel-b:     #1E1611;

  --select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M4 6l4 4 4-4' stroke='%23DCCDBC' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");

  --error:   #F2857D;  --error-bg:   #2A1614;
  --success: #6FD3A8;  --success-bg: #10241C;
  --warning: #E8B45C;  --warning-bg: #2A2013;
  --info:    #7FC3E4;  --info-bg:    #12242E;

  --shadow-1: 0 1px 3px rgba(0, 0, 0, .40);
  --shadow-2: 0 4px 12px rgba(0, 0, 0, .45);
  --shadow-3: 0 8px 24px rgba(0, 0, 0, .55);

  color-scheme: dark;
}

/* A visitor on a dark system who picks light gets the baseline back. */
:root[data-theme="light"] {
  --bg:        #FAF5EC;
  --bg-2:      #F3EADB;
  --surface:   #FFFFFF;
  --surface-2: #F7F0E4;
  --line:      #E6D9C6;
  --line-soft: #EFE5D6;
  --line-strong: #8C7A6B;

  --text:      #2B1E17;
  --text-2:    #5B4A3F;
  --text-3:    #6E5D51;

  --accent:      var(--brand-accent);
  --accent-2:    var(--brand-accent-2);
  --accent-text: var(--brand-accent);
  --accent-soft: rgba(158, 59, 27, .09);
  --on-accent:   #FFFFFF;
  --panel-a:     #EFE3D0;
  --panel-b:     #F7F0E4;

  --select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M4 6l4 4 4-4' stroke='%235B4A3F' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");

  --error:   #B3261E;  --error-bg:   #FDF0EF;
  --success: #1F6B4F;  --success-bg: #EDF8F3;
  --warning: #8A5A00;  --warning-bg: #FFF6E6;
  --info:    #1F5F80;  --info-bg:    #EDF6FA;

  --shadow-1: 0 1px 3px rgba(43, 30, 23, .07), 0 1px 2px rgba(43, 30, 23, .05);
  --shadow-2: 0 4px 12px rgba(43, 30, 23, .10), 0 2px 4px rgba(43, 30, 23, .07);
  --shadow-3: 0 8px 24px rgba(43, 30, 23, .14), 0 4px 8px rgba(43, 30, 23, .09);

  color-scheme: light;
}

/* --- baseline ------------------------------------------------------------- */
body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  /* Theme swaps are instant on purpose: a colour transition on every element
     costs a repaint of the whole page and buys nothing. */
}

/* Buttons are always solid. A transparent or border-only button reads as a hint,
   not as an action, and fails the 3:1 UI-component contrast rule on most
   backgrounds. Disabled state is opacity, never a colour that blends in. */
.btn {
  display: inline-block;
  padding: 12px 24px;
  min-height: 44px;
  border: 0;
  border-radius: var(--radius-md);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
  /* Deliberately short and GPU-cheap (filter + transform only, see the
     prefers-reduced-motion block below). A hover/press swap is not the
     "theme swaps are instant on purpose" case above: it happens on every
     interaction, not once per visit, so a felt (not just correct) response
     earns its 150ms. */
  transition: filter 150ms ease, transform 100ms ease;
}
.btn-primary { background: var(--accent); color: var(--on-accent); }
.btn-primary:hover { filter: brightness(1.08); }
.btn-primary:active { filter: brightness(.94); transform: scale(.97); }
.btn-secondary { background: var(--surface-2); color: var(--text); border: 1px solid var(--line-strong); }
.btn-secondary:active { transform: scale(.97); }

/* Form controls bound a UI component, so they take the 3:1 border. */
input, select, textarea {
  min-height: 44px;
  padding: 10px 12px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  font: inherit;
}
input[aria-invalid="true"], select[aria-invalid="true"], textarea[aria-invalid="true"] {
  border-color: var(--error);
  background: var(--error-bg);
}
/* Select fields: system chrome off, own chevron, otherwise identical to an
   input. Every engine paints its own arrow at its own position (iOS, Chrome,
   Safari all differ), so the field looks foreign next to the styled inputs
   until appearance is disabled. The OPEN list stays native on purpose:
   <option> is not styleable and a JS replacement costs accessibility and the
   familiar touch behaviour. multiple/size are excluded, they have no arrow.
   Geometry lives here (theme-neutral); only the arrow ink is themed via
   --select-arrow. Focus survives appearance:none because it is the global
   :focus-visible outline below, not a system style. Font size is 16px via
   font:inherit on the body default, so iOS Safari does not zoom on tap.
   padding-right = 13px edge + 16px arrow + 13px gap to the value text. */
select:not([multiple]):not([size]) {
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  background-image: var(--select-arrow);
  background-repeat: no-repeat;
  background-position: right 13px center;
  background-size: 16px 16px;
  padding-right: 42px;
}
.btn:disabled, .btn[aria-disabled="true"] { opacity: .5; cursor: not-allowed; }

/* Content wrapper for the reference server-rendered pages (danke/, download/,
   freebie/). Same padding rhythm as the blog templates' .bwrap/.art-wrap
   (scripts/build-blog.mjs): 24px horizontal gutter, generous vertical air.
   Added 2026-08-19 (design-audit finding): before this rule .wrap had NO
   definition anywhere, so its content sat flush against the viewport edge
   with only the default 8px body margin, confirmed BLOCKER at 390px and
   1280px alike. A fork narrows this further per page (see freebie/index.php
   .freebie-form) but every .wrap now has a floor gutter even unstyled. */
.wrap { max-width: 640px; margin: 0 auto; padding: 48px 24px 72px; }

a { color: var(--accent-text); }

:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

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