/* ─── Display face ────────────────────────────────────────────────────────────
   Max, 20 Aug: "Use the same visual style of typeface used for the VETEMENTS
   brand logo wherever this primary display typography appears in the mockups."

   That lettering is a heavy, tightly-set compressed grotesque — Impact /
   Haettenschweiler territory, considerably narrower and blacker than the
   condensed system faces we were falling back to. None of those ship reliably:
   Impact is absent on iOS, Helvetica Neue Condensed is absent on Linux and
   Android, and Ubuntu Sans Condensed is not compressed enough. So the face is
   self-hosted and the app renders identically everywhere.

   Anton (SIL OFL) is the closest free match. 33KB for both subsets.

   The weight and stretch RANGES matter: our rules ask for 700 and for
   condensed, and Anton ships a single width at a single weight. Declaring the
   ranges tells the browser this face already covers them, so it does not
   synthesise a fake bold or a fake condensed on top of a face that is already
   both.
   ──────────────────────────────────────────────────────────────────────────── */
@font-face {
  font-family: 'Anton';
  src: url('/static/fonts/anton-latin.woff2') format('woff2');
  font-weight: 100 900; font-stretch: 50% 200%; font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Anton';
  src: url('/static/fonts/anton-latin-ext.woff2') format('woff2');
  font-weight: 100 900; font-stretch: 50% 200%; font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ═══════════════════════════════════════════════════════════════
   STYLER — Design Elevation
   Direction: Editorial luxury meets streetwear energy
   Tone: Confident, aspirational, tactile
   Inspiration: Goat app × Ssense editorial × Zara's restraint
   ═══════════════════════════════════════════════════════════════ */

/* ─── Enhanced Foundation ─── */
/* Grounded in Max's mockups (docs/mockups/max/), sampled from the frames
   rather than eyeballed. See DESIGN.md for provenance and the full system. */
:root {
  /* Neutral near-white, measured off the frames. Was #F5F3F0, a warm sand —
     every screen carried a cast Max's design does not have. */
  --bg: #FDFDFD;
  --surface: #FFFFFF;
  --text: #0A0A0A;
  --text2: #5A5A5A;
  /* Was #9E9E9E: 2.63:1 on this ground, a clear accessibility failure on the
     captions it is used for. #6F6F6F measures 4.94:1 and passes AA. Max's own
     caption grey (#8D8785) is 3.48:1 and would not have passed either. */
  --text3: #6F6F6F;
  --border: rgba(0,0,0,0.06);
  --accent: #0A0A0A;
  --accent2: #E8DDD3;
  --green: #1DB954;
  --red: #FF3B30;
  --radius: 16px;
  --radius-sm: 10px;

  /* ── Prize green ──
     The only saturated flat colour in the light UI, and it appears exactly
     once: the "complete your fit & you can win $$$" banner. It reads as
     reward. A second green element halves the meaning of the first.
     Background only — 2.17:1 on white, so never use it for text. */
  --prize: #40C830;
  --prize-ink: #0A0A0A;

  /* ── Gradients mean AI, and nothing else ──
     The strongest convention in Max's board. Two families, and the split is
     doing work: you know which machine you are talking to before reading the
     label. Putting either on a non-AI action destroys that.
       garment  — magenta to coral
       stylist  — blue to green   */
  --ai-garment: linear-gradient(90deg, #C008C2 0%, #F73E55 100%);
  --ai-stylist: linear-gradient(90deg, #337CD9 0%, #69C295 100%);
  --ai-generate-item: linear-gradient(90deg, #F55548 0%, #B21B7B 55%, #580C8F 100%);
  --ai-generate-look: linear-gradient(90deg, #0947F1 0%, #158BC3 50%, #53BB9D 100%);

  /* ── AI surfaces invert ──
     A mode switch, not a dark theme: near-black ground, luminous subject,
     gradient CTA. Anything reused from the light app needs a dark variant or
     it arrives as a white slab in the middle of a black screen. */
  --ai-ground: #020407;
  --ai-surface: #0C0E10;
  --ai-ink: #F2F4F6;
  --ai-ink2: #9AA3AC;
  --ai-rule: rgba(255,255,255,0.10);

  /* ── Display face ──
     Max, 18 Aug: "Title fonts should be all caps bold condensed helvetica."
     Treated as spec, not direction.

     Condensed Helvetica is a separate face, not a weight, and it is named
     differently on every platform — so the chain names each one rather than
     hoping a generic fallback narrows. Order is genuine Helvetica condensed,
     then the platform equivalents that ARE condensed (Arial Narrow on
     Windows, Roboto Condensed on Android, Liberation Sans Narrow on Linux),
     then plain Helvetica so a miss degrades to the right family at normal
     width rather than to a serif.

     font-stretch is set alongside, which lets a variable-width face condense
     even when none of the named files are installed.

     "Ubuntu Sans" is in the tail for the Linux dev box specifically: it is the
     only variable-width family here, so without it font-stretch has nothing to
     act on and titles render at normal width locally while looking correct on
     a phone. Naming it means what we review matches what Max sees. It is
     absent on iOS/macOS/Windows/Android, so it never wins there. */
  /* The UI face. Small labels, links and metadata stay on this — on the
     board they are a normal-width bold sans, not the compressed display
     face, and Anton at 9px is a smudge. */
  --font-ui: "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-display: "Anton", "Helvetica Neue Condensed", "HelveticaNeue-CondensedBlack",
    "Helvetica Condensed", "Arial Narrow", "Roboto Condensed",
    "Liberation Sans Narrow", "Ubuntu Sans", "Helvetica Neue", Helvetica,
    Arial, sans-serif;

  /* Neutral now, to match the ground. */
  --bg-warm: #FAFAFA;
  --shimmer: linear-gradient(110deg, var(--border) 30%, rgba(255,255,255,0.6) 50%, var(--border) 70%);
}

/* Visually-hidden utility — accessible name without visual presence */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

body {
  font-family: 'Helvetica Neue', Helvetica, Arial, -apple-system, sans-serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* ─── Refined Topbar ─── */
.topbar {
  padding: 52px 18px 12px;
  background: var(--bg-warm);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
}

.topbar-title {
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  font-size: 18px;
  font-weight: 700;
  text-transform: uppercase;
  color: var(--text);
}

/* topbar-menu removed — now using bottom tab bar */

/* ─── Elevated Auth Screen ─── */
#authScreen {
  background: var(--text);
  align-items: center;
}

#authScreen {
  color: #fff;  /* All text inside auth is white */
}

#authScreen .auth-logo {
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  font-size: 48px;
  font-weight: 700;
  text-transform: uppercase;
  color: #fff;
  margin-bottom: 4px;
  text-shadow: 0 0 80px rgba(255,255,255,0.15);
}

#authScreen .hero-tagline {
  font-size: 17px;
  color: rgba(255,255,255,0.85);
  font-weight: 500;
  line-height: 1.5;
  margin-bottom: 6px;
}

#authScreen .hero-sub {
  font-size: 12px;
  color: rgba(255,255,255,0.4);
  text-transform: uppercase;
  font-weight: 500;
  margin-bottom: 32px;
}

/* Feature pills — editorial style, not SaaS cards */
.hero-features {
  display: flex;
  gap: 8px;
  justify-content: center;
  flex-wrap: wrap;
  margin-bottom: 28px;
}

.hero-pill {
  padding: 8px 18px;
  border-radius: 50px;
  border: 1px solid rgba(255,255,255,0.25);
  font-size: 12px;
  font-weight: 600;
  color: rgba(255,255,255,0.85);
  backdrop-filter: blur(10px);
  transition: all 0.3s ease;
}

/* Steps — minimal line style */
.hero-steps {
  display: flex;
  gap: 0;
  align-items: center;
  justify-content: center;
  margin-bottom: 28px;
}

.hero-step {
  display: flex;
  align-items: center;
  gap: 8px;
}

.hero-step-num {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 1.5px solid rgba(255,255,255,0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 700;
  color: rgba(255,255,255,0.8);
}

.hero-step-text {
  font-size: 12px;
  color: rgba(255,255,255,0.6);
  font-weight: 500;
}

.hero-step-line {
  width: 24px;
  height: 1px;
  background: rgba(255,255,255,0.2);
  margin: 0 6px;
}

.hero-brand-callout {
  font-size: 12px;
  color: rgba(255,255,255,0.4);
  margin-bottom: 36px;
}

/* Dark theme auth inputs — visible and usable */
#authScreen .auth-input {
  background: rgba(255,255,255,0.12);
  border: 1.5px solid rgba(255,255,255,0.2);
  color: #fff;
  border-radius: var(--radius-sm);
  font-size: 16px;
  /* Preserve 44px left padding from the base .auth-input rule so the icon doesn't overlap text */
  padding: 16px 18px 16px 44px;
}

#authScreen .auth-input::placeholder {
  color: rgba(255,255,255,0.4);
}

