/* ====================================================================
   Reusable Components — Matching PDF 42-Screen UI System
   Buttons, Cards, Badges, Table Rows, Alerts, Modals
   ==================================================================== */

/* ─── Cards & Panels ─── */
.card {
  background-color: var(--surface-card);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xs);
  overflow: hidden;
  margin-bottom: 1.25rem;
}

.card-header {
  padding: 1.15rem 1.5rem;
  border-bottom: 1px solid var(--border-light);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  background-color: var(--surface-card);
}

.card-title {
  font-size: 1rem;
  font-weight: 700;
  color: var(--text-primary);
  line-height: 1.25;
}

.card-body {
  padding: 1.5rem;
}

/* ─── Status Badges with Colored Dots (Matching PDF Page 11 & Page 07) ─── */
.badge-status-dot {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.725rem;
  font-weight: 700;
  padding: 0.2rem 0.55rem;
  border-radius: 9999px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

.badge-status-dot.approved,
.badge-approved {
  background-color: rgba(16, 185, 129, 0.12);
  color: #047857;
  border: 1px solid #A7F3D0;
}

.badge-status-dot.pending,
.badge-pending {
  background-color: rgba(245, 158, 11, 0.12);
  color: #B45309;
  border: 1px solid #FDE68A;
}

.badge-status-dot.rejected,
.badge-rejected {
  background-color: rgba(239, 68, 68, 0.12);
  color: #B91C1C;
  border: 1px solid #FECACA;
}

/* ─── Button System ─── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  padding: 0.55rem 1.15rem;
  font-size: var(--font-size-sm);
  font-weight: 600;
  border-radius: var(--radius-sm);
  transition: all var(--transition-fast);
  cursor: pointer;
  border: 1px solid transparent;
  text-decoration: none;
  line-height: 1.4;
  white-space: nowrap;
  font-family: inherit;
}

.btn:focus-visible {
  outline: 2px solid var(--primary-color);
  outline-offset: 2px;
}

.btn:disabled,
.btn[disabled] {
  opacity: 0.5;
  cursor: not-allowed;
  pointer-events: none;
}

/* Primary Crimson */
.btn-primary {
  background-color: var(--primary-color);
  color: #FFFFFF;
  border-color: var(--primary-color);
  box-shadow: 0 2px 8px rgba(var(--primary-rgb), 0.2);
}
.btn-primary:hover {
  background-color: var(--primary-hover);
  border-color: var(--primary-hover);
  transform: translateY(-1px);
}
.btn-primary:active {
  background-color: var(--primary-dark);
}

/* Outline */
.btn-outline {
  background-color: #FFFFFF;
  border-color: var(--border-color);
  color: var(--text-primary);
}
.btn-outline:hover {
  background-color: var(--bg-main);
  border-color: var(--text-tertiary);
}

/* Ghost */
.btn-ghost {
  background-color: transparent;
  color: var(--text-secondary);
  border-color: transparent;
}
.btn-ghost:hover {
  background-color: var(--bg-main);
  color: var(--text-primary);
}

/* Danger */
.btn-danger {
  background-color: var(--color-danger);
  color: #FFFFFF;
  border-color: var(--color-danger);
}
.btn-danger:hover {
  /* Was #DC2626 — a stranded Tailwind red-600. --primary-dark is darker than
     --color-danger (the base), so the hover still reads as "pressed", and the
     value now tracks the brand ramp instead of drifting from it. */
  background-color: var(--primary-dark);
}

/* Approve sits directly beside "Reject Achievement" in the admin review modals, and
   .btn-primary is brand red #CF0012 against .btn-danger's #EF4444 — two reds for
   opposite, irreversible actions. This uses the same emerald as the "Approved"
   status badge, matching .hod-btn-approve in hod-theme.css. White on #047857 is
   5.5:1, comfortably WCAG AA. Additive: no HOD element carries this class. */
.btn-approve {
  background-color: var(--status-approved-text);
  color: #FFFFFF;
  border-color: var(--status-approved-text);
}
.btn-approve:hover {
  background-color: #036249;
  border-color: #036249;
}

/* Button Sizes */
.btn-sm {
  padding: 0.35rem 0.75rem;
  font-size: 0.75rem;
  border-radius: 4px;
}

.btn-lg {
  padding: 0.75rem 1.5rem;
  font-size: var(--font-size-base);
}

/* ─── Modal Dialog (Pages 19-22 Matching) ─── */
.modal-backdrop {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(11, 17, 32, 0.6);
  backdrop-filter: blur(4px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 200;
  padding: 1.5rem;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--transition-normal), visibility var(--transition-normal);
}

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

.modal-container {
  background-color: var(--surface-card);
  border-radius: var(--radius-lg);
  max-width: 580px;
  width: 100%;
  box-shadow: var(--shadow-xl);
  overflow: hidden;
  transform: translateY(16px) scale(0.98);
  transition: transform var(--transition-normal);
  border: 1px solid var(--border-color);

  /* A dialog taller than the window has to scroll somewhere, and before this it
     scrolled nowhere: the backdrop centres the container, overflow:hidden clips
     it, and the footer holding Approve and Reject simply left the screen with
     no way to reach it. Every modal in the app was one long record away from
     that, and the AI proof panel is long enough to trigger it routinely.
     Capping the height and letting the body scroll inside keeps the header and
     the decision buttons on screen at any content length. */
  max-height: calc(100vh - 3rem);
  display: flex;
  flex-direction: column;
}

/* The scrolling region: header and footer stay put, the record moves. */
.modal-container > .modal-body {
  overflow-y: auto;
  min-height: 0;     /* without this a flex child refuses to shrink below its content */
}
.modal-container > .modal-header,
.modal-container > .modal-footer {
  flex-shrink: 0;
}

.modal-backdrop.active .modal-container {
  transform: translateY(0) scale(1);
}

.modal-header {
  padding: 1.15rem 1.5rem;
  border-bottom: 1px solid var(--border-light);
  display: flex;
  /* Brings this in line with .card-header above, which already wraps. Engages only
     when a long modal title cannot share a row with the close button. */
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
}

.modal-title {
  font-size: 1rem;
  font-weight: 700;
  color: var(--text-primary);
}

.modal-close-btn {
  font-size: 1.25rem;
  color: var(--text-tertiary);
  cursor: pointer;
  border: none;
  background: none;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 4px;
  transition: all 0.15s ease;
}

.modal-close-btn:hover {
  background-color: var(--bg-main);
  color: var(--text-primary);
}

.modal-body {
  padding: 1.5rem;
}

.modal-footer {
  padding: 0.85rem 1.5rem;
  background-color: var(--surface-secondary);
  border-top: 1px solid var(--border-light);
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
}

/* ─── Loading Spinner ─── */
.spinner {
  width: 26px;
  height: 26px;
  border: 2.5px solid var(--border-color);
  border-top-color: var(--primary-color);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
  margin: 0 auto;
}

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

/* ====================================================================
   Base Component Styles — completing the shared design system.
   These class names were referenced by dashboard, table, toast and
   header markup across the app but had no base rules (only their
   responsive overrides lived in responsive.css), so the components
   rendered unstyled. Everything below is built from the design tokens
   in variables.css.
   ==================================================================== */

/* ─── KPI Stat Cards & Metric Grid ─── */
.stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 1.25rem;
  margin-bottom: 1.5rem;
}

