/* ==========================================================================
   neobrutalism-theme / components
   --------------------------------------------------------------------------
   Five components: button, card, input, badge, nav.

   Every value here is a var() reference to tokens.css. There is not one color
   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 (nb-btn + nb-btn-primary).
   If you are on shadcn already, these map onto its variant prop directly.

   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 behavior
   The press is the register's signature: the element travels by exactly the
   shadow offset and the shadow collapses to nothing, so the surface lands
   flush against the ground. Under prefers-reduced-motion the travel token is
   zeroed in tokens.css, so the shadow still collapses but nothing moves.
   -------------------------------------------------------------------------- */
.nb-pressable {
  transition:
    transform var(--nb-duration) var(--nb-ease),
    box-shadow var(--nb-duration) var(--nb-ease);
}

.nb-pressable:active {
  transform: translate(var(--nb-press-travel), var(--nb-press-travel));
  box-shadow: 0 0 0 0 var(--nb-ink);
}

.nb-pressable:focus-visible,
.nb-focusable:focus-visible {
  outline: var(--nb-ring-width) solid var(--nb-ring);
  outline-offset: var(--nb-ring-offset);
}

/* ==========================================================================
   BUTTON
   Variants: nb-btn-primary, nb-btn-default, nb-btn-ghost, nb-btn-destructive
   Sizes:    nb-btn-sm, (base), nb-btn-lg
   ========================================================================== */
.nb-btn {
  /* 44px minimum on both axes. Anything smaller fails on a real thumb. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--nb-space-2);
  min-height: 44px;
  padding: var(--nb-space-3) var(--nb-space-6);

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

  border: var(--nb-border-width) solid var(--nb-border-color);
  border-radius: var(--nb-radius);
  box-shadow: var(--nb-shadow);
  cursor: pointer;

  transition:
    transform var(--nb-duration) var(--nb-ease),
    box-shadow var(--nb-duration) var(--nb-ease);
}

.nb-btn:active:not(:disabled) {
  transform: translate(var(--nb-press-travel), var(--nb-press-travel));
  box-shadow: 0 0 0 0 var(--nb-ink);
}

.nb-btn:focus-visible {
  outline: var(--nb-ring-width) solid var(--nb-ring);
  outline-offset: var(--nb-ring-offset);
}

.nb-btn:disabled,
.nb-btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
  box-shadow: var(--nb-shadow-sm);
}

.nb-btn-primary {
  background: var(--nb-primary);
  color: var(--nb-primary-ink);
}

.nb-btn-default {
  background: var(--nb-surface);
  color: var(--nb-ink);
}

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

/* The ghost is the one button that does not sit above the ground, so it takes
   no border and no shadow. It gets them on hover, which is how the reader
   learns it was a button all along. */
.nb-btn-ghost {
  background: transparent;
  color: var(--nb-ink);
  border-color: transparent;
  box-shadow: none;
}

.nb-btn-ghost:hover {
  background: var(--nb-surface-muted);
  border-color: var(--nb-border-color);
}

.nb-btn-sm {
  min-height: 44px; /* the visual box shrinks, the touch target does not */
  padding: var(--nb-space-2) var(--nb-space-4);
  font-size: var(--nb-text-sm);
  box-shadow: var(--nb-shadow-sm);
}

.nb-btn-sm:active:not(:disabled) {
  transform: translate(var(--nb-shadow-offset-sm), var(--nb-shadow-offset-sm));
}

.nb-btn-lg {
  padding: var(--nb-space-4) var(--nb-space-8);
  font-size: var(--nb-text-lead);
  border-width: var(--nb-border-width-heavy);
  box-shadow: var(--nb-shadow-lg);
}

.nb-btn-lg:active:not(:disabled) {
  transform: translate(var(--nb-shadow-offset-lg), var(--nb-shadow-offset-lg));
}

/* ==========================================================================
   CARD
   Variants: (base), nb-card-featured, nb-card-muted
   Parts:    nb-card-header, nb-card-title, nb-card-body, nb-card-footer
   ========================================================================== */
.nb-card {
  display: flex;
  flex-direction: column;
  background: var(--nb-surface);
  color: var(--nb-ink);
  border: var(--nb-border-width) solid var(--nb-border-color);
  border-radius: var(--nb-radius);
  box-shadow: var(--nb-shadow);
  padding: var(--nb-space-6);
}

/* The featured card is where the accent spends its one dimensional-play
   budget. One of these per screen. Two is a pattern, three is wallpaper. */
.nb-card-featured {
  border-width: var(--nb-border-width-heavy);
  box-shadow: var(--nb-shadow-accent);
}

.nb-card-muted {
  background: var(--nb-surface-muted);
}

.nb-card-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--nb-space-4);
  margin-bottom: var(--nb-space-4);
}

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

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

