/* writeupsc — shared foundations
 *
 * The design tokens, reset and navbar are identical across landing.html,
 * app.html and evaluation.html. Everything else stays inline on each page,
 * because the rest of their CSS is genuinely page-specific (measured: only
 * ~12 selectors overlapped between app and evaluation).
 *
 * Served by an explicit route in backend/api/app.py — /static is not mounted
 * when VERCEL=1, so a StaticFiles mount cannot be relied on in production.
 */

/* The palette is light by design and there is no dark variant. Without this,
   Android Chrome's auto-dark-theme inverts the whole page — which is what a
   tester's phone did: white cards became near-black and the blue accent went
   muddy. Declaring the scheme opts the page out of that automatic inversion. */
:root {
  color-scheme: light;

  --bg: #fafaf8;
  --grid: rgba(0, 0, 0, .022);
  --card: #ffffff;
  --border: #e8e6e1;
  --border-light: #f0eeea;
  --text: #1a1a1a;
  --text-secondary: #6b6560;
  --text-tertiary: #9a9590;
  --accent: #3b6ce7;
  --accent-hover: #2563eb;
  --accent-deep: #2f5fd6;
  --accent-light: #eff4ff;
  --error: #c0392b;
  --success: #27ae60;
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
}

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

body {
  font-family: "Inter", -apple-system, BlinkMacSystemFont, sans-serif;
  color: var(--text);
  line-height: 1.6;
  background-color: var(--bg);
  background-image:
    linear-gradient(var(--grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid) 1px, transparent 1px);
  background-size: 64px 64px;
  /* iOS resolves vh against the viewport with the address bar RETRACTED, so
     100vh is taller than what you can actually see and the page overflows by
     the bar's height. dvh tracks the real viewport; vh stays as the fallback
     for browsers without it. */
  min-height: 100vh;
  min-height: 100dvh;
  -webkit-font-smoothing: antialiased;
  /* Safari inflates text in landscape unless told not to. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  /* Grey flash on every tap, including non-interactive elements. */
  -webkit-tap-highlight-color: rgba(59, 108, 231, .12);
}

/* ── SAFE AREAS ──
   With viewport-fit=cover the page draws under the notch and the home
   indicator, which is what we want for the background — but not for content.
   Only the insets that actually matter are applied: the sides (landscape
   notch) and the bottom (home indicator). */
body {
  padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);
}

/* ── NAVBAR ── */
.navbar {
  position: sticky;
  top: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  max-width: 1120px;
  margin: 0 auto;
  padding: 0 2.5rem;
  height: 56px;
  background: transparent;
}
/* The sticky navbar sits at the very top, so it owns the notch inset. */
@supports (padding-top: env(safe-area-inset-top)) {
  .navbar {
    padding-top: env(safe-area-inset-top);
    height: calc(56px + env(safe-area-inset-top));
  }
}

/* The bar is transparent at rest so it sits on the page's own grid, but the
   content scrolls underneath it and shows straight through. This backdrop
   fades in once the page has moved.

   It is a fixed-position pseudo-element rather than a background on .navbar
   itself: .navbar is capped at 1120px and centred, so a background on it would
   stop short of the viewport edges. `position: fixed` with left/right 0 spans
   the full width without `100vw`, which would overflow by the scrollbar. */
.navbar::before {
  content: "";
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 56px;
  z-index: -1;
  background: rgba(250, 250, 248, 0.72);
  backdrop-filter: blur(12px) saturate(1.4);
  -webkit-backdrop-filter: blur(12px) saturate(1.4);
  border-bottom: 1px solid transparent;
  opacity: 0;
  transition: opacity 220ms var(--ease-out), border-color 220ms var(--ease-out);
  pointer-events: none;
}
.navbar.is-scrolled::before {
  opacity: 1;
  border-bottom-color: var(--border-light);
}

/* Without backdrop-filter a 0.72 alpha still lets text through, so go opaque. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .navbar::before { background: var(--bg); }
}

@media (prefers-reduced-motion: reduce) {
  .navbar::before { transition: none; }
}
.logo {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-weight: 800;
  font-size: 1.05rem;
  letter-spacing: -0.03em;
  color: var(--text);
  text-decoration: none;
}
/* The nib outline inherits .logo's colour via currentColor, so the mark
   follows the text on light and dark surfaces without a second copy.
   Stroke weight is set for this size — it is not the large cut scaled down. */