.stat-card {
  display: flex;
  align-items: center;
  gap: 1rem;
  background-color: var(--surface-card);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xs);
  padding: 1.25rem 1.5rem;
}

.stat-icon-wrapper {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border-radius: var(--radius-md);
  flex-shrink: 0;
}

.stat-icon-wrapper.primary { background-color: var(--primary-light);        color: var(--primary-color); }
.stat-icon-wrapper.warning { background-color: var(--color-warning-light);  color: var(--secondary-hover); }
.stat-icon-wrapper.success { background-color: var(--color-success-light);  color: var(--color-success); }
.stat-icon-wrapper.danger  { background-color: var(--color-danger-light);   color: var(--color-danger); }

.stat-details {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
}

.stat-value {
  font-size: var(--font-size-3xl);
  font-weight: var(--font-weight-extrabold);
  color: var(--text-primary);
  line-height: 1.1;
  letter-spacing: -0.02em;
}

.stat-label {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: var(--text-secondary);
}

/* ─── Base Badge Pill (colour variants defined higher up) ─── */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.725rem;
  font-weight: 700;
  padding: 0.2rem 0.55rem;
  border-radius: var(--radius-full);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
  line-height: 1.4;
}

.badge-symbol {
  font-size: 0.9em;
  line-height: 1;
}

/* ─── Alerts / Toasts ─── */
.alert {
  padding: 0.85rem 1.1rem;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-color);
  background-color: var(--surface-card);
  color: var(--text-primary);
  font-size: var(--font-size-base);
  font-weight: 500;
}

