/* Component styles built on tokens.css. Follows the "Precision Editor" brand system:
   Modern Corporate Minimalism, tonal-layer elevation, 4px spacing rhythm, 4px radius
   on interactive elements. */

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
}

body {
  position: relative;
  background-color: var(--color-background);
  color: var(--color-on-background);
  font-family: var(--font-family);
}

/* Ambient "AI" background — a soft pastel gradient mesh, not saturated brand
   color blurred at low opacity. That earlier approach (raw --color-primary
   etc. blurred, diluted only by a flat element-level opacity) looked muddy/
   dark wherever two blobs overlapped, because blur diffuses a color without
   desaturating it — two diffused saturated hues mixing still produce a
   darker third hue. The fix is making the colors pastel *before* they ever
   overlap: each stop is the brand hue mixed mostly with white in OKLCH (a
   perceptually even space, so the mix reads as a clean tint rather than the
   slightly chalky result plain sRGB mixing gives) — pastel-on-pastel overlap
   just stays light. Two dominant blooms (primary, secondary) carry the
   color; tertiary is a smaller, rarer third accent, not equal-weighted (three
   equal hues fighting for attention reads as confetti, not a mesh). The
   topmost layer is a white veil centered on the page, fading out by ~40% —
   it keeps the actual reading column (.page, 1280px, centered) clean and
   lets color live mostly in the margins on wide screens, which is what
   keeps this feeling premium rather than "gradient behind my paragraph."
   `position: fixed` + `inset: -20%` anchors everything to the viewport
   (not the scrollable page) and gives the blur room past the screen edge.
   The one motion signature on this page: an extremely slow 55s drift,
   reinforcing "alive"/AI without ever being something you'd consciously
   notice moving — off entirely under prefers-reduced-motion. z-index: -1
   needs `body { position: relative }` above for the stacking context;
   `.site-header`/`.card`/etc. don't need any z-index of their own, normal
   paint order already puts them above a negative-z-index sibling.
   resume_builder.css (loaded after this file, see auth_base.html's <head>
   order) hides this pseudo-element entirely for the canvas editor, where
   the resume itself should be the sole visual focus. */
body::before {
  content: "";
  position: fixed;
  inset: -20%;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(ellipse 50% 42% at 50% 42%, white, transparent 100%),
    radial-gradient(ellipse 62% 55% at 10% 15%, color-mix(in oklch, var(--color-primary) 26%, white), transparent 68%),
    radial-gradient(ellipse 58% 52% at 95% 22%, color-mix(in oklch, var(--color-secondary) 24%, white), transparent 68%),
    radial-gradient(ellipse 46% 40% at 65% 100%, color-mix(in oklch, var(--color-tertiary) 16%, white), transparent 65%);
  filter: blur(80px);
  animation: aurora-drift 55s ease-in-out infinite alternate;
}

@keyframes aurora-drift {
  from {
    transform: translate3d(0, 0, 0) scale(1);
  }
  to {
    transform: translate3d(-2%, 2%, 0) scale(1.06);
  }
}

@media (prefers-reduced-motion: reduce) {
  body::before {
    animation: none;
  }
}

a {
  color: var(--color-primary);
}

/* ── Layout ─────────────────────────────────────────────────── */

.site-header {
  position: relative;
  height: var(--header-height);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 var(--gutter);
  background: var(--color-surface-container-lowest);
  border-bottom: 1px solid var(--color-outline-variant);
}

.site-header-left {
  display: flex;
  align-items: center;
  gap: var(--space-xl);
  min-width: 0;
}

@media (max-width: 480px) {
  .site-header-left {
    gap: var(--space-md);
  }
}

.brand-mark {
  display: block;
  width: 36px;
  height: 36px;
  object-fit: contain;
  flex-shrink: 0;
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  font: 700 18px/1 var(--font-family);
  color: var(--color-primary);
  text-decoration: none;
  letter-spacing: -0.01em;
}

.brand .brand-mark {
  width: 26px;
  height: 26px;
}

@media (max-width: 480px) {
  .brand-text {
    display: none;
  }
}

.site-nav {
  display: flex;
  align-items: center;
  gap: var(--space-lg);
}

.site-nav-link {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font: 600 14px/20px var(--font-family);
  color: var(--color-on-surface-variant);
  text-decoration: none;
  white-space: nowrap;
  transition: color 0.15s ease;
}

.site-nav-link:hover {
  color: var(--color-primary);
}

/* Current tab — set server-side from request.resolver_match (see
   apps/common/templatetags/nav_extras.py), not a client-side URL match, so
   it's correct on first paint with no flash. Kept width-neutral (no padding/
   pill) on purpose: this header's breakpoint has broken from small width
   changes four times already (see the media query below) and a persistent
   underline can't shift layout the way padding could. */
.site-nav-link.is-active {
  color: var(--color-primary);
  font-weight: 700;
  box-shadow: inset 0 -2px 0 0 var(--color-primary);
  padding-bottom: 4px;
}

.site-nav-link svg {
  width: 14px;
  height: 14px;
  opacity: 0.7;
}

/* Hamburger toggle — hidden by default, only shown once .site-nav collapses
   into a dropdown below the .nav-toggle breakpoint (see below). */
.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: none;
  background: transparent;
  color: var(--color-on-surface);
  cursor: pointer;
  flex-shrink: 0;
}

.nav-toggle svg {
  width: 22px;
  height: 22px;
}

/* Seven nav links + Careers + the auth block stop fitting on one row well
   before the site's other breakpoints (e.g. .upload-columns' 860px) — this
   one is sized for this header's own content, not shared with them. Below
   it, .site-nav drops out of the header row entirely and reappears as a
   full-width dropdown panel, toggled by .nav-toggle (only visible at this
   same breakpoint). Re-verify this number by screenshot any time a nav link
   is added or removed — it was 960px for 4 links, 1100px for 5, 1300px for
   6 ("Build from Scratch"), bumped to 1450px for 7 ("Pricing") (measured
   broken at 1420px, clean at 1450px). */
@media (max-width: 1450px) {
  .nav-toggle {
    display: inline-flex;
  }

  .site-nav {
    display: none;
    position: absolute;
    top: var(--header-height);
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    background: var(--color-surface-container-lowest);
    border-bottom: 1px solid var(--color-outline-variant);
    box-shadow: var(--shadow-level3);
    padding: var(--space-sm) var(--gutter) var(--space-md);
    z-index: 10;
  }

  .site-nav.is-open {
    display: flex;
  }

  .site-nav-link {
    padding: var(--space-md) 0;
    border-bottom: 1px solid var(--color-outline-variant);
  }

  .site-nav-link:last-child {
    border-bottom: none;
  }
}

.site-header-auth {
  display: flex;
  align-items: center;
  gap: var(--space-md);
}

@media (max-width: 480px) {
  .site-header-user {
    display: none;
  }
}

.site-header-user {
  font: 600 14px/20px var(--font-family);
  color: var(--color-on-surface-variant);
}

