/* Form controls — the three treatments in HANSHI.md (Forms).

   CONTROLS ARE CLASSES, NEVER BARE ELEMENTS. Painting a bare `button`,
   `select` or `label` in this layer beats every rule of the embedded Lexxy
   editor by layer order alone — a later layer wins over any specificity —
   which is exactly how the whole toolbar once rendered as black squares
   (de693f3). Fizzy and Basecamp 5 both keep element rules in `reset` only.
   An element selector is fine when it is scoped inside a component class. */
@layer components {
  /* ——— form treatment a · the written line (dialogs & settings) ———
     the label sits above in tracked caps and a hairline baseline is the whole
     affordance — no box, because a box says "field" a second time.
     everything here is a class: a bare `input` or `select` rule in this layer
     outranks every :where() selector lexxy ships, whatever the specificity.

     THE CARET IS THE WHOLE FOCUS MARK (user, 2026-09-30, HANSHI ##
     Deviations). Hover changes nothing, focus changes nothing — no ring, no
     shu baseline, no warmed label. What the line says at rest is all it ever
     says; the caret, or the list a select opens, is what says "you are
     here". The shu ring stays on buttons and links, where nothing blinks. */
  .label {
    display: block;
    margin-block-end: var(--block-space-half);
  }

  .input {
    inline-size: 100%;
    padding-block: var(--block-space-half);
    padding-inline: 0;
    color: var(--color-ink);
    /* The caret is the whole focus mark, so it is the one thing drawn in
       shu: a browser lets us pick its colour and not its width, and the
       accent is what makes a 1px line unmistakable (user, 2026-10-06). The
       composer says the same in lexxy.css. */
    caret-color: var(--color-accent);
    background-color: transparent;
    border: none;
    border-block-end: var(--border);
    border-radius: 0;

    &::placeholder {
      color: var(--color-ink-faint);
    }

    /* The ring inherited from `base` gives way to nothing at all. Every
       field this class dresses — selects and dates included — matches
       :focus-visible on a mouse click in Chromium, so a ring here is an
       active border on every click, not a keyboard courtesy; the cost is
       that a keyboard user tabbing onto a select gets no mark until it
       opens. Recorded in HANSHI with the ruling. The one button wearing
       the class (the picker's trigger) keeps its ring: a button only ever
       earns one from the keyboard, and nothing blinks in it. */
    &:focus-visible:not(button) {
      outline: none;
    }
  }

  /* the clear × a search field draws for itself. native means chrome's own
     blue — the one colour in the app nobody chose — and it showed on every
     search field there is: the finding page, the in-room search, the filter
     boxes, every picker's line. repainted rather than removed, because
     clearing a query is worth a control: the same mask, the same ink and the
     same hairline weight every other glyph is painted in (icons.css). */
  .input::-webkit-search-cancel-button {
    appearance: none;
    inline-size: 0.8em;
    block-size: 0.8em;
    background-color: var(--color-ink-faint);
    mask-image: url("/assets/x-8fdf8036.svg");
    mask-position: center;
    mask-repeat: no-repeat;
    mask-size: contain;
    cursor: pointer;

    &:hover {
      background-color: var(--color-ink);
    }
  }

  /* selects keep the written line; the chevron is their only extra mark. it is
     two gradients rather than an svg data uri because a data uri cannot read a
     custom property — a baked stroke colour would not re-ink in dark mode.
     inline-size stays auto: a select that filled its container would push the
     apply button of the filter bar onto a line of its own. */
  .input--select {
    inline-size: auto;
    max-inline-size: 100%;
    appearance: none;
    cursor: pointer;
    padding-inline-end: calc(0.64em + var(--inline-space));
    background-image:
      linear-gradient(45deg, transparent 44%, var(--color-ink-faint) 44% 56%, transparent 56%),
      linear-gradient(135deg, transparent 44%, var(--color-ink-faint) 44% 56%, transparent 56%);
    background-repeat: no-repeat;
    background-position: right 0.32em center, right center;
    background-size: 0.32em 0.32em;
  }

  /* the one control left native: the ua draws the tick and picks its colour
     against accent-color, so it stays legible in both themes where a baked
     svg tick would vanish on dark paper's near-white ink. */
  .checkbox {
    inline-size: 1rem;
    block-size: 1rem;
    accent-color: var(--color-ink);
    cursor: pointer;
  }

  /* the radio, drawn rather than tinted — BC's mark: a hairline circle whose
     border thickens to ink when chosen, the white centre doing the work a
     dot would. accent-color can only recolour the ua's dot, and the dot is
     the wrong mark, so this one leaves native (2026-08-12). The border IS
     the state: nothing appears or disappears, so nothing transitions. */
  .radio {
    appearance: none;
    inline-size: 1.125rem;
    block-size: 1.125rem;
    flex-shrink: 0;
    border: 1.5px solid var(--color-line);
    border-radius: 50%;
    background-color: transparent;
    vertical-align: text-bottom;
    cursor: pointer;

    &:hover {
      border-color: var(--color-ink-faint);
    }

    &:checked {
      border-width: 0.34rem;
      border-color: var(--color-ink);
    }

    /* the ring needs air or it reads as part of the mark */
    &:focus-visible {
      outline-offset: 2px;
    }
  }

  /* ——— boxed, the recorded deviation (HANSHI ## Deviations, 2026-08-12) ———
     "No boxes" holds wherever a field has a label of its own. The to-do
     composer's Due on / Repeat rows are BC's sentence-shaped kind — a date or
     a select sitting mid-sentence, where a written line has nothing to
     underline and a bare value has no affordance at all. Those controls wear
     the box: hairline frame, float radius, content-sized. The frame is the
     whole affordance, at rest and in focus alike — the caret rule above. */
  .input--boxed {
    /* The box costs no height. A written-line control pays for its rhythm in
       padding alone; a boxed one would add its two edges on top and stand
       taller than the plain rows beside it — which is what a "Due on" block
       of radio rows and date boxes made obvious (user, 2026-08-13). So the
       frame comes out of the padding, and the padding halves again: the type's
       own leading is the breathing room a box does not need to restate. */
    inline-size: auto;
    padding-block: calc(var(--block-space-half) / 2 - var(--border-size));
    padding-inline: var(--inline-space);
    border: var(--border);
    border-radius: var(--border-radius-float);
  }

  /* the boxed select pairs the chevron — BC's ⬍ — the same two-gradient
     trick as .input--select, mirrored once for the upper caret, because a
     data uri still cannot read a custom property. */
  .input--select.input--boxed {
    padding-inline-end: calc(0.64em + var(--inline-space) * 2);
    background-image:
      linear-gradient(135deg, transparent 44%, var(--color-ink-faint) 44% 56%, transparent 56%),
      linear-gradient(45deg, transparent 44%, var(--color-ink-faint) 44% 56%, transparent 56%),
      linear-gradient(45deg, transparent 44%, var(--color-ink-faint) 44% 56%, transparent 56%),
      linear-gradient(135deg, transparent 44%, var(--color-ink-faint) 44% 56%, transparent 56%);
    background-position:
      right calc(var(--inline-space) + 0.26em) top calc(50% - 0.2em),
      right var(--inline-space) top calc(50% - 0.2em),
      right calc(var(--inline-space) + 0.26em) top calc(50% + 0.2em),
      right var(--inline-space) top calc(50% + 0.2em);
    background-size: 0.32em 0.32em;
  }

  /* a short set of radio choices: each one names itself and, underneath, what
     picking it will do. scoped to .choice — never a bare input rule. */
  .choices {
    display: flex;
    flex-direction: column;
    gap: var(--block-space-half);
  }

  .choice {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: baseline;
    gap: var(--inline-space);

    label {
      display: flex;
      flex-direction: column;
      cursor: pointer;
    }

    /* BC's sentence-shaped choice — "At… [16:30]": a choice whose label
       carries a boxed control flows as one line, the words and the box
       side by side. Asked of the label's own child, so a stacked row of
       chips under the words (the day picker) keeps the column. */
    label:has(> .input--boxed) {
      flex-direction: row;
      align-items: center;
      gap: var(--inline-space);
    }
  }

  /* ——— form treatment c · bare voice ———
     the sign-in code is the only thing on its screen, so it is set at display
     size in mono and the written line underneath carries the caret. */
  .code-input {
    font-family: var(--font-mono);
    font-size: var(--text-x-large);
    text-align: center;
    letter-spacing: 0.3em;
    text-transform: uppercase;
  }

  /* Chrome paints an autofilled field in its own baked light blue — fatal on
     dark paper. The 1000px inset shadow is the one paint that wins (chalet's
     defence, verbatim). */
  .input:is(:-webkit-autofill, :-webkit-autofill:hover, :-webkit-autofill:focus) {
    -webkit-text-fill-color: var(--color-ink);
    -webkit-box-shadow: 0 0 0 1000px var(--color-canvas) inset;
    caret-color: var(--color-accent);
  }
}