#authScreen .auth-input:focus {
  border-color: rgba(255,255,255,0.5);
  background: rgba(255,255,255,0.16);
}

#authScreen .auth-btn {
  background: #fff;
  color: #0A0A0A;
  border-radius: var(--radius-sm);
  font-weight: 700;
  font-size: 16px;
  padding: 16px;
  transition: all 0.2s ease;
}

#authScreen .auth-btn:active {
  transform: scale(0.98);
  opacity: 0.9;
}

#authScreen .auth-err {
  color: #FF6B6B;
  font-size: 13px;
  min-height: 20px;
}

#authScreen .auth-switch {
  color: rgba(255,255,255,0.5);
  font-size: 14px;
}

#authScreen .auth-switch span {
  color: #fff;
  font-weight: 700;
}

/* ─── Explore Feed — Editorial grid ─── */
.explore-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2px;
  margin-bottom: 2px;
}

.explore-grid-item {
  aspect-ratio: 1;
  background: var(--bg-warm);
  overflow: hidden;
  position: relative;
}

.explore-grid-item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.4s ease;
}

.explore-grid-item:active img {
  transform: scale(1.05);
}

/* User header in explore — more breathing room */
.explore-user-header {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px 10px;
}

.explore-av {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--accent2), #D4C5B5);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 14px;
  color: var(--text);
  overflow: hidden;
  flex-shrink: 0;
  /* Subtle ring */
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 3px var(--border);
}

