/* =======================================================
   FPVBASE — "FLIGHT DECK" DESIGN SYSTEM
   Mission-placard palette (French Blue / Scarlet Fire / White /
   Onyx / Lilac), square corners throughout, bold uppercase
   monospace for labels/controls, a two-tier blueprint grid behind
   everything, and real seven-segment digits for numeric readouts.
======================================================= */

:root {
  --onyx: #0f110c;
  --panel: #1c1c1c;
  --panel-raised: #232323;
  --panel-hover: #2b2b2b;
  --line: #3a3a3a;
  --line-bright: #545454;
  --cream: #ffffff;
  --cream-dim: #a8a8a8;
  --blue: #0b3d91;
  --blue-bright: #1b53b8;
  --scarlet: #fc3d21;
  --lilac: #c297b8;

  /* Scarlet/lilac AS TEXT (not border/background fill) need their
     own token: both are light/bright enough that white/near-white
     panels in light mode drop them below readable contrast (scarlet
     lands around 3.4:1, lilac worse at ~2.4:1 — both fail the 4.5:1
     text minimum). Identical to --scarlet/--lilac here in dark mode
     (already high-contrast against near-black), overridden to a
     darker shade of the same hue in the light theme below. */
  --scarlet-text: #fc3d21;
  --lilac-text: #c297b8;

  /* Doesn't flip between themes: --blue/--blue-bright stay dark
     enough in both that anything sitting on top of them (button
     text, the save/avatar-edit overlays that are always a fixed
     dark scrim regardless of theme) needs to stay light either way
     — unlike --cream, which is the general "readable on the current
     background" text color and DOES flip. */
  --on-dark: #ffffff;

  /* Ghost (unlit) seven-segment digit background — a low-opacity
     tint of the *opposite* of the current surface color, so an
     unlit segment stays faintly visible against whichever panel
     it's drawn on instead of vanishing (white-on-white in light
     mode) or looking like a smudge (white-on-dark stays as before). */
  --seg-ghost: rgba(255, 255, 255, 0.03);

  --font-mono: ui-monospace, "SF Mono", "JetBrains Mono", "Roboto Mono", Consolas, monospace;
  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
}

/* ---- Light theme ----
   Toggled via a button in the header (see .theme-toggle / auth.js),
   persisted to localStorage, applied to <html data-theme="light">
   before first paint (inline script in each page's <head>) to avoid
   a flash of the wrong theme. Only the neutral surface/text tokens
   flip — the accent colors (blue/scarlet/lilac) and --on-dark stay
   identical in both themes, so buttons/badges/links read as the
   same brand regardless of which theme is active. */
:root[data-theme="light"] {
  /* Onyx (page background) noticeably darker than white panels/
     cards on purpose — the contrast is what makes them read as
     distinct "raised" surfaces instead of blending into the page. */
  --onyx: #e2e6ec;
  --panel: #ffffff;
  --panel-raised: #f6f7f9;
  --panel-hover: #dde1e7;
  --line: #dde1e6;
  --line-bright: #b6bcc5;
  --cream: #14161a;
  --cream-dim: #5c6169;
  --seg-ghost: rgba(15, 17, 12, 0.025);

  /* Darker shades of the same scarlet/lilac hue — ~5:1 contrast
     against white instead of --scarlet/--lilac's own ~3.4:1/~2.4:1.
     --scarlet/--lilac themselves are untouched (still used for
     borders and background fills, which don't need text-level
     contrast and should stay the actual brand color). */
  --scarlet-text: #c8331c;
  --lilac-text: #8c4f74;
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  background: var(--onyx);
}

body {
  /* Two-tier blueprint grid: fine lines every 32px, a bolder rule
     every 4th line (128px) — the fine/bold convention of real
     engineering graph paper, tinted blue so it reads as
     "blueprint," not static. Kept subtle: felt before it's read. */
  background-color: var(--onyx);
  background-image:
    repeating-linear-gradient(to bottom, rgba(59, 120, 220, 0.06) 0 1px, transparent 1px 128px),
    repeating-linear-gradient(to right, rgba(59, 120, 220, 0.06) 0 1px, transparent 1px 128px),
    repeating-linear-gradient(to bottom, rgba(59, 120, 220, 0.028) 0 1px, transparent 1px 32px),
    repeating-linear-gradient(to right, rgba(59, 120, 220, 0.028) 0 1px, transparent 1px 32px);
  min-height: 100vh;
  font-family: var(--font-sans);
  color: var(--cream);
  line-height: 1.5;
}

/* ---- Typography ---- */

h1 {
  font-family: var(--font-mono);
  font-weight: 800;
  font-size: clamp(26px, 3.4vw, 38px);
  letter-spacing: 0.005em;
  text-wrap: balance;
}

h2 {
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--cream-dim);
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 4px;
}

h2::after {
  content: "";
  flex: 1;
  height: 2px;
  background: var(--line);
}

h3 {
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 700;
}

/* Blocky mono label style — tabs, tags, buttons, captions. */
.osd {
  font-family: var(--font-mono);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* ---- Header ---- */
/* min-height (not a hard height) keeps the bar at its usual size on
   one line — a bigger .logo just centers inside it via
   align-items — but still lets it grow when there isn't room for
   logo + nav side by side (narrow phones): flex-wrap moves nav to
   its own row instead of squeezing everything into a fixed box. */
.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 20px;
  min-height: 66px;
  padding: 12px 8%;
  background: var(--panel);
  color: var(--cream);
}

/* Two logo images, not one — the wordmark's "Base" half is white in
   the dark-theme file, unreadable now that the header follows the
   theme again. CSS toggles which one is visible (no flash: the
   <html data-theme> attribute is already set by the inline
   anti-FOUC script before first paint). .logo itself is just the
   clickable wrapper; sizing lives on the img so its own aspect
   ratio is preserved regardless of height. */
.logo {
  display: flex;
  align-items: center;
  cursor: pointer;
}

.logo img {
  display: block;
  height: 56px;
  width: auto;
}

/* .logo img (specificity 0,1,1) would otherwise beat a plain
   .logo-light (0,1,0) and win the display:block/none conflict —
   this needs .logo img.logo-light to actually out-rank it and stay
   hidden by default. */
.logo img.logo-light {
  display: none;
}

:root[data-theme="light"] .logo-dark {
  display: none;
}

:root[data-theme="light"] .logo-light {
  display: block;
}

/* nav itself wraps its links (rather than relying on plain inline
   text-wrap, which reads unevenly) — gap gives consistent spacing
   whether it's one row on desktop or several on a narrow phone. */
nav {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 8px 22px;
}

nav a {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--cream-dim);
  text-decoration: none;
}

nav a:hover {
  color: var(--scarlet-text);
}

/* Hamburger toggle — hidden by default (desktop keeps the plain nav
   row above); shown below the mobile breakpoint, where nav becomes
   a slide-in drawer instead (see the media query + .nav-backdrop
   further down). Three hand-drawn bars, not a "☰" glyph — same
   reasoning as every other icon in this design system. */
.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 5px;
  width: 34px;
  height: 34px;
  padding: 0;
  border: 2px solid var(--line-bright);
  background: transparent;
  cursor: pointer;
  flex-shrink: 0;
}

.nav-toggle span {
  display: block;
  width: 18px;
  height: 2px;
  background: var(--cream);
  transition: transform 0.15s ease, opacity 0.15s ease;
}

