/* Design tokens (POC.md section 18.6) - kept in lock-step with
   app/report/static/report_screen.css's own palette/font tokens so the web
   UI and the report share one design system (M3-7). */
:root {
  --color-bg: #fafafa;
  --color-surface: #ffffff;
  --color-text: #1e2430;
  --color-muted: #5b6472;
  --color-border: #d7dbe0;
  /* M3-7: distinct teal accent - the old #2f5d8a was identical to
     --color-low (see report_screen.css for the full rationale). */
  --color-accent: #0b6e73;

  --color-critical: #b3261e;
  /* M3-7: darkened for WCAG AA with white chip text (was #e07a1f, 2.92:1). */
  --color-high: #b34700;
  --color-medium: #c9a227;
  --color-low: #2f5d8a;
  --color-info: #6b7280;

  --font-sans: "Inter", "Noto Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: "JetBrains Mono", "DejaVu Sans Mono", "Consolas", monospace;
}

/* M3-7: the app shell (this file) doesn't bundle its own font files - it
   always loads alongside /report-static/report_screen.css (web/index.html),
   whose @font-face rules already register "Inter"/"JetBrains Mono" globally
   for the page, so no duplicate @font-face block is needed here. */

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: var(--font-sans);
  color: var(--color-text);
  background: var(--color-bg);
  line-height: 1.5;
}

.app {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
}

.view.hidden {
  display: none;
}

/* Generic visibility utility, distinct from .view.hidden above (that one
   is scoped to the three top-level views) - used by watch-mode elements
   (.watch-banner, .stale-note) that toggle independent of which view is
   showing. */
.hidden {
  display: none;
}

.card {
  width: 100%;
  max-width: 30rem;
  background: var(--color-surface);
  border-radius: 0.75rem;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.06);
  padding: 2rem;
}

.result-card {
  max-width: 48rem;
}

/* Owner-directed simple "P" branding (session 13). .brand-logo's own
   sizing comes from report_screen.css (already loaded on this page,
   shared with the report's cover section) - only the lockup layout is
   defined here. */
.brand-lockup {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  margin-bottom: 0.25rem;
}

.brand-lockup .brand-logo {
  margin-bottom: 0;
}

.tagline {
  text-align: center;
  color: var(--color-muted);
  margin: 0 0 1.5rem;
}

#scan-form {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}

/* Landing arrival (Task 2, UI_REDESIGN_PLAN.md) - one brief, staggered
   fade+settle on true first paint only. web/js/app.js adds .arrival-mark/
   .arrival-form once at script init, then strips both the moment a scan
   is actually submitted - so by the time Landing can ever be shown again
   (Cancel/"Scan another"), the classes are already gone and the
   animation does not replay. Deliberate choice, not an accident: a plain
   CSS animation restarts every time its container's display toggles from
   none back to visible, which showView("landing") does on every return
   trip - stripping the classes first is what makes this a one-shot
   arrival instead. Opacity/transform only, never a dimension/margin, so
   it cannot introduce horizontal overflow at any viewport width. */
@keyframes arrival-settle {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.arrival-mark,
.arrival-form {
  animation: arrival-settle 350ms ease-out both;
}

.arrival-form {
  animation-delay: 100ms;
}

@media (prefers-reduced-motion: reduce) {
  .arrival-mark,
  .arrival-form {
    animation: none;
  }
}

#target-input {
  font-size: 1rem;
  padding: 0.65rem 0.85rem;
  border: 1px solid var(--color-border);
  border-radius: 0.5rem;
  font-family: var(--font-sans);
}

#target-input:focus,
button:focus-visible,
input:focus-visible,
a:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
}

.checkbox-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.92rem;
}

details.advanced summary {
  cursor: pointer;
  color: var(--color-accent);
  font-size: 0.9rem;
}

button,
.button {
  font-size: 1rem;
  padding: 0.65rem 1rem;
  border-radius: 0.5rem;
  border: none;
  background: var(--color-accent);
  color: #fff;
  cursor: pointer;
  text-align: center;
  text-decoration: none;
  display: inline-block;
  font-family: var(--font-sans);
}

button:disabled {
  background: #a7b3c2;
  cursor: not-allowed;
}

button.secondary {
  background: transparent;
  color: var(--color-accent);
  border: 1px solid var(--color-accent);
}

.message {
  min-height: 1.25rem;
  font-size: 0.9rem;
  color: var(--color-critical);
  margin: 0;
}

.message.info {
  color: var(--color-muted);
}

/* Watch mode (Option B: joining an already-running scan via POST /scan's
   409 response, web/js/app.js's enterWatchMode) - makes ownership of what
   this view is showing unambiguous. Reuses .message.info/.checkbox-row
   rather than introducing new component styling. */
.watch-banner {
  margin: 0 0 1rem;
}

.watch-banner .message {
  margin: 0 0 0.5rem;
}

.hint {
  color: var(--color-muted);
  font-size: 0.9rem;
  margin: 0 0 1rem;
}

.progress-bar-track {
  height: 0.6rem;
  border-radius: 999px;
  background: #e5e8ec;
  overflow: hidden;
  margin: 1rem 0 0.5rem;
}

.progress-bar-fill {
  height: 100%;
  background: var(--color-accent);
  transition: width 0.4s ease;
}