.logo-mark {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  display: block;
}
.logo-mark .nib { stroke: currentColor; }
.logo-mark .ink { fill: var(--accent); }
.nav-center { display: flex; gap: 2rem; }
.nav-center a {
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--text-secondary);
  text-decoration: none;
  transition: color 180ms ease;
  letter-spacing: -0.01em;
}
@media (hover: hover) and (pointer: fine) {
  .nav-center a:hover { color: var(--text); }
}
.btn-nav {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 1.1rem;
  background: var(--text);
  color: #fff;
  font-size: 0.8rem;
  font-weight: 600;
  border: 0;
  border-radius: 8px;
  cursor: pointer;
  text-decoration: none;
  transition: transform 180ms var(--ease-out), background 180ms ease;
  letter-spacing: -0.01em;
}
.btn-nav:active { transform: scale(0.97); }

/* ── SCROLL REVEAL ──
   Every entrance animation on these pages fired on load with a fixed delay, so
   anything below the fold had finished moving before it was ever seen. These
   arrive when they actually enter the viewport instead.

   Gated on html.wu-js (set by a one-line inline script in each page head) so
   that without JavaScript the content is simply visible rather than stuck at
   opacity 0. */
.wu-js [data-reveal] {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 620ms var(--ease-out-expo), transform 620ms var(--ease-out-expo);
  will-change: opacity, transform;
}
.wu-js [data-reveal].is-revealed {
  opacity: 1;
  transform: none;
  will-change: auto;
}
/* Sideways variants, for things that read as arriving from an edge. */
.wu-js [data-reveal="left"]  { transform: translateX(-22px); }
.wu-js [data-reveal="right"] { transform: translateX(22px); }
.wu-js [data-reveal="scale"] { transform: scale(.96); }

/* ── BUTTON MOTION ──
   A light sheen travels across on hover and the button lifts; pressing it
   settles back down. Transform and opacity only, so none of it costs layout. */
.btn-nav {
  position: relative;
  overflow: hidden;
  transition: background 200ms ease,
              transform 220ms var(--ease-out),
              box-shadow 220ms var(--ease-out);
}
.btn-nav::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(110deg,
    transparent 30%, rgba(255, 255, 255, .26) 50%, transparent 70%);
  transform: translateX(-130%);
  pointer-events: none;
}
@media (hover: hover) and (pointer: fine) {
  .btn-nav:hover {
    transform: translateY(-2px);
    box-shadow: 0 8px 20px -8px rgba(24, 24, 27, .5);
  }
  .btn-nav:hover::after {
    transform: translateX(130%);
    transition: transform 700ms var(--ease-out);
  }
  .nav-user:hover { transform: translateY(-1px); }
}
.btn-nav:active { transform: translateY(0) scale(.97); }
.nav-user { transition: border-color 180ms ease, transform 200ms var(--ease-out); }

/* ── LOGO ──
   The nib's ink drop falls a little on hover. Small, and only where there is a
   real cursor to hover with. */
.logo-mark .ink,
.logo-mark .nib {
  transition: transform 480ms var(--ease-out-expo);
  transform-origin: 32px 34px;
}
@media (hover: hover) and (pointer: fine) {
  .logo:hover .logo-mark .ink { transform: translateY(2.5px) scale(1.18); }
  .logo:hover .logo-mark .nib { transform: scale(1.04); }
}

/* ── NAVBAR ARRIVAL ── */
.navbar { animation: wu-nav-in 560ms var(--ease-out-expo) both; }
@keyframes wu-nav-in {
  from { opacity: 0; transform: translateY(-100%); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .wu-js [data-reveal] {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .btn-nav, .btn-nav::after, .nav-user,
  .logo-mark .ink, .logo-mark .nib {
    transition: none !important;
    transform: none !important;
  }
  .navbar { animation: none; }
}

/* ── TOUCH TARGETS ──
   Controls sized for a cursor are ~36px tall, under the 44px a fingertip needs.
   Scoped to coarse pointers so the desktop proportions are untouched. */
@media (pointer: coarse) {
  .btn-nav,
  .nav-user {
    min-height: 44px;
    padding-top: 0;
    padding-bottom: 0;
  }
}

/* ── SKELETON LOADING ──
   One gradient sweep, shared by the submission page and the dashboard.
   background-position only: it never touches layout, so the browser can keep
   the whole thing on the compositor. */
@keyframes wu-shimmer {
  from { background-position: -140% 0; }
  to   { background-position: 240% 0; }
}

.sk {
  border-radius: 6px;
  background-color: var(--border-light);
  background-image: linear-gradient(
    90deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, .85) 50%,
    rgba(255, 255, 255, 0) 100%
  );
  background-size: 220% 100%;
  background-repeat: no-repeat;
  animation: wu-shimmer 1.6s var(--ease-out) infinite;
  color: transparent !important;
  user-select: none;
  pointer-events: none;
}
.sk-line { height: 0.75rem; margin-bottom: 0.5rem; }
.sk-line:last-child { margin-bottom: 0; width: 62%; }
.sk-title { height: 1.15rem; width: 72%; margin-bottom: 0.9rem; }
.sk-circle { border-radius: 50%; }

/* A block that has arrived: settle in, do not slam. Mirrors heroReveal on the
   result page so the two pages feel like one product. */
.sk-resolved {
  animation: wu-resolve 420ms var(--ease-out-expo) both;
}
@keyframes wu-resolve {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

/* ── NAVBAR: SIGNED-IN ── */
.nav-user {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.3rem 0.7rem 0.3rem 0.3rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--card);
  color: var(--text);
  font-size: 0.8rem;
  font-weight: 600;
  text-decoration: none;
  letter-spacing: -0.01em;
  transition: border-color 180ms ease;
}
.nav-user img, .nav-user .nav-avatar {
  width: 24px; height: 24px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--accent-light);
  color: var(--accent-deep);
  display: grid; place-items: center;
  font-size: 0.68rem; font-weight: 700;
}
@media (hover: hover) and (pointer: fine) {
  .nav-user:hover { border-color: var(--text-tertiary); }
}

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

