/* ============================================================================
   Islandr — Component primitives. Requires tokens.css first.
   Lifted from the design handoff bundle (working title there: "Bastion").
   ============================================================================ */

/* ---- Buttons ------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  height: var(--control-h); padding: 0 var(--space-4);
  font-family: var(--font-sans); font-size: var(--text-base); font-weight: var(--weight-medium);
  border-radius: var(--radius-md); border: 1px solid transparent; cursor: pointer;
  transition: background var(--dur) var(--ease-standard), border-color var(--dur) var(--ease-standard), color var(--dur) var(--ease-standard), transform var(--dur-fast) var(--ease-standard);
  white-space: nowrap; user-select: none;
}
.btn:active { transform: translateY(0.5px); }
.btn:disabled, .btn[aria-disabled="true"] { opacity: 0.5; cursor: not-allowed; transform: none; }
.btn .icon { width: 16px; height: 16px; }

.btn-primary { background: var(--accent); color: var(--fg-on-accent); border-color: var(--accent); }
.btn-primary:hover:not(:disabled) { background: var(--accent-hover); border-color: var(--accent-hover); }

.btn-secondary { background: var(--surface); color: var(--fg1); border-color: var(--border-strong); }
.btn-secondary:hover:not(:disabled) { background: var(--surface-2); border-color: var(--neutral-400); }

.btn-ghost { background: transparent; color: var(--fg2); }
.btn-ghost:hover:not(:disabled) { background: var(--surface-3); color: var(--fg1); }

.btn-danger { background: var(--danger-solid); color: #fff; border-color: var(--danger-solid); }
.btn-danger:hover:not(:disabled) { filter: brightness(0.94); }

.btn-sm { height: var(--control-h-sm); padding: 0 var(--space-3); font-size: var(--text-sm); }
.btn-lg { height: var(--control-h-lg); padding: 0 var(--space-5); font-size: var(--text-md); }
.btn-icon { width: var(--control-h); padding: 0; }
.btn-block { width: 100%; }

.btn.is-loading { color: transparent !important; position: relative; pointer-events: none; }
.btn.is-loading::after {
  content: ""; position: absolute; width: 15px; height: 15px; border-radius: 50%;
  border: 2px solid currentColor; border-top-color: transparent;
  color: var(--fg-on-accent); animation: spin 0.7s linear infinite;
}
.btn-secondary.is-loading::after, .btn-ghost.is-loading::after { color: var(--fg2); }
@keyframes spin { to { transform: rotate(360deg); } }

/* Brief success confirmation after an explicit, no-auto-save action
   completes (e.g. the ACL matrix's "Änderungen anwenden") — icon + label
   swap, not a color change, matching "status is never color-only". */
.btn.is-success { animation: btnSuccessFade var(--dur) var(--ease-standard); }
@keyframes btnSuccessFade { from { opacity: 0; } to { opacity: 1; } }

/* ---- Inputs -------------------------------------------------------------- */
.field { display: flex; flex-direction: column; gap: var(--space-2); }
.field-label { font-size: var(--text-sm); font-weight: var(--weight-medium); color: var(--fg1); }
.field-hint { font-size: var(--text-xs); color: var(--fg3); }
.field-error { font-size: var(--text-xs); color: var(--danger-fg); display: flex; align-items: center; gap: 4px; }

.input, .textarea, .select {
  height: var(--control-h); padding: 0 var(--space-3);
  font-family: var(--font-sans); font-size: var(--text-base); color: var(--fg1);
  background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--radius-md);
  transition: border-color var(--dur) var(--ease-standard), box-shadow var(--dur) var(--ease-standard);
  width: 100%;
}
.input::placeholder, .textarea::placeholder { color: var(--fg3); }
/* Compact select, matching .btn-sm's height so the two line up when they sit
   side by side (e.g. the reservation strip's duration picker + button, #72).
   width:auto because these are inline controls, not form-column fields. */
.select-sm {
  height: var(--control-h-sm); padding: 0 var(--space-2);
  font-size: var(--text-sm); width: auto;
}