.alert-success { background-color: var(--color-success-light); color: var(--status-approved-text); border-color: var(--status-approved-border); }
.alert-danger  { background-color: var(--color-danger-light);  color: var(--status-rejected-text); border-color: var(--status-rejected-border); }
.alert-warning { background-color: var(--color-warning-light); color: var(--status-pending-text);  border-color: var(--status-pending-border); }
.alert-info    { background-color: var(--color-info-light);    color: #075985;                      border-color: #BAE6FD; }

/* ─── Header User Widget ─── */
.user-profile-widget {
  display: flex;
  align-items: center;
  gap: 0.65rem;
}

.avatar-circle {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background-color: var(--primary-color);
  color: #FFFFFF;
  font-weight: 700;
  font-size: var(--font-size-sm);
  flex-shrink: 0;
}

.user-details {
  display: flex;
  flex-direction: column;
  line-height: 1.2;
}

.user-name {
  font-size: var(--font-size-base);
  font-weight: 700;
  color: var(--text-primary);
}

.user-role-tag {
  font-size: var(--font-size-xs);
  color: var(--text-secondary);
}

/* ─── Empty & Loading States ─── */
.loading-spinner {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  padding: 2rem 1rem;
  color: var(--text-muted);
  font-size: var(--font-size-sm);
}

.empty-state {
  padding: 2.5rem 1.5rem;
  text-align: center;
  color: var(--text-muted);
}

.empty-state-title {
  display: block;
  font-size: var(--font-size-md);
  font-weight: 700;
  color: var(--text-secondary);
  margin-bottom: 0.25rem;
}

.empty-state-text {
  font-size: var(--font-size-sm);
  color: var(--text-tertiary);
  max-width: 440px;
  margin: 0 auto;
}

/* ─── Utility ─── */
.w-full { width: 100%; }

/* ══════════════════════════════════════════════════════════════════
   SHARING & ACCESS (Track B)
   Used by the "Sharing & Access" fieldset on add-achievement.html and
   by the shared-research.html table. Built from the same tokens as
   everything above, so it inherits the NIET palette automatically.
   ══════════════════════════════════════════════════════════════════ */

/* Three radio "cards" — a bigger click target than a bare radio, and it
   leaves room for the one-line explanation each choice needs. */
.visibility-options {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.75rem;
}

@media (max-width: 720px) {
  .visibility-options { grid-template-columns: 1fr; }
}

.visibility-option {
  position: relative;
  display: flex;
  gap: 0.6rem;
  align-items: flex-start;
  padding: 0.85rem;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  background: var(--surface-card);
  cursor: pointer;
  transition: all var(--transition-fast);
}

.visibility-option:hover {
  border-color: var(--primary-color);
  background: rgba(var(--primary-rgb), 0.02);
}

/* The radio itself stays a real <input> so the keyboard and screen
   readers work; :has() lets the card react without any JavaScript. */
.visibility-option:has(input:checked) {
  border-color: var(--primary-color);
  background: rgba(var(--primary-rgb), 0.04);
  box-shadow: 0 0 0 1px var(--primary-color) inset;
}

.visibility-option:has(input:focus-visible) {
  outline: 2px solid var(--primary-color);
  outline-offset: 2px;
}

.visibility-option input[type="radio"] {
  margin-top: 0.15rem;
  accent-color: var(--primary-color);
  flex-shrink: 0;
  cursor: pointer;
}

.visibility-option-label {
  display: block;
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--text-primary);
  margin-bottom: 0.15rem;
}

.visibility-option-hint {
  display: block;
  font-size: 0.7rem;
  color: var(--text-secondary);
  line-height: 1.35;
}

/* The amber caution box for unlisted sharing. */
.share-warning {
  display: flex;
  gap: 0.6rem;
  align-items: flex-start;
  margin-top: 0.85rem;
  padding: 0.75rem 0.9rem;
  background: rgba(245, 158, 11, 0.09);
  border: 1px solid #FDE68A;
  border-radius: var(--radius-md);
  font-size: 0.75rem;
  line-height: 1.45;
  color: #92400E;
}

.share-warning strong { color: #78350F; }

.share-warning-icon {
  flex-shrink: 0;
  font-size: 0.95rem;
  line-height: 1.3;
}

/* Duration select + the custom date/time input that appears beside it. */
.share-expiry-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
  margin-top: 0.85rem;
}

@media (max-width: 720px) {
  .share-expiry-row { grid-template-columns: 1fr; }
}

/* A generated link, shown read-only next to a Copy button. */
.share-link-box {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  margin-top: 0.75rem;
}

.share-link-box input {
  flex: 1;
  min-width: 0;
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 0.72rem;
  background: var(--bg-main);
  color: var(--text-secondary);
}

