/* ==========================================================================
   game-console-ui-theme / components
   --------------------------------------------------------------------------
   Five components for the content that lives INSIDE a view: button, panel,
   badge, field, list. The tiles, the rows, the focus ring and the hint bar
   are not here; they are the shell, and they live in shell/shell.css. That
   split is the point of this theme: these components would work on an
   ordinary page, and the shell would work around different components.

   Every value here is a var() reference to tokens.css. There is not one
   colour literal in this file, which is what makes retheming a matter of
   editing one file rather than auditing five.

   The class names follow the shadcn shape: one base class carrying the
   structure, one variant class carrying the intent (tv-btn + tv-btn-primary).

   WHAT DISTANCE CHANGES. These are the same five components the rest of the
   collection ships, sized for a different room. Controls have a 56px
   minimum rather than 44px, because the 44px floor is a thumb on glass and
   this register is driven from a couch. Badge text is 15px, the theme's
   floor, rather than the 12px a desk register can afford. The semantic
   fills are deep and the text on them is full-strength light ink, which is
   the dark-ground inversion of the usual pale-fill convention.

   Requires tokens.css to be loaded first. Load it explicitly rather than
   letting this file pull it in, so the dependency is visible at the call
   site:

     <link rel="stylesheet" href="tokens/tokens.css" />
     <link rel="stylesheet" href="components/components.css" />
   ========================================================================== */

/* --------------------------------------------------------------------------
   Shared focus. The same ring the shell draws on a tile, on everything
   interactive inside a view. One focus treatment for the whole theme is the
   point: a reader who has learned what focus looks like on the board should
   not have to learn it again two screens in.
   -------------------------------------------------------------------------- */
.tv-btn:focus-visible,
.tv-input:focus-visible,
.tv-focusable:focus-visible {
  outline: var(--tv-ring-width) solid var(--tv-ring);
  outline-offset: var(--tv-ring-offset);
}

/* ==========================================================================
   BUTTON
   Variants: tv-btn-primary, tv-btn-default, tv-btn-ghost, tv-btn-destructive
   Sizes:    tv-btn-sm, (base)
   ========================================================================== */
.tv-btn {
  /* 56px minimum on both axes. The 44px touch floor is the smaller claim;
     this register is read and driven from across a room. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--tv-space-2);
  min-height: var(--tv-btn-min);
  padding: var(--tv-space-3) var(--tv-space-8);

  font-family: var(--tv-font-sans);
  font-size: var(--tv-text-body);
  font-weight: var(--tv-weight-medium);
  line-height: var(--tv-leading-snug);
  text-decoration: none;
  white-space: nowrap;

  color: var(--tv-ink);
  background: var(--tv-surface);
  border: var(--tv-border-width) solid var(--tv-border-strong);
  border-radius: var(--tv-radius);
  cursor: pointer;

  transition: background-color var(--tv-duration) var(--tv-ease);
}

.tv-btn:disabled,
.tv-btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
}

.tv-btn-primary {
  background: var(--tv-accent);
  color: var(--tv-accent-ink);
  border-color: var(--tv-accent);
}

.tv-btn-default:hover {
  background: var(--tv-surface-raised);
}

.tv-btn-destructive {
  background: var(--tv-destructive);
  color: var(--tv-destructive-ink);
  border-color: var(--tv-destructive);
}

/* The ghost sits flat until hovered, which is how the reader learns it was
   a button all along. */
.tv-btn-ghost {
  background: transparent;
  border-color: transparent;
}

.tv-btn-ghost:hover {
  background: var(--tv-surface);
}

.tv-btn-sm {
  min-height: var(--tv-control-min);
  padding: var(--tv-space-2) var(--tv-space-4);
  font-size: var(--tv-text-sm);
}

/* ==========================================================================
   PANEL
   A surface inside a view. Variants: (base), tv-panel-raised.
   Parts: tv-panel-title, tv-panel-body.
   ========================================================================== */
.tv-panel {
  display: flex;
  flex-direction: column;
  gap: var(--tv-space-2);
  padding: var(--tv-space-6);

  background: var(--tv-surface);
  color: var(--tv-ink);
  border: var(--tv-border-width) solid var(--tv-border);
  border-radius: var(--tv-radius-tile);
}

.tv-panel-raised {
  background: var(--tv-surface-raised);
}

.tv-panel-title {
  margin: 0;
  font-size: var(--tv-text-h3);
  font-weight: var(--tv-weight-bold);
  line-height: var(--tv-leading-snug);
  letter-spacing: var(--tv-tracking-tight);
}

.tv-panel-body {
  margin: 0;
  color: var(--tv-ink-muted);
  font-size: var(--tv-text-body);
  line-height: var(--tv-leading-body);
}

/* ==========================================================================
   BADGE
   Variants: tv-badge-neutral, tv-badge-success, tv-badge-info,
             tv-badge-warning, tv-badge-danger

   The dark-ground inversion: deep fills, light ink. Ink on every fill
   clears AA several times over (success 12.40:1, info 13.70:1, warning
   12.16:1, danger 14.01:1), so meaning lives in the hue and legibility
   never depends on it. Flip this theme to a light register and these four
   are the first thing that breaks; CUSTOMIZE.md Move 1 has the picture.
   ========================================================================== */
.tv-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--tv-space-1);
  padding: var(--tv-space-1) var(--tv-space-3);

  font-family: var(--tv-font-sans);
  font-size: var(--tv-text-xs);
  font-weight: var(--tv-weight-bold);
  line-height: var(--tv-leading-snug);
  letter-spacing: var(--tv-tracking-wide);
  text-transform: uppercase;

  color: var(--tv-ink);
  border: var(--tv-border-width) solid var(--tv-border);
  border-radius: var(--tv-radius-tight);
  white-space: nowrap;
}