.nav-toggle:hover {
  border-color: var(--blue-bright);
}

/* Open state: the three bars redraw into an X. */
.nav-toggle.open span:nth-child(1) {
  transform: translateY(7px) rotate(45deg);
}

.nav-toggle.open span:nth-child(2) {
  opacity: 0;
}

.nav-toggle.open span:nth-child(3) {
  transform: translateY(-7px) rotate(-45deg);
}

/* Dims the page behind the open drawer and gives a tap-anywhere way
   to close it — created in JS (setupMobileNav() in auth.js) rather
   than hand-placed on every page. */
.nav-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(15, 17, 12, 0.7);
  z-index: 1090;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
}

.nav-backdrop.open {
  opacity: 1;
  pointer-events: auto;
}

@media (max-width: 1000px) {
  /* Shrinks alongside a slide-in drawer — the full 56px felt
     oversized next to it, same reasoning as when this was still
     text. */
  .logo img {
    height: 32px;
  }

  .nav-toggle {
    display: flex;
  }

  /* Slides in from the right edge; links/auth actions stack top to
     bottom instead of wrapping in a row. Height is set twice on
     purpose: 100vh first as a fallback, then 100dvh — on a real
     phone (unlike a resized desktop window) 100vh is measured
     against the viewport with the browser's address bar hidden, so
     it overshoots the space actually visible and cuts off whatever
     sits lowest in the stack (the profile link, being one of the
     last items) without any obvious way to tell it needs scrolling.
     100dvh tracks the real visible height instead; overflow-y:auto
     stays as a safety net regardless. */
  .header nav {
    position: fixed;
    top: 0;
    right: 0;
    height: 100vh;
    height: 100dvh;
    width: min(62vw, 240px);
    overflow-y: auto;
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-start;
    gap: 6px;
    background: var(--panel);
    border-left: 2px solid var(--line);
    padding: 76px 26px 26px;
    transform: translateX(100%);
    transition: transform 0.2s ease;
    z-index: 1100;
  }

  .header nav.open {
    transform: translateX(0);
  }

  /* display:contents un-nests #authStatus's own children (the
     moderation link if any, the profile link, the log in/out
     button) straight into nav's column flow — so the profile link
     lands right after Home/Explore/Requests/Publish as just another
     stacked row, instead of staying visually grouped inside a
     separate inline block that was easy to miss. */
  .header nav .auth-status {
    display: contents;
  }

  /* Destinations (Home, Explore... and the profile link, once
     flattened above) read as a list; log in/out is an action, not
     a destination, so it's excluded here and styled + positioned
     on its own right below. */
  .header nav a:not(.auth-btn) {
    width: 100%;
    padding: 10px 0;
    border-bottom: 1px solid var(--line);
  }

  /* margin-top:auto pins it to the bottom of the drawer (nav is a
     column flex container) regardless of how many links sit above
     it, with its own top border as a divider — "lower down and
     visually separate," not just another row in the list. */
  .header nav .auth-btn {
    margin-top: auto;
    padding-top: 16px;
    border-top: 1px solid var(--line);
  }
}

/* ---- Hero ---- */
.hero {
  text-align: center;
  padding: 64px 8% 40px;
}

.hero h1 {
  font-size: clamp(30px, 4.4vw, 46px);
  margin-bottom: 14px;
}

.hero p {
  color: var(--cream-dim);
  font-size: 16px;
  max-width: 56ch;
  margin: 0 auto 28px;
}

#searchInput {
  width: 100%;
  max-width: 650px;
  padding: 15px 18px;
  border: 2px solid var(--blue-bright);
  background: rgba(11, 61, 145, 0.12);
  color: var(--cream);
  font-family: var(--font-sans);
  font-size: 15px;
  outline: none;
  transition: border-color 0.12s ease, background 0.12s ease;
}

#searchInput::placeholder {
  color: var(--cream-dim);
}

#searchInput:hover {
  background: rgba(11, 61, 145, 0.18);
}

#searchInput:focus {
  border-color: var(--scarlet);
  background: rgba(11, 61, 145, 0.18);
}

/* ---- Platform stats — seven-segment readout ---- */
.stats-section {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2px;
  max-width: 720px;
  margin: 36px auto 0;
  background: var(--line);
  border: 2px solid var(--line);
}

.stat-card {
  background: var(--panel-raised);
  padding: 20px 18px;
  text-align: center;
}

.stat-card strong {
  display: inline-flex;
  justify-content: center;
}

.stat-card span {
  display: block;
  margin-top: 8px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--scarlet-text);
}

/* Smaller variant — same red/bordered look, sitting under the hero
   title instead of below the search bar. The digits themselves
   stay at their normal (.stat-card) size — every attempt at
   shrinking the hand-built segment geometry further fell apart
   (glow bleed, then lost chevron points, then plain rectangles
   that still didn't read as real digits at a glance). Only the
   surrounding chrome (card padding, gaps, max-width, label size)
   is more compact. */
.stats-section.small {
  max-width: 480px;
  margin: 14px auto 6px;
}

.stats-section.small .stat-card {
  padding: 12px 10px;
}

.stats-section.small .stat-card span {
  margin-top: 6px;
  font-size: 10px;
}

/* ---- Section titles ---- */
/* Same box model as .models-grid (width:92% below 1320px, capped
   and centered above it) so a title/subtitle/controls row never
   runs wider than the card grid sitting right under it. */
.section-title {
  width: 92%;
  max-width: 1320px;
  margin: 50px auto 0;
}

.section-title p {
  color: var(--cream-dim);
  margin-top: 8px;
  max-width: 60ch;
}

/* Puts the subtitle and an adjacent control group (see
   .sort-controls) on the same row, forming one header block instead
   of two stacked ones. */
.section-title-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px 20px;
  margin-top: 8px;
}

.section-title-row p {
  margin-top: 0;
}

/* Sort controls (Trending / Most Downloaded / Recent) — sits in
   the same row as the "Popular models" subtitle. */
.sort-controls {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.sort-btn {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 10px 16px;
  border: 2px solid var(--line-bright);
  background: var(--panel-raised);
  color: var(--cream-dim);
  cursor: pointer;
  transition: border-color 0.12s ease, color 0.12s ease, background 0.12s ease;
}

.sort-btn:hover {
  border-color: var(--scarlet);
  color: var(--cream);
  background: var(--panel-hover);
}

.sort-btn.active {
  border-color: var(--scarlet);
  color: var(--scarlet-text);
  background: rgba(252, 61, 33, 0.08);
}

/* ---- Model cards ---- */
/* auto-fit still shrinks down to 3/2/1 columns on narrower screens
   on its own, same as before — the max-width is what stops it from
   ever growing past 4: at 1320px, a 5th 260px column plus its gap
   (1380px) no longer fits, so auto-fit settles on 4 and stretches
   them to fill the space instead. width:92% (rather than the old
   8% side padding) keeps the same side gutter below that width, and
   the max-width takes over once the viewport outgrows it. */
/* auto-fill, not auto-fit: with only one card, auto-fit collapses
   the unused column tracks to 0 width and lets that single 1fr
   stretch to fill the whole row — auto-fill keeps those empty
   tracks reserved instead, so one card stays card-sized (as if 3
   more were about to sit next to it) rather than blowing up to full
   width. Both behave identically once there's enough cards to fill
   every column, this only changes the sparse case. */
.models-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 20px;
  width: 92%;
  max-width: 1320px;
  margin: 0 auto;
  padding: 30px 0 60px;
}