.input:hover, .textarea:hover, .select:hover { border-color: var(--neutral-400); }
.input:focus, .textarea:focus, .select:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft-bg); }
.textarea { height: auto; padding: var(--space-3); min-height: 84px; line-height: var(--leading-normal); resize: vertical; }
.input.mono { font-family: var(--font-mono); font-size: var(--text-sm); }
.input:disabled { background: var(--surface-3); color: var(--fg-disabled); cursor: not-allowed; }
.input.is-invalid { border-color: var(--danger-solid); }
.input.is-invalid:focus { box-shadow: 0 0 0 3px var(--danger-bg); }

/* Password field with a reveal toggle. The eye button sits inside the input on
   the right; the input reserves padding so text never runs under it. */
.input-reveal { position: relative; display: block; }
.input-reveal .input { width: 100%; padding-right: calc(var(--space-3) + 26px); }
.input-reveal-btn {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; padding: 0;
  background: none; border: none; border-radius: var(--radius-sm);
  color: var(--fg3); cursor: pointer;
  transition: color var(--dur) var(--ease-standard), background var(--dur) var(--ease-standard);
}
.input-reveal-btn:hover { color: var(--fg1); background: var(--surface-3); }
.input-reveal-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent-soft-bg); }

/* ---- Toggle -------------------------------------------------------------- */
.toggle { position: relative; display: inline-flex; width: 38px; height: 22px; flex: none; }
.toggle input { opacity: 0; width: 0; height: 0; }
.toggle .track { position: absolute; inset: 0; background: var(--neutral-300); border-radius: var(--radius-full); transition: background var(--dur) var(--ease-standard); }
.toggle .thumb { position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; background: #fff; border-radius: 50%; box-shadow: var(--shadow-sm); transition: transform var(--dur) var(--ease-standard); }
.toggle input:checked + .track { background: var(--accent); }
.toggle input:checked + .track + .thumb { transform: translateX(16px); }
.toggle input:focus-visible + .track { box-shadow: var(--focus-ring); }

/* ---- Checkbox / radio ---------------------------------------------------- */
.checkbox, .radio { width: 18px; height: 18px; border: 1.5px solid var(--border-strong); background: var(--surface); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; flex: none; transition: all var(--dur) var(--ease-standard); }
.checkbox { border-radius: var(--radius-xs); }
.radio { border-radius: 50%; }
.checkbox[aria-checked="true"], .radio[aria-checked="true"] { background: var(--accent); border-color: var(--accent); color: #fff; }
.radio[aria-checked="true"]::after { content: ""; width: 7px; height: 7px; border-radius: 50%; background: #fff; }

/* ---- Cards --------------------------------------------------------------- */
.card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--shadow-sm); }
.card-pad { padding: var(--space-5); }
.card-hover { transition: box-shadow var(--dur) var(--ease-standard), border-color var(--dur) var(--ease-standard); }
.card-hover:hover { box-shadow: var(--shadow-md); border-color: var(--border-strong); }
/* Answer a press like every other control here does. */
.card-hover:active { transform: translateY(0.5px); }
.card-hover:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-md); }

/* ---- Badges / status pills (color + icon + label) ------------------------ */
.badge { display: inline-flex; align-items: center; gap: 5px; height: 22px; padding: 0 8px; font-size: var(--text-xs); font-weight: var(--weight-medium); border-radius: var(--radius-full); border: 1px solid transparent; white-space: nowrap; }
.badge .dot { width: 6px; height: 6px; border-radius: 50%; flex: none; }
.badge .icon { width: 12px; height: 12px; }
.badge-success { color: var(--success-fg); background: var(--success-bg); border-color: var(--success-border); }
.badge-success .dot { background: var(--success-solid); }
.badge-warning { color: var(--warning-fg); background: var(--warning-bg); border-color: var(--warning-border); }
.badge-warning .dot { background: var(--warning-solid); }
.badge-danger  { color: var(--danger-fg);  background: var(--danger-bg);  border-color: var(--danger-border); }
.badge-danger .dot { background: var(--danger-solid); }
.badge-neutral { color: var(--neutral-status-fg); background: var(--neutral-status-bg); border-color: var(--neutral-status-border); }
.badge-neutral .dot { background: var(--neutral-400); }
.badge-info { color: var(--info-fg); background: var(--info-bg); border-color: var(--info-border); }
.badge-info .dot { background: var(--info-solid); }