/* ─── Dressing Room — Stage presence ─── */
.dr-layout {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;
  flex: 1;
  overflow: hidden;
}

.dr-person {
  display: flex;
  align-items: stretch;
  justify-content: center;
  overflow: hidden;
  background: linear-gradient(180deg, #F2EFEB 0%, #E8E4DE 100%);
  position: relative;
}

/* Subtle vignette on person image */
.dr-person::after {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at center, transparent 60%, rgba(0,0,0,0.06) 100%);
  pointer-events: none;
}

.dr-person img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
}

.dr-item {
  aspect-ratio: 1;
  background: var(--surface);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  position: relative;
  border: 2px solid transparent;
  transition: all 0.15s ease;
}

.dr-item:active {
  transform: scale(0.95);
}

.dr-item.on {
  border-color: var(--green);
  background: rgba(29, 185, 84, 0.04);
}

.dr-item .chk {
  position: absolute;
  bottom: 3px;
  right: 3px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--green);
  color: white;
  font-size: 9px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 1px 4px rgba(0,0,0,0.2);
}

/* ─── Category Pills — Refined ─── */
.cat-pill {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 7px 16px;
  border-radius: 50px;
  font-size: 12px;
  font-weight: 500;
  color: var(--text2);
  background: var(--surface);
  border: 1px solid var(--border);
  cursor: pointer;
  transition: all 0.2s ease;
  white-space: nowrap;
}

.cat-pill:active {
  transform: scale(0.96);
}

.cat-pill.active {
  background: var(--text);
  color: #fff;
  border-color: var(--text);
  box-shadow: 0 2px 8px rgba(0,0,0,0.15);
}

/* ─── Try On Button — Floating action with presence ─── */
.dr-tryon-btn,
.dr-floating-action {
  position: fixed;
  bottom: calc(100px + env(safe-area-inset-bottom, 8px));
  left: 50%;
  transform: translateX(-50%);
  padding: 16px 36px;
  border-radius: 50px;
  background: var(--text);
  color: #fff;
  font-size: 15px;
  font-weight: 600;
  box-shadow: 0 8px 32px rgba(0,0,0,0.3), 0 2px 8px rgba(0,0,0,0.2);
  white-space: nowrap;
  transition: all 0.2s ease;
  z-index: 160;
}

.dr-tryon-btn:active {
  transform: translateX(-50%) scale(0.96);
}

/* ─── Closet Grid — Tighter, cleaner ─── */
.closet-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2px;
  padding: 0;
}

.closet-item {
  aspect-ratio: 3/4;
  background: var(--surface);
  overflow: hidden;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}

.closet-item img {
  width: 90%;
  height: 90%;
  object-fit: contain;
  transition: transform 0.3s ease;
}

.closet-item:active img {
  transform: scale(1.05);
}