.model-card {
  position: relative;
  background: var(--panel-raised);
  border: 2px solid var(--line);
  cursor: pointer;
  transition: border-color 0.12s ease, background 0.12s ease, transform 0.12s ease;
}

/* (hover: hover) and (pointer: fine) — real mouse only, not touch:
   on a phone, an element with :hover styles needs a first tap just
   to "preview" the hover state and a second one to actually fire
   the click, since the browser can't otherwise tell a hover from a
   tap. Gating this on real-hover capability means the first tap on
   a phone opens the model directly. */
@media (hover: hover) and (pointer: fine) {
  .model-card:hover {
    border-color: var(--cream-dim);
    background: var(--panel-hover);
    transform: translateY(-2px);
  }
}

.model-card:active {
  transform: translateY(0);
}

/* Archived — only ever rendered on the creator's own profile (see
   profile.js); scarlet is this design system's one alert color,
   same treatment as .archived-warning on the model page itself. */
.model-card.archived {
  border-color: var(--scarlet);
  background: rgba(252, 61, 33, 0.06);
}

.model-card.archived:hover {
  border-color: var(--scarlet);
  background: rgba(252, 61, 33, 0.1);
}

.archived-badge {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 1;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 4px 8px;
  border: 1px solid var(--scarlet);
  background: rgba(15, 17, 12, 0.85);
  color: var(--scarlet-text);
}

.model-image {
  aspect-ratio: 4 / 3;
  background: linear-gradient(135deg, rgba(11, 61, 145, 0.35), var(--panel));
  border-bottom: 2px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: center;
}

.thumb-icon {
  opacity: 0.9;
}

.model-content {
  padding: 18px;
}

/* Reserves space for exactly 2 lines, whether the title needs one
   or two — otherwise a wrapped title pushes the folder path and
   creator row down only on THAT card, throwing off alignment across
   a row of cards with shorter titles. A would-be 3rd line clamps
   with an ellipsis instead of growing the card further. */
.model-content h3 {
  margin-bottom: 8px;
  font-size: 18px;
  line-height: 1.3;
  min-height: calc(1.3em * 2);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Real <a href> around the title now (search engines only follow
   actual links, not the card's onclick) — styled to look exactly
   like plain text, same as before this existed. */
.model-content h3 a {
  color: inherit;
  text-decoration: none;
}

.model-content p {
  color: var(--cream-dim);
  font-size: 14px;
  margin-bottom: 12px;
  line-height: 1.45;
}

/* Same alignment reasoning as the title above: always exactly one
   line, so the creator/downloads row starts at the same height
   whether the folder path is short or long enough to have wrapped. */
.model-content .folder-path {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Bottom row of a model card — replaces the old "View model"
   button (the whole card is already a click target). Creator name
   on the left, download count on the right, with a divider above
   to separate it from the folder path/description. */
.card-meta {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

/* Wraps the avatar + the name badge together so they read as one
   unit and truncate together on a narrow card. */
.card-creator-group {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

/* Sized to match .card-creator's own rendered height (11px mono
   text + 4px vertical padding + 1px border, both sides) so it sits
   flush next to the name instead of looking mismatched. */
.card-creator-avatar {
  width: 26px;
  height: 26px;
  flex-shrink: 0;
  object-fit: cover;
  border: 1px solid var(--line-bright);
  display: block;
}

.card-creator-avatar-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--panel);
  color: var(--cream-dim);
  font-family: var(--font-mono);
  font-weight: 800;
  font-size: 12px;
}

.card-creator {
  display: inline-block;
  min-width: 0;
  padding: 4px 9px;
  border: 1px solid var(--blue-bright);
  background: var(--blue);
  color: var(--on-dark);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.card-downloads {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--cream-dim);
  flex-shrink: 0;
}

.card-downloads svg {
  flex-shrink: 0;
}

.tags {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 14px;
}

.tag {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  background: var(--panel);
  color: var(--cream-dim);
  border: 1px solid var(--line-bright);
  padding: 5px 9px;
  cursor: default;
}

.download-btn {
  /* display/text-align/text-decoration: no effect on a <button>
     (already its default rendering), needed so .download-btn
     behaves the same on the <a> link for
     "Download STL" (model.html). */
  display: inline-block;
  text-align: center;
  text-decoration: none;
  width: 100%;
  padding: 12px;
  border: 2px solid var(--blue);
  background: var(--blue);
  color: var(--on-dark);
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  cursor: pointer;
  box-sizing: border-box;
  transition: background 0.12s ease, border-color 0.12s ease;
}

.download-btn:hover {
  background: var(--blue-bright);
  border-color: var(--blue-bright);
}

/* ---- Vote row on request cards ---- */
.vote-btn {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 8px 12px;
  border: 2px solid var(--line-bright);
  background: var(--panel);
  color: var(--cream);
  cursor: pointer;
  transition: border-color 0.12s ease, background 0.12s ease;
}

.vote-btn:hover {
  border-color: var(--scarlet);
  background: var(--panel-hover);
}

.vote-count {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  color: var(--scarlet-text);
}

/* ---- Publish call-to-action / request suggestion ---- */
.publish-callout {
  max-width: 680px;
  margin: 40px auto;
  padding: 28px;
  text-align: center;
  background: var(--panel);
  border: 2px solid var(--line);
}

.publish-callout h2 {
  justify-content: center;
}

.publish-callout h2::after {
  display: none;
}

.publish-callout p {
  color: var(--cream-dim);
  margin: 12px 0 18px;
}

.publish-callout button {
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 12px 22px;
  border: 2px solid var(--blue);
  background: var(--blue);
  color: var(--on-dark);
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease;
}

.publish-callout button:hover {
  background: var(--blue-bright);
  border-color: var(--blue-bright);
}

/* ---- Forms (upload/user/login/comment/moderation) ---- */
.upload-section,
.user-section {
  max-width: 700px;
  margin: 20px auto 50px;
  padding: 26px;
  background: var(--panel);
  border: 2px solid var(--line);
}

.upload-section h2 {
  margin-bottom: 18px;
}

input,
textarea,
select {
  width: 100%;
  margin-bottom: 12px;
  padding: 12px 14px;
  border: 2px solid var(--line-bright);
  background: var(--panel-raised);
  color: var(--cream);
  font-family: var(--font-sans);
  font-size: 15px;
}

textarea {
  min-height: 100px;
  resize: vertical;
}

input::placeholder,
textarea::placeholder {
  color: var(--cream-dim);
  opacity: 0.75;
}

input:focus,
textarea:focus,
select:focus {
  outline: none;
  border-color: var(--scarlet);
}

select {
  cursor: pointer;
}

select option {
  background-color: var(--panel-raised);
  color: var(--cream);
}

/* Live "x / max" readout under a field with a maxlength — wired by
   attachCharCounter() in data.js. Sits right under the field it
   describes, so pull it up against the standard 12px input margin
   rather than stacking a second full gap under it. */
.char-count {
  display: block;
  margin-top: -8px;
  margin-bottom: 12px;
  text-align: right;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--cream-dim);
}

.char-count.char-count-limit {
  color: var(--scarlet-text);
}

#uploadButton,
.user-section button {
  width: 100%;
  padding: 13px;
  border: 2px solid var(--blue);
  background: var(--blue);
  color: var(--on-dark);
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease;
}

#uploadButton:hover,
.user-section button:hover {
  background: var(--blue-bright);
  border-color: var(--blue-bright);
}

#uploadMessage,
#usernameMessage {
  margin-top: 10px;
  color: var(--cream-dim);
  font-size: 13px;
}

.model-img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  display: block;
  border-bottom: 2px solid var(--line);
}

