/* ==========================================================================
   components.css
   Handyman Services Marketplace — UI Component Library
   Phase 2: CSS Design System

   Depends on: base.css (all tokens), layout.css (containers, grids)
   Pure CSS — no frameworks, no JavaScript dependencies.

   Table of contents
   -----------------
   1.  Buttons
       1a. Base
       1b. Variants — primary, secondary, ghost, danger
       1c. Sizes — sm, md (default), lg
       1d. States — disabled, loading
       1e. Icon button & block button
   2.  Cards
       2a. Base card
       2b. Service card
       2c. Member card
       2d. Review card
   3.  Forms
       3a. Layout helpers
       3b. Labels & hints
       3c. Text inputs & textarea
       3d. Select
       3e. Checkboxes
       3f. Radio buttons
       3g. Validation states — error, success
       3h. Honeypot field
   4.  Navigation
       4a. Nav links
       4b. Active & current states
       4c. Dropdown menus
   5.  Badges
       5a. Base
       5b. Verified badge
       5c. Category badge
       5d. Status badges
   6.  Alerts
       6a. Base
       6b. Variants — success, warning, error, info
   7.  Avatar
       7a. Sizes
       7b. Placeholder (initials)
       7c. Online indicator
       7d. Avatar group
   8.  Search Bar
       8a. Input + button composite
       8b. Filter tags
   9.  Star Rating
       9a. Display-only rating
       9b. Interactive rating input
   ========================================================================== */


/* ==========================================================================
   1. BUTTONS
   ========================================================================== */

/* --------------------------------------------------------------------------
   1a. Base button
   Shared geometry, typography, and transitions for all variants.
   Always pair .btn with one variant class (.btn--primary, etc.).
   -------------------------------------------------------------------------- */

.btn {
  /* Inline-flex so icon + label align naturally on a single baseline */
  display:         inline-flex;
  align-items:     center;
  justify-content: center;
  gap:             var(--space-1);

  /* Medium size by default — overridden by .btn--sm / .btn--lg */
  padding:    var(--space-1-5) var(--space-3);
  min-height: 2.5rem;   /* 40px — WCAG 2.5.5 minimum touch target */

  /* Typography */
  font-family:     var(--font-sans);
  font-size:       var(--text-sm);
  font-weight:     var(--font-semibold);
  line-height:     var(--leading-none);
  letter-spacing:  var(--tracking-wide);
  white-space:     nowrap;
  text-decoration: none;

  /* Shape */
  border:        2px solid transparent;
  border-radius: var(--radius-md);
  cursor:        pointer;

  /* Smooth color + shadow transitions; fast micro-press transform */
  transition:
    color            var(--duration-base) var(--ease-in-out),
    background-color var(--duration-base) var(--ease-in-out),
    border-color     var(--duration-base) var(--ease-in-out),
    box-shadow       var(--duration-base) var(--ease-in-out),
    transform        var(--duration-fast) var(--ease-in-out);

  /* Prevent text selection on rapid clicks */
  user-select: none;

  /* Loading spinner is absolute inside this stacking context */
  position: relative;
  overflow: hidden;
}

/* Native <button> appearance reset — font already inherited from base.css */
button.btn {
  -webkit-appearance: none;
  appearance: none;
  background: none;
}

/* Icons inside buttons inherit font-size so they scale with .btn--sm/--lg */
.btn svg {
  width:       1em;
  height:      1em;
  flex-shrink: 0;
}


/* --------------------------------------------------------------------------
   1b. Variants
   -------------------------------------------------------------------------- */

/* Primary — filled navy, highest visual weight CTA */
.btn--primary {
  background-color: var(--color-primary-600);
  border-color:     var(--color-primary-600);
  color:            var(--color-text-inverse);
}

.btn--primary:hover:not(:disabled):not([aria-disabled="true"]) {
  background-color: var(--color-primary-500);
  border-color:     var(--color-primary-500);
  box-shadow:       var(--shadow-primary);
}

.btn--primary:active:not(:disabled):not([aria-disabled="true"]) {
  background-color: var(--color-primary-700);
  border-color:     var(--color-primary-700);
  box-shadow:       none;
  transform:        translateY(1px);
}

/* Secondary — outlined navy, medium weight */
.btn--secondary {
  background-color: transparent;
  border-color:     var(--color-primary-600);
  color:            var(--color-primary-600);
}

.btn--secondary:hover:not(:disabled):not([aria-disabled="true"]) {
  background-color: var(--color-primary-50);
  border-color:     var(--color-primary-500);
  color:            var(--color-primary-500);
}

.btn--secondary:active:not(:disabled):not([aria-disabled="true"]) {
  background-color: var(--color-primary-100);
  transform:        translateY(1px);
}

/* Ghost — borderless, lowest weight; used for tertiary actions and icon rows */
.btn--ghost {
  background-color: transparent;
  border-color:     transparent;
  color:            var(--color-text-muted);
}

.btn--ghost:hover:not(:disabled):not([aria-disabled="true"]) {
  background-color: var(--color-neutral-100);
  color:            var(--color-text);
}

.btn--ghost:active:not(:disabled):not([aria-disabled="true"]) {
  background-color: var(--color-neutral-200);
  transform:        translateY(1px);
}

/* Danger — destructive actions; always confirm before submitting */
.btn--danger {
  background-color: var(--color-error-600);
  border-color:     var(--color-error-600);
  color:            var(--color-text-inverse);
}

.btn--danger:hover:not(:disabled):not([aria-disabled="true"]) {
  background-color: var(--color-error-500);
  border-color:     var(--color-error-500);
  box-shadow:       0 4px 14px 0 rgb(220 38 38 / 0.35);
}

.btn--danger:active:not(:disabled):not([aria-disabled="true"]) {
  background-color: var(--color-error-700);
  border-color:     var(--color-error-700);
  box-shadow:       none;
  transform:        translateY(1px);
}


/* --------------------------------------------------------------------------
   1c. Sizes
   -------------------------------------------------------------------------- */

.btn--sm {
  padding:       var(--space-1) var(--space-2);
  font-size:     var(--text-xs);
  min-height:    2rem;   /* 32px */
  border-radius: var(--radius-sm);
}

/* Explicit class mirrors the default for completeness in design specs */
.btn--md {
  padding:    var(--space-1-5) var(--space-3);
  font-size:  var(--text-sm);
  min-height: 2.5rem;
}

.btn--lg {
  padding:       var(--space-2) var(--space-5);
  font-size:     var(--text-base);
  min-height:    3rem;   /* 48px */
  border-radius: var(--radius-lg);
}


/* --------------------------------------------------------------------------
   1d. States — disabled, loading
   -------------------------------------------------------------------------- */

/* Disabled — pointer-events off so neither cursor nor hover fire */
.btn:disabled,
.btn[aria-disabled="true"] {
  opacity:        0.45;
  cursor:         not-allowed;
  pointer-events: none;
}

