/*
 * Shared component styles, loaded globally from base.html.
 */

/* ==========================================================================
   .reveal fallback — never leave content invisible if JS or the
   IntersectionObserver doesn't fire.

   Pages use .reveal / .reveal-left / .reveal-right with opacity:0 + transform
   as the default state, expecting JS to add .revealed when the element scrolls
   into view. If JS is disabled or stalls, the page stays blank. We fix that
   in two layers:

   1. CSS: <html> gets the .has-js class from an inline script at the top of
      <head>. Without that class, content is forced visible.
   2. JS (in base.html): after a short safety timeout, any .reveal that hasn't
      been promoted yet is force-revealed so the user is never left looking
      at a blank page.
   ========================================================================== */

html:not(.has-js) .reveal,
html:not(.has-js) .reveal-left,
html:not(.has-js) .reveal-right {
  opacity: 1 !important;
  transform: none !important;
  transition: none !important;
}

/* Respect users with reduced motion regardless of JS state. */
@media (prefers-reduced-motion: reduce) {
  .reveal,
  .reveal-left,
  .reveal-right {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
}

/* ==========================================================================
   Skeleton shimmer — reusable placeholder for async/dynamic content.

   Usage:
     <div class="skeleton skeleton--text"></div>
     <div class="skeleton skeleton--heading"></div>
     <div class="skeleton skeleton--card"></div>
   Replace with the real content (or remove the .skeleton element) once data
   has loaded.
   ========================================================================== */

.skeleton {
  display: block;
  position: relative;
  overflow: hidden;
  border-radius: 8px;
  background: var(--glass-border);
}
.skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(
    90deg,
    transparent   0%,
    color-mix(in srgb, var(--text-muted) 18%, transparent) 50%,
    transparent   100%
  );
  animation: skeleton-shimmer 1.6s ease-in-out infinite;
}
@keyframes skeleton-shimmer {
  from { transform: translateX(-100%); }
  to   { transform: translateX(100%); }
}
@media (prefers-reduced-motion: reduce) {
  .skeleton::after { animation: none; opacity: 0.6; }
}

.skeleton--text     { height: 0.95rem; margin: 0.4rem 0; border-radius: 6px; }
.skeleton--heading  { height: 1.6rem;  margin: 0.6rem 0; border-radius: 8px; width: 60%; }
.skeleton--card     { height: 180px;   border-radius: 14px; }
.skeleton--pill     { height: 1.85rem; width: 8rem; border-radius: 9999px; display: inline-block; }
.skeleton--circle   { width: 56px; height: 56px; border-radius: 50%; }


/* ==========================================================================
   ALERT BOX — canonical pattern (frontend.md §10)
   Used by static notices in templates AND by FildraFlash (fildra_flash.js).
   Colours resolve to --alert-*-* tokens defined in base.html :root.
   No raw rgba in component files: token-only.
   ========================================================================== */
.alert-box {
  padding: 1.1rem 1.35rem;
  border-radius: var(--radius-md, 12px);
  border-left: 3px solid;
  margin-bottom: 1.25rem;
  color: var(--text-primary);
  font-size: 0.95rem;
  line-height: 1.55;
}
.alert-box-danger  { background: var(--alert-danger-bg);  border-color: var(--alert-danger-border);  color: var(--alert-danger-text); }
.alert-box-success { background: var(--alert-success-bg); border-color: var(--alert-success-border); color: var(--alert-success-text); }
.alert-box-warning { background: var(--alert-warning-bg); border-color: var(--alert-warning-border); color: var(--alert-warning-text); }
.alert-box-info    { background: var(--alert-info-bg);    border-color: var(--alert-info-border);    color: var(--alert-info-text); }