/* ─── Visibility badges ─── */
.badge-visibility {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.675rem;
  font-weight: 700;
  padding: 0.18rem 0.5rem;
  border-radius: 9999px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

.badge-visibility.public {
  background-color: rgba(2, 132, 199, 0.12);
  color: #075985;
  border: 1px solid #BAE6FD;
}

.badge-visibility.unlisted {
  background-color: rgba(245, 158, 11, 0.12);
  color: #B45309;
  border: 1px solid #FDE68A;
}

.badge-visibility.private {
  background-color: rgba(100, 116, 139, 0.12);
  color: #475569;
  border: 1px solid #CBD5E1;
}

/* ─── Share-link lifecycle states ─── */
.badge-share-state {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.675rem;
  font-weight: 700;
  padding: 0.18rem 0.5rem;
  border-radius: 9999px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

.badge-share-state.active {
  background-color: rgba(16, 185, 129, 0.12);
  color: #047857;
  border: 1px solid #A7F3D0;
}

.badge-share-state.expired {
  background-color: rgba(100, 116, 139, 0.12);
  color: #475569;
  border: 1px solid #CBD5E1;
}

.badge-share-state.revoked {
  background-color: rgba(239, 68, 68, 0.12);
  color: #B91C1C;
  border: 1px solid #FECACA;
}

/* ====================================================================
   AI PROOF READING — the panel behind "Check proof with AI"

   Shared by the HOD review modal and the admin review modal, so it lives
   in components.css (both portals load it) and is built from the base
   tokens in variables.css rather than the --hod-* aliases, which only the
   HOD pages define.

   Styled as a quiet reading aid rather than a result. It is inset, greyer
   than the record above it, and borrows none of the approve/reject
   buttons' weight: the decision on this screen belongs to the person
   reading, and a panel that looked authoritative would compete for it.
   Only the row borders carry colour, and only enough to sort by eye.
   ==================================================================== */
.ai-proof-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 0.75rem; }

.ai-proof-panel {
  margin-top: 12px;
  background: var(--surface-hover);
  border: 1px solid var(--border-color);
  border-radius: 10px;
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.ai-proof-panel[hidden] { display: none; }

.ai-proof-loading { display: flex; align-items: center; gap: 12px; font-size: 13px; color: var(--text-secondary); }
/* Sized down from the shared .spinner, which is centred and page-sized. */
.ai-proof-spinner { width: 18px; height: 18px; border-width: 2px; margin: 0; flex-shrink: 0; }

.ai-proof-head { display: flex; align-items: center; gap: 8px; font-size: 14px; font-weight: 600; color: var(--text-primary); }
.ai-proof-head .material-symbols-outlined { font-size: 20px; color: var(--primary-color); }
.ai-proof-source { margin: -4px 0 0; font-size: 12px; color: var(--text-secondary); }

/* The standing caution lands here. Small, but never hidden behind a toggle. */
.ai-proof-warning { margin: 0; font-size: 12px; line-height: 1.5; color: var(--text-secondary); padding-left: 14px; position: relative; }
.ai-proof-warning::before { content: '•'; position: absolute; left: 2px; color: var(--text-tertiary); }

.ai-proof-row {
  border: 1px solid var(--border-color);
  border-left-width: 3px;
  border-radius: 6px;
  background: var(--surface-card);
  padding: 10px 12px;
}
.ai-proof-row.mismatch { border-left-color: var(--status-rejected-text); }
.ai-proof-row.absent   { border-left-color: var(--status-pending-text); }
.ai-proof-row.match    { border-left-color: var(--status-approved-text); opacity: .72; }

.ai-proof-row-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 6px; }
.ai-proof-field { font-size: 13px; font-weight: 600; color: var(--text-primary); }
.ai-proof-verdict { font-size: 11px; font-weight: 600; letter-spacing: .02em; padding: 2px 8px; border-radius: 6px; white-space: nowrap; }
.ai-proof-verdict.mismatch { background: var(--status-rejected-bg); color: var(--status-rejected-text); }
.ai-proof-verdict.absent   { background: var(--status-pending-bg); color: var(--status-pending-text); }
.ai-proof-verdict.match    { background: var(--status-approved-bg); color: var(--status-approved-text); }

/* Both values side by side, because the two together are the finding. */
.ai-proof-values { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 4px 16px; font-size: 13px; color: var(--text-primary); word-break: break-word; }
.ai-proof-vlabel { display: block; font-size: 10px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--text-secondary); margin-bottom: 1px; }
.ai-proof-empty { color: var(--text-secondary); font-style: italic; }
.ai-proof-note { margin: 6px 0 0; font-size: 12px; line-height: 1.5; color: var(--text-secondary); }

@media (max-width: 560px) {
  .ai-proof-values { grid-template-columns: 1fr; gap: 8px; }
}