/* Profile dropdown — click the name/chevron to reveal Change password /
   Log out, replacing what used to be a standalone Log out button. */
.profile-menu {
  position: relative;
}

.profile-menu-trigger {
  display: flex;
  align-items: center;
  gap: 6px;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  color: inherit;
}

.profile-menu-chevron {
  width: 14px;
  height: 14px;
  color: var(--color-on-surface-variant);
  transition: transform 0.15s ease;
}

.profile-menu-trigger[aria-expanded="true"] .profile-menu-chevron {
  transform: rotate(180deg);
}

.profile-menu-panel {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  min-width: 200px;
  background: var(--color-surface-container-lowest);
  border: 1px solid var(--color-outline-variant);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-level3);
  padding: var(--space-xs);
  z-index: 20;
}

.profile-menu-panel[hidden] {
  display: none;
}

.profile-menu-panel form {
  margin: 0;
}

.profile-menu-item {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  width: 100%;
  padding: var(--space-sm) var(--space-md);
  border: none;
  background: none;
  border-radius: var(--radius);
  font: 500 14px/20px var(--font-family);
  color: var(--color-on-surface);
  text-decoration: none;
  text-align: left;
  cursor: pointer;
}

.profile-menu-item:hover {
  background: var(--color-surface-container);
}

.page {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: var(--space-xxl) var(--gutter);
}

.page--narrow {
  max-width: 720px;
}

/* ── Cards (Level 2: white, subtle shadow) ─────────────────── */

.card {
  background: var(--color-surface-container-lowest);
  border: 1px solid var(--color-outline-variant);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-level2);
  padding: var(--space-xl);
}

.card + .card {
  margin-top: var(--space-lg);
}

/* Level 1 panel (secondary/supporting content, e.g. raw parsed text) */
.panel {
  background: var(--color-surface-container-low);
  border: 1px solid var(--color-outline-variant);
  border-radius: var(--radius-md);
  padding: var(--space-lg);
}

/* ── Buttons ────────────────────────────────────────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  font: 600 14px/16px var(--font-family);
  letter-spacing: 0.01em;
  padding: var(--space-md) var(--space-lg);
  border-radius: var(--radius);
  border: 1.5px solid transparent;
  cursor: pointer;
  text-decoration: none;
  transition: box-shadow 0.15s ease, transform 0.15s ease, filter 0.15s ease;
}

.btn:disabled {
  cursor: not-allowed;
  opacity: 0.5;
  filter: none;
  transform: none;
  box-shadow: none;
}

.btn-sm {
  padding: var(--space-xs) var(--space-md);
}

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

.btn-primary:hover {
  filter: brightness(1.08);
  box-shadow: var(--shadow-level2);
  transform: translateY(-1px);
}

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

.btn-secondary:hover {
  background: var(--color-primary-fixed);
}

.btn-ghost {
  background: transparent;
  color: var(--color-on-surface-variant);
}

.btn-ghost:hover {
  background: var(--color-surface-container-high);
}

.btn-block {
  width: 100%;
}

.btn-danger {
  background: transparent;
  border-color: var(--color-error);
  color: var(--color-error);
}

.btn-danger:hover {
  background: var(--color-error-container);
}

/* Tinted-indigo styling reserved for supplementary auth paths (e.g. SSO) per
   the brand doc's "secondary indigo = supplementary features" guidance. Kept
   visually quieter than .btn-primary so it doesn't compete with the main CTA. */
.btn-sso {
  background: var(--color-secondary-fixed);
  border-color: transparent;
  color: var(--color-on-secondary-fixed-variant);
}

.btn-sso:hover {
  filter: brightness(0.97);
}

.btn-sso .brand-mark {
  width: 18px;
  height: 18px;
}

/* ── Form fields ────────────────────────────────────────────── */

.field {
  margin-bottom: var(--space-lg);
}

.field label {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  font: 600 14px/16px var(--font-family);
  letter-spacing: 0.01em;
  color: var(--color-on-surface);
  margin-bottom: var(--space-sm);
}

.field .hint {
  display: block;
  font: 400 14px/20px var(--font-family);
  color: var(--color-on-surface-variant);
  margin-top: var(--space-xs);
}

/* Live requirement checklist (progressively enhanced, see auth_base.html) */
.pw-checks {
  list-style: none;
  margin: var(--space-xs) 0 0;
  padding: 0;
  display: flex;
  gap: var(--space-md);
}

.pw-checks li {
  display: flex;
  align-items: center;
  gap: 6px;
  font: 400 13px/20px var(--font-family);
  color: var(--color-on-surface-variant);
}

.pw-checks .dot {
  width: 14px;
  height: 14px;
  border-radius: var(--radius-full);
  border: 1.5px solid var(--color-outline);
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.pw-checks .dot svg {
  width: 9px;
  height: 9px;
  stroke: var(--color-on-primary);
  opacity: 0;
}

.pw-checks li.met {
  color: var(--color-tertiary);
}

.pw-checks li.met .dot {
  border-color: var(--color-tertiary);
  background: var(--color-tertiary);
}

.pw-checks li.met .dot svg {
  opacity: 1;
}

.field input[type="text"],
.field input[type="email"],
.field input[type="password"],
.field input[type="file"],
.field textarea,
.field select {
  width: 100%;
  background: var(--color-surface-container-low);
  border: 1px solid var(--color-outline);
  border-radius: var(--radius);
  padding: var(--space-md);
  font: 400 16px/24px var(--font-family);
  color: var(--color-on-surface);
}

.field textarea {
  min-height: 140px;
  resize: vertical;
}

.field input:focus,
.field textarea:focus,
.field select:focus {
  outline: none;
  border: 2px solid var(--color-secondary);
  box-shadow: 0 0 0 3px rgba(70, 72, 212, 0.15);
}

.field-error {
  color: var(--color-error);
  font: 400 14px/20px var(--font-family);
  margin-top: var(--space-xs);
}

/* Form-level (non-field) errors, e.g. "incorrect email or password" */
.alert {
  border-radius: var(--radius);
  padding: var(--space-md);
  font: 400 14px/20px var(--font-family);
  margin-bottom: var(--space-lg);
}

.alert-error {
  background: var(--color-error-container);
  color: var(--color-on-error-container);
}

.radio-group {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md) var(--space-lg);
}

.radio-group label {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  font: 400 16px/24px var(--font-family);
  color: var(--color-on-surface);
  cursor: pointer;
  flex-wrap: wrap;
}

@media (max-width: 480px) {
  .radio-group {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-sm);
  }
}

.checkbox-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  font: 600 14px/20px var(--font-family);
  color: var(--color-on-surface);
  cursor: pointer;
}

.checkbox-row input[type="checkbox"] {
  width: 18px;
  height: 18px;
  accent-color: var(--color-primary);
  cursor: pointer;
  flex-shrink: 0;
}