.alert-box-title {
  font-family: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin: 0 0 0.45rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.alert-box-danger  .alert-box-title { color: var(--alert-danger-icon); }
.alert-box-success .alert-box-title { color: var(--alert-success-icon); }
.alert-box-warning .alert-box-title { color: var(--alert-warning-icon); }
.alert-box-info    .alert-box-title { color: var(--alert-info-icon); }

.alert-box-text { margin: 0; }

/* ==========================================================================
   FILDRA FLASH — toast variant of alert-box, rendered by fildra_flash.js
   Two mount modes:
     .fildra-flash-stack       — floating sidebar stack (top-right)
     .pg-inline-alert-mount    — inline inside the interactive section
   Same colour tokens as alert-box, so error=red / success=green / etc.
   ========================================================================== */
.fildra-flash-stack {
  position: fixed;
  top: calc(var(--nav-height, 72px) + 0.75rem);
  right: 1rem;
  z-index: var(--z-tooltip, 1080);
  width: min(360px, calc(100vw - 2rem));
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  pointer-events: none;
}
.pg-inline-alert-mount {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  margin: 0 0 1rem;
}
.pg-inline-alert-mount:empty { display: none; }

.fildra-flash {
  pointer-events: auto;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.85rem 1rem;
  border-radius: var(--radius-md, 12px);
  border-left: 3px solid;
  box-shadow: var(--shadow-sm, 0 2px 8px color-mix(in srgb, var(--surface-0) 55%, transparent));
  animation: fildraFlashIn 180ms ease-out;
}
.fildra-flash.is-dismissing { animation: fildraFlashOut 160ms ease-in forwards; }
.fildra-flash-danger  { background: var(--alert-danger-bg);  border-color: var(--alert-danger-border);  color: var(--alert-danger-text); }
.fildra-flash-success { background: var(--alert-success-bg); border-color: var(--alert-success-border); color: var(--alert-success-text); }
.fildra-flash-warning { background: var(--alert-warning-bg); border-color: var(--alert-warning-border); color: var(--alert-warning-text); }
.fildra-flash-info    { background: var(--alert-info-bg);    border-color: var(--alert-info-border);    color: var(--alert-info-text); }

.fildra-flash__body { display: flex; align-items: flex-start; gap: 0.65rem; flex: 1; min-width: 0; }
.fildra-flash__icon { font-size: 1.05rem; line-height: 1.4; flex-shrink: 0; }
.fildra-flash-danger  .fildra-flash__icon { color: var(--alert-danger-icon); }
.fildra-flash-success .fildra-flash__icon { color: var(--alert-success-icon); }
.fildra-flash-warning .fildra-flash__icon { color: var(--alert-warning-icon); }
.fildra-flash-info    .fildra-flash__icon { color: var(--alert-info-icon); }
.fildra-flash__text  { min-width: 0; }
.fildra-flash__title { font-weight: 600; font-size: 0.92rem; margin-bottom: 0.15rem; }
.fildra-flash__msg   { font-size: 0.88rem; line-height: 1.5; word-break: break-word; }
.fildra-flash__close {
  background: none; border: 0; cursor: pointer;
  color: inherit; opacity: 0.65; font-size: 1rem; line-height: 1;
  padding: 0.15rem 0.35rem; border-radius: 6px;
  transition: opacity var(--transition-fast, 150ms ease), background var(--transition-fast, 150ms ease);
}
.fildra-flash__close:hover { opacity: 1; background: var(--glass-border); }

@keyframes fildraFlashIn  { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: translateY(0); } }
@keyframes fildraFlashOut { from { opacity: 1; transform: translateY(0); }   to { opacity: 0; transform: translateY(-6px); } }
@media (prefers-reduced-motion: reduce) {
  .fildra-flash, .fildra-flash.is-dismissing { animation: none; }
}

/* ==========================================================================
   BUTTON LOCK / PROCESSING — applied by FildraButton.lock (button_lock.js)
   Universal: any backend-hitting button uses this to block double-submit.
   ========================================================================== */
.is-processing {
  cursor: progress !important;
  opacity: 0.78;
  pointer-events: none;
}
.is-processing .fildra-spinner,
.fildra-spinner {
  display: inline-block;
  width: 0.85em; height: 0.85em;
  margin-right: 0.45em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  vertical-align: -0.12em;
  animation: fildraSpin 0.7s linear infinite;
}
@keyframes fildraSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .fildra-spinner { animation: none; border-right-color: currentColor; opacity: 0.4; }
}

/* ==========================================================================
   PLAYGROUND MAINTENANCE — applied by playground_maintenance.js
   When EC2 is down or MAINTENANCE_MODE is set, .is-disabled greys out
   interactive elements while the page itself stays visitable.
   ========================================================================== */
.is-disabled {
  opacity: 0.55 !important;
  pointer-events: none !important;
  cursor: not-allowed !important;
  filter: saturate(0.55);
}
body[data-maintenance="true"] .pg-maintenance-only { display: block; }
.pg-maintenance-only { display: none; }

/* ==========================================================================
   FILDRA CONFIRM — promise-returning replacement for window.confirm().
   Markup is created by app/views/static/js/fildra_confirm.js on first use
   and appended to <body>. Two visual modes: default + .fildra-confirm-danger
   (red CTA) — the JS helper applies the danger class by default since
   most callers are destructive actions.
   ========================================================================== */
.fildra-confirm-modal {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface-overlay, rgba(0, 0, 0, 0.55));
  z-index: 10000;
  padding: 1rem;
  animation: fildra-confirm-fadein 140ms ease;
}
.fildra-confirm-modal[hidden] { display: none; }
body.fildra-confirm-open { overflow: hidden; }