/*
 * Loading state — hides label text and shows a CSS spinner.
 * color: transparent makes text invisible while preserving button width.
 * The spinner is ::after, positioned absolutely in the center.
 */
.btn--loading {
  color:          transparent !important;
  pointer-events: none;
  cursor:         wait;
}

.btn--loading::after {
  content:       "";
  position:      absolute;
  width:         1.1em;
  height:        1.1em;
  top:           50%;
  left:          50%;
  margin-top:    -0.55em;
  margin-left:   -0.55em;
  border-radius: var(--radius-full);
  border:        2px solid transparent;
  animation:     btn-spin 0.7s linear infinite;
}

/* Spinner colors are explicit because currentColor is transparent when loading */
.btn--primary.btn--loading::after,
.btn--danger.btn--loading::after {
  border-color:     rgb(255 255 255 / 0.30);
  border-top-color: var(--color-text-inverse);
}

.btn--secondary.btn--loading::after {
  border-color:     rgb(26 100 184 / 0.20);
  border-top-color: var(--color-primary-600);
}

.btn--ghost.btn--loading::after {
  border-color:     var(--color-neutral-300);
  border-top-color: var(--color-neutral-600);
}

@keyframes btn-spin {
  to { transform: rotate(360deg); }
}

/* Focus — base.css covers :focus-visible globally; explicit here for certainty */
.btn:focus-visible {
  outline:        3px solid var(--color-focus-ring);
  outline-offset: 3px;
}


/* --------------------------------------------------------------------------
   1e. Icon button & block button
   -------------------------------------------------------------------------- */

/*
 * Icon-only square button.
 * Always include aria-label="Action name" on the element for screen readers.
 */
.btn--icon {
  padding:   var(--space-1-5);
  min-width: 2.5rem;
}

.btn--icon.btn--sm { padding: var(--space-1);  min-width: 2rem; }
.btn--icon.btn--lg { padding: var(--space-2);  min-width: 3rem; }

/* Full-width — for mobile stacked forms or narrow sidebars */
.btn--block {
  width: 100%;
}


/* ==========================================================================
   2. CARDS
   ========================================================================== */

/* --------------------------------------------------------------------------
   2a. Base card
   All card variants extend .card. Hover lift gives clickable affordance
   without needing JS; non-interactive cards can opt out with no .card:hover.
   -------------------------------------------------------------------------- */

.card {
  background-color: var(--color-surface);
  border:           1px solid var(--color-border);
  border-radius:    var(--radius-xl);
  box-shadow:       var(--shadow-sm);
  overflow:         hidden;
  transition:
    box-shadow   var(--duration-base) var(--ease-in-out),
    border-color var(--duration-base) var(--ease-in-out),
    transform    var(--duration-base) var(--ease-in-out);
}

.card:hover {
  box-shadow:   var(--shadow-lg);
  border-color: var(--color-neutral-400);
  transform:    translateY(-2px);
}

/* Anchor-wrapped or JS-driven clickable card */
a.card,
.card--clickable {
  display:         block;
  text-decoration: none;
  color:           inherit;
  cursor:          pointer;
}

a.card:focus-visible,
.card--clickable:focus-visible {
  outline:        3px solid var(--color-focus-ring);
  outline-offset: 3px;
}

/* Card anatomy */
.card__image {
  width:        100%;
  aspect-ratio: 16 / 9;
  object-fit:   cover;
  display:      block;
}

.card__body {
  padding: var(--space-3);
}

.card__footer {
  padding:          var(--space-2) var(--space-3);
  border-top:       1px solid var(--color-border);
  background-color: var(--color-bg-subtle);
}

.card__title {
  font-size:     var(--text-md);
  font-weight:   var(--font-semibold);
  color:         var(--color-neutral-900);
  line-height:   var(--leading-snug);
  margin-bottom: var(--space-0-5);
}

.card__subtitle {
  font-size:     var(--text-sm);
  color:         var(--color-text-muted);
  margin-bottom: var(--space-1-5);
}

.card__meta {
  display:     flex;
  align-items: center;
  gap:         var(--space-1);
  font-size:   var(--text-xs);
  color:       var(--color-text-subtle);
}


/* --------------------------------------------------------------------------
   2b. Service card
   Represents a single service listing on category and search-results pages.
   -------------------------------------------------------------------------- */

.card--service .card__image {
  aspect-ratio: 4 / 3;
}

/* Price in primary blue for visual hierarchy */
.card--service .card__price {
  font-size:   var(--text-md);
  font-weight: var(--font-bold);
  color:       var(--color-primary-600);
}

.card--service .card__price-unit {
  font-size:   var(--text-xs);
  font-weight: var(--font-regular);
  color:       var(--color-text-subtle);
}

.card--service .card__location {
  display:     flex;
  align-items: center;
  gap:         var(--space-0-5);
  font-size:   var(--text-sm);
  color:       var(--color-text-muted);
  margin-top:  var(--space-1);
}

.card--service .card__location svg {
  width:       0.875rem;
  height:      0.875rem;
  flex-shrink: 0;
}


/* --------------------------------------------------------------------------
   2c. Member card
   Handyman profile card — avatar centred at top, stats row, book CTA.
   -------------------------------------------------------------------------- */

.card--member {
  text-align: center;
}

.card--member .card__avatar-wrap {
  padding:        var(--space-4) var(--space-3) var(--space-2);
  display:        flex;
  flex-direction: column;
  align-items:    center;
  gap:            var(--space-2);
}

.card--member .card__name {
  font-size:   var(--text-md);
  font-weight: var(--font-bold);
  color:       var(--color-neutral-900);
}

.card--member .card__trade {
  font-size: var(--text-sm);
  color:     var(--color-text-muted);
}

/* Compact stats row — jobs completed, average rating, years active */
.card--member .card__stats {
  display:         flex;
  justify-content: center;
  gap:             var(--space-4);
  padding:         var(--space-2) var(--space-3);
  border-top:      1px solid var(--color-border);
  border-bottom:   1px solid var(--color-border);
  margin-block:    var(--space-1);
}

.card--member .card__stat {
  display:        flex;
  flex-direction: column;
  align-items:    center;
  gap:            var(--space-0-5);
}

.card--member .card__stat-value {
  font-size:   var(--text-md);
  font-weight: var(--font-bold);
  color:       var(--color-neutral-900);
}

.card--member .card__stat-label {
  font-size:      var(--text-xs);
  color:          var(--color-text-subtle);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
}


/* --------------------------------------------------------------------------
   2d. Review card
   Customer testimonial: reviewer info, star rating, truncated quote text.
   -------------------------------------------------------------------------- */

.card--review {
  padding: var(--space-3);
}

.card--review .card__reviewer {
  display:       flex;
  align-items:   center;
  gap:           var(--space-2);
  margin-bottom: var(--space-2);
}

.card--review .card__reviewer-info {
  flex:      1;
  min-width: 0;
}

.card--review .card__reviewer-name {
  font-size:     var(--text-sm);
  font-weight:   var(--font-semibold);
  color:         var(--color-neutral-900);
  white-space:   nowrap;
  overflow:      hidden;
  text-overflow: ellipsis;
}