/* ── MODAL BANNER ──
   A message that has to be dealt with, not scrolled past: the page behind it
   blurs and dims and the card sits in the middle of the viewport. Every alert
   in the app (the free-run gate, the result page's banners) renders through
   this one component. */
.wu-overlay {
  position: fixed;
  inset: 0;
  padding-bottom: calc(1.5rem + env(safe-area-inset-bottom));
  z-index: 200;
  display: grid;
  place-items: center;
  padding: 1.5rem;
  background: rgba(250, 250, 248, .62);
  backdrop-filter: blur(16px) saturate(115%);
  -webkit-backdrop-filter: blur(16px) saturate(115%);
  animation: wu-overlay-in 240ms var(--ease-out) both;
}
/* display:grid here outranks the UA's [hidden] rule. */
.wu-overlay[hidden] { display: none !important; }

/* Without backdrop-filter there is no blur to hide the page, so the scrim has
   to carry the separation on its own. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .wu-overlay { background: rgba(250, 250, 248, .95); }
}

.wu-modal {
  position: relative;
  width: min(520px, 100%);
  max-height: calc(100vh - 3rem);
  max-height: calc(100dvh - 3rem);
  overflow-y: auto;
  padding: 2.4rem 2.2rem 2.1rem;
  border: 1px solid var(--border);
  border-radius: 16px;
  background: rgba(255, 255, 255, .94);
  box-shadow: 0 28px 64px -22px rgba(24, 24, 27, .28), 0 2px 8px rgba(24, 24, 27, .05);
  text-align: center;
  animation: wu-modal-in 380ms var(--ease-out-expo) both;
}

@keyframes wu-overlay-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes wu-modal-in {
  from { opacity: 0; transform: translateY(14px) scale(.97); }
  to   { opacity: 1; transform: none; }
}

.wu-modal-eyebrow {
  font-family: "JetBrains Mono", monospace;
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-tertiary);
  margin-bottom: 0.9rem;
}
.wu-modal.error .wu-modal-eyebrow { color: var(--error); }

.wu-modal-title {
  font-size: 1.35rem;
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: -0.03em;
  color: var(--text);
}
.wu-modal-detail {
  margin-top: 0.7rem;
  font-size: 0.9rem;
  line-height: 1.65;
  color: var(--text-secondary);
}
.wu-modal-action {
  margin-top: 1.6rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
}
.wu-modal-action:empty { display: none; }

.wu-modal-close {
  position: absolute;
  top: 0.85rem;
  right: 0.85rem;
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 8px;
  background: none;
  color: var(--text-tertiary);
  cursor: pointer;
  font-size: 1.1rem;
  line-height: 1;
  transition: background 160ms ease, color 160ms ease;
}
@media (hover: hover) and (pointer: fine) {
  .wu-modal-close:hover { background: var(--border-light); color: var(--text); }
}

/* The page must not scroll behind an open modal. */
body.wu-modal-open { overflow: hidden; }

@media (max-width: 600px) {
  .wu-modal { padding: 2rem 1.5rem 1.75rem; }
  .wu-modal-title { font-size: 1.15rem; }
}

@media (prefers-reduced-motion: reduce) {
  .wu-overlay, .wu-modal { animation: none; }
  .wu-modal-close { transition: none; }
}

/* ── FOCUS VISIBILITY (keyboard users) ── */
a:focus-visible,
button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 6px;
}

/* Anchor links must not land under the sticky navbar. */
html { scroll-behavior: smooth; }
:target { scroll-margin-top: 76px; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

@media (max-width: 900px) {
  .navbar { padding: 0 1.25rem; }
  .nav-center { display: none; }
}