/* Icon-prefixed inputs (e.g. person/envelope/lock icons, password show/hide) */
.field-icon {
  position: relative;
  display: flex;
  align-items: center;
}

.field-icon .field-icon-leading {
  position: absolute;
  left: var(--space-md);
  display: flex;
  color: var(--color-outline);
  pointer-events: none;
}

.field .field-icon input {
  padding-left: 44px;
}

/* Password reveal toggle — progressively enhanced via a global script
   (auth_base.html) that auto-wraps every input[type=password]. */
.pw-wrap {
  position: relative;
  display: flex;
  align-items: center;
  flex: 1;
}

.field .pw-wrap input {
  padding-right: 44px;
}

.pw-toggle {
  position: absolute;
  right: var(--space-md);
  display: flex;
  background: none;
  border: none;
  padding: 0;
  color: var(--color-outline);
  cursor: pointer;
}

.pw-toggle:hover {
  color: var(--color-on-surface);
}

.pw-toggle svg {
  width: 18px;
  height: 18px;
}

/* ── Upload page ──────────────────────────────────────────────
   Two columns inside one card: resume (upload + live preview) on the left,
   role + job description + contact on the right — separated by a single
   hairline rule, not two competing shadowed panels. Each column opens with
   a real headline (text-headline-sm) so the hierarchy reads
   display (page title) → headline (column) → label (field) → body (input/
   hint), instead of every field label fighting for the same visual weight. */

.upload-columns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-xl);
}

/* Grid items default to min-width:auto, meaning a track won't shrink below
   the min-content width of its contents — a long nowrap filename inside
   .file-preview-name (or anything else unbreakable) would otherwise force
   this column wider than its 1fr share and push into the other column. */
.upload-columns > div {
  min-width: 0;
}

@media (max-width: 860px) {
  .upload-columns {
    grid-template-columns: 1fr;
  }
}

.upload-column--resume {
  padding-right: var(--space-xl);
  border-right: 1px solid var(--color-outline-variant);
}

@media (max-width: 860px) {
  .upload-column--resume {
    padding-right: 0;
    padding-bottom: var(--space-xl);
    border-right: none;
    border-bottom: 1px solid var(--color-outline-variant);
  }
}

.column-title {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  font: 600 20px/28px var(--font-family);
  color: var(--color-on-surface);
  margin: 0 0 var(--space-lg);
}

.column-title .icon {
  color: var(--color-primary);
}

/* Groups related fields within the right column, in fill order. Hairline
   dividers substitute for separate nested cards. */
.form-section {
  padding-bottom: var(--space-lg);
  margin-bottom: var(--space-lg);
  border-bottom: 1px solid var(--color-outline-variant);
}

.form-section:last-of-type {
  border-bottom: none;
  padding-bottom: 0;
  margin-bottom: 0;
}

.form-section--quiet label {
  color: var(--color-on-surface-variant);
  font-weight: 500;
}

.dropzone {
  position: relative;
}

.dropzone-input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
  z-index: 1;
}

.dropzone.has-file .dropzone-input,
.dropzone.is-uploading .dropzone-input {
  z-index: 0;
  pointer-events: none;
}

.dropzone-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: var(--space-xs);
  min-height: 280px;
  padding: var(--space-xl) var(--space-lg);
  border: 2px dashed var(--color-outline);
  border-radius: var(--radius-lg);
  background: var(--color-surface-container-low);
  transition: border-color 0.15s ease, background 0.15s ease;
}

.dropzone.has-file .dropzone-content,
.dropzone.is-uploading .dropzone-content {
  display: none;
}

/* Progress state — shown in place of .dropzone-content while the file is
   actually in flight to api-upload-resume (see upload.html's XHR upload
   handler). Per colors.txt's "Progress & Steppers" spec: a thin 4px bar,
   primary color for the active fill. */
.dropzone-progress {
  display: none;
  flex-direction: column;
  align-items: center;
  gap: var(--space-sm);
  min-height: 280px;
  justify-content: center;
  padding: var(--space-xl) var(--space-lg);
  border: 2px solid var(--color-outline-variant);
  border-radius: var(--radius-lg);
  background: var(--color-surface-container-low);
}

.dropzone.is-uploading .dropzone-progress {
  display: flex;
}

.dropzone-progress-label {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  width: 100%;
  max-width: 320px;
  font: 600 14px/20px var(--font-family);
  color: var(--color-on-surface);
}

.dropzone-progress-label #dropzone-progress-filename {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.dropzone-progress-label #dropzone-progress-percent {
  flex-shrink: 0;
  color: var(--color-primary);
}

.dropzone-progress .progress-track {
  width: 100%;
  max-width: 320px;
}

.progress-track {
  height: 4px;
  border-radius: var(--radius-full);
  background: var(--color-surface-container-high);
  overflow: hidden;
}

.progress-fill {
  height: 100%;
  background: var(--color-primary);
  border-radius: var(--radius-full);
  transition: width 0.15s ease;
}

.dropzone-icon {
  color: var(--color-primary);
  margin-bottom: var(--space-xs);
}

.dropzone-title {
  font: 600 16px/24px var(--font-family);
  color: var(--color-on-surface);
}

.dropzone-hint {
  font: 400 14px/20px var(--font-family);
  color: var(--color-on-surface-variant);
}

.dropzone-browse {
  color: var(--color-primary);
  font-weight: 600;
  text-decoration: underline;
}

.dropzone.is-dragover .dropzone-content,
.dropzone-input:focus-visible ~ .dropzone-content {
  border-color: var(--color-primary);
  background: var(--color-primary-fixed);
}

/* ── Resume file preview (swaps in for .dropzone-content once a file is
   picked or dropped) ───────────────────────────────────────────────── */

.file-preview {
  display: none;
  flex-direction: column;
  gap: var(--space-md);
  padding: var(--space-lg);
  border: 1px solid var(--color-outline-variant);
  border-radius: var(--radius-lg);
  background: var(--color-surface-container-lowest);
}

.dropzone.has-file .file-preview {
  display: flex;
}

.file-preview-row {
  display: flex;
  align-items: center;
  gap: var(--space-md);
}

.file-preview-icon {
  width: 40px;
  height: 40px;
  border-radius: var(--radius-md);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  background: var(--color-error-container);
}

.file-preview-icon .icon {
  width: 22px;
  height: 22px;
  color: var(--color-error);
}

.file-preview-icon--doc {
  background: var(--color-secondary-fixed);
}

.file-preview-icon--doc .icon {
  color: var(--color-secondary);
}

.file-preview-info {
  flex: 1;
  min-width: 0;
}

.file-preview-name {
  font: 600 14px/20px var(--font-family);
  color: var(--color-on-surface);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.file-preview-meta {
  display: flex;
  align-items: center;
  gap: 4px;
  font: 500 13px/18px var(--font-family);
  color: var(--color-tertiary);
}

.file-preview-meta .icon {
  width: 14px;
  height: 14px;
}

.file-preview-remove {
  position: relative;
  z-index: 2;
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-full);
  border: none;
  background: var(--color-surface-container-high);
  color: var(--color-on-surface-variant);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font: 400 16px/1 var(--font-family);
}