.card--review .card__reviewer-date {
  font-size: var(--text-xs);
  color:     var(--color-text-subtle);
}

/* Clamp at 4 lines to keep card heights uniform in a grid */
.card--review .card__quote {
  font-size:          var(--text-sm);
  color:              var(--color-text-muted);
  line-height:        var(--leading-relaxed);
  display:            -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow:           hidden;
}

/* Decorative opening quote mark — aria-hidden in markup */
.card--review .card__quote::before {
  content:      "\201C";
  font-size:    var(--text-2xl);
  line-height:  0;
  color:        var(--color-primary-100);
  float:        left;
  margin-right: var(--space-1);
  margin-top:   0.3em;
}


/* ==========================================================================
   3. FORMS
   ========================================================================== */

/* --------------------------------------------------------------------------
   3a. Layout helpers
   -------------------------------------------------------------------------- */

/* Vertical stack of labelled form groups */
.form {
  display:        flex;
  flex-direction: column;
  gap:            var(--space-4);
}

/* One field unit: label + control + hint/message */
.form-group {
  display:        flex;
  flex-direction: column;
  gap:            var(--space-1);
}

/*
 * Two-column form grid.
 * Full-width fallback on mobile; side-by-side on tablet+.
 */
.form-grid {
  display:               grid;
  grid-template-columns: 1fr;
  gap:                   var(--space-3);
}

@media (min-width: 768px) {
  .form-grid {
    grid-template-columns: 1fr 1fr;
  }
}

/* Span both columns — for long fields like address or message */
.form-group--full {
  grid-column: 1 / -1;
}


/* --------------------------------------------------------------------------
   3b. Labels & hints
   -------------------------------------------------------------------------- */

.form-label {
  display:     inline-flex;
  align-items: center;
  gap:         var(--space-0-5);
  font-size:   var(--text-sm);
  font-weight: var(--font-medium);
  color:       var(--color-neutral-800);
  line-height: var(--leading-tight);
  cursor:      default;
}

/* Required asterisk — pair with aria-required="true" on the input */
.form-label--required::after {
  content:     "*";
  color:       var(--color-error-500);
  font-weight: var(--font-bold);
}

/* Secondary help text below the input */
.form-hint {
  font-size:   var(--text-xs);
  color:       var(--color-text-subtle);
  line-height: var(--leading-relaxed);
}


/* --------------------------------------------------------------------------
   3c. Text inputs & textarea
   -------------------------------------------------------------------------- */

.form-input,
.form-textarea {
  width:            100%;
  padding:          var(--space-1-5) var(--space-2);
  font-family:      var(--font-sans);
  font-size:        var(--text-sm);
  font-weight:      var(--font-regular);
  color:            var(--color-text);
  line-height:      var(--leading-normal);
  background-color: var(--color-surface);
  border:           1.5px solid var(--color-border);
  border-radius:    var(--radius-md);
  transition:
    border-color     var(--duration-base) var(--ease-in-out),
    background-color var(--duration-base) var(--ease-in-out),
    box-shadow       var(--duration-base) var(--ease-in-out);
  -webkit-appearance: none;
  appearance:         none;
}

.form-input::placeholder,
.form-textarea::placeholder {
  color: var(--color-text-subtle);
}

.form-input:hover:not(:disabled):not([readonly]),
.form-textarea:hover:not(:disabled):not([readonly]) {
  border-color: var(--color-neutral-500);
}

.form-input:focus-visible,
.form-textarea:focus-visible {
  border-color:   var(--color-focus-ring);
  outline:        3px solid var(--color-focus-ring);
  outline-offset: 0;
  box-shadow:     0 0 0 4px rgb(26 100 184 / 0.20);
}

.form-input:disabled,
.form-textarea:disabled {
  background-color: var(--color-neutral-100);
  color:            var(--color-text-subtle);
  border-color:     var(--color-neutral-200);
  cursor:           not-allowed;
}

.form-input[readonly],
.form-textarea[readonly] {
  background-color: var(--color-bg-subtle);
  cursor:           default;
}

/* Only allow vertical resize — horizontal resize breaks grid layouts */
.form-textarea {
  resize:     vertical;
  min-height: 6rem;
}

/* Input size variants */
.form-input--sm {
  padding:       var(--space-1) var(--space-1-5);
  font-size:     var(--text-xs);
  border-radius: var(--radius-sm);
}

.form-input--lg {
  padding:       var(--space-2) var(--space-2-5);
  font-size:     var(--text-base);
  border-radius: var(--radius-lg);
}


/* --------------------------------------------------------------------------
   3d. Select
   Custom chevron via background-image — the SVG is URL-encoded inline so no
   external file is needed. Stroke colour %236c757d = --color-neutral-600.
   -------------------------------------------------------------------------- */

.form-select {
  width:            100%;
  padding:          var(--space-1-5) var(--space-2);
  padding-right:    var(--space-6);   /* room for chevron */
  font-family:      var(--font-sans);
  font-size:        var(--text-sm);
  color:            var(--color-text);
  background-color: var(--color-surface);
  border:           1.5px solid var(--color-border);
  border-radius:    var(--radius-md);
  cursor:           pointer;
  -webkit-appearance: none;
  appearance:         none;

  background-image:    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%236c757d' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat:   no-repeat;
  background-position: right var(--space-2) center;
  background-size:     1rem;

  transition:
    border-color var(--duration-base) var(--ease-in-out),
    box-shadow   var(--duration-base) var(--ease-in-out);
}

.form-select:hover:not(:disabled) {
  border-color: var(--color-neutral-500);
}

.form-select:focus-visible {
  border-color:   var(--color-focus-ring);
  outline:        3px solid var(--color-focus-ring);
  outline-offset: 0;
  box-shadow:     0 0 0 4px rgb(26 100 184 / 0.20);
}

.form-select:disabled {
  background-color: var(--color-neutral-100);
  color:            var(--color-text-subtle);
  border-color:     var(--color-neutral-200);
  cursor:           not-allowed;
  /* Muted chevron — %23adb5bd = --color-neutral-500 */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23adb5bd' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
}

.form-select--sm {
  padding:       var(--space-1) var(--space-1-5);
  padding-right: var(--space-5);
  font-size:     var(--text-xs);
  border-radius: var(--radius-sm);
}

.form-select--lg {
  padding:       var(--space-2) var(--space-2-5);
  padding-right: var(--space-8);
  font-size:     var(--text-base);
  border-radius: var(--radius-lg);
}


/* --------------------------------------------------------------------------
   3e. Checkboxes
   appearance: none removes the browser control; CSS draws the box and tick.
   The tick is an inline SVG data URI for crisp rendering at all DPIs.
   -------------------------------------------------------------------------- */

.form-checkbox {
  display:     flex;
  align-items: flex-start;
  gap:         var(--space-1-5);
  cursor:      pointer;
  user-select: none;
}