.nb-card-footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--nb-space-3);
  margin-top: var(--nb-space-6);
}

/* A card that is itself a link. The hover lifts rather than presses, so the
   two gestures stay distinguishable: hover raises, click lands. */
.nb-card-link {
  text-decoration: none;
  transition:
    transform var(--nb-duration) var(--nb-ease),
    box-shadow var(--nb-duration) var(--nb-ease);
}

.nb-card-link:hover {
  transform: translate(
    calc(var(--nb-press-travel) * -1),
    calc(var(--nb-press-travel) * -1)
  );
  box-shadow: var(--nb-shadow-lg);
}

.nb-card-link:active {
  transform: translate(0, 0);
  box-shadow: 0 0 0 0 var(--nb-ink);
}

.nb-card-link:focus-visible {
  outline: var(--nb-ring-width) solid var(--nb-ring);
  outline-offset: var(--nb-ring-offset);
}

/* ==========================================================================
   INPUT
   Parts:  nb-field, nb-label, nb-input, nb-hint, nb-error
   States: default, :focus, aria-invalid="true", :disabled
   ========================================================================== */
.nb-field {
  display: flex;
  flex-direction: column;
  gap: var(--nb-space-2);
}

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

.nb-input {
  /* 16px minimum, which is what stops iOS Safari zooming on focus. */
  width: 100%;
  min-height: 44px;
  padding: var(--nb-space-3) var(--nb-space-4);

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

  border: var(--nb-border-width) solid var(--nb-border-color);
  border-radius: var(--nb-radius);
  box-shadow: var(--nb-shadow-sm);

  transition:
    transform var(--nb-duration) var(--nb-ease),
    box-shadow var(--nb-duration) var(--nb-ease);
}

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

/* Focus presses the field into the page. The field is now the thing you are
   working in rather than a thing waiting to be worked in. */
.nb-input:focus {
  outline: var(--nb-ring-width) solid var(--nb-ring);
  outline-offset: var(--nb-ring-offset);
  transform: translate(var(--nb-shadow-offset-sm), var(--nb-shadow-offset-sm));
  box-shadow: 0 0 0 0 var(--nb-ink);
}

.nb-input[aria-invalid="true"] {
  border-color: var(--nb-destructive);
  box-shadow: var(--nb-shadow-offset-sm) var(--nb-shadow-offset-sm) 0 0
    var(--nb-destructive);
}

.nb-input:disabled {
  background: var(--nb-surface-muted);
  color: var(--nb-ink-muted);
  cursor: not-allowed;
  box-shadow: none;
}

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

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

/* Textarea and select inherit the input treatment. */
.nb-input:where(textarea) {
  min-height: 120px;
  resize: vertical;
}

/* ==========================================================================
   BADGE
   Variants: nb-badge-neutral, nb-badge-success, nb-badge-info,
             nb-badge-warning, nb-badge-danger
   ========================================================================== */
.nb-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--nb-space-1);
  padding: var(--nb-space-1) var(--nb-space-2);

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

  /* Ink on every fill. The border carries the boundary at 17:1, so the fill
     is chosen for meaning rather than for legibility. */
  color: var(--nb-ink);
  border: var(--nb-border-width) solid var(--nb-border-color);
  border-radius: var(--nb-radius-tight);
  box-shadow: var(--nb-shadow-sm);
  white-space: nowrap;
}

.nb-badge-neutral {
  background: var(--nb-surface-muted);
}

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

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

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

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