.file-preview-remove:hover {
  background: var(--color-error-container);
  color: var(--color-on-error-container);
}

.file-preview-embed {
  width: 100%;
  height: 240px;
  border: 1px solid var(--color-outline-variant);
  border-radius: var(--radius);
  background: var(--color-surface-container-low);
}

/* ── Inline "Generate with AI" button ───────────────────────────────
   The one deliberately bold/flashy element on this page (colors.txt says
   avoid heavy gradients for backgrounds/panels — this is a single small
   pill-shaped CTA, not a panel, and the gradient is a widely-recognized
   signal for "this action is AI-powered", not decoration for its own sake). */

.jd-textarea-wrap {
  position: relative;
}

.jd-textarea-wrap textarea {
  padding-top: 52px;
}

.ai-generate-btn {
  position: absolute;
  top: var(--space-sm);
  right: var(--space-sm);
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px var(--space-md);
  border: none;
  border-radius: var(--radius-full);
  background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-secondary) 100%);
  color: var(--color-on-primary);
  font: 600 13px/18px var(--font-family);
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(184, 0, 73, 0.3);
  transition: transform 0.15s ease, box-shadow 0.15s ease, filter 0.15s ease;
}

.ai-generate-btn .icon {
  width: 15px;
  height: 15px;
}

.ai-generate-btn:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 14px rgba(184, 0, 73, 0.4);
  filter: brightness(1.06);
}

.ai-generate-btn:active {
  transform: translateY(0);
}

.ai-generate-btn:disabled {
  cursor: progress;
  opacity: 0.85;
  transform: none;
}

.ai-generate-btn-spinner {
  display: none;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.45);
  border-top-color: var(--color-on-primary);
  animation: ai-generate-spin 0.7s linear infinite;
}

.ai-generate-btn.is-loading .ai-generate-btn-spinner {
  display: inline-block;
}

.ai-generate-btn.is-loading .ai-generate-btn-icon {
  display: none;
}

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

@media (prefers-reduced-motion: reduce) {
  .ai-generate-btn-spinner {
    animation: none;
  }
}

/* ── "Shiny" AI action buttons ─────────────────────────────────────────
   For buttons whose whole job is "trigger an AI action" — Build/Regenerate
   with AI — not every button that happens to touch AI somewhere downstream.
   Same primary→secondary diagonal gradient as .ai-generate-btn above (this
   app's one existing "AI-powered" signal), generalized into a normal
   inline-flow modifier class rather than that button's position:absolute
   textarea-overlay treatment, so it drops onto any .btn anywhere. A slow
   glow-strength pulse gives it a persistent "alive" quality at rest; a
   one-time light sweep plays on hover instead of looping unprompted, so it
   reads as premium rather than distracting. Both motions respect
   prefers-reduced-motion; disabled state freezes both. */
.btn-ai {
  position: relative;
  overflow: hidden;
  background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-secondary) 100%);
  border-color: transparent;
  color: var(--color-on-primary);
  box-shadow: 0 2px 10px rgba(184, 0, 73, 0.3);
  animation: btn-ai-pulse 2.6s ease-in-out infinite;
}

.btn-ai::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(115deg, transparent 40%, rgba(255, 255, 255, 0.5) 50%, transparent 60%);
  background-size: 250% 250%;
  background-position: 200% 0;
  transition: background-position 0.7s ease;
  pointer-events: none;
}

.btn-ai:hover {
  filter: brightness(1.08);
  box-shadow: 0 4px 16px rgba(184, 0, 73, 0.45);
  transform: translateY(-1px);
}

.btn-ai:hover::after {
  background-position: -50% 0;
}

.btn-ai:active {
  transform: translateY(0);
}

.btn-ai:disabled {
  animation: none;
  filter: none;
  transform: none;
  box-shadow: none;
}

@keyframes btn-ai-pulse {
  0%, 100% {
    box-shadow: 0 2px 10px rgba(184, 0, 73, 0.3);
  }
  50% {
    box-shadow: 0 2px 20px rgba(184, 0, 73, 0.55);
  }
}

@media (prefers-reduced-motion: reduce) {
  .btn-ai {
    animation: none;
  }
  .btn-ai::after {
    transition: none;
  }
}

/* ── Full-page processing overlay (upload.html submit) ────────────────
   Shown the instant the form actually submits — see the JS comment in
   upload.html for why this can't be a real progress bar (synchronous
   request, no channel to report real backend progress over). */
.processing-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.55);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.2s ease;
}

.processing-overlay.is-visible {
  opacity: 1;
  visibility: visible;
}

.processing-overlay-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-lg);
  background: var(--color-surface-container-lowest);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-level3);
  padding: var(--space-xxl) var(--space-xl);
  max-width: 340px;
  text-align: center;
}

.processing-spinner {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: 4px solid var(--color-surface-container-high);
  border-top-color: var(--color-primary);
  border-right-color: var(--color-secondary);
  animation: processing-spin 0.8s linear infinite;
}

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

.processing-overlay-message {
  font: 600 16px/24px var(--font-family);
  color: var(--color-on-surface);
  margin: 0;
  min-height: 24px;
}

@media (prefers-reduced-motion: reduce) {
  .processing-spinner {
    animation: none;
  }
}

.jd-char-count {
  text-align: right;
  font: 400 12px/16px var(--font-family);
  color: var(--color-on-surface-variant);
  margin-top: var(--space-xs);
}

.jd-char-count.is-near-limit {
  color: var(--color-error);
  font-weight: 600;
}

.ai-generate-error {
  display: none;
  color: var(--color-error);
  font: 400 13px/20px var(--font-family);
  margin-top: var(--space-xs);
}

.ai-generate-error.is-visible {
  display: block;
}

/* ── Tip callout ─────────────────────────────────────────────── */

/* A light surface + colored accent border/icon, not a saturated fill — the
   "-fixed" tokens aren't equivalent tonal weights across color families
   (tertiary-fixed is a much more vivid green than secondary-fixed's pale
   lavender), so a full-bleed fill read as a neon alert banner for one
   variant and a subtle tint for the other. This treatment stays calm and
   consistent regardless of which hue is used. */
.tip-callout {
  display: flex;
  gap: var(--space-sm);
  align-items: flex-start;
  padding: var(--space-md);
  border-radius: var(--radius);
  border-left: 3px solid var(--color-secondary);
  background: var(--color-surface-container-low);
  color: var(--color-on-surface-variant);
  font: 400 13px/20px var(--font-family);
  margin-top: var(--space-sm);
}

.tip-callout .icon {
  color: var(--color-secondary);
  flex-shrink: 0;
  margin-top: 1px;
}

.tip-callout--positive {
  border-left-color: var(--color-tertiary);
}

.tip-callout--positive .icon {
  color: var(--color-tertiary);
}