.form-checkbox__input {
  -webkit-appearance: none;
  appearance:         none;

  /* Fixed px-equivalent size — not em so it doesn't scale with font-size changes */
  width:       1.125rem;
  height:      1.125rem;
  flex-shrink: 0;
  margin-top:  0.1em;   /* optical alignment with first line of label text */

  background-color: var(--color-surface);
  border:           2px solid var(--color-neutral-400);
  border-radius:    var(--radius-sm);
  cursor:           pointer;

  transition:
    background-color var(--duration-fast) var(--ease-in-out),
    border-color     var(--duration-fast) var(--ease-in-out),
    box-shadow       var(--duration-fast) var(--ease-in-out);
}

.form-checkbox__input:hover:not(:disabled) {
  border-color: var(--color-primary-500);
}

.form-checkbox__input:checked {
  background-color:    var(--color-primary-600);
  border-color:        var(--color-primary-600);
  background-image:    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 6l2.5 2.5 4.5-5' stroke='white' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round' fill='none'/%3E%3C/svg%3E");
  background-repeat:   no-repeat;
  background-position: center;
}

/* Dash mark — for "select all" parent checkboxes in a group */
.form-checkbox__input:indeterminate {
  background-color:    var(--color-primary-600);
  border-color:        var(--color-primary-600);
  background-image:    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cline x1='2.5' y1='6' x2='9.5' y2='6' stroke='white' stroke-width='1.75' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat:   no-repeat;
  background-position: center;
}

.form-checkbox__input:focus-visible {
  outline:        3px solid var(--color-focus-ring);
  outline-offset: 2px;
  box-shadow:     0 0 0 4px rgb(26 100 184 / 0.20);
}

.form-checkbox__input:disabled {
  opacity:          0.5;
  cursor:           not-allowed;
  background-color: var(--color-neutral-100);
  border-color:     var(--color-neutral-300);
}

/* Dim the entire row when its input is disabled */
.form-checkbox:has(.form-checkbox__input:disabled) {
  cursor:  not-allowed;
  opacity: 0.6;
}

.form-checkbox__label {
  font-size:   var(--text-sm);
  color:       var(--color-neutral-800);
  line-height: var(--leading-snug);
}


/* --------------------------------------------------------------------------
   3f. Radio buttons
   Same custom-control technique as checkboxes — circular, filled-dot indicator.
   -------------------------------------------------------------------------- */

.form-radio {
  display:     flex;
  align-items: flex-start;
  gap:         var(--space-1-5);
  cursor:      pointer;
  user-select: none;
}

.form-radio__input {
  -webkit-appearance: none;
  appearance:         none;

  width:       1.125rem;
  height:      1.125rem;
  flex-shrink: 0;
  margin-top:  0.1em;

  background-color: var(--color-surface);
  border:           2px solid var(--color-neutral-400);
  border-radius:    var(--radius-full);
  cursor:           pointer;

  transition:
    background-color var(--duration-fast) var(--ease-in-out),
    border-color     var(--duration-fast) var(--ease-in-out),
    box-shadow       var(--duration-fast) var(--ease-in-out);
}

.form-radio__input:hover:not(:disabled) {
  border-color: var(--color-primary-500);
}

.form-radio__input:checked {
  background-color:    var(--color-primary-600);
  border-color:        var(--color-primary-600);
  /* White inner dot */
  background-image:    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 18'%3E%3Ccircle cx='9' cy='9' r='4' fill='white'/%3E%3C/svg%3E");
  background-repeat:   no-repeat;
  background-position: center;
}

.form-radio__input:focus-visible {
  outline:        3px solid var(--color-focus-ring);
  outline-offset: 2px;
  box-shadow:     0 0 0 4px rgb(26 100 184 / 0.20);
}

.form-radio__input:disabled {
  opacity:          0.5;
  cursor:           not-allowed;
  background-color: var(--color-neutral-100);
  border-color:     var(--color-neutral-300);
}

.form-radio:has(.form-radio__input:disabled) {
  cursor:  not-allowed;
  opacity: 0.6;
}

.form-radio__label {
  font-size:   var(--text-sm);
  color:       var(--color-neutral-800);
  line-height: var(--leading-snug);
}

/* Vertical stack of related checkboxes or radios */
.form-check-group {
  display:        flex;
  flex-direction: column;
  gap:            var(--space-2);
}

.form-check-group--horizontal {
  flex-direction: row;
  flex-wrap:      wrap;
  gap:            var(--space-3);
}


/* --------------------------------------------------------------------------
   3g. Validation states — error & success
   Apply .is-error or .is-success to the .form-group wrapper.
   The message element must have id="X-msg" and the input aria-describedby="X-msg".
   -------------------------------------------------------------------------- */

/* Error */
.form-group.is-error .form-input,
.form-group.is-error .form-textarea,
.form-group.is-error .form-select {
  border-color:     var(--color-error-500);
  background-color: var(--color-error-50);
}

.form-group.is-error .form-input:focus-visible,
.form-group.is-error .form-textarea:focus-visible,
.form-group.is-error .form-select:focus-visible {
  outline-color: var(--color-error-500);
  box-shadow:    0 0 0 4px rgb(239 68 68 / 0.20);
}

.form-group.is-error .form-label {
  color: var(--color-error-700);
}

.form-error-msg {
  display:     flex;
  align-items: flex-start;
  gap:         var(--space-0-5);
  font-size:   var(--text-xs);
  font-weight: var(--font-medium);
  color:       var(--color-error-600);
  line-height: var(--leading-snug);
}

.form-error-msg svg {
  width:       0.875rem;
  height:      0.875rem;
  flex-shrink: 0;
  margin-top:  0.05em;
}

/* Success */
.form-group.is-success .form-input,
.form-group.is-success .form-textarea,
.form-group.is-success .form-select {
  border-color:     var(--color-success-500);
  background-color: var(--color-success-50);
}

.form-group.is-success .form-input:focus-visible,
.form-group.is-success .form-textarea:focus-visible,
.form-group.is-success .form-select:focus-visible {
  outline-color: var(--color-success-600);
  box-shadow:    0 0 0 4px rgb(34 197 94 / 0.20);
}

.form-group.is-success .form-label {
  color: var(--color-success-700);
}

.form-success-msg {
  display:     flex;
  align-items: flex-start;
  gap:         var(--space-0-5);
  font-size:   var(--text-xs);
  font-weight: var(--font-medium);
  color:       var(--color-success-600);
  line-height: var(--leading-snug);
}

.form-success-msg svg {
  width:       0.875rem;
  height:      0.875rem;
  flex-shrink: 0;
  margin-top:  0.05em;
}


/* --------------------------------------------------------------------------
   3h. Honeypot field
   Invisible to humans — bots fill it, server rejects the submission.

   HTML must also set on the wrapper:
     aria-hidden="true"    — screen readers skip it
     tabindex="-1"         — keyboard users cannot tab in
     autocomplete="off"    — browser autofill skips it

   Do NOT use display:none — some bots detect and ignore those fields.
   Off-screen absolute positioning fools bots while the field remains
   inaccessible to sighted and keyboard users.
   -------------------------------------------------------------------------- */