.upload-section label {
  display: block;
  margin-top: 14px;
  margin-bottom: 6px;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--cream-dim);
}

.upload-hint {
  font-size: 13px;
  color: var(--cream-dim);
  margin-bottom: 8px;
}

.image-preview {
  display: none;
  width: 100%;
  max-height: 260px;
  object-fit: cover;
  margin: 12px 0 18px;
  border: 2px solid var(--line-bright);
}

.folder-path {
  color: var(--cream-dim);
  font-size: 13px;
  margin: 8px 0;
}

/* ---- File list (STL uploads / new-version files) ---- */
.files-preview {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: 8px 0 4px;
}

.file-preview-item {
  font-family: var(--font-mono);
  font-size: 12.5px;
  color: var(--cream-dim);
  padding: 8px 10px;
  background: var(--panel-raised);
  border: 1px solid var(--line);
}

/* =======================
   EXPLORER FOLDERS
======================= */

.breadcrumb {
  margin: 22px 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.breadcrumb button {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  background: var(--panel-raised);
  color: var(--cream);
  border: 2px solid var(--line-bright);
  padding: 8px 12px;
  cursor: pointer;
}

.breadcrumb button:hover {
  border-color: var(--scarlet);
}

.breadcrumb span {
  color: var(--cream-dim);
  font-family: var(--font-mono);
  font-size: 12px;
}

.folders-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px;
  margin-top: 18px;
}

.folder-card {
  background: var(--panel-raised);
  border: 2px solid var(--line);
  padding: 18px;
  display: flex;
  align-items: center;
  gap: 14px;
  cursor: pointer;
  transition: border-color 0.12s ease, background 0.12s ease, transform 0.12s ease;
}

.folder-card:hover {
  border-color: var(--cream-dim);
  background: var(--panel-hover);
  transform: translateY(-2px);
}

.folder-icon {
  font-size: 30px;
}

.folder-card h3 {
  margin-bottom: 4px;
}

.folder-card p {
  color: var(--cream-dim);
  font-size: 14px;
}

.folder-search {
  width: 100%;
  max-width: 600px;
  padding: 14px 16px;
  border: 2px solid var(--blue-bright);
  background: rgba(11, 61, 145, 0.12);
  color: var(--cream);
  font-size: 15px;
  margin-bottom: 26px;
  transition: border-color 0.12s ease, background 0.12s ease;
}

.folder-search::placeholder {
  color: var(--cream-dim);
}

.folder-search:hover {
  background: rgba(11, 61, 145, 0.18);
}

.folder-search:focus {
  outline: none;
  border-color: var(--scarlet);
  background: rgba(11, 61, 145, 0.18);
}

.selected-folder-box {
  background: var(--panel-raised);
  border: 2px solid var(--blue-bright);
  padding: 18px;
  margin: 20px 0;
}

.selected-folder-label {
  color: var(--cream-dim);
  font-family: var(--font-mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 8px;
}

.selected-folder-path {
  color: var(--cream);
  font-size: 16px;
  font-weight: 600;
  word-break: break-word;
}

.active-folder {
  border-color: var(--scarlet) !important;
  background: var(--panel-hover);
  box-shadow: 0 0 0 1px var(--scarlet);
}

.active-folder .folder-icon {
  font-size: 36px;
}

.active-folder h3 {
  color: var(--scarlet-text);
}

.current-folder-big {
  margin-bottom: 22px;
  max-width: 420px;
}

/* =======================
   MODEL DETAIL PAGE
======================= */

.model-page {
  padding: 40px 8%;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 28px;
}

.model-detail-image-wrapper,
.model-detail-content,
.model-info-panel,
.comments-section {
  background: var(--panel);
  border: 2px solid var(--line);
  padding: 26px;
}

.model-detail-image {
  width: 100%;
  max-height: 440px;
  object-fit: cover;
  display: block;
}

.model-detail-placeholder {
  height: 360px;
  background: linear-gradient(135deg, rgba(11, 61, 145, 0.35), var(--panel));
  display: flex;
  align-items: center;
  justify-content: center;
}

.model-detail-placeholder .thumb-icon,
.model-detail-placeholder svg {
  width: 88px;
  height: 88px;
}

.model-actions {
  display: flex;
  gap: 10px;
  align-items: center;
  margin: 22px 0;
  flex-wrap: wrap;
}

.model-actions button {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 11px 15px;
  border: 2px solid var(--line-bright);
  background: var(--panel-raised);
  color: var(--cream);
  cursor: pointer;
  transition: border-color 0.12s ease, background 0.12s ease;
}

.model-actions button:hover {
  border-color: var(--cream);
  background: var(--panel-hover);
}

#saveButton.saved {
  border-color: var(--lilac);
  color: var(--lilac-text);
}

#saveButton.saved:hover {
  background: rgba(194, 151, 184, 0.1);
}

.save-readout {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

/* ---- Card save (bookmark) button ----
   Not saved: invisible until the card is hovered/focused, and
   reads as a labeled "Save" badge rather than a bare glyph. Once
   saved: the glyph alone stays permanently visible (in Lilac, held
   back everywhere else in the palette) so "already saved" reads at
   a glance without needing to hover every card to check. */
.card-save-btn {
  position: absolute;
  top: 10px;
  right: 10px;
  min-width: 34px;
  height: 34px;
  padding: 0 10px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border: 2px solid var(--line-bright);
  background: rgba(15, 17, 12, 0.85);
  color: var(--on-dark);
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}

.card-save-btn .save-icon {
  display: none;
  font-size: 16px;
  line-height: 1;
}

.card-save-btn .save-label {
  display: none;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* Not saved, card focused (keyboard, or a tap that lands on the
   button itself): reveal the "Save" label — works on touch too,
   unlike the :hover variant below. */
.model-card:focus-within .card-save-btn:not(.saved) {
  opacity: 1;
}

.model-card:focus-within .card-save-btn:not(.saved) .save-label {
  display: inline;
}

/* Same reveal, mouse-hover only (see the .model-card:hover media
   query above for why touch is excluded) — a phone would otherwise
   need a first tap just to reveal Save before a second one could
   actually press it. */
@media (hover: hover) and (pointer: fine) {
  .model-card:hover .card-save-btn:not(.saved) {
    opacity: 1;
  }

  .model-card:hover .card-save-btn:not(.saved) .save-label {
    display: inline;
  }
}

/* Saved: glyph only, always visible, Lilac. */
.card-save-btn.saved {
  opacity: 1;
  border-color: var(--lilac);
  color: var(--lilac-text);
}

.card-save-btn.saved .save-icon {
  display: inline;
}

@media (hover: hover) and (pointer: fine) {
  .card-save-btn:hover {
    border-color: var(--lilac);
    color: var(--lilac-text);
  }
}

/* Report: stays discreet by default, stands out on hover so it
   doesn't permanently draw the eye next to Like/Save. */
.report-btn:hover {
  border-color: var(--scarlet);
  color: var(--scarlet-text);
}

/* Content already reported by the user — same visual logic as
   the hover state, but permanent until it's cancelled. */
.report-btn.reported {
  border-color: var(--scarlet);
  color: var(--scarlet-text);
}

/* Content the current viewer has personally reported (see
   hasUserReported() in data.js) — blurred as a self-serve "hide
   until I un-report it" cue. Purely client-side and purely
   personal: nobody else's view is affected, and the report button
   itself is deliberately excluded so it stays the easy way to
   undo a mistaken report. */
.reported-by-you {
  filter: blur(16px) grayscale(1);
  opacity: 0.4;
  transition: filter 0.2s, opacity 0.2s;
}

.model-info-panel,
.comments-section {
  grid-column: span 2;
}

.comments-section textarea {
  width: 100%;
  min-height: 100px;
  margin: 16px 0;
}

@media (max-width: 800px) {
  .model-page {
    grid-template-columns: 1fr;
  }

  .model-info-panel,
  .comments-section {
    grid-column: span 1;
  }
}

/* =======================
   COMMENTS - BUBBLES
======================= */

.comment-bubble {
  background: var(--panel-raised);
  border: 1px solid var(--line);
  padding: 16px;
  margin-bottom: 12px;
}

.comment-header {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--blue-bright);
  margin-bottom: 8px;
}

.comment-text {
  color: var(--cream);
  line-height: 1.5;
  margin-bottom: 12px;
}

.comment-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

.comment-like-btn,
.comment-report-btn,
.comment-delete-btn {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  padding: 7px 11px;
  border: 1px solid var(--line-bright);
  background: var(--panel);
  color: var(--cream-dim);
  cursor: pointer;
}

.comment-like-btn:hover {
  border-color: var(--blue-bright);
  color: var(--blue-bright);
}

.comment-report-btn:hover,
.comment-delete-btn:hover {
  border-color: var(--scarlet);
  color: var(--scarlet-text);
}

.comment-report-btn.reported {
  border-color: var(--scarlet);
  color: var(--scarlet-text);
}

/* =======================
   REPORT MODAL
======================= */

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(15, 17, 12, 0.75);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  z-index: 1000;
}

