/* ==========================================================================
   Shared components. Load AFTER tokens.css and styles.css, BEFORE page CSS
   so pages can still override.

   Everything here uses CSS logical properties (inline-start/inline-end)
   rather than left/right, so the RTL layout is expressed directly instead of
   being hand-mirrored.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Focus — a single visible ring for every interactive element.
   Only shows for keyboard users; pointer users never see it.
   -------------------------------------------------------------------------- */

/* No border-radius here — the outline already follows the element's own
   radius. Forcing one turned round icon buttons into rounded squares. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--primary-color);
  outline-offset: var(--focus-ring-offset);
}

/* Never leave a control with no focus affordance. */
:where(a, button, input, select, textarea, [tabindex]):focus:not(:focus-visible) {
  outline: none;
}

/* --------------------------------------------------------------------------
   Skip link — first tab stop on every page. Without it a keyboard user tabs
   through ~15 chrome controls before reaching content.
   -------------------------------------------------------------------------- */

.skip-link {
  position: fixed;
  inset-block-start: var(--space-2);
  inset-inline-start: var(--space-2);
  z-index: var(--z-toast);
  padding: var(--space-3) var(--space-5);
  background: var(--primary-color);
  color: var(--on-primary);
  font-weight: var(--weight-semibold);
  font-size: var(--text-base);
  border-radius: var(--radius-sm);
  box-shadow: var(--elev-4);
  text-decoration: none;
  transform: translateY(calc(-100% - var(--space-4)));
  transition: transform var(--transition-fast);
}

.skip-link:focus {
  transform: translateY(0);
}

/* --------------------------------------------------------------------------
   Screen-reader-only text
   -------------------------------------------------------------------------- */

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.sr-only-focusable:focus {
  position: static;
  width: auto;
  height: auto;
  margin: 0;
  overflow: visible;
  clip-path: none;
  white-space: normal;
}

/* --------------------------------------------------------------------------
   Card — one base for what were 7 independent card variants, each redeclaring
   background / border / radius / padding / shadow.
   -------------------------------------------------------------------------- */

.card {
  background: var(--surface-1);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  box-shadow: var(--elev-2);
  transition: box-shadow var(--transition-normal),
              transform var(--transition-normal),
              border-color var(--transition-normal);
}

.card--glass {
  background: var(--glass-bg);
  border-color: var(--glass-border);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
}

.card--flat {
  box-shadow: none;
}

.card--padded {
  padding: var(--space-6);
}

.card--compact {
  padding: var(--space-4);
}

/* Interactive cards get real affordances. Lift via transform + shadow only —
   never a size change, which would reflow neighbours on hover. */
.card--interactive {
  cursor: pointer;
  text-align: start;
  width: 100%;
  font: inherit;
  color: inherit;
}

.card--interactive:hover {
  box-shadow: var(--elev-4);
  transform: translateY(-2px);
  border-color: rgba(var(--primary-rgb), 0.35);
}

.card--interactive:active {
  transform: translateY(0);
  box-shadow: var(--elev-2);
}

@media (hover: none) {
  /* Touch devices have no hover; the sticky hover state just looks stuck. */
  .card--interactive:hover {
    transform: none;
    box-shadow: var(--elev-2);
    border-color: var(--border-color);
  }
}

/* --------------------------------------------------------------------------
   Button — one base for what were 17 independent button variants.
   Every button meets the 44px minimum tap target.
   -------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--tap-target);
  padding-block: var(--space-3);
  padding-inline: var(--space-5);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  font-family: var(--font-ui);
  font-size: var(--text-base);
  font-weight: var(--weight-semibold);
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  user-select: none;
  transition: background-color var(--transition-fast),
              border-color var(--transition-fast),
              color var(--transition-fast),
              box-shadow var(--transition-fast),
              opacity var(--transition-fast);
}

.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
  pointer-events: none;
}

/* Async state: keeps the label width so the button does not resize mid-flight. */
.btn[data-loading="true"] {
  position: relative;
  color: transparent;
  pointer-events: none;
}

.btn[data-loading="true"]::after {
  content: "";
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  width: 1.1em;
  height: 1.1em;
  margin-block-start: -0.55em;
  margin-inline-start: -0.55em;
  border: 2px solid currentColor;
  border-block-start-color: transparent;
  border-radius: var(--radius-full);
  color: var(--on-primary);
  animation: spin 0.7s linear infinite;
}

.btn--primary {
  background: var(--primary-color);
  color: var(--on-primary);
  box-shadow: var(--elev-1);
}

.btn--primary:hover {
  background: var(--primary-light);
  box-shadow: var(--elev-3);
}

.btn--primary:active {
  background: var(--primary-dark);
  box-shadow: var(--elev-1);
}

