/*
 * Wilmink app design system.
 *
 * Ported from `docs/Wilmink App Design System.html`, which is the source of
 * truth. That document declares the theme through `@plugin "daisyui/theme"`,
 * and this project ships daisyUI prebuilt, so the same names are declared on
 * the theme's own selector here. The names and the values are identical.
 *
 * Tokens come in three layers, and each one may only reach the layer above it:
 *
 *   primitive  what a value IS          --wm-primary-500: #129de0
 *        ↓
 *   semantic   what it is FOR           --wm-action-primary: var(--wm-primary-500)
 *        ↓
 *   component  what one component uses  --wm-button-primary-bg: var(--wm-action-primary)
 *        ↓
 *   daisyUI    the class a screen writes .btn-primary
 *
 * A component never points at a primitive, because the primitive says nothing
 * about intent and a rebrand would then have to find every call site.
 *
 * Load order matters: daisyui.min.css first, then this file.
 *
 * Sections
 *   1. Fonts
 *   2. Primitive tokens
 *   3. Semantic tokens
 *   4. Component tokens
 *   5. daisyUI theme "wilmink"
 *   6. Base elements
 *   7. Type scale utilities
 *   8. Structure, borders and motion
 *   9. Component wiring
 */

/* ═══════════════════════════════════════════════════
   1. FONTS
   Self hosted, all under the OFL. Google Sans for UI, Roboto Condensed for wide
   catalogue tables, Roboto Mono for article numbers, OE references and VINs.

   These are variable fonts, so a single file covers the whole weight range.
   Google Sans comes in two files split by `unicode-range`, so a page loads the
   extended Latin one only when it has a character from it.
═══════════════════════════════════════════════════ */

