/* Reusable card + button + form-control primitives. Document new patterns in
 * DESIGN.md before adding them here. */

@layer components {
  /* Flat card: a hairline border alone gives enough separation on white —
     no shadow, so a page of cards reads as calm, not a stack of raised boxes.
     Floating surfaces (combobox/modal/toast) keep --shadow-1 for real lift. */
  .card {
    background: var(--c-surface);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-md);
    padding: var(--sp-5);
    margin-bottom: var(--sp-4);
  }

  /* Neutral hint / empty-state / sub-label text — the app-wide muted, small
     line (empty states, form labels, chart figure strips, table sub-heads).
     Declared BEFORE .error-banner so `.note-meta.error-banner` (see
     partials/generic/error-note.html) still takes the danger colour via source order.
     Context rules only tweak margin (.form/.admin kill it) or shrink further
     (.mz-sheet → --fs-xs); they no longer re-declare colour/size. */
  .note-meta { font-size: var(--fs-sm); color: var(--c-text-muted); }

  /* Error/alert banner — the one danger-tinted card state (role="alert"), shared
     by every view. Replaces the per-entity copies .plan-error/.cf-error/.ma-error/
     .admin-error. Always combined with .card, so it just recolours the border. */
  .error-banner { color: var(--c-danger); border-color: var(--c-danger); }

  /* Warning note — the amber-tinted sibling of .error-banner, for a *precondition*
     that blocks a feature until the user acts ("Buchen ist noch nicht
     konfiguriert", kein Bexio-Benutzer verknüpft, Integration nicht eingerichtet).
     Distinct from .note-meta (a neutral empty-state / hint) and from .error-banner
     (a genuine failure). Carries role="alert" so a screen reader announces it, and
     leads with an <x-icon name="triangle-alert"> — bind x-text on the trailing
     <span>, never on the <p> itself (that would wipe the icon). See DESIGN.md →
     Warning note. */
  .warn-note {
    display: flex;
    align-items: flex-start;
    gap: var(--sp-2);
    margin: var(--sp-3) 0;
    padding: var(--sp-2) var(--sp-3);
    border: 1px solid var(--c-warn);
    border-radius: var(--radius-sm);
    background: var(--c-warn-tint);
    color: var(--c-warn);
    font-size: var(--fs-sm);
  }
  .warn-note > x-icon { margin-top: 0.1em; } /* optical align glyph to first line */
  /* Optionaler Nachweis-Knopf am Ende einer Warnung ("welche Rechnungen sind
     das?" in Debitoren). Hängt rechts am Rand und schrumpft nicht mit — der
     Warntext daneben umbricht stattdessen, wie ohne Knopf auch. */
  .warn-note__action { flex: none; margin-left: auto; align-self: flex-start; }

  .btn {
    /* Every button is exactly one control tall (--control-h), centred by
       inline-flex, so it can never render taller than the field/combobox beside
       it — the app-wide "buttons ≤ neighbours" rule. line-height:1 keeps the
       label from re-inflating the box; min-height (not height) lets a rare
       two-line label grow instead of clipping. Horizontal padding still shapes
       the width. The dense variants below (--small, --icon) opt out.
       See DESIGN.md → Control height. */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* The one icon↔label distance. A flex container drops whitespace-only text
       nodes, so `<x-icon></x-icon><span>Label</span>` renders glued together
       without a gap — which is why half a dozen views had each re-spelled their
       own on a .btn-based class while the rest simply looked wrong. Owned here
       once; --icon (single child) is unaffected, --small tightens it below. */
    gap: var(--sp-2);
    min-height: var(--control-h);
    font: inherit;
    /* After `font: inherit` — the shorthand resets line-height, so it must be
       set afterwards or the inherited 1.55 re-inflates the box past --control-h. */
    line-height: 1;
    font-weight: var(--fw-medium);
    padding: var(--sp-2) var(--sp-3);
    border-radius: var(--radius-sm);
    border: 1px solid var(--c-border);
    background: var(--c-surface-2);
    color: var(--c-text);
    cursor: pointer;
    /* Ein `<a class="btn">` ist ein Knopf, kein Link im Fliesstext: ohne das hier
       trägt genau er als einziger einen Unterstrich und liest sich neben seinen
       Nachbarn wie ein Fremdkörper. Betrifft die Datei-/Deep-Link-Knöpfe
       (Prüfblatt Briefpapier, „in bexio öffnen", PDF-Viewer). */
    text-decoration: none;
    transition: background var(--motion-fast) var(--ease-out);
  }
  .btn:hover:not(:disabled) { background: var(--c-border); }

  /* A disabled button must LOOK disabled — .btn--icon says so below, the text
     buttons never did. `.btn--primary:disabled` therefore rendered in full accent
     blue at full opacity: "Als Entwurf nach bexio" sat there as the loudest thing
     on the phone screen while being unclickable, with the amber note underneath
     explaining why. Same dimming as the icon variant, so both read alike. */
  .btn:disabled { opacity: 0.5; cursor: default; }

  /* Compact size for dense rows/cells (moved here from entities/cashflow.css so
     it sits with the other .btn primitives — it is used across views). Opts out
     of the shared control height (min-height:0) and stays as small as its
     padding — that is the whole point of the dense variant. */
  .btn--small {
    min-height: 0; padding: 2px var(--sp-2); font-size: var(--fs-sm);
    gap: var(--sp-1); /* dense box → tighter icon↔label distance than the full-size .btn */
  }

  .btn--primary {
    background: var(--c-accent);
    color: var(--c-accent-text);
    border-color: transparent;
  }
  /* `:not(:disabled)` is not decoration here, it is the specificity that makes
     this rule apply at all: the generic `.btn:hover:not(:disabled)` above scores
     (0,3,0), so a bare `.btn--primary:hover` (0,2,0) LOST to it — hovering a
     primary button swapped the accent fill for the grey --c-border while the
     white --c-accent-text stayed, i.e. white on light grey (~1.2:1). Matching the
     selector shape of the --danger sibling puts both at (0,3,0) and lets source
     order decide. Keep the `:not(:disabled)` if this selector is ever touched. */
  .btn--primary:hover:not(:disabled) { background: var(--c-accent-hover); }

  /* Filled destructive button — the confirm action in a delete/ignore dialog,
     where a plain .btn wouldn't signal irreversibility. Text sibling of
     .btn--icon-danger. */
  .btn--danger {
    background: var(--c-danger);
    color: var(--c-danger-text);
    border-color: transparent;
  }
  .btn--danger:hover:not(:disabled) { background: var(--c-danger-hover); }

  /* Icon-only button — square, equalized padding, centers the <x-icon>. The
     glyph inherits font-size (1em); the host owns the name via title/aria-label.
     Space-saving stand-in for a text button in tables and dense rows. */
  .btn--icon {
    /* Square at the shared control height — inherits min-height + inline-flex
       centring from .btn; min-width squares it off so it lines up with the
       text buttons and fields around it. */
    min-width: var(--control-h);
    padding: var(--sp-2);
  }
  .btn--icon:hover:not(:disabled) { color: var(--c-accent); }
  .btn--icon:disabled { opacity: 0.5; cursor: default; }
  .btn--icon-danger:hover:not(:disabled) {
    color: var(--c-danger); border-color: var(--c-danger);
  }

  /* dot-brand focus ring: cyan border + soft glow.
   *
   * Der KONTRAST-Träger ist die Border, nicht der Schein: --c-accent-glow ist
   * rgba(0,150,200,0.12) und liegt über Weiss bei ~1.15:1 — als Indikator allein
   * unsichtbar. Der Wechsel der Border auf --c-accent trägt 3.39:1 gegen die
   * Fläche und erfüllt damit WCAG 1.4.11. Wer den Schein je als alleinigen
   * Indikator einsetzt, muss ihn zwangsläufig bis fast auf die volle Akzentfarbe
   * hochziehen: eine 12-%-Tönung KANN 3:1 rechnerisch nicht erreichen.
   *
   * `outline: 2px solid transparent` statt `outline: none`: im
   * Forced-Colors-Modus (Windows-Kontrastdesign) ersetzt das System sowohl
   * border-color als auch box-shadow durch eigene Farben — beide Hälften des
   * Rings fielen dort aus und der Fokus war schlicht unsichtbar. Ein
   * transparentes Outline ist normal nicht zu sehen, wird vom System aber
   * eingefärbt und bringt den Fokus dort zurück. Kostet visuell nichts. */
  .btn:focus-visible,
  .field:focus {
    outline: 2px solid transparent;
    outline-offset: 2px;
    border-color: var(--c-accent);
    box-shadow: 0 0 0 3px var(--c-accent-glow);
  }

  .field {
    display: block;
    width: 100%;
    font: inherit;
    /* Same shared control height as .btn — line-height:1 pulls the natural box
       below --control-h so the min-height governs and every single-line input,
       month picker and combobox stands exactly one control tall next to its
       button. The browser vertically centres the text in the taller box. */
    line-height: 1;
    min-height: var(--control-h);
    padding: var(--sp-2) var(--sp-3);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-sm);
    background: var(--c-surface);
    color: var(--c-text);
    margin-bottom: var(--sp-3);
  }
  /* Multi-line inputs are the exception: taller box, and restore the readable
     base line-height that .field flattened to 1 for the single-line case. */
  textarea.field { min-height: 5rem; line-height: var(--lh-base); resize: vertical; }

  /* Native checkbox / radio — brand-tint the mark (accent-color keeps the real
     control, so keyboard + screen-reader behaviour stays intact) and give it a
     legible, consistent box; the browser default is small and grey. margin:0 so
     it sits flush in the form grid / table cell it lives in. */
  input[type="checkbox"],
  input[type="radio"] {
    accent-color: var(--c-accent);
    width: 1.05rem;
    height: 1.05rem;
    margin: 0;
    cursor: pointer;
  }
  input[type="checkbox"]:disabled,
  input[type="radio"]:disabled { cursor: default; }

  /* x-collapse wrapper: keep it a block formatting context so the inner card's
     margin-bottom stays inside the animated height. Without this the plugin
     measures a start height that excludes the child margin, then snaps ~16px
     when overflow:hidden re-includes it. (The wrapper itself carries no padding/
     border/margin — see DESIGN.md "x-collapse".) The plugin toggles inline
     display, which correctly overrides this when hidden. */
  [x-collapse] { display: flow-root; }

  .row { display: flex; gap: var(--sp-2); align-items: center; }
  /* Horizontal form controls line up on a shared baseline — the field's
     block-flow bottom margin would otherwise offset it against sibling buttons. */
  .row > .field { margin-bottom: 0; }

  /* Native date/month: WebKit (iOS Safari — und damit auch Chrome/Firefox auf
     iOS, die dieselbe Engine fahren) rendert den Wert NICHT als zentrierten
     Text, sondern in einem eigenen ::-webkit-date-and-time-value mit eigenem
     Default-Rand, das oben in der Box sitzt und rechtsbündig ausrichtet. In
     einem Feld, dessen Höhe von --control-h kommt (44px am Telefon) statt vom
     Inhalt (~32px), heisst das: das Datum klebt oben und hängt am rechten Rand
     — der Rest der Zeile (Picker-Glyph, Nachbar-Button) steht mittig, das
     Datum nicht. Die Box selbst zum Flex-Container machen, dann zentriert
     `align-items` den Wert wie bei jedem anderen Feld; Rand und Ausrichtung
     des Pseudo-Elements zurücksetzen. Blink/Gecko auf dem Desktop kennen das
     Pseudo-Element nicht und legen ein einzeiliges Control als Flex-Container
     identisch aus — die Regel ist dort wirkungslos, aber nicht schädlich. */
  input.field[type="date"],
  input.field[type="month"] {
    display: flex;
    align-items: center;
  }
  input.field[type="date"]::-webkit-date-and-time-value,
  input.field[type="month"]::-webkit-date-and-time-value {
    margin: 0;
    text-align: left;
  }

  /* ── Touch (any width) ────────────────────────────────────────────────────
     Two rules that follow the POINTER, not the viewport. Both used to sit in
     the ≤640px block below, which meant a tablet — a finger-only device that is
     768–1366 CSS px wide — matched neither. See DESIGN.md → Breakpoints.

     Die Breiten-Bedingung steht daneben, nicht statt ihr — genau wie am
     --control-h-Token (tokens/spacing.css). Der Umzug auf die Zeigerart allein
     gewann das Tablet und verlor dabei das schmale Fenster OHNE Grobzeiger: die
     16px-Untergrenze unten galt vorher für jede Breite ≤640px, danach nur noch
     für Finger-Geräte. `tests/smoke/mobile-touch-targets.spec.js` misst genau
     das (360px, kein Touch) und stand auf 13.6px. Beide Hälften sind nötig. */
  @media (pointer: coarse), (max-width: 640px) {
    /* Safari auto-zooms the page when a focused text control renders below 16px.
       This is a WebKit-on-touch behaviour, not a phone one: iPadOS does it too,
       so on a tablet every tap into a dense grid cell yanked the page to a zoomed
       viewport it never zoomed back out of. The dense grids run at --fs-sm
       (~13.6px), so that was every cell/field input in the app. Floor any
       text-entry control at 1rem (16px); `max(1rem, 1em)` keeps larger inputs
       (e.g. the ⌘K search) at their own size. Checkboxes/radios are exempt (no
       text, no zoom). Lives in @layer components so it wins over the `font:
       inherit` on .field. */
    .field,
    input:not([type="checkbox"]):not([type="radio"]),
    textarea {
      font-size: max(1rem, 1em);
    }

    /* Icon-only buttons drop their fill + frame. --control-h grows to 44px on
       touch (spacing.css) so the tap target is right, but a *filled and
       bordered* 44px square weighs far more than the 17px glyph it carries: a
       toolbar cluster or a period-nav pair reads as grey tiles instead of
       actions. Keep the target, drop the box — the glyph plus the accent hover
       carry the affordance. Text buttons and the .btn--primary/--danger fills
       are unaffected; entities/accounts.css already does exactly this for the
       tree-row actions, and layout/base.css for the header chrome.
       Tied to the same query as the 44px token on purpose: the two must move
       together, or the tablet gets the tile wall this rule exists to prevent. */
    .btn--icon { background: transparent; border-color: transparent; }
  }

  /* ── Phone (≤640px) ───────────────────────────────────────────────────────
     What is left here is genuinely about ROOM, not about fingers. */
  @media (max-width: 640px) {
    /* A native date/month input renders exactly one fixed-format value plus its
       picker glyph — its content width IS the control, there is nothing to fill
       with. Stretched to the full column of a one-column phone form it reads as
       a huge, mostly-empty box (~307px of frame around ~120px of date). Let the
       browser size it to that content instead: --control-h still holds the 44px
       tap target, only the width goes. `fit-content` rather than a guessed rem
       value, which would clip wherever the locale renders a longer format
       (mm/dd/yyyy vs. dd.mm.yyyy). Type selector so it outranks the
       `.form > .field` / `.form__group > .field` width rules in form.css, and
       `flex: 0 0 auto` so a wrapped .form__group line (the admin employment
       range) doesn't hand each half the whole row via its 12rem basis. */
    input.field[type="date"],
    input.field[type="month"] {
      width: fit-content;
      max-width: 100%;
      flex: 0 0 auto;
    }
  }
}