.btn--secondary {
  background: var(--surface-2);
  color: var(--text-color);
  border-color: var(--border-color);
}

.btn--secondary:hover {
  background: var(--surface-3);
  border-color: var(--border-strong);
}

.btn--ghost {
  background: transparent;
  color: var(--primary-color);
}

.btn--ghost:hover {
  background: rgba(var(--primary-rgb), 0.1);
}

.btn--danger {
  background: var(--danger);
  color: #fff;
}

.btn--danger:hover {
  filter: brightness(1.08);
}

.btn--block {
  width: 100%;
}

.btn--sm {
  min-height: 36px;
  padding-block: var(--space-2);
  padding-inline: var(--space-4);
  font-size: var(--text-sm);
}

.btn--lg {
  min-height: 52px;
  padding-inline: var(--space-6);
  font-size: var(--text-lg);
}

/* Icon-only button. Always pair with an aria-label — an icon font glyph has
   no text node, so without one the control is announced as just "button". */
.btn--icon {
  padding: 0;
  width: var(--tap-target);
  min-width: var(--tap-target);
  height: var(--tap-target);
  border-radius: var(--radius-full);
}

.btn--icon.btn--sm {
  width: 36px;
  min-width: 36px;
  height: 36px;
}

/* Icons inside buttons should never be announced separately. */
.btn > i[class*="bi-"] {
  font-size: 1.1em;
  line-height: 1;
}

/* --------------------------------------------------------------------------
   Form controls
   -------------------------------------------------------------------------- */

.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.field-label {
  font-size: var(--text-base);
  font-weight: var(--weight-semibold);
  color: var(--text-color);
}

.field-hint {
  font-size: var(--text-sm);
  color: var(--text-secondary);
}

.field-error {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  color: var(--danger);
}

.input,
.select,
.textarea {
  width: 100%;
  min-height: var(--tap-target);
  padding-block: var(--space-3);
  padding-inline: var(--space-4);
  background: var(--surface-1);
  color: var(--text-color);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  font-family: var(--font-ui);
  /* 16px minimum prevents iOS Safari from zooming the viewport on focus. */
  font-size: var(--text-md);
  line-height: var(--leading-snug);
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

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

.input:hover,
.select:hover,
.textarea:hover {
  border-color: var(--border-strong);
}

.input:focus-visible,
.select:focus-visible,
.textarea:focus-visible {
  outline: none;
  border-color: var(--primary-color);
  box-shadow: var(--focus-ring);
}

.input[aria-invalid="true"],
.select[aria-invalid="true"],
.textarea[aria-invalid="true"] {
  border-color: var(--danger);
}

.textarea {
  min-height: 96px;
  resize: vertical;
  line-height: var(--leading-normal);
}

.select {
  appearance: none;
  /* Chevron sits on the inline-end edge; in RTL that is the left. */
  padding-inline-end: var(--space-10);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236b7c73'%3E%3Cpath d='M4.5 6.5 8 10l3.5-3.5z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: left var(--space-4) center;
  background-size: 16px;
}

[data-theme="dark"] .select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%238a9a90'%3E%3Cpath d='M4.5 6.5 8 10l3.5-3.5z'/%3E%3C/svg%3E");
}

/* Search field with a leading icon */
.search-field {
  position: relative;
  display: flex;
  align-items: center;
}

.search-field .input {
  padding-inline-start: var(--space-10);
}

.search-field > i[class*="bi-"] {
  position: absolute;
  inset-inline-start: var(--space-4);
  color: var(--text-muted);
  pointer-events: none;
  font-size: 1rem;
}

/* --------------------------------------------------------------------------
   Switch — an accessible replacement for the `<div class="toggle-switch">`
   controls, which had no role, no keyboard path and no accessible name.
   Use on a <button role="switch" aria-checked="...">.
   -------------------------------------------------------------------------- */

.switch {
  position: relative;
  flex-shrink: 0;
  width: 52px;
  height: 32px;
  padding: 0;
  border: none;
  border-radius: var(--radius-full);
  background: var(--surface-3);
  cursor: pointer;
  transition: background-color var(--transition-normal);
}

.switch::after {
  content: "";
  position: absolute;
  inset-block-start: 4px;
  inset-inline-start: 4px;
  width: 24px;
  height: 24px;
  border-radius: var(--radius-full);
  background: var(--surface-1);
  box-shadow: var(--elev-2);
  transition: transform var(--transition-normal);
}

.switch[aria-checked="true"] {
  background: var(--primary-color);
}

/* Logical translation: moves toward inline-end in both directions. */
.switch[aria-checked="true"]::after {
  transform: translateX(calc(-1 * (52px - 32px)));
}