.tv-badge-neutral {
  background: var(--tv-surface-raised);
}

.tv-badge-success {
  background: var(--tv-fill-success);
}

.tv-badge-info {
  background: var(--tv-fill-info);
}

.tv-badge-warning {
  background: var(--tv-fill-warning);
}

.tv-badge-danger {
  background: var(--tv-fill-danger);
}

/* ==========================================================================
   FIELD
   Parts:  tv-field-group, tv-label, tv-input, tv-hint-text-sm, tv-error
   States: default, :focus, aria-invalid="true", :disabled

   The base class is tv-field-group rather than tv-field, because tv-field
   is the shell's body class and one name cannot be two things.
   ========================================================================== */
.tv-field-group {
  display: flex;
  flex-direction: column;
  gap: var(--tv-space-2);
}

.tv-label {
  font-family: var(--tv-font-sans);
  font-size: var(--tv-text-sm);
  font-weight: var(--tv-weight-bold);
  color: var(--tv-ink);
}

.tv-input {
  width: 100%;
  min-height: var(--tv-btn-min);
  padding: var(--tv-space-3) var(--tv-space-4);

  font-family: var(--tv-font-sans);
  font-size: var(--tv-text-body);
  color: var(--tv-ink);
  background: var(--tv-surface);

  border: var(--tv-border-width) solid var(--tv-border-strong);
  border-radius: var(--tv-radius);
}

.tv-input::placeholder {
  color: var(--tv-ink-muted);
}

.tv-input:focus {
  outline: var(--tv-ring-width) solid var(--tv-ring);
  outline-offset: var(--tv-ring-offset);
}

.tv-input[aria-invalid="true"] {
  border-color: var(--tv-destructive);
}

.tv-input:disabled {
  background: var(--tv-surface-raised);
  color: var(--tv-ink-muted);
  cursor: not-allowed;
}

.tv-note {
  font-size: var(--tv-text-sm);
  color: var(--tv-ink-muted);
}

.tv-error {
  font-size: var(--tv-text-sm);
  font-weight: var(--tv-weight-bold);
  color: var(--tv-destructive);
}

/* Textarea inherits the input treatment. */
.tv-input:where(textarea) {
  min-height: var(--tv-textarea-min);
  resize: vertical;
}

/* ==========================================================================
   LIST
   Rows with a name, an optional description, and a value at the end. The
   shape a catalogue product's views are full of.
   Parts: tv-list, tv-list-row, tv-list-name, tv-list-desc, tv-list-end
   ========================================================================== */
.tv-list {
  margin: 0;
  padding: 0;
  list-style: none;
  border: var(--tv-border-width) solid var(--tv-border);
  border-radius: var(--tv-radius-tile);
  background: var(--tv-surface);
  overflow: hidden;
}

.tv-list-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--tv-space-4);
  padding: var(--tv-space-4) var(--tv-space-6);
}

/* The hairline between rows is the one place in this theme where losing the
   border costs something: two rows on the same fill with no divider read as
   one paragraph. Everywhere else a border sits next to a surface change and
   is tidiness only. */
.tv-list-row + .tv-list-row {
  border-top: var(--tv-border-width) solid var(--tv-border);
}

.tv-list-name {
  font-weight: var(--tv-weight-medium);
  color: var(--tv-ink);
}

.tv-list-desc {
  display: block;
  font-size: var(--tv-text-sm);
  color: var(--tv-ink-muted);
}

.tv-list-end {
  flex: none;
  font-family: var(--tv-font-mono);
  font-size: var(--tv-text-sm);
  color: var(--tv-ink-muted);
}

/* A keyboard hint chip, for the shell's keyboard story. Real text in a kbd
   element, not an image of a key, and never a letter in a circle. */
.tv-kbd {
  display: inline-block;
  padding: 0 var(--tv-space-2);
  font-family: var(--tv-font-mono);
  font-size: var(--tv-text-xs);
  line-height: 1.9;
  color: var(--tv-ink);
  background: var(--tv-surface-raised);
  border: var(--tv-border-width) solid var(--tv-border-strong);
  border-bottom-width: 2px;
  border-radius: var(--tv-radius-tight);
}