/* ── Trust row (generic — auth.css has its own centered variant for
   the split-screen auth pages; this one is left-aligned for in-app use) ─ */
.trust-row {
  display: flex;
  gap: var(--space-md);
  flex-wrap: wrap;
}

/* ── Chips / badges ─────────────────────────────────────────── */

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  background: var(--color-surface-container);
  color: var(--color-on-surface-variant);
  border-radius: var(--radius);
  padding: var(--space-xs) var(--space-sm);
  font: 500 12px/16px var(--font-family);
}

/* Affirmative trust signals (compliance/verification claims) get a tertiary
   tint so they read as confirmed rather than neutral metadata. */
.chip--positive {
  background: var(--color-tertiary-fixed);
  color: var(--color-on-tertiary-fixed-variant);
}

/* ── Keyword match chips (result.html) ─────────────────────────────── */

.keyword-group {
  margin-bottom: var(--space-lg);
}

.keyword-group:last-child {
  margin-bottom: 0;
}

.keyword-group-title {
  font: 600 13px/18px var(--font-family);
  color: var(--color-on-surface-variant);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin: 0 0 var(--space-sm);
}

.chip-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
}

.badge {
  display: inline-flex;
  align-items: center;
  border-radius: var(--radius);
  padding: var(--space-xs) var(--space-sm);
  font: 500 12px/16px var(--font-family);
  white-space: nowrap;
}

.badge--high {
  background: var(--color-error-container);
  color: var(--color-on-error-container);
}

.badge--medium {
  background: var(--color-secondary-fixed);
  color: var(--color-on-secondary-fixed-variant);
}

.badge--low {
  background: var(--color-tertiary-fixed);
  color: var(--color-on-tertiary-fixed-variant);
}

/* ── Score display ──────────────────────────────────────────── */

.score-hero {
  display: flex;
  align-items: center;
  gap: var(--space-xl);
  flex-wrap: wrap;
}

.score-number {
  font: 700 48px/56px var(--font-family);
  letter-spacing: -0.02em;
}

.score-number--good {
  color: var(--color-tertiary);
}

.score-number--fair {
  color: var(--color-secondary);
}

.score-number--poor {
  color: var(--color-error);
}

.score-row {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  margin-bottom: var(--space-md);
}

.score-row .label {
  width: 140px;
  font: 600 14px/16px var(--font-family);
  color: var(--color-on-surface);
  flex-shrink: 0;
}

.score-row-label {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}

.score-row-swatch {
  width: 10px;
  height: 10px;
  border-radius: var(--radius-full);
  flex-shrink: 0;
}

.score-row-points {
  min-width: 64px;
  text-align: right;
  color: var(--color-on-surface-variant);
  flex-shrink: 0;
}

.score-track {
  flex: 1;
  height: 8px;
  border-radius: var(--radius-full);
  background: var(--color-surface-container-high);
  overflow: hidden;
}

.score-fill {
  height: 100%;
  background: var(--color-primary);
  border-radius: var(--radius-full);
}

/* ── Score gauge (circular) ─────────────────────────────────── */

.score-gauge {
  position: relative;
  width: 128px;
  height: 128px;
  flex-shrink: 0;
}

.score-gauge svg {
  width: 100%;
  height: 100%;
  transform: rotate(-90deg);
}

.score-gauge-track {
  fill: none;
  stroke: var(--color-surface-container-high);
  stroke-width: 10;
}

/* Each category gets its own <circle>, stacked on the same r=52 path as a
   dash-of-length-L starting at its cumulative offset (computed in JS — see
   result.html) — a segmented ring instead of one solid arc. Colors come from
   apps/resume_checker/views.py's BREAKDOWN_COLORS (a chart-specific 5-hue
   set validated for colorblind-safety; the app's own primary/secondary/
   tertiary/error UI tokens don't clear that bar at 5 categorical slots). */
.score-gauge-segment {
  fill: none;
  stroke: var(--segment-color, var(--color-primary));
  stroke-width: 10;
  stroke-linecap: butt;
  stroke-dasharray: 0 326.7256;
  stroke-dashoffset: 326.7256;
  cursor: pointer;
  pointer-events: stroke;
  transition: stroke-dashoffset 1s cubic-bezier(0.16, 1, 0.3, 1), filter 0.15s ease;
}

.score-gauge-segment:hover,
.score-gauge-segment:focus-visible {
  filter: brightness(1.18);
  outline: none;
}

.score-gauge-label {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.score-gauge-number {
  font: 700 32px/36px var(--font-family);
  letter-spacing: -0.02em;
  color: var(--color-on-surface);
}

.score-gauge--good .score-gauge-number {
  color: var(--color-tertiary);
}

.score-gauge--fair .score-gauge-number {
  color: var(--color-secondary);
}

.score-gauge--poor .score-gauge-number {
  color: var(--color-error);
}

.score-gauge-suffix {
  font: 500 12px/16px var(--font-family);
  color: var(--color-on-surface-variant);
}

/* ── Chart tooltip (score gauge segment hover/focus) ──────────────────
   One shared floating tooltip, positioned in JS near the pointer/segment.
   Values lead (bold, high-contrast) and the category name follows, per the
   dataviz skill's tooltip hierarchy — the reader already has the color, they
   want the number. */
.chart-tooltip {
  position: fixed;
  z-index: 1000;
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  background: var(--color-inverse-surface);
  color: var(--color-inverse-on-surface);
  border-radius: var(--radius);
  padding: 6px var(--space-sm);
  font: 500 13px/18px var(--font-family);
  box-shadow: var(--shadow-level3);
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transform: translateY(2px);
  transition: opacity 0.1s ease, transform 0.1s ease;
  white-space: nowrap;
}

.chart-tooltip.is-visible {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.chart-tooltip-swatch {
  width: 8px;
  height: 8px;
  border-radius: var(--radius-full);
  flex-shrink: 0;
}

.chart-tooltip-value {
  font-weight: 700;
}

.chart-tooltip-label {
  color: var(--color-inverse-on-surface);
  opacity: 0.85;
}

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

/* ── Result page layout ─────────────────────────────────────── */

.result-hero-grid {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-xl);
  align-items: center;
}

/* Same fix as .upload-columns > div: this is a 1fr grid item, so it needs an
   explicit min-width:0 or a long filename/target-role forces the column
   wider than its share instead of truncating. */
.result-hero-info {
  min-width: 0;
}

.result-hero-meta {
  margin: 0 0 var(--space-md);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.result-hero-filename-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}

.result-hero-filename {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

@media (max-width: 700px) {
  .result-hero-grid {
    grid-template-columns: 1fr;
    justify-items: center;
    text-align: center;
  }

  /* justify-items:center makes the grid item shrink-wrap to its content width
     instead of stretching to fill the track — which silently defeats
     .result-hero-meta/.result-hero-filename's overflow:hidden+ellipsis (an
     unconstrained box has nothing to truncate against). Force the item back
     to the full column width; its block children then fill that width
     normally, so long text actually truncates on mobile. */
  .result-hero-info {
    width: 100%;
    max-width: 100%;
  }

  .result-hero-filename-row {
    justify-content: center;
  }

  .result-hero-filename {
    flex: 1;
  }
}

/* ── Mistake list ───────────────────────────────────────────── */

.mistake {
  display: flex;
  align-items: flex-start;
  gap: var(--space-md);
  padding: var(--space-md) 0;
  border-bottom: 1px solid var(--color-outline-variant);
}

.mistake:last-child {
  border-bottom: none;
}

.mistake .message {
  font: 400 16px/24px var(--font-family);
  color: var(--color-on-surface);
}

/* .mistake is a flex ROW by default (badge + message side by side, for the
   "Detected issues" list). Rows that stack multiple lines of their own
   content (a diff line + its reason, or an original/suggested/reason
   triplet) need column direction instead, or those lines would try to sit
   side by side in the same row. */
.mistake--stacked {
  flex-direction: column;
  align-items: stretch;
}

/* ── Mistake sections (spell check / grammar check / AI rewrites) ────────
   One full-width card per category, stacked in a column — not squeezed
   into a 2-column grid — so a diff line + its reason (or an original →
   suggested rewrite + its reason) has room to read as a sentence instead
   of wrapping awkwardly in a half-width column. */
.mistake-sections {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
}

.mistake-sections > .card + .card {
  margin-top: 0;
}

.mistake-section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  flex-wrap: wrap;
  margin-bottom: var(--space-md);
}

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

/* Each category gets one of the three brand hues (see colors.txt) as a
   tonal chip, the same "-fixed" tonal pairing used elsewhere (.badge,
   .chip--positive) — a consistent color code across cards, not a one-off. */
.mistake-section-icon--spelling {
  background: var(--color-primary-fixed);
  color: var(--color-on-primary-fixed-variant);
}

.mistake-section-icon--grammar {
  background: var(--color-secondary-fixed);
  color: var(--color-on-secondary-fixed-variant);
}

.mistake-section-icon--rewrite {
  background: var(--color-tertiary-fixed);
  color: var(--color-on-tertiary-fixed-variant);
}

.badge--count {
  background: var(--color-surface-container-high);
  color: var(--color-on-surface-variant);
}

.diff-before {
  color: var(--color-on-surface-variant);
  text-decoration: line-through;
  text-decoration-color: var(--color-outline);
}

.diff-after {
  font-weight: 600;
  color: var(--color-on-surface);
}

.rewrite-label {
  display: block;
  font: 600 11px/16px var(--font-family);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-on-surface-variant);
}