/* Progress checklist (POC.md section 18.1: "genericized phase checklist +
   bar; each row pending / active / done") - replaces a single line of text
   that silently swapped in place; state is conveyed by icon fill/border
   *and* text weight/colour together, never colour alone (section 18.6
   accessibility floor), plus a visually-hidden state word for screen
   readers on each row. */
.progress-checklist {
  list-style: none;
  margin: 0.85rem 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
}

.phase-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-size: 0.88rem;
  color: var(--color-muted);
}

.phase-icon {
  flex: none;
  width: 0.8rem;
  height: 0.8rem;
  border-radius: 999px;
  border: 2px solid var(--color-border);
  box-sizing: border-box;
  /* Task 3 (UI_REDESIGN_PLAN.md): lets the active<->done flip settle
     instead of snapping instantly. Only visible now that web/js/app.js's
     renderChecklist updates each row's className on a persistent <li>
     element rather than rebuilding it from scratch every call - a
     from-scratch node has no prior state for a transition to animate from. */
  transition: border-color 0.3s ease, background-color 0.3s ease;
}

.phase-active .phase-text {
  color: var(--color-text);
  font-weight: 600;
}

.phase-active .phase-icon {
  border-color: var(--color-accent);
  /* Task 3: finite (2 iterations), not infinite - a phase can legitimately
     stay active for a minute or more; an endless pulse reads as ambient/
     scattered motion rather than the skill's "orchestrated moment". */
  animation: phase-pulse 1.2s ease-in-out 2;
}

.phase-done .phase-text {
  color: var(--color-text);
}

.phase-done .phase-icon {
  border-color: var(--color-accent);
  background: var(--color-accent);
}

@keyframes phase-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(11, 110, 115, 0.35); }
  50% { box-shadow: 0 0 0 4px rgba(11, 110, 115, 0.15); }
}

@media (prefers-reduced-motion: reduce) {
  .phase-active .phase-icon {
    animation: none;
  }
  .phase-icon {
    transition: none;
  }
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* Rotating educational tip, filling the wait (no tool names - the tip
   copy itself is generic security-awareness content, never scan-specific
   detail). */
.tip {
  color: var(--color-muted);
  font-size: 0.85rem;
  font-style: italic;
  margin: 0 0 1rem;
  min-height: 2.4em;
  opacity: 0;
  transition: opacity 0.25s ease;
}

.tip.tip-visible {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .tip {
    transition: none;
  }
}

.elapsed {
  color: var(--color-muted);
  font-size: 0.85rem;
  margin: 0 0 1rem;
}

/* Staleness note (web/js/app.js's updateStalenessNote) - applies to any
   scan being watched, self-started or not, once its phase/percent hasn't
   changed in a while. --color-medium (already used for MEDIUM-severity
   chips elsewhere) reuses an existing token rather than adding a new
   color, and reads as "worth noticing" without the alarm of
   --color-critical - this is a possibility, not a confirmed failure. The
   message itself (not colour) carries the meaning, per section 18.6's
   "never colour-only" rule. */
.stale-note {
  color: var(--color-medium);
  font-weight: 600;
  font-size: 0.85rem;
  margin: 0 0 1rem;
}

.result-hero {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 1rem;
}

.risk-badge {
  font-weight: 700;
  letter-spacing: 0.04em;
  padding: 0.35rem 0.75rem;
  border-radius: 999px;
  color: #fff;
  font-size: 0.85rem;
}

/* Severity colours are identical across chips/tables/charts (section 18.6)
   and always paired with a text label - never colour-only. */
.risk-critical {
  background: var(--color-critical);
}
.risk-high {
  background: var(--color-high);
}
.risk-medium {
  background: var(--color-medium);
  color: var(--color-text);
}
.risk-low {
  background: var(--color-low);
}
.risk-info {
  background: var(--color-info);
}

.severity-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  font-size: 0.8rem;
}

.severity-chips span {
  padding: 0.2rem 0.55rem;
  border-radius: 999px;
  background: #eef1f4;
}

.result-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin: 1rem 0;
}

.report-inline {
  border-top: 1px solid var(--color-border);
  margin-top: 1rem;
  padding-top: 1rem;
  overflow-x: auto;
  font-size: 0.9rem;
}

.report-inline table {
  border-collapse: collapse;
  width: 100%;
}

.report-inline code,
.report-inline pre {
  font-family: var(--font-mono);
}

/* Result-view orchestrated reveal (Task 4, UI_REDESIGN_PLAN.md) - the
   actual payoff moment of the whole product, sequenced instead of an
   instant all-at-once swap: risk badge first, then .severity-chips as one
   group (never each chip separately - animating 5 chips individually
   reads as scattered effects), then the rest of the card. Reuses Task 2's
   arrival-settle keyframe/duration for a consistent feel. Unlike the
   Landing arrival, these classes are permanent (web/index.html and
   web/js/app.js's showResult never strip them) - #view-result toggles
   display: none -> visible fresh on every single result, so the same
   animation restarts naturally each scan, which is exactly the wanted
   behaviour here (this is the payoff every time, not a one-shot flourish). */
.reveal-badge,
.reveal-chips,
.reveal-rest {
  animation: arrival-settle 350ms ease-out both;
}

.reveal-chips {
  animation-delay: 100ms;
}

.reveal-rest {
  animation-delay: 200ms;
}

@media (prefers-reduced-motion: reduce) {
  .reveal-badge,
  .reveal-chips,
  .reveal-rest {
    animation: none;
  }
}

@media (max-width: 480px) {
  .card {
    padding: 1.25rem;
  }
}