.form-field--honeypot {
  position:       absolute;
  left:           -9999px;
  top:            auto;
  width:          1px;
  height:         1px;
  overflow:       hidden;
  opacity:        0;
  pointer-events: none;
}


/* ==========================================================================
   4. NAVIGATION
   ========================================================================== */

/* --------------------------------------------------------------------------
   4a. Nav links — generic; usable in any nav context
   -------------------------------------------------------------------------- */

.nav-link {
  display:         inline-flex;
  align-items:     center;
  gap:             var(--space-0-5);
  padding:         var(--space-1) var(--space-1-5);
  font-size:       var(--text-sm);
  font-weight:     var(--font-medium);
  color:           var(--color-text-muted);
  border-radius:   var(--radius-md);
  text-decoration: none;
  white-space:     nowrap;
  transition:      var(--transition-colors);
}

.nav-link:hover {
  color:            var(--color-primary-600);
  background-color: var(--color-primary-50);
  text-decoration:  none;
}

.nav-link svg {
  width:       1em;
  height:      1em;
  flex-shrink: 0;
}

/* Dark-background sidebar variant */
.nav-link--sidebar {
  width:   100%;
  color:   var(--color-neutral-300);
  padding: var(--space-1-5) var(--space-2);
}

.nav-link--sidebar:hover {
  color:            var(--color-neutral-0);
  background-color: rgb(255 255 255 / 0.10);
  text-decoration:  none;
}

/* Underline tab-bar variant */
.nav-link--underline {
  border-radius:   0;
  padding-block:   var(--space-1-5);
  border-bottom:   2px solid transparent;
}

.nav-link--underline:hover {
  background-color:    transparent;
  border-bottom-color: var(--color-primary-300);
}


/* --------------------------------------------------------------------------
   4b. Active & current states
   -------------------------------------------------------------------------- */

.nav-link[aria-current="page"],
.nav-link.is-active {
  color:            var(--color-primary-600);
  background-color: var(--color-primary-50);
  font-weight:      var(--font-semibold);
}

.nav-link--sidebar[aria-current="page"],
.nav-link--sidebar.is-active {
  color:            var(--color-neutral-0);
  background-color: var(--color-primary-600);
}

.nav-link--underline[aria-current="page"],
.nav-link--underline.is-active {
  background-color:    transparent;
  color:               var(--color-primary-600);
  border-bottom-color: var(--color-primary-600);
}


/* --------------------------------------------------------------------------
   4c. Dropdown menus
   CSS-only open via :focus-within (keyboard accessibility).
   JS should toggle aria-expanded="true/false" for mouse/touch.
   -------------------------------------------------------------------------- */

.nav-dropdown {
  position: relative;
  display:  inline-flex;
}

/* Toggle — works as <a> or <button> */
.nav-dropdown__toggle {
  display:         inline-flex;
  align-items:     center;
  gap:             var(--space-0-5);
  padding:         var(--space-1) var(--space-1-5);
  font-family:     var(--font-sans);
  font-size:       var(--text-sm);
  font-weight:     var(--font-medium);
  color:           var(--color-text-muted);
  background:      none;
  border:          none;
  border-radius:   var(--radius-md);
  cursor:          pointer;
  white-space:     nowrap;
  text-decoration: none;
  transition:      var(--transition-colors);
}

.nav-dropdown__toggle:hover {
  color:            var(--color-primary-600);
  background-color: var(--color-primary-50);
}

/* Chevron rotates 180° when the panel is open */
.nav-dropdown__chevron {
  width:       0.875rem;
  height:      0.875rem;
  flex-shrink: 0;
  transition:  transform var(--duration-base) var(--ease-in-out);
}

.nav-dropdown[aria-expanded="true"] .nav-dropdown__chevron,
.nav-dropdown:focus-within         .nav-dropdown__chevron {
  transform: rotate(180deg);
}

/* Panel */
.nav-dropdown__menu {
  position:         absolute;
  top:              calc(100% + var(--space-1));
  left:             0;
  z-index:          var(--z-dropdown);
  min-width:        14rem;
  padding:          var(--space-1);
  background-color: var(--color-surface);
  border:           1px solid var(--color-border);
  border-radius:    var(--radius-lg);
  box-shadow:       var(--shadow-xl);

  /* Closed */
  opacity:    0;
  visibility: hidden;
  transform:  translateY(-0.375rem);
  transition:
    opacity    var(--duration-base) var(--ease-out),
    visibility var(--duration-base) step-end,
    transform  var(--duration-base) var(--ease-out);
}

.nav-dropdown:focus-within .nav-dropdown__menu,
.nav-dropdown[aria-expanded="true"] .nav-dropdown__menu {
  opacity:    1;
  visibility: visible;
  transform:  translateY(0);
  transition:
    opacity    var(--duration-base) var(--ease-out),
    visibility var(--duration-base) step-start,
    transform  var(--duration-base) var(--ease-out);
}

/* Right-aligned panel — flush with right edge of toggle */
.nav-dropdown__menu--right {
  left:  auto;
  right: 0;
}

/* Individual menu item — works as <a> or <button> */
.nav-dropdown__item {
  display:         flex;
  align-items:     center;
  gap:             var(--space-1-5);
  width:           100%;
  padding:         var(--space-1-5) var(--space-2);
  font-family:     var(--font-sans);
  font-size:       var(--text-sm);
  color:           var(--color-text);
  background:      none;
  border:          none;
  border-radius:   var(--radius-md);
  text-decoration: none;
  text-align:      left;
  cursor:          pointer;
  transition:      var(--transition-colors);
}

.nav-dropdown__item:hover {
  background-color: var(--color-primary-50);
  color:            var(--color-primary-600);
  text-decoration:  none;
}

.nav-dropdown__item[aria-current],
.nav-dropdown__item.is-active {
  background-color: var(--color-primary-50);
  color:            var(--color-primary-600);
  font-weight:      var(--font-semibold);
}

/* Destructive item variant */
.nav-dropdown__item--danger {
  color: var(--color-error-600);
}

.nav-dropdown__item--danger:hover {
  background-color: var(--color-error-50);
  color:            var(--color-error-700);
}

.nav-dropdown__item svg {
  width:       1rem;
  height:      1rem;
  flex-shrink: 0;
  color:       var(--color-text-subtle);
}

/* Thin rule between item groups */
.nav-dropdown__divider {
  height:           1px;
  margin:           var(--space-1) 0;
  background-color: var(--color-border);
}

/* Section label inside dropdown */
.nav-dropdown__label {
  padding:        var(--space-1-5) var(--space-2) var(--space-0-5);
  font-size:      var(--text-xs);
  font-weight:    var(--font-semibold);
  color:          var(--color-text-subtle);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
}


/* ==========================================================================
   5. BADGES
   ========================================================================== */

/* --------------------------------------------------------------------------
   5a. Base badge
   -------------------------------------------------------------------------- */