.fildra-confirm-card {
  width: min(440px, 100%);
  background: var(--glass-bg, var(--surface-2, #1a1f2e));
  border: 1px solid var(--glass-border, var(--border-subtle, rgba(255,255,255,0.08)));
  border-left: 4px solid var(--color-primary, #2dd28a);
  border-radius: var(--radius-lg, 12px);
  padding: 1.6rem 1.6rem 1.4rem;
  box-shadow: var(--shadow-lg, 0 18px 48px rgba(0, 0, 0, 0.45));
  backdrop-filter: var(--glass-blur, blur(12px));
  -webkit-backdrop-filter: var(--glass-blur, blur(12px));
  animation: fildra-confirm-pop 160ms cubic-bezier(.2, .9, .35, 1.2);
}
.fildra-confirm-danger .fildra-confirm-card {
  border-left-color: var(--color-danger, #c62828);
}

.fildra-confirm-title {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  font-family: 'DM Sans', 'IBM Plex Sans', system-ui, sans-serif;
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--text-primary, #fff);
  margin-bottom: 0.55rem;
}
.fildra-confirm-icon {
  font-size: 0.95rem;
  color: var(--color-primary, #2dd28a);
}
.fildra-confirm-danger .fildra-confirm-icon {
  color: var(--color-danger, #c62828);
}

.fildra-confirm-body {
  margin: 0 0 1.25rem;
  font-family: 'DM Sans', 'IBM Plex Sans', system-ui, sans-serif;
  font-size: 0.92rem;
  line-height: 1.55;
  color: var(--text-secondary, #b0c4de);
}

.fildra-confirm-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.55rem;
}

.fildra-confirm-cancel,
.fildra-confirm-ok {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  border-radius: var(--radius-sm, 8px);
  padding: 0.55rem 1rem;
  font-family: 'DM Sans', 'IBM Plex Sans', system-ui, sans-serif;
  font-size: 0.88rem;
  font-weight: 500;
  cursor: pointer;
  transition: background 150ms ease, border-color 150ms ease, transform 80ms ease;
  min-height: 38px;
}
.fildra-confirm-cancel {
  background: transparent;
  border: 1px solid var(--border-subtle, rgba(255,255,255,0.14));
  color: var(--text-secondary, #b0c4de);
}
.fildra-confirm-cancel:hover {
  background: var(--surface-raised, rgba(255,255,255,0.05));
  color: var(--text-primary, #fff);
}
.fildra-confirm-ok {
  background: var(--color-primary, #2dd28a);
  border: 1px solid var(--color-primary, #2dd28a);
  color: var(--text-on-primary, #06111f);
}
.fildra-confirm-danger .fildra-confirm-ok {
  background: var(--color-danger, #c62828);
  border-color: var(--color-danger, #c62828);
  color: #fff;
}
.fildra-confirm-ok:hover { filter: brightness(1.08); }
.fildra-confirm-ok:active,
.fildra-confirm-cancel:active { transform: translateY(1px); }

@keyframes fildra-confirm-fadein {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes fildra-confirm-pop {
  from { opacity: 0; transform: translateY(8px) scale(0.98); }
  to   { opacity: 1; transform: translateY(0)   scale(1);    }
}

/* ==========================================================================
   BOOTSTRAP ALERT BRIDGE — server-rendered flash messages (Bootstrap classes
   in get_flashed_messages) inherit our --alert-* tokens so they look
   identical to client-side FildraFlash and inline .alert-box variants.
   This keeps the user-visible promise: error=red, success=green, always.
   ========================================================================== */
.alert.alert-danger,
.alert.alert-error {
  background-color: var(--alert-danger-bg) !important;
  border-color: var(--alert-danger-border) !important;
  color: var(--alert-danger-text) !important;
}
.alert.alert-success {
  background-color: var(--alert-success-bg) !important;
  border-color: var(--alert-success-border) !important;
  color: var(--alert-success-text) !important;
}
.alert.alert-warning {
  background-color: var(--alert-warning-bg) !important;
  border-color: var(--alert-warning-border) !important;
  color: var(--alert-warning-text) !important;
}
.alert.alert-info {
  background-color: var(--alert-info-bg) !important;
  border-color: var(--alert-info-border) !important;
  color: var(--alert-info-text) !important;
}
.alert.alert-danger  i,
.alert.alert-error   i { color: var(--alert-danger-icon); }
.alert.alert-success i { color: var(--alert-success-icon); }
.alert.alert-warning i { color: var(--alert-warning-icon); }
.alert.alert-info    i { color: var(--alert-info-icon); }

/* ==========================================================================
   PILL — small coloured chip used for categories, tags, status, badges.

   Every .pill--<colour> resolves to --pill-<colour>-bg / -border / -text
   tokens, paired in base.html (dark) and light-mode.css (light). Tint and
   text are SEPARATE tokens — that's what stops the "tint stays translucent
   but text never flips to a strong 700-shade" contrast bug that kept
   regressing on cv_models in light mode.

   Usage:
     <span class="pill pill--emerald">Flagship</span>
     <span class="pill pill--indigo pill--lg">Generative AI</span>
   ========================================================================== */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0.25rem 0.625rem;
  font-size: var(--font-size-1, 0.875rem);
  font-weight: 600;
  line-height: 1.2;
  border-radius: var(--radius-full, 9999px);
  border: 1px solid transparent;
  background: var(--pill-slate-bg);
  color: var(--pill-slate-text);
  border-color: var(--pill-slate-border);
  white-space: nowrap;
}
.pill--emerald { background: var(--pill-emerald-bg); color: var(--pill-emerald-text); border-color: var(--pill-emerald-border); }
.pill--indigo  { background: var(--pill-indigo-bg);  color: var(--pill-indigo-text);  border-color: var(--pill-indigo-border); }
.pill--amber   { background: var(--pill-amber-bg);   color: var(--pill-amber-text);   border-color: var(--pill-amber-border); }
.pill--slate   { background: var(--pill-slate-bg);   color: var(--pill-slate-text);   border-color: var(--pill-slate-border); }
.pill--rose    { background: var(--pill-rose-bg);    color: var(--pill-rose-text);    border-color: var(--pill-rose-border); }
.pill--ghost   { background: transparent; color: var(--text-secondary, var(--text-secondary)); border-color: var(--glass-border); }
.pill--sm      { padding: 0.15rem 0.45rem; font-size: 0.72rem; }
.pill--lg      { padding: 0.35rem 0.9rem;  font-size: 0.95rem; }

/* ==========================================================================
   CARD — neutral container that flips theme via --surface-1 / --glass-border.
   Replaces hand-rolled rgba "panel" markup that previously needed a manual
   override in light-mode.css.

   Usage:
     <div class="card">…</div>
     <div class="card card--elevated">…</div>
     <a class="card card--clickable" href="…">…</a>
   ========================================================================== */
.card {
  background: var(--surface-1);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-lg, 16px);
  padding: 1.5rem;
  color: var(--text-primary);
}
.card--elevated  { box-shadow: var(--shadow-md, 0 10px 35px color-mix(in srgb, var(--surface-0) 75%, transparent)); }
.card--clickable {
  transition: transform var(--transition-base, 250ms ease),
              box-shadow var(--transition-base, 250ms ease),
              border-color var(--transition-base, 250ms ease);
  cursor: pointer;
}
.card--clickable:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-lg, 0 20px 60px color-mix(in srgb, var(--surface-0) 90%, transparent));
  border-color: var(--glass-border-hover);
}

/* ==========================================================================
   SURFACE — flat panel utility, used for stat blocks, inline info boxes,
   and brand-tinted call-outs. The "tinted" variants pair a soft background
   with a strong-text token that flips per theme — same anti-drift pattern
   as .pill.
   ========================================================================== */
.surface {
  background: var(--surface-2);
  border-radius: var(--radius-md, 12px);
  padding: 1rem 1.25rem;
}
.surface--strong { background: var(--surface-3); }
.surface--tinted-emerald {
  background: var(--tinted-emerald-bg);
  color: var(--tinted-emerald-text);
}
.surface--tinted-amber {
  background: var(--tinted-amber-bg);
  color: var(--tinted-amber-text);
}
.surface--tinted-rose {
  background: var(--tinted-rose-bg);
  color: var(--tinted-rose-text);
}

/* ==========================================================================
   TOUCH TARGETS — Apple HIG 44x44 minimum, applied only on touch input.
   `pointer: coarse` matches phones/tablets, not mouse-driven desktop. So
   desktop UI keeps Bootstrap's denser .btn padding; mobile gets the floor.
   ========================================================================== */
@media (pointer: coarse) {
  .btn,
  button.btn,
  input[type="submit"],
  input[type="button"],
  .auth-input,
  .form-control:not(textarea):not(.input-sm) {
    min-height: 44px;
  }
  /* Icon-only round buttons opt out via .btn-icon-sm (e.g., the FildraFlash
     close button); they're not primary tap targets and 32px is acceptable. */
  .btn-icon-sm,
  .fildra-flash__close {
    min-height: 32px;
  }
}