.modal-card {
  position: relative;
  width: 100%;
  max-width: 480px;
  max-height: 90vh;
  overflow-x: hidden;
  overflow-y: auto;
  background: var(--panel);
  border: 2px solid var(--line);
  padding: 26px;
  text-align: left;
}

/* Sit flush with the card's own edges (not -2px past them, as this
   used to be): a corner bracket bleeding outside the box counts as
   real overflow content once the card has overflow-y:auto, which
   was quietly forcing both a vertical AND a horizontal scrollbar
   to appear — for basically every .modal-card, not just this one —
   even though there was nothing worth scrolling to. */
.modal-card::before,
.modal-card::after {
  content: "";
  position: absolute;
  width: 16px;
  height: 16px;
}

.modal-card::before {
  top: 0;
  left: 0;
  border-top: 3px solid var(--scarlet);
  border-left: 3px solid var(--scarlet);
}

.modal-card::after {
  bottom: 0;
  right: 0;
  border-bottom: 3px solid var(--scarlet);
  border-right: 3px solid var(--scarlet);
}

.modal-card label {
  display: block;
  margin-top: 12px;
  margin-bottom: 6px;
  font-family: var(--font-mono);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--cream-dim);
}

.modal-card textarea {
  min-height: 70px;
}

.modal-card .form-actions {
  display: flex;
  gap: 10px;
  margin-top: 14px;
}

/* Checkbox list: more specific than ".modal-card label" so it
   doesn't inherit its display:block (we want the checkbox and
   text aligned side by side, not stacked). */
.modal-card .report-reasons-list {
  margin: 10px 0 16px;
}

.modal-card .report-reasons-list label {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 8px;
  text-transform: none;
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--cream);
  cursor: pointer;
}

.modal-card .report-reasons-list input[type="checkbox"] {
  width: auto;
  margin: 0;
}

.form-actions button {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 11px 15px;
  border: 2px solid var(--blue);
  background: var(--blue);
  color: var(--on-dark);
  cursor: pointer;
  flex: 1;
}

.form-actions button:hover {
  background: var(--blue-bright);
  border-color: var(--blue-bright);
}

.form-actions button#cancelReportButton,
.form-actions button#cancelEditButton,
.form-actions button#cancelVersionButton,
.form-actions button#cancelDeleteAccountButton {
  background: transparent;
  border-color: var(--line-bright);
  color: var(--cream-dim);
}

.form-actions button#cancelReportButton:hover,
.form-actions button#cancelEditButton:hover,
.form-actions button#cancelVersionButton:hover,
.form-actions button#cancelDeleteAccountButton:hover {
  border-color: var(--cream);
  color: var(--cream);
}

/* =======================
   CONFIRM / ALERT MODAL
   In-DA-system replacement for window.confirm()/alert() — same
   .modal-card corner-bracket panel as the report modal, built once
   in JS (showConfirm()/showAlert() in data.js) and reused for every
   call, not hand-placed per page. See .form-actions button above
   for the shared blue base button style.
======================= */

.confirm-modal-card {
  text-align: center;
}

.confirm-modal-message {
  font-size: 15px;
  line-height: 1.5;
  margin-bottom: 4px;
  white-space: pre-line;
}

.form-actions .confirm-modal-cancel {
  background: transparent;
  border-color: var(--line-bright);
  color: var(--cream-dim);
}

.form-actions .confirm-modal-cancel:hover {
  border-color: var(--cream);
  color: var(--cream);
}

/* Only set on the confirm button when the action being confirmed
   reduces visibility/is otherwise consequential (e.g. archiving) —
   plain confirmations (unarchiving, generic OK) stay the default
   blue above. */
.form-actions .confirm-modal-confirm.danger {
  background: var(--scarlet);
  border-color: var(--scarlet);
}

.form-actions .confirm-modal-confirm.danger:hover {
  background: var(--onyx);
  color: var(--scarlet-text);
}

/* =======================
   COMMENTS - SHOW MORE
======================= */

.show-more-comments-btn {
  margin-top: 8px;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  padding: 10px 14px;
  border: 2px solid var(--line-bright);
  background: var(--panel-raised);
  color: var(--cream-dim);
  cursor: pointer;
}

.show-more-comments-btn:hover {
  border-color: var(--blue-bright);
  color: var(--cream);
}

/* =======================
   UPLOAD - MULTI-IMAGE PREVIEW
======================= */

.images-preview-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 10px;
  margin: 14px 0 20px;
}

.image-preview-thumb {
  width: 100%;
  height: 120px;
  object-fit: cover;
  border: 1px solid var(--line-bright);
}

/* Removable thumbnail (model.js edit form): wraps a thumbnail with
   a small "✕" button so an existing or just-added image can be
   dropped before saving. */
.edit-image-thumb-wrapper {
  position: relative;
}

.edit-image-thumb-wrapper .image-preview-thumb {
  display: block;
}

.remove-image-btn {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 24px;
  height: 24px;
  border: 1px solid var(--scarlet);
  background: rgba(15, 17, 12, 0.85);
  color: var(--scarlet-text);
  cursor: pointer;
  font-size: 13px;
  line-height: 1;
}

.remove-image-btn:hover {
  background: var(--scarlet);
  color: var(--onyx);
}

/* =======================
   MODEL PAGE - IMAGE GALLERY
======================= */