/* ── Spelling / grammar mistake table ────────────────────────────
   Original/Suggested/Explanation used to sit side by side as one running
   line per mistake — cramped, and with nothing tying "Explanation" to the
   other two as a column. A real column-aligned table (built with CSS grid
   + display:contents, not a literal <table>, so the responsive collapse
   below can restructure it freely) reads far better across several rows,
   and fits this brand's own "Precision: tight alignment, rigorous grid
   system" pillar (colors.txt) better than a sentence-per-row list did. */
.mistake-table {
  display: grid;
  grid-template-columns: minmax(110px, 1fr) minmax(110px, 1fr) minmax(160px, 1.6fr);
}

.mistake-table-head,
.mistake-table-row {
  display: contents;
}

.mistake-table-head span {
  background: var(--color-surface-container-high);
  color: var(--color-on-surface-variant);
  font: 600 12px/16px var(--font-family);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: var(--space-sm) var(--space-md);
}

.mistake-table-head span:first-child {
  border-top-left-radius: var(--radius-md);
}

.mistake-table-head span:last-child {
  border-top-right-radius: var(--radius-md);
}

.mistake-cell {
  padding: var(--space-md);
  border-bottom: 1px solid var(--color-outline-variant);
  font: 400 15px/22px var(--font-family);
}

.mistake-table-row:last-child .mistake-cell {
  border-bottom: none;
}

.mistake-cell--explanation {
  color: var(--color-on-surface-variant);
  font-size: 14px;
}

.mistake-cell-label {
  display: none;
}

/* Below 700px the 3-column grid has no room to breathe — collapse each row
   to a labeled stack instead (same "Precision Editor" reduce-crowding call
   already made for .result-hero-grid/.upload-columns at this breakpoint). */
@media (max-width: 700px) {
  /* .mistake-table-row switching to display:block isn't enough on its own —
     the grid container itself still has 3 column tracks defined, so the
     browser keeps auto-placing multiple block-level rows side by side into
     those tracks instead of stacking them. The container has to drop out of
     grid mode too. */
  .mistake-table {
    display: block;
  }

  .mistake-table-head {
    display: none;
  }

  .mistake-table-row {
    display: block;
    padding: var(--space-md) 0;
    border-bottom: 1px solid var(--color-outline-variant);
  }

  .mistake-table-row:last-child {
    border-bottom: none;
  }

  .mistake-cell {
    display: block;
    padding: 0 0 var(--space-sm);
    border-bottom: none;
  }

  .mistake-cell:last-child {
    padding-bottom: 0;
  }

  .mistake-cell-label {
    display: block;
    font: 600 11px/16px var(--font-family);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-on-surface-variant);
    margin-bottom: 2px;
  }
}

/* ── Static SVG icons (static/icons/, Material Symbols exports) ──
   Rendered via CSS mask rather than <img src>: an <img>-loaded SVG is its
   own isolated document, so fill="currentColor" inside the file can't see
   the page's CSS and would always render black. Masking the shape and
   painting it with background-color lets these icons recolor with `color`
   like every other icon in this app. Usage:
   <span class="icon" style="--icon-src: url('{% static "icons/target.svg" %}')"></span> */
.icon {
  display: inline-block;
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  background-color: currentColor;
  -webkit-mask-image: var(--icon-src);
  mask-image: var(--icon-src);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}

.icon-sm {
  width: 16px;
  height: 16px;
}

.icon-lg {
  width: 28px;
  height: 28px;
}

/* Heading + icon pairing. Two variants: .heading-icon for real headings
   (h1-h4, safe to make flex), .heading-icon-inline for content that must
   stay inline (e.g. inside <summary>, whose native disclosure-triangle
   marker relies on default display and breaks under display:flex). */
.heading-icon {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}

.heading-icon-inline {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  vertical-align: middle;
}

/* ── My Resumes ─────────────────────────────────────────────────
   Two tabs — uploaded files, and resumes built in resume_builder — rather
   than two stacked sections: with a real upload history, a second stacked
   section pushed "Built with AI" below the fold, so reaching it always cost
   a scroll even though it's equally primary content, not a lesser one.
   Tabs put both one click away instead. Underline style (not a segmented
   pill toggle) to read as "which slice of my content" rather than "which
   mode/setting" — the two are filters over the same page, not different
   screens. Both share one card grammar (.resume-card) so the page still
   reads as one system, differentiated by their tag (file type vs. draft/
   finished status) and preview icon rather than a different layout per tab.
   Office365/OneDrive-style file grid: each card leads with a large tonal
   preview tile carrying just a glyph (no real page-content thumbnail — see
   project decision, this app doesn't render PDF/DOCX/resume pages), a tag
   pinned to its corner, then name/date/actions below. */
