/* ─────────────────────────────────────────────────────────────────────────────
   THE CONTROL CONTRACT — what every control inherits, in one file.

   visual-design-system.md calls this "the button, and what every control
   inherits", and until now it was inherited by being retyped: ten primitives
   each wrote the focus ring, ten each wrote the disabled colours, and the
   landing page wrote none of them at all. Three of its buttons had no
   :focus-visible whatsoever, so a keyboard user could not see where they were.

   Two documents read this file, the same way they read tokens.css:

     the product      app/globals.css imports it. shadcn's primitives already
                      carry data-slot on every part, so these rules reach
                      inside components no file here would otherwise touch.
     the landing page public/waku/*.html link it, and mark their controls with
                      the same data-slot. An iframe is its own document.

   Why data-slot and not a class: it is the one hook that survives both worlds.
   The product's controls are Tailwind classes generated by cva; the landing
   page's are hand-written CSS. data-slot is in the DOM either way, it says
   what a thing IS rather than how it is painted, and it was already there.

   Why this file is not in a cascade layer: Tailwind puts its utilities in
   @layer utilities, and an unlayered rule beats every layered one regardless
   of specificity. That is what lets one declaration here own a state across
   both documents instead of losing to whichever utility happened to be
   generated. It also means a level's own colours must NOT be repeated here —
   see the split below.

   ── the split ────────────────────────────────────────────────────────────
   Here            what is true of every control, at every level: the shape,
                   the one duration, the focus ring, what pressed does, what
                   disabled looks like.
   Not here        which level a control is. Primary's paper ground, secondary's
                   lighter rule, destructive's --bad label, and the border
                   colour each one moves to on hover. Those stay with the
                   component — cva variants in the product, .waitlist-btn and
                   .chips in the landing page — because they answer "which of
                   the four is this", which is a question about the screen.
   ───────────────────────────────────────────────────────────────────────────── */

/* The list is written out rather than matched with a wildcard. [data-slot]
   exists on layout parts too — dialog-header, card-content, table-row — and a
   focus ring on a table row is not a thing anyone decided. */
[data-slot="button"],
[data-slot="input"],
[data-slot="textarea"],
[data-slot="select-trigger"],
[data-slot="checkbox"],
[data-slot="toggle"],
[data-slot="toggle-group-item"],
[data-slot="tabs-trigger"] {
  border-radius: var(--radius);
}

/* The exempt shapes, and the exemption is a token so that nothing later
   "fixes" it. A circle means one choice out of several and a pill reads as
   something that slides; flattening either to 2px spends the only cue that
   says which rules apply. The radius governs rectangles, and these are not
   rectangles. */
[data-slot="radio-group-item"] { border-radius: var(--shape-circle); }
[data-slot="switch"] { border-radius: var(--shape-pill); }
[data-slot="avatar"] { border-radius: var(--shape-circle); }

/* Badge joined them on 2026-08-27. A chip is a value lifted out of a sentence
   and made pick-up-able, and round ends are what say so — at 2px a 20px-tall
   chip reads as a box.

   It has to be HERE and not in badge.tsx, and the first attempt got that
   wrong. A `rounded-[var(--shape-chip)]` in the cva is a @layer utilities
   rule, and the list above is unlayered, so the list won and the chips stayed
   square while every other signal — the token, the generated utility, the
   class on the element — said they were round. That is this file's own
   opening paragraph, discovered the slow way. */
[data-slot="badge"] { border-radius: var(--shape-chip); }

/* One duration and one curve for everything that moves. Only the three
   properties a control actually changes — transition-all would animate layout
   and make a reflow look like a decision. */
[data-slot="button"],
[data-slot="input"],
[data-slot="textarea"],
[data-slot="select-trigger"],
[data-slot="checkbox"],
[data-slot="radio-group-item"],
[data-slot="switch"],
[data-slot="toggle"],
[data-slot="toggle-group-item"],
[data-slot="tabs-trigger"],
[data-slot="badge"] {
  transition-property: color, background-color, border-color;
  transition-duration: var(--motion-fast);
  transition-timing-function: var(--motion-ease);
}

/* ── focus ────────────────────────────────────────────────────────────────
   The loudest state, on purpose. It is the only one a keyboard user has and
   the only one that fails silently when it is too quiet: a hover that goes
   unnoticed costs nothing, because the mouse already says where you are.

   The consequence is stated rather than discovered — a focused control already
   looks hovered, so hovering it adds nothing. Focus wins that collision rather
   than avoiding it.

   The ring is --accent at every level, destructive included, so it is one
   thing to learn. An outline takes no layout, so a focused control never
   nudges its neighbours. :focus-visible keeps it off the mouse. */
[data-slot="button"]:focus-visible,
[data-slot="input"]:focus-visible,
[data-slot="textarea"]:focus-visible,
[data-slot="select-trigger"]:focus-visible,
[data-slot="checkbox"]:focus-visible,
[data-slot="radio-group-item"]:focus-visible,
[data-slot="switch"]:focus-visible,
[data-slot="toggle"]:focus-visible,
[data-slot="toggle-group-item"]:focus-visible,
[data-slot="tabs-trigger"]:focus-visible,
[data-slot="badge"]:focus-visible {
  outline: var(--focus-ring-width) solid var(--accent);
  outline-offset: var(--focus-ring-offset);
}

/* ── pressed ──────────────────────────────────────────────────────────────
   The ground sinks, because a control cannot press down. Nothing in this
   system casts a shadow, so there is no surface for it to move toward; it gets
   heavier instead. The border stays where hover put it.

   --surface-sunk is named apart from --surface-raised for exactly this: a
   control described as "raised" while being pressed is the wrong word in the
   one place someone will go looking. */
[data-slot="button"]:active:not(:disabled),
[data-slot="toggle"]:active:not(:disabled),
[data-slot="toggle-group-item"]:active:not(:disabled),
[data-slot="tabs-trigger"]:active:not(:disabled) {
  background-color: var(--surface-sunk);
}

/* ── disabled ─────────────────────────────────────────────────────────────
   Colour, not opacity. Opacity fades the border, the label and the ground
   together, which dropped a disabled field's edge to about 1.5:1 — under the
   3:1 that same edge had just been raised to clear. Naming the colours puts
   every part of a disabled control on a value the contrast floor knows about,
   and a disabled control still reads as a control.

   [data-disabled] and aria-disabled are here because Radix marks its own
   parts that way rather than with the DOM attribute. */
[data-slot="button"]:disabled,
[data-slot="input"]:disabled,
[data-slot="textarea"]:disabled,
[data-slot="select-trigger"]:disabled,
[data-slot="checkbox"]:disabled,
[data-slot="radio-group-item"]:disabled,
[data-slot="switch"]:disabled,
[data-slot="toggle"]:disabled,
[data-slot="toggle-group-item"]:disabled,
[data-slot="tabs-trigger"]:disabled,
[data-slot="button"][data-disabled],
[data-slot="tabs-trigger"][aria-disabled="true"] {
  color: var(--disabled-fg);
  border-color: var(--disabled-edge);
}

/* Not here: pointer-events. "Nothing responds" is what :disabled already does
   natively, and switching pointer events off as well takes the cursor
   feedback with it — a disabled field stops being able to say
   `cursor: not-allowed`. Whether a given control also swallows the pointer is
   left to the control. */

@media (prefers-reduced-motion: reduce) {
  [data-slot] { transition-duration: 0s !important; }
}