/* ---- Roadwarrior corner flag (peer tables) --------------------------------
   A category marker, not a status — a filled triangle right in the row's
   own top-left corner at rest (flush with the row's hairline, overlapping
   it rather than inset from it), expanding into a labeled ribbon on
   hover/focus. No tooltip, no title attribute: the label lives in the
   ribbon itself. Positioned on the cell itself (position:relative), not on
   an inner content wrapper — an absolutely positioned child's containing
   block is its ancestor's padding edge, so anchoring to the <td> directly
   (whose own top/left padding is 0 — .table td only pads left/right) lands
   the triangle exactly at the cell's outer corner. Neither the cell nor any
   ancestor may clip overflow, since the expanded ribbon extends past the
   flag's own 28x28 hit area. */
.peer-name-cell { position: relative; }
.rw-flag { position: absolute; top: 0; left: 0; width: 28px; height: 28px; outline: none; cursor: default; }
.rw-band {
  position: absolute; top: 0; left: 0;
  height: 16px; line-height: 16px;
  padding: 0 22px 0 8px;
  font-size: var(--text-2xs); white-space: nowrap;
  background: var(--rw-bg);
  color: transparent;
  clip-path: polygon(0 0, 14px 0, 0 14px, 0 14px);
  transition: clip-path var(--dur) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
}
.rw-flag:hover .rw-band,
.rw-flag:focus-visible .rw-band {
  clip-path: polygon(0 0, 100% 0, calc(100% - 12px) 100%, 0 100%);
  color: var(--rw-fg);
}
/* peers-row-flags: the same corner treatment for "this peer dials an edge
   hub". Bottom-left rather than top-left, so a peer that is both a roadwarrior
   and an edge peer shows two distinct marks instead of one covering the other.
   Both are informational, both are focusable, and neither hides an action. */
.edge-flag { position: absolute; bottom: 0; left: 0; width: 28px; height: 28px; outline: none; cursor: default; }
.edge-band {
  position: absolute; bottom: 0; left: 0;
  height: 16px; line-height: 16px;
  padding: 0 22px 0 8px;
  font-size: var(--text-2xs); white-space: nowrap;
  background: var(--info-solid);
  color: transparent;
  clip-path: polygon(0 100%, 14px 100%, 0 calc(100% - 14px), 0 calc(100% - 14px));
  transition: clip-path var(--dur) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
}
.edge-flag:hover .edge-band,
.edge-flag:focus-visible .edge-band {
  clip-path: polygon(0 0, calc(100% - 12px) 0, 100% 100%, 0 100%);
  color: #fff;
}

@media (prefers-reduced-motion: reduce) {
  .rw-band,
  .edge-band { transition: none; }
}

.tag { display: inline-flex; align-items: center; height: 20px; padding: 0 8px; font-size: var(--text-xs); font-weight: var(--weight-medium); border-radius: var(--radius-sm); background: var(--surface-3); color: var(--fg2); border: 1px solid var(--border); }

/* ---- Avatar -------------------------------------------------------------- */
.avatar { display: inline-flex; align-items: center; justify-content: center; border-radius: 50%; font-family: var(--font-sans); font-weight: var(--weight-semibold); color: #fff; overflow: hidden; flex: none; background-size: cover; background-position: center; }
.avatar-16 { width: 16px; height: 16px; font-size: 8px; }
.avatar-24 { width: 24px; height: 24px; font-size: 10px; }
.avatar-32 { width: 32px; height: 32px; font-size: 12px; }
.avatar-48 { width: 48px; height: 48px; font-size: 17px; }
.avatar-80 { width: 80px; height: 80px; font-size: 28px; }

/* ---- Table --------------------------------------------------------------- */
.table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); }
.table th { text-align: left; font-size: var(--text-xs); font-weight: var(--weight-semibold); color: var(--fg3); text-transform: uppercase; letter-spacing: var(--tracking-wide); padding: 0 var(--space-4); height: 36px; border-bottom: 1px solid var(--border); white-space: nowrap; }
.table td { padding: 0 var(--space-4); height: var(--row-height); border-bottom: 1px solid var(--border-subtle); color: var(--fg1); vertical-align: middle; }
/* The hairline, not the grey, is the part worth keeping: a row that highlights
   in grey is every tool ever built. Inset rather than a border so no column
   shifts when it appears. */