/* ==========================================================================
   NAV
   A header bar with a brand block, inline links on desktop, and a disclosure
   panel on mobile. The disclosure is a <details> element, so the pattern
   needs no JavaScript and keeps working with the page's scripts disabled.
   ========================================================================== */
.nb-nav {
  background: var(--nb-surface);
  border-bottom: var(--nb-border-width-heavy) solid var(--nb-border-color);
}

.nb-nav-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--nb-space-4);
  max-width: var(--nb-container);
  margin: 0 auto;
  padding: var(--nb-space-3) var(--nb-space-4);
}

.nb-nav-brand {
  display: inline-flex;
  align-items: center;
  gap: var(--nb-space-3);
  font-family: var(--nb-font-sans);
  font-size: var(--nb-text-h3);
  font-weight: var(--nb-weight-display);
  letter-spacing: var(--nb-tracking-tight);
  color: var(--nb-ink);
  text-decoration: none;
}

/* The brand mark is a square that sits above the bar on its own hard shadow.
   It is the smallest possible statement of the whole register. */
.nb-nav-mark {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  background: var(--nb-accent);
  color: var(--nb-accent-ink);
  border: var(--nb-border-width) solid var(--nb-border-color);
  border-radius: var(--nb-radius-tight);
  box-shadow: var(--nb-shadow-sm);
  font-size: var(--nb-text-body);
  font-weight: var(--nb-weight-display);
}

.nb-nav-links {
  display: none;
  align-items: center;
  gap: var(--nb-space-2);
  list-style: none;
  margin: 0;
  padding: 0;
}

.nb-nav-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: var(--nb-space-2) var(--nb-space-3);

  font-family: var(--nb-font-sans);
  font-size: var(--nb-text-sm);
  font-weight: var(--nb-weight-bold);
  color: var(--nb-ink);
  text-decoration: none;

  border: var(--nb-border-width) solid transparent;
  border-radius: var(--nb-radius);
}

.nb-nav-link:hover {
  background: var(--nb-surface-muted);
  border-color: var(--nb-border-color);
}

.nb-nav-link[aria-current="page"] {
  background: var(--nb-fill-warning);
  border-color: var(--nb-border-color);
  box-shadow: var(--nb-shadow-sm);
}

.nb-nav-link:focus-visible {
  outline: var(--nb-ring-width) solid var(--nb-ring);
  outline-offset: var(--nb-ring-offset);
}

.nb-nav-cta {
  display: none;
}

/* Mobile disclosure */
.nb-nav-disclosure {
  position: relative;
}

.nb-nav-disclosure > summary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--nb-space-2);
  min-width: 44px;
  min-height: 44px;
  padding: var(--nb-space-2) var(--nb-space-3);

  font-family: var(--nb-font-sans);
  font-size: var(--nb-text-sm);
  font-weight: var(--nb-weight-bold);
  color: var(--nb-ink);
  background: var(--nb-surface);

  border: var(--nb-border-width) solid var(--nb-border-color);
  border-radius: var(--nb-radius);
  box-shadow: var(--nb-shadow-sm);
  cursor: pointer;
  list-style: none;
}

.nb-nav-disclosure > summary::-webkit-details-marker {
  display: none;
}

.nb-nav-disclosure > summary:focus-visible {
  outline: var(--nb-ring-width) solid var(--nb-ring);
  outline-offset: var(--nb-ring-offset);
}

.nb-nav-panel {
  position: absolute;
  right: 0;
  z-index: 20;
  min-width: 220px;
  margin-top: var(--nb-space-2);
  padding: var(--nb-space-2);

  list-style: none;
  background: var(--nb-surface);
  border: var(--nb-border-width-heavy) solid var(--nb-border-color);
  border-radius: var(--nb-radius);
  box-shadow: var(--nb-shadow-lg);
}

.nb-nav-panel .nb-nav-link {
  display: flex;
  width: 100%;
}

/* At 768px the links come inline and the disclosure retires. */
@media (min-width: 768px) {
  .nb-nav-inner {
    padding: var(--nb-space-3) var(--nb-space-6);
  }

  .nb-nav-links,
  .nb-nav-cta {
    display: flex;
  }

  .nb-nav-disclosure {
    display: none;
  }
}