/* ─── Modals — Glass morphism ─── */
.modal-overlay {
  background: rgba(0,0,0,0.5);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

.modal {
  background: var(--surface);
  border-radius: 24px 24px 0 0;
  padding: 24px;
  box-shadow: 0 -8px 40px rgba(0,0,0,0.15);
}

.modal-bar {
  width: 40px;
  height: 4px;
  border-radius: 4px;
  background: rgba(0,0,0,0.12);
  margin: 0 auto 20px;
}

.modal h3 {
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-weight: 700; text-transform: uppercase;
  font-size: 22px;
}

/* ─── Chat Bubbles — Softer ─── */
.chat-bubble.mine {
  background: var(--text);
  border-bottom-right-radius: 6px;
  box-shadow: 0 1px 4px rgba(0,0,0,0.1);
}

.chat-bubble.theirs {
  background: var(--bg-warm);
  border: none;
  border-bottom-left-radius: 6px;
}

/* ─── Search Bar ─── */
.social-search {
  padding: 12px 16px;
  border: none;
  border-radius: var(--radius);
  font-size: 14px;
  background: var(--surface);
  box-shadow: 0 1px 4px rgba(0,0,0,0.06);
  transition: box-shadow 0.2s ease;
}

.social-search:focus {
  box-shadow: 0 2px 12px rgba(0,0,0,0.1);
  outline: none;
}

/* ─── Toast — More refined ─── */
.toast-msg {
  padding: 12px 24px;
  border-radius: 14px;
  font-size: 13px;
  font-weight: 600;
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  box-shadow: 0 4px 20px rgba(0,0,0,0.15);
}

.toast-msg.success { background: rgba(29,185,84,0.95); }
.toast-msg.error { background: rgba(255,59,48,0.95); }
.toast-msg.info { background: rgba(10,10,10,0.9); }

/* ─── Tab Bar — Clean, persistent ─── */
.tab-bar {
  background: var(--surface);
  border-top: 1px solid var(--border);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
}

.tab-bar-item {
  transition: color 0.15s ease;
}

.tab-bar-item:active {
  transform: scale(0.92);
}

.tab-bar-item.active svg {
  stroke-width: 2;
}

/* ─── Skeleton — Warmer ─── */
.skel {
  background: linear-gradient(110deg, var(--accent2) 30%, rgba(255,255,255,0.5) 50%, var(--accent2) 70%);
  background-size: 200% 100%;
  animation: shimmer 1.5s ease infinite;
}

/* ─── Type Chips — Tactile ─── */
.type-chip {
  padding: 10px 18px;
  border-radius: 50px;
  font-size: 13px;
  font-weight: 500;
  border: 1.5px solid var(--border);
  background: var(--surface);
  transition: all 0.2s ease;
}

.type-chip:active {
  transform: scale(0.95);
}

.type-chip.on {
  background: var(--text);
  color: #fff;
  border-color: var(--text);
  box-shadow: 0 2px 8px rgba(0,0,0,0.2);
}

/* ─── Onboarding — Refined ─── */
.onb h2 {
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-weight: 700; text-transform: uppercase;
  font-size: 32px;
}

.onb-upload {
  width: 170px;
  height: 220px;
  border: 2px dashed rgba(0,0,0,0.12);
  border-radius: var(--radius);
  transition: all 0.2s ease;
}

.onb-upload:active {
  border-color: var(--text2);
  transform: scale(0.98);
}

/* ─── Profile ─── */
.profile-avatar-ring {
  width: 96px;
  height: 96px;
  border-radius: 50%;
  padding: 3px;
  background: linear-gradient(135deg, var(--text), var(--text2));
  margin: 0 auto 14px;
}

/* ─── Global Touch Feedback ─── */
button:active, .closet-item:active, .chat-item:active {
  opacity: 0.85;
}

/* ─── Smooth page transitions ─── */
.screen {
  animation: none;
}
.screen.active {
  animation: screenIn 0.2s ease;
}
@keyframes screenIn {
  from { opacity: 0.8; }
  to { opacity: 1; }
}


/* ── Titles ──
   All caps, bold, condensed — applied through one class so a change to the
   face is one edit rather than a hunt through inline styles. */
.display,
.topbar-title,
.home-tab,
.cl-subtab,
.hf-sec-head h3,
.ob-head,
.hf-banner {
  font-family: var(--font-display); font-stretch: condensed;
  font-stretch: condensed;
  text-transform: uppercase;
  font-weight: 700;
}
/* Condensed faces read tighter, so the wide tracking that suited the normal
   width now over-separates. Pulled back per size. */
.topbar-title { letter-spacing: 0.04em; }
.home-tab { letter-spacing: 0.06em; }
.tab-label, .cl-subtab, .hf-sec-head h3, .ob-head { letter-spacing: 0.10em; }


/* ═══════════════════════════════════════════════════════════════
   AI SCREENS  (Max's "AI Clothing Designer" / "AI Stylist")
   A mode switch, not a dark theme: near-black ground, one luminous subject,
   gradient CTA. Everything here carries its own dark tokens — reusing a light
   component would land a white slab in the middle of a black screen.

   The hologram chrome renders whether or not there is a subject, because Max
   requires the layout to hold with no content. An empty frame reads as "ready",
   which is also the loading state: the subject materialises into it.
   ═══════════════════════════════════════════════════════════════ */
/* Max, 20 Aug: these screens must be "completely locked to the viewport — no
   vertical scrolling, no horizontal scrolling, no overscroll behavior, no hidden
   or swipe-revealed controls." So the screen is a fixed flex column and the
   sections inside it divide the available height; nothing overflows and nothing
   scrolls. The Generate control is the last row, so it is anchored by the layout
   rather than by a magic offset. */
.ai-screen{position:fixed;inset:0;z-index:900;background:var(--ai-ground);
  color:var(--ai-ink);overflow:hidden;overscroll-behavior:none;
  display:flex;flex-direction:column;}
/* Installed to the home screen there is no browser chrome, so this surface owns
   the whole display — the notch sits over the top 59px and the home indicator
   over the bottom 34px. Without the insets the close button lands under the
   notch, which is unreachable, and the CTA runs under the indicator. In a
   browser tab both insets resolve to 0, so the same rule is correct in both. */
.ai-inner{max-width:560px;width:100%;margin:0 auto;flex:1;min-height:0;
  display:flex;flex-direction:column;
  padding:var(--sat) 0 calc(14px + var(--sab));box-sizing:border-box;}
/* The stage takes whatever is left after the fixed rows, so the screen fits any
   height without a scrollbar. */
.ai-screen .ai-stage{flex:1;min-height:120px;height:auto;margin:8px 12px 0;}
/* Chips and example prompts wrap instead of scrolling sideways — a row you have
   to swipe is a hidden control. */
.ai-screen .ai-chips{overflow:visible;}
.ai-screen .ai-cards{display:grid;grid-template-columns:1fr 1fr;gap:8px;overflow:visible;}
.ai-screen .ai-cta{margin-top:auto;}
/* Everything that is not the stage is sized to a fixed, compact height, so the
   stage can take the remainder and the whole screen fits without scrolling.
   Measured against the shortest phone we support (667pt) with both insets. */
.ai-screen .ai-top{padding:10px 16px 4px;}
.ai-screen .ai-title{font-size:20px;}
.ai-screen .ai-sub{font-size:10px;line-height:1.35;}
.ai-screen .ai-label{margin:8px 16px 5px;}
.ai-screen .ai-chip{padding:6px 3px;height:auto;min-height:58px;font-size:7.5px;min-width:0;}
.ai-screen .ai-chips{gap:5px;}
.ai-screen .ai-card{padding:9px 10px;min-height:0;}
.ai-screen .ai-card .pt{font-size:10px;line-height:1.25;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}
.ai-screen .ai-ta textarea{min-height:78px;height:78px;}
.ai-screen .ai-row{height:40px;}
.ai-screen .ai-status{min-height:14px;font-size:10px;}
.ai-top{position:relative;padding:18px 16px 6px;text-align:center;}
.ai-close{position:absolute;left:14px;top:14px;width:36px;height:36px;border-radius:50%;
  border:1px solid var(--ai-rule);background:transparent;color:var(--ai-ink);
  font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center;}
.ai-title{font-family:var(--font-display);font-stretch:condensed;font-stretch:condensed;text-transform:uppercase;
  font-weight:700;font-size:24px;letter-spacing:0.02em;display:flex;gap:8px;
  align-items:center;justify-content:center;margin:0;}
.ai-sub{font-family:var(--font-ui);font-stretch:normal;font-weight:500;text-transform:uppercase;
  font-size:11px;letter-spacing:0.08em;color:var(--ai-ink2);margin:8px 0 0;line-height:1.5;}

/* ── The hologram stage ── */
.ai-stage{position:relative;margin:14px 12px 0;height:330px;display:flex;
  align-items:center;justify-content:center;overflow:hidden;}
.ai-frame{position:absolute;inset:16px 28px 54px;border:1px solid rgba(120,200,255,0.16);}
/* Corner brackets — drawn, not an image, so they stay crisp at any size. */
.ai-frame::before,.ai-frame::after{content:'';position:absolute;width:26px;height:26px;
  border:2px solid rgba(120,200,255,0.5);}
.ai-frame::before{top:-1px;left:-1px;border-right:0;border-bottom:0;}
.ai-frame::after{bottom:-1px;right:-1px;border-left:0;border-top:0;}
.ai-subject{max-height:250px;max-width:74%;object-fit:contain;position:relative;z-index:2;
  filter:drop-shadow(0 0 22px rgba(90,180,255,0.35));
  animation:aiMaterialise 700ms ease-out;}
@keyframes aiMaterialise{from{opacity:0;transform:translateY(8px) scale(0.985);}to{opacity:1;}}
@media (prefers-reduced-motion:reduce){.ai-subject{animation:none;}}
/* The ring reads as a stage the subject stands on, and gives the empty frame
   something to be about. */
.ai-ring{position:absolute;bottom:22px;left:50%;transform:translateX(-50%);
  width:62%;height:52px;border-radius:50%;
  border:1px solid rgba(120,200,255,0.35);
  box-shadow:0 0 26px rgba(80,170,255,0.28), inset 0 0 20px rgba(80,170,255,0.16);}
.ai-ring.two{width:44%;height:34px;bottom:32px;opacity:0.55;}
.ai-empty{position:relative;z-index:2;text-align:center;color:var(--ai-ink2);
  font-size:12px;line-height:1.55;padding:0 30px;max-width:280px;}

/* ── Generating ──
   A garment takes 30-60s to render. A line of small text under the button is
   not enough feedback for that: the frame sits inert and reads as nothing
   happening. The stage itself becomes the progress indicator — the rings pulse
   and a scan line sweeps the frame, so the screen is visibly working. */
.ai-stage.busy .ai-ring{animation:aiPulse 1.9s ease-in-out infinite;}
.ai-stage.busy .ai-ring.two{animation-delay:0.5s;}
@keyframes aiPulse{
  0%,100%{opacity:0.45;transform:translateX(-50%) scale(0.97);}
  50%{opacity:1;transform:translateX(-50%) scale(1.03);}
}
.ai-stage.busy .ai-frame::after{content:'';position:absolute;left:0;right:0;height:2px;
  background:linear-gradient(90deg,transparent,rgba(120,210,255,0.9),transparent);
  border:0;width:auto;box-shadow:0 0 14px rgba(120,210,255,0.7);
  animation:aiScan 2.1s linear infinite;}
@keyframes aiScan{0%{top:0;opacity:0;}10%{opacity:1;}90%{opacity:1;}100%{top:100%;opacity:0;}}
.ai-busy-text{position:relative;z-index:3;text-align:center;color:var(--ai-ink);
  font-family:var(--font-display);font-stretch:condensed;font-stretch:condensed;text-transform:uppercase;
  font-size:12px;letter-spacing:0.12em;}
/* Failed: the motion stops and the frame goes warm. The stage stays the place
   you look, so the state change reads without a separate error box. */
.ai-stage.failed .ai-frame{border-color:rgba(255,120,90,0.55);}
.ai-stage.failed .ai-ring{animation:none;opacity:0.35;
  border-color:rgba(255,120,90,0.4);}
.ai-busy-text.bad{color:#FFB4A0;}
.ai-busy-text.bad small{color:rgba(255,180,160,0.7);}
.ai-busy-text small{display:block;margin-top:6px;font-family:inherit;font-size:10px;
  letter-spacing:0.08em;color:var(--ai-ink2);text-transform:none;}
@media (prefers-reduced-motion:reduce){
  .ai-stage.busy .ai-ring{animation:none;opacity:0.9;}
  .ai-stage.busy .ai-frame::after{animation:none;top:50%;}
}

/* ── Labels and controls ── */
.ai-label{font-family:var(--font-ui);font-stretch:normal;font-weight:500;text-transform:uppercase;
  font-size:11px;letter-spacing:0.1em;color:var(--ai-ink2);margin:20px 16px 8px;}
.ai-cards{display:flex;gap:8px;overflow-x:auto;padding:0 16px;scrollbar-width:none;}
.ai-cards::-webkit-scrollbar{display:none;}
.ai-card{flex:0 0 46%;min-height:112px;background:var(--ai-surface);
  border:1px solid var(--ai-rule);border-radius:10px;padding:12px;cursor:pointer;
  display:flex;flex-direction:column;gap:8px;text-align:left;color:var(--ai-ink);}
.ai-card .pt{font-size:12px;line-height:1.35;text-transform:uppercase;
  font-family:var(--font-ui);font-stretch:normal;font-weight:500;letter-spacing:0.03em;}
.ai-card .arrow{margin-top:auto;color:var(--ai-ink2);}
/* Six across in a single row. As a flex row with a min-width they wrapped
   5 + 1, which is the broken layout Max flagged. */
.ai-chips{display:grid;grid-template-columns:repeat(6,1fr);gap:6px;padding:0 16px;
  overflow:visible;scrollbar-width:none;}
.ai-chips::-webkit-scrollbar{display:none;}
.ai-chip{min-width:0;min-height:66px;background:var(--ai-surface);
  border:1px solid var(--ai-rule);border-radius:10px;color:var(--ai-ink2);cursor:pointer;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;
  font-family:var(--font-display);font-stretch:condensed;font-stretch:condensed;text-transform:uppercase;
  font-size:7.5px;letter-spacing:0.02em;padding:7px 3px;line-height:1.1;text-align:center;}
.ai-chip svg{width:20px;height:20px;flex:0 0 auto;}
.ai-chip span{display:block;width:100%;overflow:hidden;text-overflow:ellipsis;}
.ai-chip.on{border-color:#C008C2;color:var(--ai-ink);}
.ai-row{display:flex;align-items:center;gap:10px;margin:0 16px;padding:12px 14px;
  background:var(--ai-surface);border:1px solid var(--ai-rule);border-radius:10px;
  color:var(--ai-ink);font-size:13px;min-height:48px;}
/* Without appearance:none the native control keeps its own white chrome, which
   on a black screen renders as a bright slab where the frame shows a dark row.
   The chevron has to be drawn back in once the native one is gone. */
.ai-row select{flex:1;background:transparent;border:none;color:var(--ai-ink);
  font-size:13px;outline:none;-webkit-appearance:none;appearance:none;
  padding-right:20px;cursor:pointer;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23AEB6BE' stroke-width='2' stroke-linecap='round'><polyline points='6 9 12 15 18 9'/></svg>");
  background-repeat:no-repeat;background-position:right center;background-size:14px;}
.ai-row select option{background:#14171A;color:#F2F4F6;}
.ai-ta{margin:0 16px;position:relative;}
.ai-ta textarea{width:100%;min-height:96px;resize:vertical;background:var(--ai-surface);
  border:1px solid var(--ai-rule);border-radius:10px;color:var(--ai-ink);
  padding:12px 12px 26px;font-size:13px;font-family:inherit;outline:none;}
.ai-ta .count{position:absolute;right:12px;bottom:10px;font-size:10px;color:var(--ai-ink2);}
.ai-cta{display:block;width:calc(100% - 32px);margin:18px 16px 0;min-height:52px;
  border:none;border-radius:12px;color:#fff;cursor:pointer;
  font-family:var(--font-display);font-stretch:condensed;font-stretch:condensed;text-transform:uppercase;
  font-weight:700;font-size:15px;letter-spacing:0.06em;}
.ai-cta:disabled{opacity:0.55;}
.ai-cta.garment{background:var(--ai-generate-item);}
.ai-cta.stylist{background:var(--ai-generate-look);}
.ai-status{margin:12px 16px 0;font-size:12px;color:var(--ai-ink2);text-align:center;min-height:16px;}

/* Nav labels are small caps in a normal-width face on the board, not the
   compressed display type — Anton at 11px under an icon is a smudge. */
.tab-label { font-family: var(--font-ui); font-stretch: normal; }

/* Short phones (SE-class, 667pt) minus a 59pt notch and a 34pt indicator leave
   roughly 560pt of usable height. The screen still may not scroll, so the fixed
   rows tighten again and the stage floor drops. */
@media (max-height: 740px) {
  .ai-screen .ai-stage{min-height:80px;}
  .ai-screen .ai-top{padding:6px 16px 2px;}
  .ai-screen .ai-title{font-size:18px;}
  .ai-screen .ai-label{margin:5px 16px 3px;}
  .ai-screen .ai-chip{height:30px;}
  .ai-screen .ai-ta textarea{min-height:62px;height:62px;}
  .ai-screen .ai-card{padding:7px 9px;}
  .ai-screen .ai-cta{min-height:46px;}
}

/* ─── The Generate control can never be pushed off screen ────────────────────
   Max: locked to the viewport, no scrolling, "the Generate control anchored at
   the bottom while remaining fully visible at all times."

   overflow:hidden alone does not deliver that. On a phone whose browser chrome
   eats 130px, the content simply did not fit, and hidden CLIPS — so the example
   prompts were cut in half and the Generate button was gone entirely. An
   unreachable primary action is a worse outcome than a scrollbar.

   So the CTA lives outside the flexible region. Everything between the header
   and the button absorbs the overflow; the button is pinned by the layout, not
   by a magic offset, and is visible on any height. On screens where it all
   fits — which is most — nothing scrolls and the result is identical.
   ─────────────────────────────────────────────────────────────────────────── */
.ai-mid {
  flex: 1; min-height: 0; display: flex; flex-direction: column;
  overflow-y: auto; overscroll-behavior: contain;
  scrollbar-width: none;
}
.ai-mid::-webkit-scrollbar { display: none; }
.ai-screen .ai-cta { flex: 0 0 auto; margin-top: 10px; }
.ai-screen .ai-status { flex: 0 0 auto; }

/* The subject must fit the stage it sits in. max-height:250px against a stage
   that flexes shorter than that meant the image was clipped — a headless torso
   on a white rectangle. */
.ai-screen .ai-subject {
  max-height: 100%; max-width: 74%; height: auto; width: auto; object-fit: contain;
}

/* ─── AI screens, matched to Max's frame ─────────────────────────────────────
   The stage is the hero on the board — a tall scene the figure stands in, not a
   thin empty box above a form. It keeps a real floor so it still reads as a
   stage when the screen is short, and takes every pixel the fixed rows leave.
   ─────────────────────────────────────────────────────────────────────────── */
.ai-screen .ai-stage { min-height: 210px; }
@media (max-height: 780px) { .ai-screen .ai-stage { min-height: 170px; } }
@media (max-height: 700px) { .ai-screen .ai-stage { min-height: 130px; } }
@media (max-height: 640px) { .ai-screen .ai-stage { min-height: 96px; } }

/* Three across, centred, compact — the board's proportions. Two big
   left-aligned cards read as list rows and ate the stage's height. */
.ai-screen .ai-cards {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 7px;
}
.ai-screen .ai-card {
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  text-align: center; padding: 9px 6px; min-height: 0;
}
.ai-screen .ai-card .sp { font-size: 13px; }
.ai-screen .ai-card .pt {
  font-size: 9px; line-height: 1.25; text-align: center;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.ai-screen .ai-card .arrow { font-size: 11px; opacity: 0.55; }

/* Labels are quiet on the board — they name a control, they are not headings. */
.ai-screen .ai-label {
  font-size: 9px; letter-spacing: 0.08em; opacity: 0.62; font-weight: 600;
}

/* The avatar row shows the actual avatar, as drawn. */
.ai-screen .ai-row .av {
  width: 26px; height: 26px; border-radius: 50%; object-fit: cover; flex: 0 0 auto;
  background: rgba(255,255,255,0.08);
}
.ai-screen .ai-row .av.ph { display: inline-block; }

/* Measured across 22 device/chrome combinations: the Generate button was
   visible on all of them, but nine still scrolled — including a current iPhone
   by 3px and 11px, which is close enough to be worth closing rather than
   explaining. This trims the fixed rows so every mainstream phone in portrait
   fits with nothing to scroll. SE-class (568pt) still cannot: at that height
   the stage is already down to 96px and compressing further would leave no
   stage at all, so those scroll by design and keep the CTA pinned. */
@media (max-height: 900px) {
  .ai-screen .ai-top { padding: 8px 16px 2px; }
  .ai-screen .ai-sub { font-size: 9.5px; }
  .ai-screen .ai-label { margin: 6px 16px 4px; }
  .ai-screen .ai-cards { gap: 6px; }
  .ai-screen .ai-card { padding: 7px 5px; }
  .ai-screen .ai-row { height: 42px; }
  .ai-screen .ai-ta textarea { min-height: 72px; height: 72px; }
  .ai-screen .ai-cta { min-height: 46px; margin-top: 8px; }
}
@media (max-height: 820px) {
  .ai-screen .ai-stage { min-height: 150px; }
  .ai-screen .ai-row { height: 40px; }
}
@media (max-height: 700px) { .ai-screen .ai-stage { min-height: 112px; } }
@media (max-height: 640px) { .ai-screen .ai-stage { min-height: 84px; } }

/* ─── Holographic stage ──────────────────────────────────────────────────────
   Vlad: "when u press design with ai you have like moving hologram or movie
   orbit." The board's stage is a scene the piece stands in; ours sat still
   until a generation started, so the screen read as a form with a box on it.

   Pure CSS on transform/opacity only — no images, no JS, nothing to load, and
   nothing that costs a generation. It idles slowly and quickens while working,
   so the motion also carries information.
   ─────────────────────────────────────────────────────────────────────────── */
.ai-orbit{
  position:absolute; left:50%; bottom:12%; width:78%; height:34%;
  transform:translateX(-50%); pointer-events:none;
  perspective:340px; perspective-origin:50% 20%;
}
.ai-o{
  position:absolute; left:50%; top:50%; border-radius:50%;
  border:1px solid rgba(120,200,255,0.30);
  transform-style:preserve-3d;
  animation:aiOrbit 14s linear infinite;
}
.ai-o.o1{ width:96%; height:96%; margin:-48% 0 0 -48%; }
.ai-o.o2{ width:66%; height:66%; margin:-33% 0 0 -33%;
  border-color:rgba(120,200,255,0.42); animation-duration:10s; animation-direction:reverse; }
.ai-o.o3{ width:38%; height:38%; margin:-19% 0 0 -19%;
  border-color:rgba(150,220,255,0.55); animation-duration:7s; }
@keyframes aiOrbit{
  from{ transform:rotateX(74deg) rotateZ(0deg); }
  to  { transform:rotateX(74deg) rotateZ(360deg); }
}

/* A soft pool of light under the plinth, breathing slowly. */
.ai-stage::after{
  content:''; position:absolute; left:50%; bottom:10%;
  width:60%; height:16%; transform:translateX(-50%);
  background:radial-gradient(ellipse at center, rgba(90,180,255,0.22), rgba(90,180,255,0) 70%);
  filter:blur(6px); pointer-events:none;
  animation:aiGlow 5.5s ease-in-out infinite;
}
@keyframes aiGlow{ 0%,100%{ opacity:0.55; } 50%{ opacity:1; } }

/* A slow scan even at rest, so the frame reads as a live display.
   NOT on .ai-frame::before — that is the top-left corner bracket. This rides on
   the orbit wrapper, which owns no pseudo-elements. */
.ai-orbit::before{
  content:''; position:absolute; left:-14%; right:-14%; height:1px; top:0;
  background:linear-gradient(90deg, transparent, rgba(120,200,255,0.55), transparent);
  animation:aiIdleScan 7s ease-in-out infinite;
}
@keyframes aiIdleScan{ 0%{ top:-160%; opacity:0; } 12%{ opacity:0.75; } 88%{ opacity:0.75; } 100%{ top:120%; opacity:0; } }

/* Working: everything quickens. The motion is the progress indicator. */
.ai-stage.busy .ai-o{ animation-duration:4.5s; border-color:rgba(150,220,255,0.6); }
.ai-stage.busy .ai-o.o2{ animation-duration:3.2s; }
.ai-stage.busy .ai-o.o3{ animation-duration:2.2s; }
.ai-stage.busy::after{ animation-duration:1.8s; }

/* Failed: the scene stops and cools. */
.ai-stage.failed .ai-o{ animation-play-state:paused; border-color:rgba(255,120,90,0.35); }
.ai-stage.failed::after{ animation:none; opacity:0.25; }

/* The piece breathes once it has landed, so it sits in the scene rather than
   on top of it. */
.ai-subject{ animation:aiMaterialise 700ms ease-out, aiFloat 6s ease-in-out 700ms infinite; }
@keyframes aiFloat{ 0%,100%{ transform:translateY(0); } 50%{ transform:translateY(-5px); } }

@media (prefers-reduced-motion:reduce){
  .ai-o, .ai-stage::after, .ai-orbit::before, .ai-subject{ animation:none !important; }
  .ai-orbit::before{ opacity:0; }
}

/* Frame difference, not an inconsistency: the designer's prompt cards are
   left-aligned with the arrow under the text; the stylist's are centred. */
.ai-screen[data-kind="garment"] .ai-card{align-items:flex-start;text-align:left;}
.ai-screen[data-kind="garment"] .ai-card .arrow{align-self:flex-start;}
.ai-screen[data-kind="stylist"] .ai-card{align-items:center;text-align:center;}
.ai-screen[data-kind="stylist"] .ai-card .arrow{align-self:center;}

/* The wireframe hologram fills the stage, under whatever the stage swaps in
   later (the busy text, then the generated image). Once there is a real result
   the hologram would only compete with it, so it steps aside. */
.ai-holo{position:absolute;inset:0;width:100%;height:100%;z-index:1;pointer-events:none;
  transition:opacity .35s ease;}
.ai-stage:has(.ai-subject) .ai-holo,
.ai-stage:has(.ai-busy-text) .ai-holo{opacity:0;}