@font-face {
  font-family: "Google Sans";
  src: url("../fonts/google-sans.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Google Sans";
  src: url("../fonts/google-sans-ext.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Roboto Condensed";
  src: url("../fonts/roboto-condensed.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Roboto Mono";
  src: url("../fonts/roboto-mono.woff2") format("woff2");
  font-weight: 100 700;
  font-style: normal;
  font-display: swap;
}

/* ═══════════════════════════════════════════════════
   2. PRIMITIVE TOKENS
   Layer one of three. A primitive says what a value IS and nothing about where
   it is used, so nothing outside this block may be a raw hex.

   Five ramps, eleven steps each, 50 lightest to 950 darkest. The 500 step of each
   is the brand colour the design system names, and the steps carrying a comment
   are values the brand already used, kept exactly rather than regenerated. The
   rest are interpolated in oklch at the ramp's own hue.

   Spacing, sizing, breakpoints and opacity are deliberately absent: Tailwind
   already ships those scales and this project uses them as they are.
═══════════════════════════════════════════════════ */

:root {
  /* primary */
  --wm-primary-50: #e7f5fc; /* info tint, table row hover */
  --wm-primary-100: #d5ebfb; /* table header */
  --wm-primary-200: #aed8f5;
  --wm-primary-300: #86c5ef;
  --wm-primary-400: #5ab1e8;
  --wm-primary-500: #129de0; /* the brand, and what wilminkengineparts.com uses */
  --wm-primary-600: #0f84bd; /* hover, and the button edge on their site */
  --wm-primary-700: #0e7ab0; /* pressed, and the only blue that carries text */
  --wm-primary-800: #0a5c85; /* info tint text */
  --wm-primary-900: #0b4f72; /* navigation rail */
  --wm-primary-950: #001d31;

  /* neutral */
  --wm-neutral-50: #f6f9fc; /* inset surface */
  --wm-neutral-100: #f0f3f6;
  --wm-neutral-200: #e3e6ea; /* hairline */
  --wm-neutral-300: #d0d1d2; /* disabled border */
  --wm-neutral-400: #9a9d9f; /* muted text */
  --wm-neutral-500: #575859; /* secondary */
  --wm-neutral-600: #4a4b4d;
  --wm-neutral-700: #3e3f41;
  --wm-neutral-800: #323435;
  --wm-neutral-900: #27282a; /* body text */
  --wm-neutral-950: #191b1c;

  /* success */
  --wm-success-50: #f0f6e4; /* success tint */
  --wm-success-100: #e1efd3;
  --wm-success-200: #c9e0b0;
  --wm-success-300: #b0d08b;
  --wm-success-400: #98c165;
  --wm-success-500: #81b138; /* brand */
  --wm-success-600: #658c2b;
  --wm-success-700: #4a681e; /* success tint text */
  --wm-success-800: #3a5313;
  --wm-success-900: #2a3f08;
  --wm-success-950: #121f01; /* text on a success fill */

  /* warning */
  --wm-warning-50: #fff6dd; /* warning tint */
  --wm-warning-100: #ffefcc;
  --wm-warning-200: #ffe2a9;
  --wm-warning-300: #fed684;
  --wm-warning-400: #fcc859;
  --wm-warning-500: #fbba00; /* brand */
  --wm-warning-600: #b88900;
  --wm-warning-700: #7a5a00; /* warning tint text */
  --wm-warning-800: #644700;
  --wm-warning-900: #4e3600;
  --wm-warning-950: #261800;

  /* error */
  --wm-error-50: #fdeaea; /* error tint */
  --wm-error-100: #fed7d1;
  --wm-error-200: #fdb0a5;
  --wm-error-300: #f7877a;
  --wm-error-400: #ef594d;
  --wm-error-500: #e30613; /* brand */
  --wm-error-600: #c00511; /* error tint text */
  --wm-error-700: #a30004;
  --wm-error-800: #870000;
  --wm-error-900: #6c0000;
  --wm-error-950: #3d0000;

  /* ── Families ── */
  --wm-font-sans: "Google Sans", ui-sans-serif, system-ui, sans-serif;
  --wm-font-condensed: "Roboto Condensed", "Google Sans", sans-serif;
  --wm-font-mono: "Roboto Mono", ui-monospace, monospace;

  /* ── Type scale. Eleven steps, base 14px because these are data screens.
        Nothing below 12px, ever. Weights are 400, 500 and 700 only. ── */
  --wm-text-display: 2.5rem; /* 40 */
  --wm-text-h1: 2rem; /* 32 */
  --wm-text-h2: 1.5rem; /* 24 */
  --wm-text-h3: 1.25rem; /* 20 */
  --wm-text-h4: 1rem; /* 16 */
  --wm-text-body-lg: 1rem; /* 16 */
  --wm-text-body: 0.875rem; /* 14, base */
  --wm-text-body-sm: 0.8125rem; /* 13 */
  --wm-text-caption: 0.75rem; /* 12 */
  --wm-text-label: 0.6875rem; /* 11 */

  /* ── Weights. Medium is the natural UI weight, so there is no 600. ── */
  --wm-weight-regular: 400;
  --wm-weight-medium: 500;
  --wm-weight-bold: 700;

  /* ── Corners. 4px, which is the radius wilminkengineparts.com uses, and the
        same on a box as on a control. The two roles keep separate tokens even
        while they hold one value, because they are separate decisions and the
        day a box wants a different corner is the day this matters. ── */
  --wm-radius-box: 0.25rem;
  --wm-radius-control: 0.25rem;

  /* ── Motion. Ease out only, no spring, no bounce, no looping. ── */
  --wm-motion-hover: 120ms;
  --wm-motion-reveal: 160ms;
  --wm-motion-overlay: 240ms;
  --wm-motion-ease: ease-out;

  /* ── A Dutch number plate. Outside the palette on purpose: these are not
        brand colours, they are what a plate looks like in the country, and a
        person recognises the box by them before reading the label on it.
        Changing them to fit the theme would make it stop being a plate. ── */
  --wm-plate-face: #f4c12c;
  --wm-plate-edge: #2c2c2c;
  --wm-plate-band: #2563eb;
  --wm-plate-band-content: #ffffff;
}

/* ═══════════════════════════════════════════════════
   3. SEMANTIC TOKENS
   Layer two of three. A semantic token says what a value is FOR, and every one
   of them points at a primitive rather than carrying a value of its own. This
   is the layer a screen reaches for, and the only layer a theme has to redefine.
═══════════════════════════════════════════════════ */

:root {
  /* ── Surface. What a thing is drawn on. ── */
  --wm-surface-page: #ffffff; /* the canvas, and the only white in the system */
  --wm-surface-raised: var(--wm-surface-page); /* cards and modals, held by a border */
  --wm-surface-inset: var(--wm-neutral-50); /* tables and panels, set into the page */
  --wm-surface-inset-strong: var(--wm-neutral-100);
  --wm-surface-accent: var(--wm-primary-50);
  --wm-surface-accent-strong: var(--wm-primary-100);
  --wm-surface-inverse: var(--wm-primary-900); /* the navigation rail */
  --wm-surface-inverse-hover: var(--wm-primary-700);
  --wm-surface-inverse-selected: var(--wm-primary-500);
  --wm-surface-disabled: var(--wm-neutral-200);

  /* ── Content. Ink on those surfaces. ── */
  --wm-content-primary: var(--wm-neutral-900);
  --wm-content-secondary: var(--wm-neutral-500);
  --wm-content-muted: var(--wm-neutral-400);
  --wm-content-disabled: var(--wm-neutral-400);
  --wm-content-inverse: var(--wm-surface-page);
  --wm-content-brand: var(--wm-primary-700); /* 4.74:1 on white; the brand step is 3.03:1 */
  --wm-content-on-accent: var(--wm-primary-900);

  /* ── Border. Depth is a border here, never a shadow. ── */
  --wm-border-default: var(--wm-neutral-200);
  --wm-border-strong: var(--wm-neutral-500);
  --wm-border-selected: var(--wm-primary-500);
  --wm-border-accent: var(--wm-primary-200);
  --wm-border-disabled: var(--wm-neutral-300);

  /* ── Action. Every interactive role, in its four states. A state is a
        designed step on the ramp, not a mechanical darkening of the default. ── */
  --wm-action-primary: var(--wm-primary-500);
  --wm-action-primary-hover: var(--wm-primary-600);
  --wm-action-primary-active: var(--wm-primary-700);
  --wm-action-primary-content: var(--wm-content-inverse);

  --wm-action-secondary: var(--wm-neutral-500);
  --wm-action-secondary-hover: var(--wm-neutral-600);
  --wm-action-secondary-active: var(--wm-neutral-700);
  --wm-action-secondary-content: var(--wm-content-inverse);

  --wm-action-accent: var(--wm-primary-800);
  --wm-action-accent-hover: var(--wm-primary-900);
  --wm-action-accent-active: var(--wm-primary-950);
  --wm-action-accent-content: var(--wm-content-inverse);

  --wm-action-ghost: transparent;
  --wm-action-ghost-hover: var(--wm-neutral-100);
  --wm-action-ghost-active: var(--wm-neutral-200);
  --wm-action-ghost-content: var(--wm-content-primary);

  --wm-action-danger: var(--wm-error-500);
  --wm-action-danger-hover: var(--wm-error-600);
  --wm-action-danger-active: var(--wm-error-700);
  --wm-action-danger-content: var(--wm-content-inverse);

  --wm-action-disabled: var(--wm-surface-disabled);
  --wm-action-disabled-content: var(--wm-content-disabled);

  /* ── Feedback. Four roles, each a flat pair plus a solid, because a badge and
        a banner need a fixed background and text colour rather than an opacity. ── */
  --wm-feedback-info-surface: var(--wm-primary-50);
  --wm-feedback-info-border: var(--wm-primary-200);
  --wm-feedback-info-content: var(--wm-primary-800);
  --wm-feedback-info-solid: var(--wm-primary-700); /* white sits on this, so 4.74:1 */
  --wm-feedback-info-solid-content: var(--wm-content-inverse);

  --wm-feedback-success-surface: var(--wm-success-50);
  --wm-feedback-success-border: var(--wm-success-200);
  --wm-feedback-success-content: var(--wm-success-700);
  --wm-feedback-success-solid: var(--wm-success-500);
  --wm-feedback-success-solid-content: var(--wm-success-950);

  --wm-feedback-warning-surface: var(--wm-warning-50);
  --wm-feedback-warning-border: var(--wm-warning-200);
  --wm-feedback-warning-content: var(--wm-warning-700);
  --wm-feedback-warning-solid: var(--wm-warning-500);
  --wm-feedback-warning-solid-content: var(--wm-content-primary);

  --wm-feedback-error-surface: var(--wm-error-50);
  --wm-feedback-error-border: var(--wm-error-200);
  --wm-feedback-error-content: var(--wm-error-600);
  --wm-feedback-error-solid: var(--wm-error-500);
  --wm-feedback-error-solid-content: var(--wm-content-inverse);

  /* ── Focus. Accessibility is a token, not something remembered at the end. ── */
  --wm-focus-ring-color: var(--wm-primary-500);
  --wm-focus-ring-width: 2px;
  --wm-focus-ring-offset: 2px;

  /* ── Overlay. The blur is the overlay drawer's alone. ── */
  --wm-overlay-scrim: rgb(39 40 42 / 55%);
  --wm-overlay-blur: 4px;

  /* ── Elevation. This system has no shadows, so an elevation is which border
        holds the thing up. Named anyway, so a component asks for a level
        rather than picking a line. ── */
  --wm-elevation-surface: none;
  --wm-elevation-raised: 1px solid var(--wm-border-default);
  --wm-elevation-floating: 1px solid var(--wm-border-default);
  --wm-elevation-modal: 1px solid var(--wm-border-strong);

  /* ── The names the codebase already used, kept pointing at the layer above so
        no call site and no mail template has to change. ── */
  --wm-base-100: var(--wm-surface-page);
  --wm-base-200: var(--wm-surface-inset);
  --wm-base-300: var(--wm-neutral-200);
  --wm-content: var(--wm-content-primary);
  --wm-primary: var(--wm-action-primary);
  --wm-primary-content: var(--wm-action-primary-content);
  --wm-secondary: var(--wm-action-secondary);
  --wm-success: var(--wm-feedback-success-solid);
  --wm-warning: var(--wm-feedback-warning-solid);
  --wm-error: var(--wm-feedback-error-solid);
  --wm-info: var(--wm-feedback-info-solid);
  --wm-tint-info: var(--wm-feedback-info-surface);
  --wm-tint-info-content: var(--wm-feedback-info-content);
  --wm-tint-success: var(--wm-feedback-success-surface);
  --wm-tint-success-content: var(--wm-feedback-success-content);
  --wm-tint-warning: var(--wm-feedback-warning-surface);
  --wm-tint-warning-content: var(--wm-feedback-warning-content);
  --wm-tint-error: var(--wm-feedback-error-surface);
  --wm-tint-error-content: var(--wm-feedback-error-content);
  --wm-hairline: var(--wm-border-default);
  --wm-line-emphasis: var(--wm-border-strong);
  --wm-line-selection: var(--wm-border-selected);
  --wm-scrim: var(--wm-overlay-scrim);
}

/* ═══════════════════════════════════════════════════
   4. COMPONENT TOKENS
   Layer three of three, and the closest to what a person clicks. A component
   token points at a semantic one, never at a primitive, so a component never
   reaches past the layer that carries the intent.

   Only components with a state worth designing are here. A component that is
   fully described by the semantic layer does not need a block of its own.
═══════════════════════════════════════════════════ */

:root {
  /* ── Table. A list is drawn on the inset with a tinted head, so it reads as
        one block on a white page. ── */
  --wm-table-surface: var(--wm-surface-inset);
  --wm-table-header-bg: var(--wm-surface-accent-strong);
  --wm-table-header-content: var(--wm-content-on-accent);
  --wm-table-row-bg: transparent;
  --wm-table-row-bg-hover: var(--wm-surface-accent);
  --wm-table-row-bg-selected: var(--wm-surface-accent-strong);
  --wm-table-row-bg-zebra: var(--wm-surface-page);
  --wm-table-border: var(--wm-border-default);

  /* ── Button. daisyUI derives a hover by mixing 10% black into the fill,
        which is a calculation rather than a decision. These point at the ramp
        instead, so the pressed state of a button is a colour somebody chose.

        `edge` is the line along the bottom of a solid button, one step down the
        ramp from its own fill, which is how wilminkengineparts.com draws every
        action. It is the step the button's hover lands on, so hovering takes
        the fill down to meet the edge and the control reads as pushed in. ── */
  --wm-button-primary-bg: var(--wm-action-primary);
  --wm-button-primary-bg-hover: var(--wm-action-primary-hover);
  --wm-button-primary-bg-active: var(--wm-action-primary-active);
  --wm-button-primary-edge: var(--wm-action-primary-hover);
  --wm-button-primary-content: var(--wm-action-primary-content);

  --wm-button-secondary-bg: var(--wm-action-secondary);
  --wm-button-secondary-bg-hover: var(--wm-action-secondary-hover);
  --wm-button-secondary-bg-active: var(--wm-action-secondary-active);
  --wm-button-secondary-edge: var(--wm-action-secondary-hover);
  --wm-button-secondary-content: var(--wm-action-secondary-content);

  --wm-button-accent-bg: var(--wm-action-accent);
  --wm-button-accent-bg-hover: var(--wm-action-accent-hover);
  --wm-button-accent-bg-active: var(--wm-action-accent-active);
  --wm-button-accent-edge: var(--wm-action-accent-hover);
  --wm-button-accent-content: var(--wm-action-accent-content);

  --wm-button-ghost-bg: var(--wm-action-ghost);
  --wm-button-ghost-bg-hover: var(--wm-action-ghost-hover);
  --wm-button-ghost-bg-active: var(--wm-action-ghost-active);
  --wm-button-ghost-content: var(--wm-action-ghost-content);

  --wm-button-danger-bg: var(--wm-action-danger);
  --wm-button-danger-bg-hover: var(--wm-action-danger-hover);
  --wm-button-danger-bg-active: var(--wm-action-danger-active);
  --wm-button-danger-edge: var(--wm-action-danger-hover);
  --wm-button-danger-content: var(--wm-action-danger-content);

  --wm-button-bg-disabled: var(--wm-action-disabled);
  --wm-button-content-disabled: var(--wm-action-disabled-content);

  /* The only line in the system thicker than the 2px selection rule, because it
     is depth rather than separation. */
  --wm-button-edge-width: 3px;

  /* ── Input. ── */
  --wm-input-bg: var(--wm-surface-page);
  --wm-input-border: var(--wm-border-default);
  --wm-input-border-hover: var(--wm-border-strong);
  --wm-input-border-focus: var(--wm-border-selected);
  --wm-input-border-error: var(--wm-feedback-error-solid);
  --wm-input-content: var(--wm-content-primary);
  --wm-input-placeholder: var(--wm-content-muted);
  --wm-input-bg-disabled: var(--wm-surface-disabled);
  --wm-input-content-disabled: var(--wm-content-disabled);
  --wm-input-label-content: var(--wm-content-primary);
  --wm-input-note-content: var(--wm-content-secondary);

  /* ── Badge. Two flavours on purpose: a tint for a status somebody reads, a
        solid for a count somebody spots. ── */
  /* ── Filter pill. An unset pill is drawn like a field, the input's hairline,
        and sits on a bottom edge the way a solid button does, one grey down. ── */
  /* ── A token in the search bar, one filter as a query reads it: the key, the
        operator muted and the value in the blue that carries text. White,
        because that blue falls under AA on the inset grey. ── */
  --wm-query-token-bg: var(--wm-surface-page);
  --wm-query-token-border: var(--wm-border-default);
  --wm-query-token-border-hover: var(--wm-border-strong);
  --wm-query-token-content: var(--wm-content-primary);
  --wm-query-token-operator: var(--wm-content-secondary);
  --wm-query-token-value: var(--wm-content-brand);

  --wm-pill-border: var(--wm-border-default);
  --wm-pill-border-hover: var(--wm-border-strong);
  --wm-pill-edge: var(--wm-border-disabled);

  --wm-badge-info-bg: var(--wm-feedback-info-surface);
  --wm-badge-info-content: var(--wm-feedback-info-content);
  --wm-badge-success-bg: var(--wm-feedback-success-surface);
  --wm-badge-success-content: var(--wm-feedback-success-content);
  --wm-badge-warning-bg: var(--wm-feedback-warning-surface);
  --wm-badge-warning-content: var(--wm-feedback-warning-content);
  --wm-badge-error-bg: var(--wm-feedback-error-surface);
  --wm-badge-error-content: var(--wm-feedback-error-content);

  /* ── App shell: the dark frame and rail, the rounded panel set into it, and
        the section column inside that. The panel is the one box rounded past
        the 4px corner, twice the box radius. ── */
  --wm-shell-frame-bg: var(--wm-surface-inverse);
  --wm-shell-frame-content: var(--wm-content-inverse);
  --wm-shell-radius: calc(var(--wm-radius-box) * 2);
  --wm-shell-panel-border: var(--wm-border-default);
  --wm-rail-item-bg-hover: var(--wm-surface-inverse-hover);
  --wm-rail-item-bg-active: var(--wm-surface-inverse-selected);
  --wm-rail-item-content-active: var(--wm-content-inverse);
  --wm-subnav-width: 15rem;
  --wm-subnav-bg: var(--wm-surface-accent);
  --wm-subnav-border: var(--wm-border-accent);
  --wm-subnav-item-bg-hover: var(--wm-surface-accent-strong);
  --wm-subnav-item-bg-active: var(--wm-action-primary-active);
  --wm-subnav-item-content-active: var(--wm-action-primary-content);
  /* The drawer is the column's twin, wider, so its longest rows stand on one line:
     a Windows chord beside a Dutch label, and an operator beside every spelling. */
  --wm-drawer-width: 24rem;

  /* ── A customer note: a pale blue sheet with ruled lines and a brand blue
        top line, and the deck of them on a customer. ── */
  --wm-note-face: var(--wm-surface-accent);
  --wm-note-content: var(--wm-content-primary);
  --wm-note-edge: var(--wm-border-accent);
  --wm-note-rule: var(--wm-border-accent);
  --wm-note-top: var(--wm-border-selected);
  --wm-note-meta: var(--wm-content-brand);
  --wm-note-rule-gap: 1.25rem;
  --wm-note-card-width: 11rem;
  --wm-note-card-height: 9rem;
  --wm-note-lift: 0.75rem;
}

/* ═══════════════════════════════════════════════════
   5. DAISYUI THEME "WILMINK"
   daisyUI 5 reads these names directly, so this block is the design system
   document's `@plugin "daisyui/theme"` block with one difference: the document
   declares it through the plugin, and this project ships daisyUI prebuilt, so
   the same names are declared on the theme's own selector instead.

   Every value points at the semantic layer. Nothing here is a hex.

   Load order matters. daisyUI declares its own `:root` theme inside
   `@layer base`, and this file is unlayered, so it wins whatever the order of
   the two stylesheets, but the link still comes first for clarity.
═══════════════════════════════════════════════════ */

[data-theme="wilmink"] {
  color-scheme: light;

  --color-base-100: var(--wm-surface-page);
  --color-base-200: var(--wm-surface-inset);
  --color-base-300: var(--wm-neutral-200);
  --color-base-content: var(--wm-content-primary);
  --color-primary: var(--wm-action-primary);
  --color-primary-content: var(--wm-action-primary-content);
  --color-secondary: var(--wm-action-secondary);
  --color-secondary-content: var(--wm-action-secondary-content);
  --color-accent: var(--wm-action-accent);
  --color-accent-content: var(--wm-action-accent-content);
  --color-neutral: var(--wm-neutral-900);
  --color-neutral-content: var(--wm-neutral-50);
  --color-info: var(--wm-feedback-info-solid);
  --color-info-content: var(--wm-feedback-info-solid-content);
  --color-success: var(--wm-feedback-success-solid);
  --color-success-content: var(--wm-feedback-success-solid-content);
  --color-warning: var(--wm-feedback-warning-solid);
  --color-warning-content: var(--wm-feedback-warning-solid-content);
  --color-error: var(--wm-feedback-error-solid);
  --color-error-content: var(--wm-feedback-error-solid-content);

  --radius-selector: var(--wm-radius-control); /* badge, checkbox, toggle track */
  --radius-field: var(--wm-radius-control); /* button, input, select, tab */
  --radius-box: var(--wm-radius-box); /* card, modal, alert, dropdown */
  --size-selector: 0.25rem;
  --size-field: 0.25rem;
  --border: 1px;
  --depth: 0; /* no shadows, hierarchy is a line */
  --noise: 0;
}


/* ═══════════════════════════════════════════════════
   6. BASE ELEMENTS
═══════════════════════════════════════════════════ */

html {
  font-family: var(--wm-font-sans);
  font-size: 100%;
}

body {
  font-size: var(--wm-text-body);
  font-weight: 400;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Prices, quantities and stock counts are compared down a column, so the digits
   have to line up. */
th,
td {
  font-variant-numeric: tabular-nums;
}

code,
kbd,
samp,
pre {
  font-family: var(--wm-font-mono);
  font-size: var(--wm-text-body-sm);
  font-weight: 500;
}

:focus-visible {
  outline: var(--wm-focus-ring-width) solid var(--wm-focus-ring-color);
  outline-offset: var(--wm-focus-ring-offset);
}

/* ═══════════════════════════════════════════════════
   7. TYPE SCALE UTILITIES
   Five of the eleven steps are Tailwind defaults and are used as those
   utilities at the call site: h2 is text-2xl, h3 is text-xl, h4 and body-lg are
   text-base, body is text-sm, caption is text-xs. The steps below have no
   Tailwind equivalent, so they live here.
═══════════════════════════════════════════════════ */

.text-display {
  font-size: var(--wm-text-display);
  line-height: 1.1;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.text-h1 {
  font-size: var(--wm-text-h1);
  line-height: 1.15;
  font-weight: 700;
  letter-spacing: -0.015em;
}

.text-h2 {
  font-size: var(--wm-text-h2);
  line-height: 1.2;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.text-h3 {
  font-size: var(--wm-text-h3);
  line-height: 1.3;
  font-weight: 500;
}

.text-h4 {
  font-size: var(--wm-text-h4);
  line-height: 1.4;
  font-weight: 500;
}

.text-body-lg {
  font-size: var(--wm-text-body-lg);
  line-height: 1.55;
  font-weight: 400;
}

.text-body-sm {
  font-size: var(--wm-text-body-sm);
  line-height: 1.45;
  font-weight: 400;
}

.text-label {
  font-size: var(--wm-text-label);
  line-height: 1;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* Roughly 12% horizontal space for wide catalogue tables. Never mid paragraph. */
.font-condensed {
  font-family: var(--wm-font-condensed);
}

.font-mono {
  font-family: var(--wm-font-mono);
}

/* ═══════════════════════════════════════════════════
   8. STRUCTURE, BORDERS AND MOTION
   Hierarchy comes from lines, not shadows.
═══════════════════════════════════════════════════ */

.border-hairline {
  border: 1px solid var(--wm-hairline);
  border-radius: var(--wm-radius-box);
}

.border-emphasis {
  border: 1px solid var(--wm-line-emphasis);
}

.border-selection {
  border: 2px solid var(--wm-line-selection);
}

/* The same lines on one edge, for separating without boxing. `.border-hairline`
   is the `border` shorthand, so pairing it with Tailwind's `border-t` draws all
   four sides and not the rule that was wanted. These say which edge. */
.border-t-hairline {
  border-top: 1px solid var(--wm-hairline);
}

.border-l-selection {
  border-left: 2px solid var(--wm-line-selection);
}

/* The hairline between stacked children, the rule a list draws between its rows.
   Tailwind's own divider takes a theme colour, and the hairline is not one. */
.divide-y-hairline > :not(:last-child) {
  border-bottom: 1px solid var(--wm-hairline);
}

.tint-info {
  background: var(--wm-tint-info);
  color: var(--wm-tint-info-content);
}

.tint-success {
  background: var(--wm-tint-success);
  color: var(--wm-tint-success-content);
}

.tint-warning {
  background: var(--wm-tint-warning);
  color: var(--wm-tint-warning-content);
}

.tint-error {
  background: var(--wm-tint-error);
  color: var(--wm-tint-error-content);
}

/* No shadows anywhere. The theme's `--depth: 0` already tells daisyUI that, and
   these clear the few it draws outside that switch. */
[data-theme="wilmink"] .card,
[data-theme="wilmink"] .dropdown-content,
[data-theme="wilmink"] .modal-box,
[data-theme="wilmink"] .navbar,
[data-theme="wilmink"] .alert {
  box-shadow: none;
}

/* Elevation is a border in this system, so a raised thing asks for the level
   rather than naming a line. */
[data-theme="wilmink"] .card,
[data-theme="wilmink"] .alert {
  border: var(--wm-elevation-raised);
}

[data-theme="wilmink"] .dropdown-content {
  border: var(--wm-elevation-floating);
}

[data-theme="wilmink"] .modal-box {
  border: var(--wm-elevation-modal);
}

[data-theme="wilmink"] .navbar {
  border-bottom: var(--wm-elevation-raised);
}

/* ═══════════════════════════════════════════════════
   9. COMPONENT WIRING
   Where layer three meets daisyUI. Everything below points at a `--wm-*`
   component token and never at a colour, so restyling a button is a change in
   section 4 rather than a change in here.
═══════════════════════════════════════════════════ */

/* ── Table ──
   The surface is the inset and the head is a tint of the brand, so a list reads
   as one block on a white page. daisyUI stripes even rows and pins a head in
   base-200 and base-100, which are now the surface and the page, so both are
   pointed back at the pair that still reads. */
[data-theme="wilmink"] .table thead tr,
[data-theme="wilmink"] .table tfoot tr,
[data-theme="wilmink"] .table-pin-rows thead tr,
[data-theme="wilmink"] .table-pin-rows tfoot tr {
  background-color: var(--wm-table-header-bg);
  color: var(--wm-table-header-content);
}

.wm-table-surface {
  background-color: var(--wm-table-surface);
  border-radius: var(--wm-radius-box);
}

[data-theme="wilmink"] .table-zebra tbody tr:nth-child(2n),
[data-theme="wilmink"] .table-zebra tbody tr:nth-child(2n) :where(.table-pin-cols tr th) {
  background-color: var(--wm-table-row-bg-zebra);
}

/* Opt in per table, because a table nobody clicks should not light up under the
   pointer. The table macro's `hoverable` parameter adds this class. */
[data-theme="wilmink"] .wm-table-hover tbody tr:hover,
[data-theme="wilmink"] .table tr.hover:hover {
  background-color: var(--wm-table-row-bg-hover);
}

[data-theme="wilmink"] .table tr[aria-selected="true"] {
  background-color: var(--wm-table-row-bg-selected);
}

/* ── List ──
   An editable row works only inside `list(editable=true)`, which carries the
   swap it relies on. Anywhere else it hides what it holds and names the mistake
   in red, so a developer sees it on the first render rather than on a click. */
:not([data-list-editable]) > [data-list-row-editable] {
  pointer-events: none;
  outline: 2px dashed var(--wm-feedback-error-solid);
  outline-offset: -2px;
  background-color: var(--wm-feedback-error-surface);
}

:not([data-list-editable]) > [data-list-row-editable] > * {
  display: none;
}

:not([data-list-editable]) > [data-list-row-editable]::before {
  content: "list_row_editable #" attr(id) " needs list(editable=true) around it";
  color: var(--wm-feedback-error-content);
  font-family: var(--wm-font-mono);
}

/* An open row edits its value where it stands. Its inputs keep the value's size
   and have no border, ring or fill of their own, and their padding is pulled back
   by as much margin, so the text does not move. The one being typed into is set
   into the row on the inset surface, and one in error takes the error tint. */
[data-list-row-editable] :is(.input, .select, .textarea) {
  font-size: inherit;
  border-width: 0;
  background-color: transparent;
  padding-inline-start: 0.5rem;
  margin-inline-start: -0.5rem;
}

[data-list-row-editable] :is(.input, .textarea) {
  padding-inline-end: 0.5rem;
}

[data-list-row-editable] :is(.input, .select, .textarea):focus,
[data-list-row-editable] :is(.input, .select, .textarea):focus-visible {
  outline: none;
  background-color: var(--wm-surface-inset);
}

[data-list-row-editable] :is(.input, .select, .textarea):is(.input-error, .select-error, .textarea-error) {
  background-color: var(--wm-feedback-error-surface);
}

/* ── Button ──
   daisyUI derives every hover by mixing 10% black into the fill. These replace
   that calculation with the step of the ramp somebody chose.

   A solid button also sits on a darker line along its bottom, which is the one
   piece of depth this system allows because it is a border rather than a
   shadow. The colour is the step the button's own hover lands on, so
   hovering levels the two and pressing takes the whole control below them. The
   width is set once and the colour per role, low enough in specificity that the
   state rules below still own it. A button that should stay level, one in a
   strip of them, takes `wm-btn-flat`. */
[data-theme="wilmink"] :is(.btn-primary, .btn-secondary, .btn-accent, .btn-error):not(
    .wm-btn-flat, .btn-outline, .btn-dash, .btn-soft, .btn-ghost, .btn-link) {
  border-bottom-width: var(--wm-button-edge-width);
}

[data-theme="wilmink"] .btn-primary {
  background-color: var(--wm-button-primary-bg);
  border-color: var(--wm-button-primary-bg);
  border-bottom-color: var(--wm-button-primary-edge);
  color: var(--wm-button-primary-content);
}

[data-theme="wilmink"] .btn-primary:hover {
  background-color: var(--wm-button-primary-bg-hover);
  border-color: var(--wm-button-primary-bg-hover);
}

[data-theme="wilmink"] .btn-primary:active {
  background-color: var(--wm-button-primary-bg-active);
  border-color: var(--wm-button-primary-bg-active);
}

[data-theme="wilmink"] .btn-secondary {
  background-color: var(--wm-button-secondary-bg);
  border-color: var(--wm-button-secondary-bg);
  border-bottom-color: var(--wm-button-secondary-edge);
  color: var(--wm-button-secondary-content);
}

[data-theme="wilmink"] .btn-secondary:hover {
  background-color: var(--wm-button-secondary-bg-hover);
  border-color: var(--wm-button-secondary-bg-hover);
}

[data-theme="wilmink"] .btn-secondary:active {
  background-color: var(--wm-button-secondary-bg-active);
  border-color: var(--wm-button-secondary-bg-active);
}

[data-theme="wilmink"] .btn-accent {
  background-color: var(--wm-button-accent-bg);
  border-color: var(--wm-button-accent-bg);
  border-bottom-color: var(--wm-button-accent-edge);
  color: var(--wm-button-accent-content);
}

[data-theme="wilmink"] .btn-accent:hover {
  background-color: var(--wm-button-accent-bg-hover);
  border-color: var(--wm-button-accent-bg-hover);
}

[data-theme="wilmink"] .btn-accent:active {
  background-color: var(--wm-button-accent-bg-active);
  border-color: var(--wm-button-accent-bg-active);
}

[data-theme="wilmink"] .btn-error {
  background-color: var(--wm-button-danger-bg);
  border-color: var(--wm-button-danger-bg);
  border-bottom-color: var(--wm-button-danger-edge);
  color: var(--wm-button-danger-content);
}

[data-theme="wilmink"] .btn-error:hover {
  background-color: var(--wm-button-danger-bg-hover);
  border-color: var(--wm-button-danger-bg-hover);
}

[data-theme="wilmink"] .btn-error:active {
  background-color: var(--wm-button-danger-bg-active);
  border-color: var(--wm-button-danger-bg-active);
}

[data-theme="wilmink"] .btn-ghost:hover {
  background-color: var(--wm-button-ghost-bg-hover);
  border-color: transparent;
}

[data-theme="wilmink"] .btn-ghost:active {
  background-color: var(--wm-button-ghost-bg-active);
  border-color: transparent;
}

[data-theme="wilmink"] .btn:disabled,
[data-theme="wilmink"] .btn[disabled] {
  background-color: var(--wm-button-bg-disabled);
  border-color: var(--wm-button-bg-disabled);
  color: var(--wm-button-content-disabled);
}

/* A button's spinner takes no room until its request is in flight. htmx only
   fades an indicator out, which left an invisible spinner beside every label
   and pushed the label off centre. The request marks the form around the
   button or the button itself, so both are covered. */
.btn > .htmx-indicator {
  display: none;
}

.htmx-request .btn > .htmx-indicator,
.btn.htmx-request > .htmx-indicator {
  display: inline-block;
}

/* ── Filter pill ──
   Scoped to `btn-outline`, which this application uses in exactly one place.
   Hovering darkens the line but not the edge below it, since an edge that
   matched the line would read as the pill being pressed. */
[data-theme="wilmink"] .btn-outline {
  --btn-border: var(--wm-pill-border);
  border-bottom-width: var(--wm-button-edge-width);
  border-bottom-color: var(--wm-pill-edge);
}

[data-theme="wilmink"] .btn-outline:hover {
  --btn-border: var(--wm-pill-border-hover);
}

/* daisyUI 5 borders every `join-item`, and a pill's dropdown is one so the join
   rounds it. That line framed the button inside in body text colour, so it goes
   and the button takes the dropdown's corners instead. */
.join > .dropdown.join-item {
  border-width: 0;
}

.join > .dropdown.join-item > .btn {
  border-radius: inherit;
}

/* ── Input ──
   daisyUI already draws an input's background, border and text from the theme
   block, so none of that is repeated here. It was, and a rule carrying a theme
   selector outweighs both `input-ghost` and any utility on the element, so
   every borderless input, the catalogue search and the number plate among them,
   came back as a white box with a border. The theme block is the wiring.

   What is left is the placeholder, which daisyUI mixes out of base content
   rather than naming, and the two states. The ghost variants are excluded
   because a borderless input is one that has opted out of this. */
[data-theme="wilmink"] .input:not(.input-ghost)::placeholder,
[data-theme="wilmink"] .textarea:not(.textarea-ghost)::placeholder {
  color: var(--wm-input-placeholder);
}

[data-theme="wilmink"] .input:disabled,
[data-theme="wilmink"] .select:disabled,
[data-theme="wilmink"] .textarea:disabled,
[data-theme="wilmink"] .file-input:disabled {
  background-color: var(--wm-input-bg-disabled);
  color: var(--wm-input-content-disabled);
}

[data-theme="wilmink"] .input-error,
[data-theme="wilmink"] .select-error,
[data-theme="wilmink"] .textarea-error {
  border-color: var(--wm-input-border-error);
}

/* daisyUI sets a legend at weight 600, and this system has 400, 500 and 700
   only, so the field name takes the medium step instead. */
[data-theme="wilmink"] .fieldset-legend {
  color: var(--wm-input-label-content);
  font-weight: var(--wm-weight-medium);
}

[data-theme="wilmink"] .label {
  color: var(--wm-input-note-content);
}

/* ── Badge. The tint pair, because a status is read rather than spotted. ── */
[data-theme="wilmink"] .badge-info {
  background-color: var(--wm-badge-info-bg);
  border-color: var(--wm-feedback-info-border);
  color: var(--wm-badge-info-content);
}

[data-theme="wilmink"] .badge-success {
  background-color: var(--wm-badge-success-bg);
  border-color: var(--wm-feedback-success-border);
  color: var(--wm-badge-success-content);
}

[data-theme="wilmink"] .badge-warning {
  background-color: var(--wm-badge-warning-bg);
  border-color: var(--wm-feedback-warning-border);
  color: var(--wm-badge-warning-content);
}

[data-theme="wilmink"] .badge-error {
  background-color: var(--wm-badge-error-bg);
  border-color: var(--wm-feedback-error-border);
  color: var(--wm-badge-error-content);
}

/* ── Entry box ──
   A lead slot, a field and a submit that read as one control, so focus rings
   the strip rather than the field sitting inside it. The submit keeps its own
   ring, because it is a separate thing to land on. */
.wm-entry-box {
  border-radius: var(--wm-radius-box);
  overflow: hidden;
}

.wm-entry-box:has(.input:focus) {
  outline: var(--wm-focus-ring-width) solid var(--wm-focus-ring-color);
  outline-offset: var(--wm-focus-ring-offset);
}

/* Both states, because daisyUI rings on `:focus` and this file's own rule rings
   on `:focus-visible`, and a text field matches the two together. */
.wm-entry-box .input:focus,
.wm-entry-box .input:focus-visible {
  outline: none;
}

/* daisyUI 5 gives a button and a field a height of their own, and a flex row
   cannot stretch a height that is already set, so both stood 40px tall inside a
   48px strip: pinned to the top, a gap along the bottom, and the text sitting
   high in the box. The height belongs to the strip, so it is handed back here. */
.wm-entry-box > .btn,
.wm-entry-box > .input {
  height: auto;
  align-self: stretch;
}

/* The strip is one control, so only the strip has corners. Its parts each carry
   4px of their own from the theme, which rounded them where they meet: the field
   notched against the lead slot and the submit notched against the field, all of
   it inside a box whose outside was already rounded. The parts square
   themselves and `overflow: hidden` above draws the outer four.

   `join` would say the same thing and is what daisyUI offers for it, but it
   would put three more classes in two templates to reach a result the strip can
   state once. */
.wm-entry-box > * {
  border-radius: 0;
}

/* ── The search bar's tokens ──
   One filter set, inside the box it was typed into. The parts are coloured
   here because a value in the brand blue has no utility of its own. */
.wm-query-token {
  background-color: var(--wm-query-token-bg);
  border: 1px solid var(--wm-query-token-border);
  border-radius: var(--wm-radius-control);
  color: var(--wm-query-token-content);
}

.wm-query-token:hover,
.wm-query-token:focus-within {
  border-color: var(--wm-query-token-border-hover);
}

.wm-query-token-operator {
  color: var(--wm-query-token-operator);
}

.wm-query-token-value {
  color: var(--wm-query-token-value);
  font-weight: 500;
}

.wm-query-token-clear {
  color: var(--wm-query-token-operator);
}

.wm-query-token-clear:hover {
  color: var(--wm-query-token-content);
}

/* ── App shell ──
   A dark frame holding the rail, with one rounded panel set into it. The panel
   holds the section's own column, `#sidebar`, and the page. */
.app-frame {
  background-color: var(--wm-shell-frame-bg);
  color: var(--wm-shell-frame-content);
}

.app-panel {
  background-color: var(--wm-surface-page);
  color: var(--wm-content-primary);
  border: 1px solid var(--wm-shell-panel-border);
  border-radius: var(--wm-shell-radius);
}

.rail-icon {
  border-radius: var(--wm-shell-radius);
  transition: background-color var(--wm-motion-hover) var(--wm-motion-ease);
}

.rail-item:hover .rail-icon {
  background-color: var(--wm-rail-item-bg-hover);
}

.rail-item-active .rail-icon,
.rail-item-active:hover .rail-icon {
  background-color: var(--wm-rail-item-bg-active);
  color: var(--wm-rail-item-content-active);
}

/* ── The section column ──
   Hiding is the `data-collapsed` attribute alone, so a morph swap changes no
   class. The column narrows to nothing while its body slides out to the left. */
.subnav {
  width: var(--wm-subnav-width);
  overflow: hidden;
  transition:
    width var(--wm-motion-overlay) var(--wm-motion-ease),
    visibility 0s;
}

.subnav-body {
  width: var(--wm-subnav-width);
  background-color: var(--wm-subnav-bg);
  border-right: 1px solid var(--wm-subnav-border);
  transition: transform var(--wm-motion-overlay) var(--wm-motion-ease);
}

/* Hidden only once the slide has finished, so nothing in it takes focus. */
.subnav[data-collapsed="true"] {
  width: 0;
  visibility: hidden;
  transition:
    width var(--wm-motion-overlay) var(--wm-motion-ease),
    visibility 0s linear var(--wm-motion-overlay);
}

.subnav[data-collapsed="true"] .subnav-body {
  transform: translateX(-100%);
}

.subnav-item {
  border-radius: var(--wm-radius-control);
}

.subnav-item:hover {
  background-color: var(--wm-subnav-item-bg-hover);
}

.subnav-item-active,
.subnav-item-active:hover {
  background-color: var(--wm-subnav-item-bg-active);
  color: var(--wm-subnav-item-content-active);
}

/* The rail offers hide while the column shows and show while it is hidden. Each
   posts a fixed state, so neither needs redrawing after the swap. */
.subnav-show {
  display: none;
}

.app-frame:has(.subnav[data-collapsed="true"]) .subnav-hide {
  display: none;
}

.app-frame:has(.subnav[data-collapsed="true"]) .subnav-show {
  display: flex;
}

/* Nothing chosen yet and no room for the column, so below lg `auto` draws what
   `true` does. 63.999rem is one step under Tailwind's `lg`. */
@media (max-width: 63.999rem) {
  .subnav[data-collapsed="auto"] {
    width: 0;
    visibility: hidden;
    transition:
      width var(--wm-motion-overlay) var(--wm-motion-ease),
      visibility 0s linear var(--wm-motion-overlay);
  }

  .subnav[data-collapsed="auto"] .subnav-body {
    transform: translateX(-100%);
  }

  .app-frame:has(.subnav[data-collapsed="auto"]) .subnav-hide {
    display: none;
  }

  .app-frame:has(.subnav[data-collapsed="auto"]) .subnav-show {
    display: flex;
  }
}

/* ── The drawer ──
   The section column's twin on the panel's right edge: its surface, its border on
   the inner edge and its slide, at a width of its own. A drawer is a popover in the top layer,
   so it is placed rather than laid out, inside the 2.75rem header, the 0.25rem
   margin and the 1px border `base.html` gives the panel, and it rounds the two
   corners the panel would have clipped. */
.wm-drawer {
  inset: calc(2.75rem + 1px) calc(0.25rem + 1px) calc(0.25rem + 1px) auto;
  width: var(--wm-drawer-width);
  height: auto;
  margin: 0;
  padding: 0;
  overflow: hidden;
  border: 0;
  border-left: 1px solid var(--wm-subnav-border);
  border-radius: 0 calc(var(--wm-shell-radius) - 1px) calc(var(--wm-shell-radius) - 1px) 0;
  background-color: var(--wm-subnav-bg);
  color: var(--wm-content-primary);
  transform: translateX(calc(100% + 0.25rem + 1px));
  transition:
    transform var(--wm-motion-overlay) var(--wm-motion-ease),
    display var(--wm-motion-overlay) allow-discrete,
    overlay var(--wm-motion-overlay) allow-discrete;
}

.wm-drawer:popover-open {
  display: flex;
  flex-direction: column;
  transform: none;
}

@starting-style {
  .wm-drawer:popover-open {
    transform: translateX(calc(100% + 0.25rem + 1px));
  }
}

/* The pushing drawer makes room for itself, so the page beside it stays in use. */
.app-panel {
  transition: padding-right var(--wm-motion-overlay) var(--wm-motion-ease);
}

.app-frame:has(.wm-drawer:not(.wm-drawer-overlay):popover-open) .app-panel {
  padding-right: var(--wm-drawer-width);
}

/* The overlay drawer has the attention, so the page under it is scrimmed and
   blurred. This is the one blur the design system allows. */
.wm-drawer-overlay::backdrop {
  background-color: var(--wm-scrim);
  backdrop-filter: blur(var(--wm-overlay-blur)); /* design-system: overlay blur */
}

/* Below md there is no room to push, so a drawer covers everything under the
   header. 47.999rem is one step under Tailwind's `md`. */
@media (max-width: 47.999rem) {
  .wm-drawer {
    left: 0.25rem;
    width: auto;
    border-radius: calc(var(--wm-shell-radius) - 1px);
  }

  .app-frame:has(.wm-drawer:not(.wm-drawer-overlay):popover-open) .app-panel {
    padding-right: 0;
  }
}

/* Table rows and lists never animate on data change, so transitions are scoped
   to the controls that actually respond to a pointer.

   The property list is named rather than left to default. `transition-property`
   starts out as `all`, so declaring only a duration put every anchor in the
   application on a 120ms tween for anything that changed, padding and width
   included. Collapsing the rail then had each row easing its own padding
   underneath the width animation, which is what made the whole thing look
   rubbery. A pointer changes colour, and nothing else. */
.btn,
.input,
.select,
.tab,
a {
  transition-property: background-color, border-color, color, outline-color, opacity;
  transition-duration: var(--wm-motion-hover);
  transition-timing-function: var(--wm-motion-ease);
}

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

/* ── The catalogue search box, which is two boxes on one card.

      A 3D flip, the way keuro does it: both faces live in the DOM at once,
      stacked in the same slot, and the card rotates on its X axis to show the
      other one. The back is pre-rotated so it reads right side up once the card
      has turned, and `backface-visibility` means whichever face is turned away
      is simply not drawn.

      keuro drives the rotation with GSAP and a click handler. This is a
      checkbox and a sibling selector, so the flip costs no script and no
      request: the state is already in the document and the browser animates it.
      The server only decides which way up the card starts, which is what makes
      a shared `?mode=plate` link open on the plate.

      Pointer events follow the rotation. A hidden face still occupies the box,
      so without this the form you cannot see is the one catching the clicks. ── */
.wm-flip {
  perspective: 1800px;
}

.wm-flip-inner {
  position: relative;
  transform-style: preserve-3d;
  transition: transform var(--wm-motion-overlay) ease-out;
}

.wm-flip-face {
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}

.wm-flip-face--back {
  position: absolute;
  inset: 0;
  transform: rotateX(180deg);
  pointer-events: none;
}

.wm-flip-toggle:checked ~ .wm-flip-inner {
  transform: rotateX(180deg);
}

.wm-flip-toggle:checked ~ .wm-flip-inner .wm-flip-face--back {
  pointer-events: auto;
}

.wm-flip-toggle:checked ~ .wm-flip-inner .wm-flip-face--front {
  pointer-events: none;
}

/* The control that turns the card is a label, so the checkbox itself is only
   there to hold the state. Hidden from sight, kept for the keyboard and for a
   screen reader, and its focus ring drawn on the label instead. */
.wm-flip-toggle {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.wm-flip-toggle:focus-visible ~ .wm-flip-inner .wm-flip-link {
  outline: var(--wm-focus-ring-width) solid var(--wm-focus-ring-color);
  outline-offset: var(--wm-focus-ring-offset);
}

@media (prefers-reduced-motion: reduce) {
  .wm-flip-inner {
    transition: none;
  }
}


/* ── The licence plate face.
      Yellow face, dark edge, blue EU band carrying NL, characters spaced the
      way they are stamped. A person recognises a plate before reading the label
      on it, which is the whole reason for borrowing the shape. ── */
.wm-plate {
  background: var(--wm-plate-face);
  border: 2px solid var(--wm-plate-edge);
  border-radius: var(--wm-radius-box);
}

.wm-plate-band {
  background: var(--wm-plate-band);
  color: var(--wm-plate-band-content);
}

.wm-plate-input {
  background: transparent;
  border: 0;
  color: var(--wm-plate-edge);
  letter-spacing: 0.25em;
  text-transform: uppercase;
}

.wm-plate-input::placeholder {
  color: var(--wm-plate-edge);
  opacity: 0.45;
  letter-spacing: 0.15em;
}

.wm-plate:has(.input:focus) {
  outline-color: var(--wm-plate-edge);
}


/* ── The customer note.
      `wm-note` is the sheet, pale blue with a brand blue line along the top.
      `wm-note-lines` rules the element holding the writing: a repeating
      background with hard stops, lines rather than a fade, at the text's own
      line height so each line of writing sits on one. ── */
.wm-note {
  display: block;
  background: var(--wm-note-face);
  color: var(--wm-note-content);
  border: 1px solid var(--wm-note-edge);
  border-top: 2px solid var(--wm-note-top);
  padding: 0.5rem 0.75rem 0.75rem;
}

.wm-note-lines {
  display: block;
  background: repeating-linear-gradient(
    to bottom,
    transparent 0,
    transparent calc(var(--wm-note-rule-gap) - 1px),
    var(--wm-note-rule) calc(var(--wm-note-rule-gap) - 1px),
    var(--wm-note-rule) var(--wm-note-rule-gap)
  );
}

/* Every line of writing on the rule spacing, whatever size its text is. */
.wm-note-lines,
.wm-note-lines * {
  line-height: var(--wm-note-rule-gap);
}

.wm-note-meta {
  color: var(--wm-note-meta);
}

/* ── The deck of notes.
      A plain list until note_deck.js marks it ready, then one row of cards it
      places itself, overlapping, with room above for a lifted card. ── */
.wm-note-deck {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.wm-note-deck[data-deck-ready] {
  display: block;
  position: relative;
  height: calc(var(--wm-note-lift) + var(--wm-note-card-height));
  overflow-x: auto;
  overflow-y: hidden;
}

.wm-note-deck[data-deck-ready] > [data-note-card] {
  position: absolute;
  top: var(--wm-note-lift);
  left: 0;
  width: var(--wm-note-card-width);
}

.wm-note-deck[data-deck-ready] .wm-note-card {
  height: var(--wm-note-card-height);
  overflow: hidden;
}

.wm-note-deck[data-deck-ready] .wm-note-card .wm-note-lines {
  height: 100%;
}

.wm-note-deck[data-deck-ready] .wm-note-excerpt {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 5;
  overflow: hidden;
}

/* A covered card shows its day on the strip that is visible, not half a sentence. */
.wm-note-deck[data-deck-ready] > [data-note-card]:not(:last-child):not([data-lifted]) .wm-note-excerpt {
  visibility: hidden;
}

/* Writing on the sheet: a clear, borderless textarea over the rules, its focus
   shown by the whole sheet taking the brand blue edge. */
.wm-note-input {
  display: block;
  width: 100%;
  min-height: calc(var(--wm-note-rule-gap) * 4);
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  resize: none;
  overflow: hidden;
}

.wm-note-input:focus,
.wm-note-input:focus-visible {
  outline: none;
}

.wm-note-input::placeholder {
  color: var(--wm-note-content);
  opacity: 0.45;
}

.wm-note:focus-within {
  border-color: var(--wm-note-top);
}

/* The open note: the same sheet, larger, and moved by the script alone. */
.wm-note-modal {
  --wm-note-rule-gap: 1.75rem;
  transition: none;
  max-width: 32rem;
  padding: 1.25rem 1.5rem 1.75rem;
}