.table tbody tr { transition: background var(--dur-fast) var(--ease-standard), box-shadow var(--dur-fast) var(--ease-standard); box-shadow: inset 2px 0 0 0 transparent; }
.table tbody tr:hover { background: var(--surface-2); box-shadow: inset 2px 0 0 0 var(--accent); }
.table .mono { font-family: var(--font-mono); font-size: var(--text-sm); color: var(--fg2); }
.table th.table-actions,
.table td.table-actions {
  width: 1%;
  text-align: right;
  /* One line, always. Four labelled buttons wrapped into three lines at 1280px
     and tripled the height of every row in the table — the row became a block
     and the list stopped being scannable. Keeping them on one line pushes the
     squeeze onto the text columns beside it, which can wrap without costing
     anything. */
  white-space: nowrap;
}

/* A table inside its own scroll box (see .table-scroll) keeps its header in
   view while the rows move under it — with eight columns, a header that has
   scrolled away leaves the reader counting columns to work out which is which.
   Needs an explicit background: the sticky row would otherwise be transparent
   and the rows would slide visibly underneath it. */
.table-scroll {
  overflow: auto;
  max-height: var(--table-scroll-max, 46vh);
}

.table-scroll .table thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--surface);
}

/* ---- Misc ---------------------------------------------------------------- */
.divider { height: 1px; background: var(--border); border: 0; margin: 0; }
.kbd { font-family: var(--font-mono); font-size: var(--text-xs); padding: 2px 6px; border-radius: var(--radius-xs); border: 1px solid var(--border-strong); background: var(--surface-2); color: var(--fg2); box-shadow: 0 1px 0 var(--border-strong); }
.skeleton { background: linear-gradient(90deg, var(--surface-3) 25%, var(--surface-2) 50%, var(--surface-3) 75%); background-size: 200% 100%; animation: shimmer 1.4s infinite; border-radius: var(--radius-sm); }
@keyframes shimmer { to { background-position: -200% 0; } }

/* Determinate progress bar for a running, data-driven job (e.g. a device
   scan) — distinct from .skeleton (placeholder shimmer for content that
   hasn't loaded yet). Width is driven by real progress, not looped. */
.progress { height: 6px; border-radius: var(--radius-full); background: var(--surface-3); overflow: hidden; }
.progress-fill { height: 100%; background: var(--accent); border-radius: inherit; transition: width var(--dur) var(--ease-standard); }

/* peers-list-at-a-glance: a 14-day activity sparkline inside a table row.
   Bars, not a line: the series is one measurement per day, and a line would
   draw slopes between days that were never measured. Sized in em so it
   follows the row's type scale rather than fighting it. */
.peer-spark {
  display: inline-flex;
  align-items: flex-end;
  gap: 2px;
  height: 1.6em;
  width: 6em;
}
.peer-spark i {
  flex: 1 1 0;
  min-width: 2px;
  background: var(--accent);
  border-radius: 1px;
}
/* A day with no connection is drawn as a short neutral stub, not a faded
   accent bar: at low opacity the whole row read as a dotted rule rather than
   as a series. The stub says "measured, nothing there" and keeps the active
   days the only thing coloured. */
.peer-spark i.is-quiet {
  background: var(--border);
  opacity: 1;
}

/* peers-table-density: a horizontal escape hatch for a wide table. Used where
   the action cell must not wrap (see .table-actions) — the row then has a
   minimum width, and below it the table scrolls sideways rather than folding
   its buttons into a block three lines tall. */
.table-xscroll {
  overflow-x: auto;
}