.resume-tabs {
  display: flex;
  gap: var(--space-xl);
  border-bottom: 1px solid var(--color-outline-variant);
  margin-bottom: var(--space-xl);
}

.resume-tab {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-md) 2px;
  margin-bottom: -1px;
  border: none;
  border-bottom: 2px solid transparent;
  background: none;
  font: 600 15px/20px var(--font-family);
  color: var(--color-on-surface-variant);
  cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease;
}

.resume-tab:hover {
  color: var(--color-on-surface);
}

.resume-tab.is-active {
  color: var(--color-primary);
  border-bottom-color: var(--color-primary);
}

.resume-tab .icon {
  opacity: 0.7;
}

.resume-tab.is-active .icon {
  opacity: 1;
}

.resume-tab-panel {
  display: none;
}

.resume-tab-panel.is-active {
  display: block;
}

.my-resumes-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: var(--radius-full);
  background: var(--color-surface-container-high);
  color: var(--color-on-surface-variant);
  font: 700 11px/16px var(--font-family);
}

.resume-tab.is-active .my-resumes-count {
  background: var(--color-primary-fixed);
  color: var(--color-on-primary-fixed-variant);
}

.resume-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--space-lg);
}

.resume-card {
  display: flex;
  flex-direction: column;
  background: var(--color-surface-container-lowest);
  border: 1px solid var(--color-outline-variant);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-level2);
  overflow: hidden;
  transition: box-shadow 0.15s ease, transform 0.15s ease;
}

.resume-card:hover {
  box-shadow: var(--shadow-level3);
  transform: translateY(-2px);
}

.resume-card-preview {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 128px;
  /* Deliberately not var(--color-error-container)/--color-secondary-fixed —
     tinting the tile pink or lavender by file type made a resume preview
     read through a color cast for no real reason; the icon's own color
     already distinguishes PDF from DOCX. Same reasoning as the resume
     builder canvas's neutral well (static/css/resume_builder.css). */
  background: #eef0f2;
}

.resume-card-preview .icon {
  width: 44px;
  height: 44px;
  color: var(--color-error);
}

.resume-card-preview--doc .icon {
  color: var(--color-secondary);
}

/* Built resumes have no file type to signal (red/indigo above mean PDF/DOCX
   specifically) — neutral ink instead, so the icon doesn't accidentally
   borrow "error" red's meaning from the uploaded-resume cards next to it. */
.resume-card-preview--built .icon {
  color: var(--color-outline);
}

.resume-card-tag {
  position: absolute;
  top: var(--space-sm);
  right: var(--space-sm);
  background: var(--color-surface-container-lowest);
  color: var(--color-on-surface);
  border-radius: var(--radius);
  padding: 2px var(--space-sm);
  font: 700 11px/16px var(--font-family);
  letter-spacing: 0.04em;
  box-shadow: var(--shadow-level2);
}

/* Built-resume status — same tonal "-fixed" pairing already used for
   badge--medium/chip--positive elsewhere in this app (indigo = in progress,
   green = done), not a new color decision. */
.resume-card-tag--draft {
  background: var(--color-secondary-fixed);
  color: var(--color-on-secondary-fixed-variant);
  box-shadow: none;
}

.resume-card-tag--finished {
  background: var(--color-tertiary-fixed);
  color: var(--color-on-tertiary-fixed-variant);
  box-shadow: none;
}

.resume-card-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--space-md);
  flex: 1;
}

.resume-card-name {
  font: 600 14px/20px var(--font-family);
  color: var(--color-on-surface);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.resume-card-date {
  font: 400 13px/18px var(--font-family);
  color: var(--color-on-surface-variant);
  display: flex;
  align-items: center;
  gap: 6px;
}

/* Which template a built resume uses — a secondary signal, so a small dot
   rather than tinting the whole preview tile (that read as an arbitrary
   color cast when tried on the uploaded-resume tiles above, see that
   comment) or coloring the card's icon a fourth way. */
.resume-card-template-dot {
  width: 8px;
  height: 8px;
  border-radius: var(--radius-full);
  flex-shrink: 0;
}

.resume-card-actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  padding: 0 var(--space-md) var(--space-md);
}

/* Download/Delete stay a 2-up row; "View Result" (when present) sits above
   as its own full-width row, not squeezed in as a 3rd flex:1 item — that's
   what caused real overflow at the card's min width the first time this row
   had only 2 buttons in it, see the resume-card-actions .btn truncation
   rules below. */
.resume-card-actions-row {
  display: flex;
  gap: var(--space-xs);
}

.resume-card-actions-row .btn {
  flex: 1;
  min-width: 0;
  padding-left: var(--space-sm);
  padding-right: var(--space-sm);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.resume-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-md);
  padding: var(--space-xxl) var(--space-lg);
  color: var(--color-on-surface-variant);
}

.resume-empty .icon {
  width: 40px;
  height: 40px;
  color: var(--color-outline);
}

/* ── Confirm dialog (delete confirmation) ──────────────────────────
   Same fixed-overlay structure as .processing-overlay, reused here for a
   Cancel/Confirm prompt instead of a status message — no <dialog> element
   used elsewhere in this codebase yet, so this stays consistent with the
   one overlay pattern that already exists. */
.confirm-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.55);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.2s ease;
}

.confirm-overlay.is-visible {
  opacity: 1;
  visibility: visible;
}

.confirm-dialog {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  background: var(--color-surface-container-lowest);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-level3);
  padding: var(--space-xl);
  max-width: 360px;
  width: calc(100% - var(--space-xl));
}

.confirm-dialog-title {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  font: 600 18px/24px var(--font-family);
  color: var(--color-on-surface);
  margin: 0;
}

.confirm-dialog-title .icon {
  color: var(--color-error);
}

.confirm-dialog-message {
  font: 400 14px/20px var(--font-family);
  color: var(--color-on-surface-variant);
  margin: 0;
}

.confirm-dialog-filename {
  font-weight: 600;
  color: var(--color-on-surface);
}

.confirm-dialog-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-sm);
  margin-top: var(--space-xs);
}

/* ── Resume guidelines page ─────────────────────────────────────────
   Each category tile reuses the same --tile-color custom-property pattern
   as .score-gauge-segment's --segment-color, fed the exact same 5 hex values
   (views.py's BREAKDOWN_COLORS) — so a reader who's seen a result page
   recognizes the color↔category mapping here. color-mix() derives a pale
   tonal background from that one hex, same effect as the app's hand-picked
   "-fixed" tokens but without needing 5 new bespoke tint tokens. */
.guideline-card {
  display: flex;
  gap: var(--space-lg);
  align-items: flex-start;
}