.badge {
  display:        inline-flex;
  align-items:    center;
  gap:            var(--space-0-5);
  padding:        0.2em 0.65em;
  font-size:      var(--text-xs);
  font-weight:    var(--font-semibold);
  line-height:    var(--leading-none);
  border:         1px solid transparent;
  border-radius:  var(--radius-full);
  white-space:    nowrap;
  vertical-align: middle;
  letter-spacing: var(--tracking-wide);
}


/* --------------------------------------------------------------------------
   5b. Verified badge — background-checked / identity-confirmed
   -------------------------------------------------------------------------- */

.badge--verified {
  background-color: var(--color-primary-100);
  border-color:     var(--color-primary-200);
  color:            var(--color-primary-700);
}

.badge--verified svg {
  width:  0.75rem;
  height: 0.75rem;
}


/* --------------------------------------------------------------------------
   5c. Category badge — amber accent ties to "tools / energy" brand tone
   -------------------------------------------------------------------------- */

.badge--category {
  background-color: var(--color-accent-100);
  border-color:     var(--color-accent-200);
  color:            var(--color-accent-700);
}


/* --------------------------------------------------------------------------
   5d. Status badges — coloured dot communicates state at a glance
   -------------------------------------------------------------------------- */

.badge--status {
  background-color: var(--color-neutral-100);
  border-color:     var(--color-neutral-200);
  color:            var(--color-neutral-700);
}

/* Shared dot pseudo-element for stateful badges */
.badge--status-active::before,
.badge--status-pending::before,
.badge--status-inactive::before,
.badge--status-error::before {
  content:       "";
  display:       inline-block;
  width:         0.45em;
  height:        0.45em;
  border-radius: var(--radius-full);
  flex-shrink:   0;
  align-self:    center;
}

.badge--status-active {
  background-color: var(--color-success-100);
  border-color:     var(--color-success-200);
  color:            var(--color-success-700);
}
.badge--status-active::before { background-color: var(--color-success-500); }

.badge--status-pending {
  background-color: var(--color-warning-100);
  border-color:     var(--color-warning-200);
  color:            var(--color-warning-700);
}
.badge--status-pending::before { background-color: var(--color-warning-500); }

.badge--status-inactive,
.badge--status-error {
  background-color: var(--color-error-100);
  border-color:     var(--color-error-200);
  color:            var(--color-error-700);
}
.badge--status-inactive::before,
.badge--status-error::before { background-color: var(--color-error-500); }


/* ==========================================================================
   6. ALERTS
   ========================================================================== */

/* --------------------------------------------------------------------------
   6a. Base alert
   Alerts use role="alert" or role="status" in markup for screen readers.
   -------------------------------------------------------------------------- */

.alert {
  display:       flex;
  align-items:   flex-start;
  gap:           var(--space-2);
  padding:       var(--space-2) var(--space-3);
  border:        1px solid transparent;
  border-radius: var(--radius-lg);
  font-size:     var(--text-sm);
  line-height:   var(--leading-relaxed);
}

.alert__icon {
  flex-shrink: 0;
  width:       1.25rem;
  height:      1.25rem;
  margin-top:  0.1em;   /* optical alignment with first text line */
}

.alert__body {
  flex:      1;
  min-width: 0;
}

.alert__title {
  font-weight:   var(--font-semibold);
  line-height:   var(--leading-snug);
  margin-bottom: var(--space-0-5);
}

/* Dismiss button — only rendered for dismissible alerts */
.alert__dismiss {
  flex-shrink:     0;
  display:         flex;
  align-items:     center;
  justify-content: center;
  width:           1.5rem;
  height:          1.5rem;
  margin-left:     auto;
  background:      none;
  border:          none;
  border-radius:   var(--radius-sm);
  cursor:          pointer;
  color:           inherit;
  opacity:         0.55;
  transition:
    opacity          var(--duration-fast) var(--ease-in-out),
    background-color var(--duration-fast) var(--ease-in-out);
}

.alert__dismiss:hover {
  opacity:          1;
  background-color: rgb(0 0 0 / 0.08);
}

.alert__dismiss svg {
  width:  1rem;
  height: 1rem;
}

.alert__dismiss:focus-visible {
  outline:        3px solid currentColor;
  outline-offset: 2px;
  opacity:        1;
}


/* --------------------------------------------------------------------------
   6b. Variants
   -------------------------------------------------------------------------- */

.alert--success {
  background-color: var(--color-success-50);
  border-color:     var(--color-success-200);
  color:            var(--color-success-700);
}

.alert--warning {
  background-color: var(--color-warning-50);
  border-color:     var(--color-warning-200);
  color:            var(--color-warning-700);
}

.alert--error {
  background-color: var(--color-error-50);
  border-color:     var(--color-error-200);
  color:            var(--color-error-700);
}

.alert--info {
  background-color: var(--color-primary-50);
  border-color:     var(--color-primary-200);
  color:            var(--color-primary-700);
}


/* ==========================================================================
   7. AVATAR
   ========================================================================== */

/* --------------------------------------------------------------------------
   7a. Base & sizes
   -------------------------------------------------------------------------- */

.avatar {
  position:        relative;
  display:         inline-flex;
  align-items:     center;
  justify-content: center;
  flex-shrink:     0;
  border-radius:   var(--radius-full);
  overflow:        hidden;
  background-color: var(--color-neutral-200);
  color:           var(--color-neutral-600);
  font-weight:     var(--font-semibold);
  font-family:     var(--font-sans);
  user-select:     none;
}

/* Extra small — 24px */
.avatar--xs {
  width:     1.5rem;
  height:    1.5rem;
  font-size: var(--text-xs);
}

/* Small — 32px */
.avatar--sm {
  width:     2rem;
  height:    2rem;
  font-size: var(--text-xs);
}

/* Medium (default) — 40px */
.avatar--md {
  width:     2.5rem;
  height:    2.5rem;
  font-size: var(--text-sm);
}

/* Large — 56px */
.avatar--lg {
  width:     3.5rem;
  height:    3.5rem;
  font-size: var(--text-base);
}

/* Extra large — 80px */
.avatar--xl {
  width:     5rem;
  height:    5rem;
  font-size: var(--text-md);
}

/* 2xl — 112px; profile hero banner */
.avatar--2xl {
  width:     7rem;
  height:    7rem;
  font-size: var(--text-xl);
}

/* Photo inside avatar */
.avatar__img {
  width:      100%;
  height:     100%;
  object-fit: cover;
  display:    block;
}


/* --------------------------------------------------------------------------
   7b. Placeholder — initials fallback when no photo is available
   -------------------------------------------------------------------------- */

.avatar--placeholder {
  background-color: var(--color-primary-100);
  color:            var(--color-primary-700);
  letter-spacing:   var(--tracking-wide);
  text-transform:   uppercase;
}

/* Alternate palette variants — assigned by a name-hash in JS */
.avatar--placeholder-amber {
  background-color: var(--color-accent-100);
  color:            var(--color-accent-700);
}