.model-thumbnails {
  display: flex;
  gap: 8px;
  margin-top: 14px;
  flex-wrap: wrap;
}

.model-thumbnail {
  width: 70px;
  height: 70px;
  object-fit: cover;
  border: 1px solid var(--line-bright);
  cursor: pointer;
  opacity: 0.6;
}

.active-thumbnail {
  border-color: var(--scarlet);
  opacity: 1;
}

/* =======================
   MODEL PAGE - CREATOR ACTIONS
======================= */

.creator-actions {
  position: relative;
  margin-top: 22px;
  padding: 20px;
  border: 2px solid var(--line);
  background: var(--panel-raised);
}

.creator-actions::before,
.creator-actions::after {
  content: "";
  position: absolute;
  width: 14px;
  height: 14px;
}

.creator-actions::before {
  top: -2px;
  left: -2px;
  border-top: 3px solid var(--blue-bright);
  border-left: 3px solid var(--blue-bright);
}

.creator-actions::after {
  bottom: -2px;
  right: -2px;
  border-bottom: 3px solid var(--blue-bright);
  border-right: 3px solid var(--blue-bright);
}

.creator-actions h3 {
  margin-bottom: 14px;
}

.creator-actions button {
  display: block;
  width: 100%;
  margin-bottom: 10px;
  padding: 12px;
  border: 2px solid var(--line-bright);
  background: var(--panel);
  color: var(--cream);
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: border-color 0.12s ease, background 0.12s ease;
}

.creator-actions button:hover {
  border-color: var(--cream);
  background: var(--panel-hover);
}

/* =======================
   "CURRENT VERSION" BADGE (top of page)
======================= */

.current-version-badge {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 4px 0 16px;
  color: var(--cream-dim);
}

.current-version-badge strong {
  color: var(--cream);
}

#currentVersionLabel {
  font-family: var(--font-mono);
  font-weight: 700;
  color: var(--blue-bright);
}

/* =======================
   VERSION HISTORY
======================= */

.version-history {
  margin-top: 10px;
}

.version-entry {
  padding: 12px 0;
  border-bottom: 1px solid var(--line);
}

.version-entry:last-child {
  border-bottom: none;
}

.version-entry-header {
  display: flex;
  align-items: center;
  gap: 8px;
}

.version-entry .version-number {
  font-family: var(--font-mono);
  color: var(--blue-bright);
  font-weight: 700;
}

.version-entry .version-date {
  color: var(--cream-dim);
  font-size: 13px;
}

.version-entry .version-changelog {
  margin-top: 4px;
  color: var(--cream-dim);
}

.version-entry .version-files {
  margin-top: 6px;
  font-size: 13px;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 14px;
}

.version-file-link {
  font-family: var(--font-mono);
  color: var(--blue-bright);
  text-decoration: none;
}

.version-file-link:hover {
  text-decoration: underline;
}

/* File published before real storage: shown but not clickable,
   rather than silently disappearing. */
.version-file-unavailable {
  color: var(--cream-dim);
  cursor: not-allowed;
}

/* =======================
   "NEW VERSION" FORM / EDIT FORM
======================= */

.new-version-form {
  margin-top: 14px;
  margin-bottom: 10px;
  padding: 18px;
  border: 2px solid var(--line);
  background: var(--panel);
  text-align: left;
}

.new-version-form label {
  display: block;
  margin-top: 12px;
  margin-bottom: 6px;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--cream-dim);
}

.new-version-form textarea {
  min-height: 80px;
}

.new-version-form .form-actions {
  display: flex;
  gap: 10px;
  margin-top: 14px;
}

.new-version-form .form-actions button {
  width: auto;
  flex: 1;
}

/* Warning shown when a model is archived — scarlet is the one
   alert color, reserved for exactly this kind of notice. */
.archived-warning {
  margin-top: 18px;
  padding: 14px 16px;
  border: 2px solid var(--scarlet);
  background: rgba(252, 61, 33, 0.08);
  color: var(--cream);
  font-size: 14px;
}

/* =======================
   LOGIN STATUS (HEADER)
======================= */

.auth-status {
  /* inline-flex so its own children (username link + logout
     button, or just the login link) lay out in a row — spacing
     against the other nav items comes from nav's own gap now, not
     a margin here, so it stays even whether nav wraps or not. */
  display: inline-flex;
  align-items: center;
  gap: 12px;
  vertical-align: middle;
}

/* Same blue badge as .card-creator on model cards — "this is a
   person's name" reads the same way everywhere on the site, and it
   stands out from the plain uppercase nav links (Home/Explore/...)
   sitting right next to it in the header. */
.auth-username {
  display: inline-block;
  padding: 4px 9px;
  border: 1px solid var(--blue-bright);
  background: var(--blue);
  color: var(--on-dark);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  text-decoration: none;
  max-width: 160px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: middle;
}

.auth-username:hover {
  background: var(--blue-bright);
}

.auth-btn {
  /* text-align:center matters here: a <button> centers its content
     by default, but the "Log in" state is a plain <a> (left-aligned
     by default) — without it, Log in and Log out looked
     inconsistently positioned inside the same-looking box.
     appearance:none strips the browser's native button chrome
     (its own default border/background/line-height), which is what
     made the border look off on hover — without it, our border sat
     on top of the platform's own button skin instead of replacing
     it outright. */
  appearance: none;
  -webkit-appearance: none;
  display: inline-block;
  text-align: center;
  line-height: normal;
  margin: 0;
  background: transparent;
  border: 1px solid var(--line-bright);
  color: var(--cream-dim);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  text-decoration: none;
  padding: 6px 10px;
  cursor: pointer;
}

.auth-btn:hover {
  border-color: var(--scarlet);
  color: var(--scarlet-text);
}

/* =======================
   LOGIN PAGE — CARD + TABS
======================= */

.login-card {
  max-width: 700px;
  margin: 0 auto;
}

/* Shared by any page title that sits right above a 700px-wide
   card/form (.login-card, .upload-section) — same width/centering
   as that card, so the intro text starts at the same left edge
   instead of the page's own left edge (left-aligned, just within a
   block sized to match), with breathing room before the card below. */
.page-intro {
  max-width: 700px;
  margin: 0 auto 24px;
  padding: 0;
  text-align: left;
}

.auth-tabs {
  display: flex;
  gap: 2px;
  margin-bottom: 18px;
  background: var(--line);
  border: 2px solid var(--line);
}

.auth-tab {
  flex: 1;
  padding: 13px;
  border: none;
  background: var(--panel-raised);
  color: var(--cream-dim);
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  cursor: pointer;
}

.auth-tab:hover {
  color: var(--cream);
}

.auth-tab.active {
  background: var(--panel-hover);
  color: var(--cream);
  box-shadow: inset 0 -3px 0 var(--blue-bright);
}

/* Inline text link inside a message (e.g. suggestion to create
   an account next to a login error). */
.auth-inline-link {
  background: none;
  border: none;
  color: var(--blue-bright);
  text-decoration: underline;
  cursor: pointer;
  font-size: inherit;
  padding: 0;
}

/* =======================
   MODERATION — REPORT CARDS
======================= */

.report-card {
  background: var(--panel-raised);
  border: 1px solid var(--line);
  padding: 18px;
  margin-bottom: 14px;
}

.report-card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 10px;
}

.report-date {
  color: var(--cream-dim);
  font-family: var(--font-mono);
  font-size: 12px;
}