.guideline-card + .guideline-card {
  margin-top: var(--space-lg);
}

.icon-tile {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--tile-color) 18%, white);
  flex-shrink: 0;
}

.icon-tile .icon {
  color: var(--tile-color);
}

.guideline-card-body {
  min-width: 0;
}

.guideline-card-weight {
  color: var(--color-on-surface-variant);
  margin: 0 0 var(--space-md);
  font-weight: 600;
}

.guideline-list {
  margin: 0;
  padding-left: 1.1em;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.guideline-list li {
  font: 400 15px/22px var(--font-family);
  color: var(--color-on-surface);
}

/* ── Credits (see apps/credits, static/js/credits.js) ──────────── */

.credit-balance-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font: 600 13px/20px var(--font-family);
  color: var(--color-on-surface-variant);
  background: var(--color-surface-container);
  border-radius: var(--radius-full);
  padding: 4px 12px;
}
.credit-balance-chip .icon { width: 14px; height: 14px; opacity: 0.8; }

@media (max-width: 480px) {
  .credit-balance-chip { display: none; }
}

.credit-popup-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(40, 23, 26, 0.45);
  padding: var(--space-lg);
}
/* The [hidden] attribute's default UA-stylesheet `display: none` loses to
   the unconditional `display: flex` above (a same-element rule with equal
   specificity wins by source order) — without this, the overlay stays
   visually present (and intercepts every click on the page) even while
   hidden="" is set. */
.credit-popup-overlay[hidden] {
  display: none;
}

.credit-popup {
  position: relative;
  width: 100%;
  max-width: 440px;
  max-height: calc(100vh - var(--space-xxl));
  overflow-y: auto;
  background: var(--color-surface-container-lowest);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-level3);
  padding: var(--space-xl);
}

.credit-popup-close {
  position: absolute;
  top: var(--space-md);
  right: var(--space-md);
  width: 28px;
  height: 28px;
  border: none;
  background: transparent;
  color: var(--color-on-surface-variant);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  border-radius: 50%;
}
.credit-popup-close:hover { background: var(--color-surface-container); }

.credit-popup-loading {
  font: 500 14px/20px var(--font-family);
  color: var(--color-on-surface-variant);
  text-align: center;
  padding: var(--space-lg) 0;
}

.credit-popup-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-sm);
}

.credit-pack-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-sm);
  margin-bottom: var(--space-lg);
}

.credit-pack-card {
  border: 1px solid var(--color-outline-variant);
  border-radius: var(--radius);
  padding: var(--space-md);
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.credit-pack-name { font: 600 13px/18px var(--font-family); color: var(--color-on-surface); }
.credit-pack-credits { font: 700 18px/24px var(--font-family); color: var(--color-primary); }
.credit-pack-price { font: 500 13px/18px var(--font-family); color: var(--color-on-surface-variant); margin-bottom: 6px; }
.credit-pack-card .btn { width: 100%; }

.credit-custom-row {
  border-top: 1px solid var(--color-outline-variant);
  padding-top: var(--space-md);
  margin-bottom: var(--space-md);
}
.credit-custom-input-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-top: 6px;
}
.credit-custom-input-row input {
  flex: 1;
  min-width: 0;
  padding: 8px 10px;
  border: 1px solid var(--color-outline-variant);
  border-radius: var(--radius);
  font: 400 14px/20px var(--font-family);
}
.credit-custom-input-row input:focus {
  outline: none;
  border-color: var(--color-primary);
}
.credit-custom-price {
  font: 600 14px/20px var(--font-family);
  color: var(--color-on-surface);
  white-space: nowrap;
}

.credit-popup-insufficient-cancel {
  width: 100%;
}

/* ── Welcome popup (registration only, base.html + accounts.context_processors) ── */

.welcome-popup-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(40, 23, 26, 0.45);
  padding: var(--space-lg);
}

.welcome-confetti {
  position: fixed;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 1001;
}

.welcome-confetti-piece {
  position: absolute;
  top: -16px;
  width: 8px;
  height: 14px;
  border-radius: 2px;
  opacity: 0.9;
  animation-name: welcome-confetti-fall;
  animation-timing-function: ease-in;
  animation-fill-mode: forwards;
}

@keyframes welcome-confetti-fall {
  to {
    transform: translateY(100vh) rotate(600deg);
    opacity: 0;
  }
}

.welcome-popup {
  position: relative;
  z-index: 1002;
  width: 100%;
  max-width: 420px;
  max-height: calc(100vh - var(--space-xxl));
  overflow-y: auto;
  background: var(--color-surface-container-lowest);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-level3);
  padding: var(--space-xl);
  text-align: center;
}

.welcome-popup-emoji {
  font-size: 48px;
  line-height: 1;
  margin-bottom: var(--space-md);
}

.welcome-popup-badges {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  margin-bottom: var(--space-lg);
}

.welcome-popup-badge {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  background: var(--color-tertiary-fixed);
  color: var(--color-on-tertiary-fixed-variant);
  border-radius: var(--radius-full);
  padding: var(--space-sm) var(--space-lg);
  font: 600 14px/20px var(--font-family);
}

/* ── Credits admin dashboard (apps/credits/admin_views.py, superuser-only) ── */

.credits-admin-search {
  display: flex;
  gap: var(--space-sm);
  margin-bottom: var(--space-md);
  max-width: 480px;
}
.credits-admin-search-input {
  flex: 1;
  min-width: 0;
  padding: 8px 12px;
  border: 1px solid var(--color-outline-variant);
  border-radius: var(--radius);
  font: 400 14px/20px var(--font-family);
}
.credits-admin-search-input:focus {
  outline: none;
  border-color: var(--color-primary);
}

.credits-admin-table {
  width: 100%;
  border-collapse: collapse;
  min-width: 780px;
}
.credits-admin-table th {
  text-align: left;
  font: 600 12px/16px var(--font-family);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-on-surface-variant);
  padding: var(--space-md);
  border-bottom: 1px solid var(--color-outline-variant);
}
.credits-admin-table td {
  padding: var(--space-md);
  border-bottom: 1px solid var(--color-outline-variant);
  vertical-align: middle;
}
.credits-admin-table tr:last-child td {
  border-bottom: none;
}

.credits-admin-add-form {
  display: flex;
  gap: 6px;
  align-items: center;
}
.credits-admin-add-input {
  width: 80px;
  padding: 6px 8px;
  border: 1px solid var(--color-outline-variant);
  border-radius: var(--radius);
  font: 400 13px/18px var(--font-family);
}
.credits-admin-add-note {
  width: 140px;
  padding: 6px 8px;
  border: 1px solid var(--color-outline-variant);
  border-radius: var(--radius);
  font: 400 13px/18px var(--font-family);
}
.credits-admin-add-input:focus,
.credits-admin-add-note:focus {
  outline: none;
  border-color: var(--color-primary);
}

.credits-admin-pagination {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  margin-top: var(--space-lg);
}