:dir(ltr) .switch[aria-checked="true"]::after {
  transform: translateX(calc(52px - 32px));
}

/* Hit area padding without visually enlarging the track. */
.switch::before {
  content: "";
  position: absolute;
  inset: 50% auto auto 50%;
  width: var(--tap-target);
  height: var(--tap-target);
  transform: translate(50%, -50%);
}

/* --------------------------------------------------------------------------
   Dialog chrome
   -------------------------------------------------------------------------- */

.dialog-backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
  background: rgba(8, 16, 12, 0.55);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--transition-normal), visibility var(--transition-normal);
}

.dialog-backdrop.active {
  opacity: 1;
  visibility: visible;
}

.dialog-panel {
  width: min(100%, 440px);
  max-height: min(85vh, 720px);
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--surface-1);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-xl);
  box-shadow: var(--elev-5);
  transform: translateY(12px) scale(0.98);
  transition: transform var(--transition-normal);
}

.dialog-backdrop.active .dialog-panel {
  transform: translateY(0) scale(1);
}

/* --------------------------------------------------------------------------
   Empty & loading states
   -------------------------------------------------------------------------- */

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-12) var(--space-5);
  text-align: center;
  color: var(--text-secondary);
}

.empty-state-icon {
  display: grid;
  place-items: center;
  width: 64px;
  height: 64px;
  border-radius: var(--radius-full);
  background: var(--surface-2);
  color: var(--text-muted);
  font-size: 1.75rem;
}

.empty-state-title {
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
  color: var(--text-color);
}

.empty-state-text {
  font-size: var(--text-base);
  max-width: 42ch;
  line-height: var(--leading-normal);
}

/* Skeletons reserve space so async content does not shift the layout in. */
.skeleton {
  position: relative;
  overflow: hidden;
  background: var(--surface-2);
  border-radius: var(--radius-sm);
}

.skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    90deg,
    transparent,
    rgba(var(--primary-rgb), 0.08),
    transparent
  );
  animation: skeleton-sweep 1.4s ease-in-out infinite;
}

@keyframes skeleton-sweep {
  from { transform: translateX(-100%); }
  to   { transform: translateX(100%); }
}

.skeleton-text {
  height: 0.9em;
  margin-block: 0.3em;
}

.skeleton-text:last-child {
  width: 60%;
}

/* --------------------------------------------------------------------------
   Utilities
   -------------------------------------------------------------------------- */

.stack       { display: flex; flex-direction: column; gap: var(--space-4); }
.stack-sm    { display: flex; flex-direction: column; gap: var(--space-2); }
.stack-lg    { display: flex; flex-direction: column; gap: var(--space-6); }
.row         { display: flex; align-items: center; gap: var(--space-3); }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.wrap        { flex-wrap: wrap; }
.grow        { flex: 1 1 auto; min-width: 0; }

.text-secondary { color: var(--text-secondary); }
.text-muted     { color: var(--text-muted); }
.measure        { max-width: var(--measure); }

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--space-4);
}

.container-narrow {
  max-width: var(--container-narrow);
}

/* Wide content (tables, code, diagrams) scrolls inside its own box rather
   than making the whole page scroll sideways. */
.scroll-x {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
}

/* ==========================================================================
   Tap-target floor

   A live measurement pass at 390px found controls between 24px and 41px tall
   across the app: header icon buttons (40), sidebar close (32), settings
   toggles (26), mood/dhikr chips (37-38), and inline links in features,
   references and bio (24-30).

   These rules raise the interactive area without changing the painted size,
   so nothing looks bigger but everything is comfortably tappable.
   ========================================================================== */

.mood-chip,
.dhikr-chip,
.feature-open-link,
.ref-card-link,
.contact-link,
.dashboard-link,
.font-size-btn {
  min-height: var(--tap-target);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* Narrow controls also need the horizontal minimum, not just the vertical. */
.mood-chip,
.font-size-btn {
  min-width: var(--tap-target);
}

/* Inline text links inside prose keep their visual size but gain a padded
   hit area via a pseudo-element, so line spacing is unaffected. */
.ref-card-link,
.contact-link {
  position: relative;
}

.ref-card-link::after,
.contact-link::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  inset-block: 50%;
  block-size: var(--tap-target);
  transform: translateY(-50%);
}

/* The settings switch track stays 50x26 visually; the hit area is expanded. */
.toggle-switch {
  position: relative;
}

.toggle-switch::before {
  content: "";
  position: absolute;
  inset-block: 50%;
  inset-inline: 50%;
  inline-size: var(--tap-target);
  block-size: var(--tap-target);
  transform: translate(50%, -50%);
}