.report-card .form-actions {
  margin-top: 14px;
}

.report-card .form-actions button {
  border-color: var(--line-bright);
  background: var(--panel);
}

.report-card .form-actions button:hover {
  border-color: var(--cream);
  background: var(--panel-hover);
}

.report-card .remove-content-btn {
  border-color: var(--scarlet);
  color: var(--scarlet-text);
  background: transparent;
}

.report-card .remove-content-btn:hover {
  background: var(--scarlet);
  color: var(--onyx);
}

/* Publish/comment restriction controls, shown under "Posted by" on
   a report card (see formatUserRestrictionBox() in moderation.js).
   Its own small box, distinct from Dismiss/Remove content below it
   — those act on the reported content, this acts on the account.
   Lilac carries this notice, held back everywhere else so it stays
   a legible "something to decide here" signal. */
.user-restriction-box {
  margin-top: 10px;
  padding: 12px;
  border: 1px solid var(--lilac);
  background: rgba(194, 151, 184, 0.08);
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.user-restriction-box p {
  flex: 1 1 100%;
  color: var(--lilac-text);
  margin: 0 0 4px;
}

.user-restriction-box select,
.user-restriction-box button {
  padding: 8px 12px;
  border: 1px solid var(--line-bright);
  background: var(--panel-raised);
  color: var(--cream);
  font-family: var(--font-mono);
  font-size: 12px;
  cursor: pointer;
}

.user-restriction-box .ban-user-btn,
.user-restriction-box .lift-restriction-btn {
  border-color: var(--scarlet);
  color: var(--scarlet-text);
}

.user-restriction-box .ban-user-btn:hover,
.user-restriction-box .lift-restriction-btn:hover {
  background: var(--scarlet);
  color: var(--onyx);
}

/* Banned word chip (moderation.html) — a .tag with an inline
   remove button, rather than the absolute-positioned overlay used
   for removable image thumbnails (see .remove-image-btn), since a
   word chip has no image to sit on top of. */
.banned-word-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.remove-banned-word-btn,
.remove-allowed-phrase-btn {
  background: none;
  border: none;
  color: var(--scarlet-text);
  cursor: pointer;
  font-size: 12px;
  padding: 0;
  line-height: 1;
}

.remove-banned-word-btn:hover,
.remove-allowed-phrase-btn:hover {
  color: var(--cream);
}

/* Floating panel shown on a model page when arriving from a
   moderation link (see initModerationPanel() in model.js) — stays
   fixed on screen while scrolling so the actions are always
   reachable without a trip back to the moderation queue. */
.mod-panel {
  position: fixed;
  bottom: 20px;
  right: 20px;
  max-width: 280px;
  background: var(--panel);
  border: 2px solid var(--scarlet);
  padding: 16px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
  z-index: 999;
}

.mod-panel p {
  color: var(--lilac-text);
  margin-bottom: 10px;
  font-size: 14px;
}

.mod-panel-actions {
  display: flex;
  gap: 10px;
}

.mod-panel-actions button {
  flex: 1;
  padding: 10px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  border: 1px solid var(--line-bright);
  background: var(--panel-raised);
  color: var(--cream);
  cursor: pointer;
}

#modRemoveButton {
  border-color: var(--scarlet);
  color: var(--scarlet-text);
}

#modRemoveButton:hover {
  background: var(--scarlet);
  color: var(--onyx);
}

/* Points a moderator straight at the reported comment among
   possibly many others on the page. */
.mod-highlighted-comment {
  border-color: var(--lilac) !important;
  box-shadow: 0 0 0 2px rgba(194, 151, 184, 0.35);
}

/* =======================
   ACCOUNT DELETION — DANGER ZONE (profile.html)
======================= */

.danger-zone {
  position: relative;
  margin-top: 50px;
  padding: 22px;
  border: 2px solid var(--scarlet);
  background: rgba(252, 61, 33, 0.05);
}

.danger-zone h2 {
  color: var(--scarlet-text);
}

.danger-zone h2::after {
  background: var(--scarlet);
  opacity: 0.4;
}

.danger-zone > button {
  margin-top: 14px;
  padding: 12px 16px;
  border: 2px solid var(--scarlet);
  background: transparent;
  color: var(--scarlet-text);
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  cursor: pointer;
}

.danger-zone > button:hover {
  background: var(--scarlet);
  color: var(--onyx);
}

.danger-zone .form-actions button {
  border-color: var(--line-bright);
  background: var(--panel-raised);
}

.danger-zone .form-actions button:hover {
  border-color: var(--cream);
  background: var(--panel-hover);
}

.danger-zone #confirmDeleteAccountButton {
  border-color: var(--scarlet);
  color: var(--scarlet-text);
  background: transparent;
}

.danger-zone #confirmDeleteAccountButton:hover {
  background: var(--scarlet);
  color: var(--onyx);
}

/* =======================
   PROFILE HEADER (profile.html)
======================= */

.profile-header {
  display: flex;
  gap: 30px;
  align-items: flex-start;
  padding-bottom: 32px;
  border-bottom: 2px solid var(--line);
}

/* Square, not round — matches the square-corners rule that runs
   through the whole design system. */
.profile-avatar-wrap {
  position: relative;
  width: 120px;
  height: 120px;
  flex-shrink: 0;
}

.profile-avatar,
.profile-avatar-placeholder {
  width: 120px;
  height: 120px;
  border: 2px solid var(--line-bright);
}

.profile-avatar {
  object-fit: cover;
  display: block;
}

.profile-avatar-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--panel-raised);
  font-family: var(--font-mono);
  font-size: 44px;
  font-weight: 800;
  color: var(--cream-dim);
}

/* Hover-reveal upload control — same interaction as the model card
   save button (.card-save-btn): invisible until you're looking
   right at it, only ever shown at all on your own profile. */
.profile-avatar-edit {
  position: absolute;
  inset: 0;
  display: none;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 8px;
  background: rgba(15, 17, 12, 0.82);
  border: 2px solid var(--blue-bright);
  color: var(--on-dark);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.12s ease;
}

.profile-avatar-wrap:hover .profile-avatar-edit,
.profile-avatar-wrap:focus-within .profile-avatar-edit {
  opacity: 1;
}

.profile-header-info {
  flex: 1;
  min-width: 0;
}

.profile-name-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 18px;
}

.profile-name-row h1 {
  margin: 0;
}

.follower-readout {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.follower-readout .osd {
  font-size: 11px;
  color: var(--cream-dim);
}

/* "Follow" (neutral blue) / "Following" (lilac, same "already did
   this" convention as the saved-model glyph and the save readout). */
.follow-btn {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 9px 16px;
  border: 2px solid var(--blue-bright);
  background: transparent;
  color: var(--cream);
  cursor: pointer;
  transition: border-color 0.12s ease, color 0.12s ease, background 0.12s ease;
}

.follow-btn:hover {
  background: rgba(11, 61, 145, 0.18);
}

.follow-btn.following {
  border-color: var(--lilac);
  color: var(--lilac-text);
  background: rgba(194, 151, 184, 0.08);
}

.follow-btn.following:hover {
  background: rgba(194, 151, 184, 0.16);
}

.profile-bio {
  margin-top: 14px;
  max-width: 60ch;
  color: var(--cream-dim);
}

.profile-edit-bio-btn {
  margin-top: 10px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 7px 12px;
  border: 2px solid var(--line-bright);
  background: transparent;
  color: var(--cream-dim);
  cursor: pointer;
  transition: border-color 0.12s ease, color 0.12s ease;
}

.profile-edit-bio-btn:hover {
  border-color: var(--cream);
  color: var(--cream);
}

.profile-bio-edit {
  margin-top: 12px;
  max-width: 60ch;
}

.profile-bio-edit textarea {
  min-height: 70px;
  margin-bottom: 4px;
}

.profile-stats-row {
  display: flex;
  gap: 32px;
  margin-top: 24px;
}

.profile-stat span {
  display: block;
  margin-top: 6px;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--cream-dim);
}