.avatar--placeholder-green {
  background-color: var(--color-success-100);
  color:            var(--color-success-700);
}


/* --------------------------------------------------------------------------
   7c. Online status indicator dot
   The indicator must live outside .avatar (which has overflow:hidden).
   Use a .avatar-wrap container so the dot renders on top of the circle.

   Markup pattern:
     <div class="avatar-wrap">
       <div class="avatar avatar--md avatar--placeholder">JD</div>
       <span class="avatar__indicator avatar__indicator--online" aria-hidden="true"></span>
     </div>
   -------------------------------------------------------------------------- */

.avatar-wrap {
  position:    relative;
  display:     inline-flex;
  flex-shrink: 0;
}

.avatar__indicator {
  position:         absolute;
  bottom:           2px;
  right:            2px;
  width:            28%;
  height:           28%;
  min-width:        0.5rem;
  min-height:       0.5rem;
  border-radius:    var(--radius-full);
  border:           2px solid var(--color-surface);
  background-color: var(--color-neutral-400);   /* offline / unknown */
}

.avatar__indicator--online { background-color: var(--color-success-500); }
.avatar__indicator--away   { background-color: var(--color-warning-500); }
.avatar__indicator--busy   { background-color: var(--color-error-500);   }


/* --------------------------------------------------------------------------
   7d. Avatar group — overlapping stack (e.g. job assignees)
   -------------------------------------------------------------------------- */

.avatar-group {
  display:     inline-flex;
  align-items: center;
}

.avatar-group .avatar-wrap,
.avatar-group .avatar {
  border:      2px solid var(--color-surface);
  margin-left: -0.625rem;
}

.avatar-group .avatar-wrap:first-child,
.avatar-group .avatar:first-child {
  margin-left: 0;
}

/* "+N more" overflow count shown when the list is truncated */
.avatar-group__overflow {
  display:          inline-flex;
  align-items:      center;
  justify-content:  center;
  margin-left:      -0.625rem;
  border:           2px solid var(--color-surface);
  border-radius:    var(--radius-full);
  background-color: var(--color-neutral-200);
  color:            var(--color-neutral-600);
  font-size:        var(--text-xs);
  font-weight:      var(--font-semibold);
  font-family:      var(--font-sans);
}


/* ==========================================================================
   8. SEARCH BAR
   ========================================================================== */

/* --------------------------------------------------------------------------
   8a. Input + button composite
   The entire bar is one visual unit. :focus-within outlines the container
   rather than the raw input, which feels more cohesive for a compound control.
   -------------------------------------------------------------------------- */

.search-bar {
  display:          flex;
  align-items:      stretch;
  width:            100%;
  background-color: var(--color-surface);
  border:           1.5px solid var(--color-border);
  border-radius:    var(--radius-full);
  overflow:         hidden;
  transition:
    border-color var(--duration-base) var(--ease-in-out),
    box-shadow   var(--duration-base) var(--ease-in-out);
}

.search-bar:focus-within {
  border-color: var(--color-focus-ring);
  box-shadow:   0 0 0 4px rgb(26 100 184 / 0.20);
}

/* Decorative leading icon — not interactive */
.search-bar__icon {
  display:        flex;
  align-items:    center;
  padding-left:   var(--space-2);
  color:          var(--color-text-subtle);
  flex-shrink:    0;
  pointer-events: none;
}

.search-bar__icon svg {
  width:  1.125rem;
  height: 1.125rem;
}

/* Input — grows to fill available space; no individual outline */
.search-bar__input {
  flex:        1;
  min-width:   0;
  padding:     var(--space-1-5) var(--space-1-5);
  font-family: var(--font-sans);
  font-size:   var(--text-sm);
  color:       var(--color-text);
  background:  none;
  border:      none;
  outline:     none;
}

.search-bar__input::placeholder {
  color: var(--color-text-subtle);
}

/* Clear button — JS adds .has-value to .search-bar to show it */
.search-bar__clear {
  display:         none;
  align-items:     center;
  justify-content: center;
  padding:         0 var(--space-1);
  background:      none;
  border:          none;
  cursor:          pointer;
  color:           var(--color-text-subtle);
  border-radius:   var(--radius-full);
  flex-shrink:     0;
  transition:      color var(--duration-fast) var(--ease-in-out);
}

.search-bar__clear:hover { color: var(--color-text); }
.search-bar__clear svg { width: 1rem; height: 1rem; }

.search-bar.has-value .search-bar__clear { display: flex; }

/* Visual separator before the submit button */
.search-bar__divider {
  width:            1px;
  flex-shrink:      0;
  margin-block:     var(--space-1);
  background-color: var(--color-border);
}

/* Submit button — rounded on right only, square-joined on left */
.search-bar__btn {
  display:          flex;
  align-items:      center;
  gap:              var(--space-1);
  padding:          var(--space-1-5) var(--space-3);
  font-family:      var(--font-sans);
  font-size:        var(--text-sm);
  font-weight:      var(--font-semibold);
  color:            var(--color-text-inverse);
  background-color: var(--color-primary-600);
  border:           none;
  border-radius:    0 var(--radius-full) var(--radius-full) 0;
  cursor:           pointer;
  white-space:      nowrap;
  flex-shrink:      0;
  transition:       background-color var(--duration-base) var(--ease-in-out);
}

.search-bar__btn:hover  { background-color: var(--color-primary-500); }
.search-bar__btn:active { background-color: var(--color-primary-700); }

.search-bar__btn svg { width: 1rem; height: 1rem; }

.search-bar__btn:focus-visible {
  outline:        3px solid var(--color-focus-ring);
  outline-offset: 2px;
}

/* Large variant — homepage hero search */
.search-bar--lg .search-bar__input {
  padding:   var(--space-2) var(--space-2);
  font-size: var(--text-base);
}

.search-bar--lg .search-bar__btn {
  padding:   var(--space-2) var(--space-4);
  font-size: var(--text-base);
}

.search-bar--lg .search-bar__icon svg {
  width:  1.375rem;
  height: 1.375rem;
}


/* --------------------------------------------------------------------------
   8b. Filter tags — applied filters shown as removable pills
   -------------------------------------------------------------------------- */

.filter-tags {
  display:     flex;
  flex-wrap:   wrap;
  align-items: center;
  gap:         var(--space-1);
}

.filter-tags__label {
  font-size:      var(--text-xs);
  font-weight:    var(--font-semibold);
  color:          var(--color-text-subtle);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  white-space:    nowrap;
}

/* Individual tag */
.filter-tag {
  display:          inline-flex;
  align-items:      center;
  gap:              var(--space-0-5);
  padding:          var(--space-0-5) var(--space-1-5);
  font-size:        var(--text-xs);
  font-weight:      var(--font-medium);
  color:            var(--color-primary-700);
  background-color: var(--color-primary-50);
  border:           1px solid var(--color-primary-200);
  border-radius:    var(--radius-full);
  white-space:      nowrap;
  transition:       var(--transition-colors);
}