.profile-tabs {
  display: flex;
  gap: 8px;
  margin-top: 40px;
  margin-bottom: 20px;
}

/* =======================
   SITE FOOTER
======================= */

.site-footer {
  margin-top: 60px;
  padding: 30px 8%;
  background: var(--panel);
  color: var(--cream);
  text-align: center;
}

.footer-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 16px;
  margin-bottom: 12px;
}

.footer-links a {
  font-family: var(--font-mono);
  color: var(--cream-dim);
  text-decoration: none;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.footer-links a:hover {
  color: var(--scarlet-text);
}

/* Theme switch — deliberately boxed like the site's other buttons
   (border, not plain text) so it reads as a real control sitting
   among the footer's plain links, not one more of them. Both icons
   stay in the DOM at all times; :root[data-theme] decides which one
   is "lit" via CSS alone, no JS class-toggling needed for the
   visual state (setupThemeToggle() in auth.js only flips the
   attribute + localStorage). */
#themeToggle {
  appearance: none;
  -webkit-appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  border: 1px solid var(--line-bright);
  background: transparent;
  padding: 4px;
  cursor: pointer;
}

#themeToggle:hover {
  border-color: var(--blue-bright);
}

.theme-switch-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 20px;
  color: var(--cream-dim);
  transition: background 0.15s ease, color 0.15s ease;
}

/* Dark theme (default): moon lit. */
.theme-switch-moon {
  background: var(--panel-hover);
  color: var(--blue-bright);
}

:root[data-theme="light"] .theme-switch-moon {
  background: none;
  color: var(--cream-dim);
}

:root[data-theme="light"] .theme-switch-sun {
  background: var(--panel-hover);
  color: var(--scarlet-text);
}

.footer-copyright {
  font-family: var(--font-mono);
  color: var(--cream-dim);
  opacity: 0.7;
  font-size: 11px;
  letter-spacing: 0.03em;
}

/* Centered-and-wrapped reads as a ragged, uneven block once there's
   only room for one or two words per row (7 links of very different
   lengths) — a single stacked column scans much more cleanly below
   that point. */
@media (max-width: 1000px) {
  .footer-links {
    flex-direction: column;
    align-items: center;
    gap: 12px;
  }
}

/* =======================
   LEGAL / POLICY PAGES
======================= */

.legal-content {
  max-width: 820px;
  margin: 0 auto;
}

.legal-content h2 {
  margin-top: 36px;
  margin-bottom: 12px;
}

.legal-content h2:first-of-type {
  margin-top: 24px;
}

.legal-content p {
  color: var(--cream-dim);
  line-height: 1.6;
  margin-bottom: 14px;
}

.legal-content ul {
  color: var(--cream-dim);
  line-height: 1.6;
  margin: 0 0 14px 20px;
}

.legal-content a {
  color: var(--blue-bright);
}

.legal-updated {
  color: var(--cream-dim);
  font-family: var(--font-mono);
  font-size: 12px;
  margin-bottom: 30px;
}

.legal-table {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: 20px;
}

.legal-table th,
.legal-table td {
  text-align: left;
  padding: 10px 12px;
  border: 1px solid var(--line);
  color: var(--cream-dim);
  font-size: 14px;
  line-height: 1.5;
}

.legal-table th {
  background: var(--panel-raised);
  color: var(--cream);
  font-family: var(--font-mono);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

/* =======================
   SEVEN-SEGMENT DIGITS (calculator/LCD readout)
   Built from real on/off segments (see renderSevenSegment() in
   data.js), not a font — used only for purely numeric values:
   likes, votes, download counts, platform stats.
======================= */

.seven-seg {
  display: inline-flex;
  gap: 4px;
  align-items: center;
  --seg-color: var(--scarlet-text);
}

.seven-seg.blue {
  --seg-color: var(--blue-bright);
}

.seven-seg.cream {
  --seg-color: var(--cream);
}

.seven-seg.lilac {
  --seg-color: var(--lilac-text);
}

/* K/M suffix on a large value (see renderCompactSevenSegment() in
   data.js) — real text, not segments (letters don't map cleanly to
   seven segments), colored to match via the container's
   --seg-color so it reads as part of the same readout. */
.seven-seg-suffix {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 13px;
  color: var(--seg-color);
}

/* The base .digit size further down (15x26) is only right for a
   tight inline readout (comment likes, vote counts). Any standalone
   headline number reuses the exact .stat-card size instead — the
   one geometry that survived the platform-stats debugging round
   without breaking down into unrecognizable shapes. */
.stat-card .seven-seg-suffix,
.profile-stats-row .seven-seg-suffix {
  font-size: 18px;
}

.digit {
  display: inline-block;
  position: relative;
  width: 15px;
  height: 26px;
}

/* Ghost (off) segments kept faint enough to never be mistaken for
   a lit one — "128" must never scan as "828" at a glance. */
.seg {
  position: absolute;
  background: var(--seg-ghost);
}

.seg.on {
  background: var(--seg-color);
  box-shadow: 0 0 6px var(--seg-color);
}

.seg-a, .seg-g, .seg-d {
  left: 2px;
  right: 2px;
  height: 3px;
  clip-path: polygon(14% 0, 86% 0, 100% 50%, 86% 100%, 14% 100%, 0 50%);
}

.seg-a { top: 0; }
.seg-g { top: 11.5px; }
.seg-d { bottom: 0; }

.seg-f, .seg-b, .seg-e, .seg-c {
  width: 3px;
  height: 10px;
  clip-path: polygon(0 14%, 50% 0, 100% 14%, 100% 86%, 50% 100%, 0 86%);
}

.seg-f { left: 0; top: 2px; }
.seg-b { right: 0; top: 2px; }
.seg-e { left: 0; bottom: 2px; }
.seg-c { right: 0; bottom: 2px; }

.seg-colon, .seg-dot {
  display: inline-block;
  width: 5px;
  height: 26px;
  position: relative;
}

.seg-colon::before, .seg-colon::after {
  content: "";
  position: absolute;
  left: 1px;
  width: 3px;
  height: 3px;
  background: var(--seg-color);
  box-shadow: 0 0 4px var(--seg-color);
}

.seg-colon::before { top: 8px; }
.seg-colon::after { bottom: 8px; }

.seg-dot::after {
  content: "";
  position: absolute;
  left: 1px;
  bottom: 0;
  width: 3px;
  height: 3px;
  background: var(--seg-color);
  box-shadow: 0 0 4px var(--seg-color);
}

.stat-card .seven-seg .digit,
.profile-stats-row .seven-seg .digit,
.follower-readout .seven-seg .digit {
  width: 20px;
  height: 34px;
}

@media (prefers-reduced-motion: reduce) {
  * {
    transition: none !important;
  }
}