.filter-tag--removable:hover {
  background-color: var(--color-primary-100);
  border-color:     var(--color-primary-300);
}

/* Remove (×) button inside the tag */
.filter-tag__remove {
  display:         flex;
  align-items:     center;
  justify-content: center;
  width:           0.875rem;
  height:          0.875rem;
  padding:         0;
  background:      none;
  border:          none;
  cursor:          pointer;
  color:           inherit;
  opacity:         0.65;
  border-radius:   var(--radius-full);
  flex-shrink:     0;
  transition:      opacity var(--duration-fast) var(--ease-in-out);
}

.filter-tag__remove:hover { opacity: 1; }
.filter-tag__remove svg { width: 0.75rem; height: 0.75rem; }

.filter-tag__remove:focus-visible {
  outline:        2px solid var(--color-focus-ring);
  outline-offset: 1px;
  opacity:        1;
}

/* "Clear all" text link at end of the row */
.filter-tags__clear-all {
  display:               inline-flex;
  align-items:           center;
  padding:               var(--space-0-5) var(--space-1);
  font-family:           var(--font-sans);
  font-size:             var(--text-xs);
  font-weight:           var(--font-medium);
  color:                 var(--color-text-subtle);
  background:            none;
  border:                none;
  border-radius:         var(--radius-sm);
  cursor:                pointer;
  text-decoration:       underline;
  text-underline-offset: 0.2em;
  transition:            color var(--duration-base) var(--ease-in-out);
}

.filter-tags__clear-all:hover { color: var(--color-error-600); }


/* ==========================================================================
   9. STAR RATING
   ========================================================================== */

/* --------------------------------------------------------------------------
   9a. Display-only rating
   Stars are SVG icons coloured via currentColor. A half-star uses two
   overlapping stars in a wrapper: the filled one is clipped to 50%.

   Markup for a half star:
     <span class="rating__star-wrap">
       <svg class="rating__star">…</svg>
       <svg class="rating__star rating__star--filled rating__star--half">…</svg>
     </span>
   -------------------------------------------------------------------------- */

.rating {
  display:     inline-flex;
  align-items: center;
  gap:         var(--space-1);
}

.rating__stars {
  display:     inline-flex;
  align-items: center;
  gap:         0.125rem;   /* 2px — stars sit close together like a real star row */
}

.rating__star {
  width:       1rem;
  height:      1rem;
  flex-shrink: 0;
  color:       var(--color-neutral-300);   /* empty state */
}

.rating__star--filled {
  color: var(--color-accent-500);
}

/* Half-star wrapper — stacks two copies; clips the top (filled) to left 50% */
.rating__star-wrap {
  position:    relative;
  display:     inline-flex;
  flex-shrink: 0;
}

.rating__star-wrap .rating__star { width: 1rem; height: 1rem; }

.rating__star-wrap .rating__star--half {
  position:  absolute;
  inset:     0;
  clip-path: inset(0 50% 0 0);
}

/* Score and count labels */
.rating__score {
  font-size:   var(--text-sm);
  font-weight: var(--font-bold);
  color:       var(--color-neutral-800);
  line-height: 1;
}

.rating__count {
  font-size:   var(--text-xs);
  color:       var(--color-text-subtle);
  white-space: nowrap;
}

/* Size modifiers */
.rating--sm .rating__star,
.rating--sm .rating__star-wrap .rating__star { width: 0.75rem; height: 0.75rem; }
.rating--sm .rating__score                   { font-size: var(--text-xs); }

.rating--lg .rating__star,
.rating--lg .rating__star-wrap .rating__star { width: 1.5rem; height: 1.5rem; }
.rating--lg .rating__score                   { font-size: var(--text-md); }


/* --------------------------------------------------------------------------
   9b. Interactive star rating input
   Pure CSS technique using reversed DOM order + flex row-reverse +
   general sibling selector to fill lower-value stars on hover/select.

   Required markup (stars ordered 5 → 1 in DOM; row-reverse displays 1 → 5):
   <fieldset class="rating-input">
     <legend class="rating-input__prompt">Your rating</legend>
     <div class="rating-input__stars">
       <input class="rating-input__input" type="radio" name="r" id="r5" value="5">
       <label class="rating-input__label" for="r5" aria-label="5 stars">★</label>
       <input class="rating-input__input" type="radio" name="r" id="r4" value="4">
       <label class="rating-input__label" for="r4" aria-label="4 stars">★</label>
       <!-- ... r3, r2, r1 ... -->
     </div>
   </fieldset>
   -------------------------------------------------------------------------- */

.rating-input {
  border:  none;
  padding: 0;
  margin:  0;
}

.rating-input__prompt {
  display:       block;
  font-size:     var(--text-sm);
  font-weight:   var(--font-medium);
  color:         var(--color-text-muted);
  margin-bottom: var(--space-1);
}

.rating-input__stars {
  display:        inline-flex;
  flex-direction: row-reverse;   /* visual order: 1 2 3 4 5 left-to-right */
  gap:            0.125rem;
}

/* Visually hidden but keyboard-focusable — clip instead of display:none */
.rating-input__input {
  position:    absolute;
  width:       1px;
  height:      1px;
  margin:      -1px;
  overflow:    hidden;
  clip:        rect(0, 0, 0, 0);
  white-space: nowrap;
}

.rating-input__label {
  display:     flex;
  align-items: center;
  cursor:      pointer;
  color:       var(--color-neutral-300);
  font-size:   1.75rem;
  line-height: 1;
  transition:
    color     var(--duration-fast) var(--ease-in-out),
    transform var(--duration-fast) var(--ease-in-out);
}

/*
 * Hover: colour the focused star AND all siblings that come after it in DOM
 * (= lower-value stars that appear to the left on screen due to row-reverse).
 */
.rating-input__label:hover,
.rating-input__label:hover ~ .rating-input__label {
  color:     var(--color-accent-400);
  transform: scale(1.20);
}

/*
 * Checked: fill the selected star's label (input + label pair) and all
 * lower-value labels that follow in DOM via the sibling combinator.
 */
.rating-input__input:checked + .rating-input__label,
.rating-input__input:checked ~ .rating-input__input + .rating-input__label {
  color: var(--color-accent-500);
}

/* Remove scale on non-hovered checked stars */
.rating-input__input:checked + .rating-input__label:not(:hover),
.rating-input__input:checked ~ .rating-input__input + .rating-input__label:not(:hover) {
  transform: none;
}

/*
 * Reset checked colours while the user hovers a different value,
 * so the hover preview takes full visual precedence.
 */
.rating-input__stars:hover .rating-input__input:checked + .rating-input__label,
.rating-input__stars:hover .rating-input__input:checked ~ .rating-input__input + .rating-input__label {
  color: var(--color-neutral-300);
}

/* Focus ring on the visible label when its hidden input is keyboard-focused */
.rating-input__input:focus-visible + .rating-input__label {
  outline:        3px solid var(--color-focus-ring);
  outline-offset: 2px;
  border-radius:  var(--radius-sm);
}
