// ============================================================================
// StyleKro — COMMON module (split from app.jsx on 2026-06-26).
// MUST load FIRST. Defines React/Router destructuring, API/helpers, the shop
// registry, translations, Auth/Confirm providers, shared components, NavBar and
// the misc pages (Login/Profile/ChooseExperience/Privacy/DeleteAccount).
// Loaded as <script type="text/babel">; top-level const/function are shared
// across the chat/wardrobe/shopwise/app_root babel scripts via the global
// lexical scope. See index.html for the (order-sensitive) script tags.
// ============================================================================
// Stylekro — React + React Router (HashRouter) client
const STYLEKRO_BUILD = '20260626y';
// Build stamp so you can confirm at a glance which bundle the tab is running.
// If this DOESN'T print (or prints an older value) the tab is on stale code —
// fully close it and reopen.
try { console.log('[stylekro] build', STYLEKRO_BUILD); } catch (_) {}
const { useState, useEffect, useRef, useContext, createContext, useCallback } = React;
const { HashRouter, Routes, Route, Navigate, useNavigate, Link, NavLink, useParams } = ReactRouterDOM;

const API = (location.hostname === 'localhost' || location.hostname === '127.0.0.1')
  ? 'http://localhost:8000'
  : ''; // same-origin in production (FastAPI serves the SPA + API)

const imgUrl = (p) => (p ? `${API}${p}` : '');

// Image-prep helpers live in image_prep.js (loaded by index.html before this
// script). Exposed on window.ImagePrep so they can be reused by mobile
// shells, ads pages, etc. without dragging in React/Babel.
const _ImagePrep = (typeof window !== 'undefined' && window.ImagePrep) || null;
const prepareCameraImage = (file) => _ImagePrep ? _ImagePrep.prepare(file) : Promise.resolve(file);
const normalizeImageFile = (file) => _ImagePrep ? _ImagePrep.normalize(file) : Promise.resolve(file);
const downscaleImageFile = (file, maxSide, quality) => _ImagePrep
  ? _ImagePrep.downscale(file, maxSide, quality)
  : Promise.resolve(file);
const readFileAsDataURL = (file) => _ImagePrep
  ? _ImagePrep.readAsDataURL(file)
  : new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = () => resolve(typeof reader.result === 'string' ? reader.result : null);
      reader.onerror = () => reject(reader.error || new Error('Failed to read file'));
      reader.readAsDataURL(file);
    });

// Shared try-on stream runner — see web/stylekro_gen.js.
const _StyleKroGen = (typeof window !== 'undefined' && window.StyleKroGen) || null;
if (!_StyleKroGen) console.error('StyleKroGen helper not loaded — check web/stylekro_gen.js script tag in index.html');

// Tiny shared utility helpers — see web/utils.js.
const _Utils = (typeof window !== 'undefined' && window.StyleKroUtils) || null;
if (!_Utils) console.error('StyleKroUtils helper not loaded — check web/utils.js script tag in index.html');
const makeCountMap = (items, getKey, opts) => _Utils
  ? _Utils.makeCountMap(items, getKey, opts)
  : (items || []).reduce((m, x) => { const k = (typeof getKey === 'function' ? getKey(x) : (x && x[getKey])) || ''; if (opts && opts.skipEmpty && !k) return m; m[k] = (m[k] || 0) + 1; return m; }, {});
const errMsg = (e) => _Utils ? _Utils.errMsg(e) : String((e && e.message) || e || '');

// PHONE-AUTH: mobile-number (OTP) sign-in + linking UI lives in web/phone_auth.js
// (exposed on window.StyleKroPhoneAuth). Optional add-on — null when not loaded.
const _PhoneAuth = (typeof window !== 'undefined' && window.StyleKroPhoneAuth) || null;

// Sort a list of garments/items newest-first (latest created_at first, older
// last). Falls back to the backend order when created_at is missing. Returns a
// new array; never mutates the input.
const sortByCreatedDesc = (list) => {
  if (!Array.isArray(list)) return [];
  return [...list].sort((a, b) => {
    const ta = a && a.created_at ? Date.parse(a.created_at) : NaN;
    const tb = b && b.created_at ? Date.parse(b.created_at) : NaN;
    const va = Number.isNaN(ta) ? -Infinity : ta;
    const vb = Number.isNaN(tb) ? -Infinity : tb;
    return vb - va;
  });
};

// ---------------------------------------------------------------------------
// Shop-category registry — the per-category metadata + helpers live in
// web/shops/<name>.js (loaded by index.html before this file). To add a new
// category, drop a new file in web/shops/ and a <script> tag in index.html.
// ---------------------------------------------------------------------------
const _ShopCategories = (typeof window !== 'undefined' && window.ShopCategories) || null;
if (!_ShopCategories) {
  console.error('ShopCategories registry not loaded — check web/shops/*.js script tags in index.html');
}
const SHOP_CATEGORIES = _ShopCategories ? _ShopCategories.legacyMetaMap : {};
const GENERIC_SHOP_META = _ShopCategories ? _ShopCategories.GENERIC_META : {
  key: '', label: '', defaultAssetKind: 'garment',
  kindOptions: null, kindLabels: {}, extraFields: [], detailFields: [],
};
const shopMeta = (shopOrCategory) => _ShopCategories
  ? _ShopCategories.shopMeta(shopOrCategory)
  : GENERIC_SHOP_META;
const SHOP_CATEGORY_LABELS = _ShopCategories ? _ShopCategories.LABELS : {};
const shopCategoryLabel = (c) => _ShopCategories
  ? _ShopCategories.shopCategoryLabel(c)
  : (c ? c.charAt(0).toUpperCase() + c.slice(1) : '');

// Friendly label for an asset_kind (looked up across every category).
const assetKindLabel = (kind) => {
  if (!kind) return null;
  for (const meta of Object.values(SHOP_CATEGORIES)) {
    if (meta.kindLabels && meta.kindLabels[kind]) return meta.kindLabels[kind];
  }
  return null;
};

// Fire-and-forget drift check: compare the hardcoded keys the frontend ships
// with against the canonical sets the backend recognises (/garments/ui-constants).
// Logs a console warning if a value will silently be ignored server-side —
// catches forgotten-to-sync bugs the moment the page boots.
(function checkUiConstantDrift() {
  if (typeof fetch !== 'function') return;
  const tailorMeta = _ShopCategories?.byKey?.tailor;
  const tileMeta = _ShopCategories?.byKey?.tile;
  if (!tailorMeta && !tileMeta) return;
  fetch('/garments/ui-constants').then((r) => r.ok ? r.json() : null).then((data) => {
    if (!data) return;
    const warn = (label, frontKeys, backendKeys) => {
      const missing = (frontKeys || []).filter((k) => !backendKeys.includes(k));
      if (missing.length) console.warn(`[ui-constants drift] ${label} keys not recognised by backend:`, missing);
    };
    if (tailorMeta?.PIECE_OPTIONS) warn('tailor PIECE_OPTIONS', tailorMeta.PIECE_OPTIONS.map((o) => o.value), data.tailor_pieces || []);
    if (tailorMeta?.SLOT_LABELS) warn('tailor SLOT_LABELS', Object.keys(tailorMeta.SLOT_LABELS), data.tailor_slots || []);
    if (tileMeta?.ROOM_SURFACES) warn('tile ROOM_SURFACES', tileMeta.ROOM_SURFACES.map((s) => s.value), data.tile_room_surfaces || []);
  }).catch(() => {});
})();

const shopUsesGarmentTaxonomy = (shopOrCategory) => {
  const key = shopMeta(shopOrCategory).key;
  return !key || key === 'tailor';
};

// Best-effort mapping from Google Places `types` (returned by Autocomplete)
// to our internal shop category keys. Used to pre-fill the "shop type"
// dropdown when the seller picks their shop from Google Maps.
const PLACE_TYPE_TO_SHOP_CATEGORY = {
  beauty_salon: 'salon',
  hair_care: 'salon',
  spa: 'salon',
  jewelry_store: 'jewelry',
  clothing_store: 'garment',
  shoe_store: 'garment',
  department_store: 'garment',
  shopping_mall: 'garment',
};
const placeTypesToShopCategory = (types) => {
  if (!Array.isArray(types)) return '';
  for (const t of types) {
    const k = (t || '').toLowerCase();
    if (PLACE_TYPE_TO_SHOP_CATEGORY[k]) return PLACE_TYPE_TO_SHOP_CATEGORY[k];
  }
  return '';
};

const ITEM_COLOR_PALETTE = [
  { value: 'Black',  swatch: '#1f1a14' },
  { value: 'White',  swatch: '#f6f4ef' },
  { value: 'Gray',   swatch: '#9ca3af' },
  { value: 'Beige',  swatch: '#d9c4a3' },
  { value: 'Brown',  swatch: '#8a5a3c' },
  { value: 'Red',    swatch: '#c63d3d' },
  { value: 'Pink',   swatch: '#ef7aa7' },
  { value: 'Orange', swatch: '#f39b4a' },
  { value: 'Yellow', swatch: '#e5c84b' },
  { value: 'Green',  swatch: '#5b8d62' },
  { value: 'Blue',   swatch: '#4f7cff' },
  { value: 'Navy',   swatch: '#233a74' },
  { value: 'Purple', swatch: '#815fd0' },
  { value: 'Gold',   swatch: '#c9a64b' },
  { value: 'Silver', swatch: '#bcc3cf' },
  { value: 'Multi',  swatch: 'linear-gradient(135deg, #ff5f6d 0%, #ffc371 35%, #72c96d 68%, #4f7cff 100%)' },
];

const normalizePaletteValue = (value) => String(value || '').trim().toLowerCase();

function ColorPaletteField({ label = 'Color', value, onChange, disabled = false, compact = false }) {
  const normalizedValue = normalizePaletteValue(value);
  const hasKnownColor = ITEM_COLOR_PALETTE.some((option) => normalizePaletteValue(option.value) === normalizedValue);
  const customValue = value && !hasKnownColor ? value.trim() : '';

  return (
    <div className={`ge-input ge-color-field ge-span-full${compact ? ' is-compact' : ''}${disabled ? ' is-disabled' : ''}`}>
      <span>{label}</span>
      <div className="ge-color-palette" role="list">
        {ITEM_COLOR_PALETTE.map((option) => {
          const active = normalizePaletteValue(option.value) === normalizedValue;
          return (
            <button
              type="button"
              key={option.value}
              className={`ge-color-swatch${active ? ' is-active' : ''}${normalizePaletteValue(option.value) === 'white' ? ' is-light' : ''}`}
              onClick={() => onChange(option.value)}
              disabled={disabled}
              aria-pressed={active}
              aria-label={option.value}
              title={option.value}
            >
              <span className="ge-color-swatch-fill" style={{ background: option.swatch }} aria-hidden="true" />
            </button>
          );
        })}
      </div>
      <div className="ge-color-meta">
        <span className="ge-color-value">{value || 'No color selected'}</span>
        {!!value && (
          <button type="button" className="ge-color-clear" onClick={() => onChange('')} disabled={disabled}>
            Clear
          </button>
        )}
      </div>
      {customValue && <small className="ge-color-custom">Saved color: {customValue}</small>}
    </div>
  );
}

// Append a cache-buster query param to a URL (used for profile photos that
// can change underneath a stable URL like `/profiles/{id}/photo`).
const bust = (url, version) => {
  if (!url || !version) return url || '';
  return `${url}${url.includes('?') ? '&' : '?'}v=${version}`;
};

// ===== Native Android image picker bridge =====
// When running inside the StyleYou Android shell, the WebView's
// <input type=file> bytes-delivery path is unreliable on Samsung One UI
// (one-shot URI permission expires before JS can read it). The shell
// exposes window.AndroidShell.pickImage(callbackId, multiple) which
// reads bytes inside onActivityResult and returns {items:[{name,mime,size,b64}]}
// via window.__styleyouOnImagePicked(callbackId, jsonString).
function nativePickImages(multiple) {
  return new Promise((resolve, reject) => {
    if (!(window.AndroidShell && typeof window.AndroidShell.pickImage === 'function')) {
      reject(new Error('AndroidShell.pickImage unavailable'));
      return;
    }
    const id = `pick-${Date.now()}-${Math.random().toString(36).slice(2)}`;
    const prev = window.__styleyouOnImagePicked;
    window.__styleyouOnImagePicked = function (cbId, payload) {
      if (cbId !== id) {
        if (typeof prev === 'function') { try { prev(cbId, payload); } catch {} }
        return;
      }
      try { window.__styleyouOnImagePicked = prev || null; } catch {}
      try {
        const data = typeof payload === 'string' ? JSON.parse(payload) : payload;
        if (data && data.error) reject(new Error(data.error));
        else resolve((data && data.items) || []);
      } catch (e) { reject(e); }
    };
    try { window.AndroidShell.pickImage(id, !!multiple); }
    catch (e) { window.__styleyouOnImagePicked = prev || null; reject(e); }
  });
}

// True when the Android shell exposes the native picker bridge.
function hasNativePicker() {
  return !!(typeof window !== 'undefined'
    && window.AndroidShell
    && typeof window.AndroidShell.pickImage === 'function');
}

// Run the Android native picker and convert each {name,mime,b64} item into a
// real File object so the rest of the UI can treat the result identically to
// an <input type=file> change event.
function nativePickFiles(multiple) {
  return nativePickImages(multiple).then((items) => {
    if (!items || !items.length) return [];
    return items.map((it) => {
      const raw = atob(it.b64 || '');
      const arr = new Uint8Array(raw.length);
      for (let i = 0; i < raw.length; i++) arr[i] = raw.charCodeAt(i);
      return new File([arr], it.name || 'photo.jpg', { type: it.mime || 'image/jpeg' });
    });
  });
}

// If we're inside the Android shell, route downloads through the system
// DownloadManager — the WebView ignores the <a download=...> attribute
// and just navigates instead of saving the file.
function handleDownloadClick(e, url, filename) {
  e.stopPropagation();
  try {
    if (window.AndroidShell && typeof window.AndroidShell.downloadUrl === 'function') {
      e.preventDefault();
      let abs = url;
      try { abs = new URL(url, location.href).toString(); } catch {}
      window.AndroidShell.downloadUrl(abs, filename || 'stylekro-image.png');
    }
  } catch {}
}

// Share an image — prefer Web Share API with a File (works on mobile),
// fall back to sharing the URL, then to copying the URL to clipboard.
async function handleShareClick(e, url, filename) {
  e.stopPropagation();
  e.preventDefault();
  const name = filename || 'stylekro-image.png';
  let abs = url;
  try { abs = new URL(url, location.href).toString(); } catch {}
  try {
    if (window.AndroidShell && typeof window.AndroidShell.shareUrl === 'function') {
      window.AndroidShell.shareUrl(abs, name);
      return;
    }
  } catch {}
  try {
    if (navigator.share) {
      try {
        const res = await fetch(url);
        const blob = await res.blob();
        const file = new File([blob], name, { type: blob.type || 'image/png' });
        if (navigator.canShare && navigator.canShare({ files: [file] })) {
          await navigator.share({ files: [file], title: 'Stylekro' });
          return;
        }
      } catch {}
      try {
        await navigator.share({ title: 'Stylekro', url: abs });
        return;
      } catch {}
    }
  } catch {}
  try {
    await navigator.clipboard.writeText(abs);
    try { alert('Image link copied to clipboard'); } catch {}
  } catch {}
}

// Icon action row shown under a lightbox image: Share + Download.
function LightboxActions({ url, filename, onRotate360 }) {
  return (
    <div className="lightbox-actions" onClick={(e) => e.stopPropagation()}>
      {onRotate360 && (
        <button
          type="button"
          className="lightbox-action"
          title="360° view"
          aria-label="360 degree view"
          onClick={onRotate360}
        >
          <span style={{ fontWeight: 600, fontSize: 13, letterSpacing: 0.3 }}>360°</span>
        </button>
      )}
      <button
        type="button"
        className="lightbox-action"
        title="Share"
        aria-label="Share"
        onClick={(e) => handleShareClick(e, url, filename)}
      >
        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
          <circle cx="18" cy="5" r="3"/>
          <circle cx="6" cy="12" r="3"/>
          <circle cx="18" cy="19" r="3"/>
          <line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/>
          <line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/>
        </svg>
      </button>
      <a
        className="lightbox-action"
        href={url}
        download={filename || 'stylekro-image.png'}
        title="Download"
        aria-label="Download"
        onClick={(e) => handleDownloadClick(e, url, filename)}
      >
        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
          <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
          <polyline points="7 10 12 15 17 10"/>
          <line x1="12" y1="15" x2="12" y2="3"/>
        </svg>
      </a>
    </div>
  );
}

// Shared StyleKro generate button + logo lockup — single source of truth,
// defined in web/stylekro_button.js and loaded before app.jsx. Using the
// window globals (instead of a local copy) keeps the brand "Generate" control
// identical across every shop, the wardrobe page and the buyer item page.
const StyleKroButton = (typeof window !== 'undefined' && window.StyleKroButton) || (() => null);
const StyleKroLockup = (typeof window !== 'undefined' && window.StyleKroLockup) || (() => null);
if (typeof window !== 'undefined' && !window.StyleKroButton) {
  console.error('StyleKroButton helper not loaded — check the stylekro_button.js <script> tag in index.html');
}

// ===== Google Maps Places (lazy-loaded) =====
// Browser key — restrict by HTTP referrer in Google Cloud Console.
const GOOGLE_MAPS_API_KEY = 'AIzaSyCrG6XTewgn-rwdG5gF6P8WPWzrv-A0hdw';
let __googleMapsPromise = null;
function loadGoogleMaps() {
  if (typeof window === 'undefined') return Promise.reject(new Error('no window'));
  if (window.google && window.google.maps && window.google.maps.places) {
    return Promise.resolve(window.google);
  }
  if (__googleMapsPromise) return __googleMapsPromise;
  __googleMapsPromise = new Promise((resolve, reject) => {
    const cbName = '__styleyouGoogleMapsReady';
    window[cbName] = () => resolve(window.google);
    const s = document.createElement('script');
    s.src = `https://maps.googleapis.com/maps/api/js?key=${GOOGLE_MAPS_API_KEY}&libraries=places&loading=async&callback=${cbName}`;
    s.async = true;
    s.defer = true;
    s.onerror = () => { __googleMapsPromise = null; reject(new Error('Failed to load Google Maps')); };
    document.head.appendChild(s);
  });
  return __googleMapsPromise;
}

// ===== 360-view sheet modal =================================================
// Loads a contact-sheet PNG (server-generated by gpt-image-2) and renders it
// in a canvas with auto-spin + pointer-drag to scrub frames manually.
function Sheet360Viewer({ active, fetchSheet, onExit, exitLabel = 'Close' }) {
  const [state, setState] = React.useState({ status: 'idle' });
  const canvasRef = React.useRef(null);
  const dragRef = React.useRef({ active: false, startX: 0, startFrame: 0 });
  const [frame, setFrame] = React.useState(0);
  const [autospin, setAutospin] = React.useState(true);

  React.useEffect(() => {
    if (!active) return;
    let cancelled = false;
    setState({ status: 'loading' });
    setFrame(0);
    setAutospin(true);
    (async () => {
      try {
        const { blob, rows, cols, frames } = await fetchSheet();
        if (cancelled) return;
        const url = URL.createObjectURL(blob);
        const img = new Image();
        img.onload = () => {
          if (cancelled) { URL.revokeObjectURL(url); return; }
          setState({ status: 'ready', img, rows, cols, frames, url });
        };
        img.onerror = () => {
          if (cancelled) return;
          URL.revokeObjectURL(url);
          setState({ status: 'error', error: 'Could not load 360 image' });
        };
        img.src = url;
      } catch (e) {
        if (!cancelled) setState({ status: 'error', error: e?.message || 'Generation failed' });
      }
    })();
    return () => { cancelled = true; };
  }, [active, fetchSheet]);

  React.useEffect(() => () => { if (state.url) URL.revokeObjectURL(state.url); }, [state.url]);

  React.useEffect(() => {
    if (state.status !== 'ready' || !autospin) return undefined;
    const id = setInterval(() => setFrame((f) => (f + 1) % state.frames), 420);
    return () => clearInterval(id);
  }, [state.status, state.frames, autospin]);

  React.useEffect(() => {
    if (state.status !== 'ready') return;
    const cvs = canvasRef.current;
    if (!cvs) return;
    const { img, rows, cols, frames } = state;
    const cw = Math.floor(img.naturalWidth / cols);
    const ch = Math.floor(img.naturalHeight / rows);
    cvs.width = cw;
    cvs.height = ch;
    const idx = ((frame % frames) + frames) % frames;
    const r = Math.floor(idx / cols);
    const c = idx % cols;
    const ctx = cvs.getContext('2d');
    ctx.clearRect(0, 0, cw, ch);
    ctx.drawImage(img, c * cw, r * ch, cw, ch, 0, 0, cw, ch);
  }, [state, frame]);

  function onPointerDown(e) {
    if (state.status !== 'ready') return;
    setAutospin(false);
    dragRef.current = { active: true, startX: e.clientX, startFrame: frame };
    e.currentTarget.setPointerCapture?.(e.pointerId);
  }
  function onPointerMove(e) {
    if (!dragRef.current.active || state.status !== 'ready') return;
    const dx = e.clientX - dragRef.current.startX;
    const w = e.currentTarget.clientWidth || 320;
    const delta = Math.round((dx / w) * state.frames * 1.4);
    const next = ((dragRef.current.startFrame + delta) % state.frames + state.frames) % state.frames;
    setFrame(next);
  }
  function onPointerUp() { dragRef.current.active = false; }

  return (
    <>
      {state.status === 'loading' && (
        <div className="sheet360-loading">
          <span className="spinner" aria-hidden="true" />
          <span>Styling 360°…</span>
        </div>
      )}
      {state.status === 'error' && (
        <div className="sheet360-error">⚠ {state.error}</div>
      )}
      {state.status === 'ready' && (
        <>
          <canvas
            ref={canvasRef}
            className="sheet360-canvas"
            onPointerDown={onPointerDown}
            onPointerMove={onPointerMove}
            onPointerUp={onPointerUp}
            onPointerCancel={onPointerUp}
          />
          <div className="sheet360-controls">
            <button
              type="button"
              className="sheet360-btn"
              onClick={() => setAutospin((s) => !s)}
            >{autospin ? '⏸ Pause' : '▶ Auto-spin'}</button>
            <span className="sheet360-hint">Drag to rotate</span>
            {onExit && (
              <button type="button" className="sheet360-btn" onClick={onExit}>{exitLabel}</button>
            )}
          </div>
        </>
      )}
    </>
  );
}

function Sheet360Modal({ open, fetchSheet, onClose }) {
  if (!open) return null;
  return (
    <div className="lightbox sheet360-modal" onClick={onClose}>
      <div className="sheet360-stage" onClick={(e) => e.stopPropagation()}>
        <Sheet360Viewer active={open} fetchSheet={fetchSheet} />
        <button type="button" className="sheet360-close" onClick={onClose} aria-label="Close">×</button>
      </div>
    </div>
  );
}

// Helper: POST to a sheet360 endpoint and return blob + grid dimensions.
async function fetchSheet360(path) {
  const headers = {};
  const token = localStorage.getItem('styleyou_token');
  if (token) headers.Authorization = `Bearer ${token}`;
  const r = await fetch(`${API}${path}`, { method: 'POST', headers });
  if (!r.ok) {
    let msg = 'Could not generate 360° view';
    try { const j = await r.json(); msg = j.detail || msg; } catch {}
    throw new Error(msg);
  }
  const rows = Number(r.headers.get('x-sheet360-rows')) || 2;
  const cols = Number(r.headers.get('x-sheet360-cols')) || 4;
  const frames = Number(r.headers.get('x-sheet360-frames')) || rows * cols;
  const blob = await r.blob();
  return { blob, rows, cols, frames };
}

// ===== i18n =====
const LANGS = [
  { code: 'en', label: 'English', native: 'English' },
  { code: 'hi', label: 'Hindi', native: 'हिन्दी' },
];
const STRINGS = {
  en: {
    'nav.chat': 'Chat',
    'nav.shops': 'Shops',
    'nav.looks': 'My Looks',
    'nav.wardrobe': 'Wardrobe',
    'nav.profile': 'Profile',
    'nav.myShop': 'My Shop',
    'nav.shopSettings': 'Shop Details',
    'menu.profile': 'My profile',
    'menu.switchToBuyer': 'Switch to buyer',
    'menu.switchToSeller': 'Switch to seller',
    'menu.chooseExperience': 'Choose experience',
    'menu.experience': 'Experience',
    'menu.buyer': 'Buyer',
    'menu.seller': 'Seller',
    'menu.help': 'Help & feedback',
    'menu.privacy': 'Privacy policy',
    'menu.deleteAccount': 'Delete account',
    'menu.signOut': 'Sign out',
    'menu.language': 'Language',
    'login.continue': 'Continue',
    'login.signingIn': 'Signing in…',
    'login.hero': 'See Yourself in Any Outfit',
    'login.tagline': 'Your private AI stylist. Upload one photo and try on anything — before you buy.',
    'login.heading': 'Sign in to continue',
    'login.subheading': 'Choose a quick and easy way to sign in',
    'login.emailFallback': 'or continue with email',
    'login.namePlaceholder': 'Your name (new users)',
    'login.emailPlaceholder': 'you@example.com',
    'login.disclaimer': 'By continuing, you agree to our Terms of Use and Privacy Policy.',
    'login.helpLink': 'Need help? Contact us',
    'login.demoLabel': 'Live try-on',
    'login.step1': 'Upload your photo',
    'login.step2': 'Choose any outfit',
    'login.step3': 'See yourself styled',
    'login.googleError': 'Could not start Google sign-in:',
    'login.googleSignInFailed': 'Google sign-in failed',
    'login.androidAppTitle': 'Get the Stylekro Android app',
    'login.androidAppBody': 'Smoother try-on, faster uploads, and a native experience built for your phone.',
    'login.androidAppCta': 'Install from Play Store',
    'login.androidAppDismiss': 'Continue in browser',
    'chat.placeholder': 'Ask Vogue anything…',
    'chat.send': 'Send',
    'chat.agentName': 'Vogue',
    'chat.agentTitle': 'Your AI stylist',
    'chat.profilePhotoHint': 'Profile photo on file',
    'chat.loadingOlder': 'Loading older messages…',
    'chat.scrollOlder': 'Scroll up for older messages',
    'chat.refiningLook': 'Styling 360',
    'chat.copy': 'Copy',
    'chat.openImage': 'Open image',
    'chat.dropHint': 'Drop a garment image to try it on',
    'chat.remove': 'Remove',
    'chat.uploadOutfit': 'Upload an outfit photo',
    'chat.composerPlaceholder': 'Ask anything. Paste the product screenshot or product link.',
    'chat.sendAria': 'Send message',
    'chat.composerHint': 'Tip: paste a product URL, drop a garment image, or just say "try this on". Share a screenshot to see how you\u2019d look.',
    'chat.download': 'Download',
    'chat.loading': 'Loading…',
    'chat.generatingTryOn': 'Generating your try-on…',
    'chat.confirmClearHistory': 'Clear all chat history?',
    'chat.clearHistoryError': 'Could not clear history:',
    'chat.welcome.hi': 'Hi',
    'chat.welcome.body': "I'm Vogue, your stylist ✨ Tell me what's on your wardrobe agenda, or drop a garment photo and I'll dress you up.",
    'chat.welcome.fresh': 'fresh start ✦ What shall we style today?',
    'chat.suggestion1': 'Date-night ideas',
    'chat.suggestion1Prompt': 'What outfit would suit me for a first date?',
    'chat.suggestion2': 'Roast my fit',
    'chat.suggestion2Prompt': 'Roast my current outfit and tell me how to fix it.',
    'chat.suggestion3': 'Autumn capsule',
    'chat.suggestion3Prompt': 'Build me a 7-piece capsule for autumn.',
    'chat.suggestion4': 'Best colors for me',
    'chat.suggestion4Prompt': 'What colors actually flatter me?',
    'chat.suggestion5': 'Which look suits me?',
    'chat.suggestion5Prompt': 'Look at my profile photo and the last try-on image you generated for me — which look suits me better and why? Be specific about silhouette, color harmony, and proportion.',
    'chooseExp.title': 'How will you use Stylekro?',
    'chooseExp.sub': 'Pick the experience that fits you best. You can switch anytime from your profile menu.',
    'chooseExp.buyer': "I'm a Buyer",
    'chooseExp.seller': "I'm a Seller",
    'chooseExp.welcome': 'Welcome',
    'chooseExp.buyerHead': 'Buyer',
    'chooseExp.buyerDesc': 'Try on outfits with Vogue.',
    'chooseExp.sellerHead': 'Seller',
    'chooseExp.sellerDesc': 'List & sell from your shop.',
    'chooseExp.continue': 'Continue',
    'chooseExp.getStarted': 'Get started',
    'help.aria': 'Help and feedback',
    'help.close': 'Close',
    'help.eyebrow': 'Support \u00b7 Feedback',
    'help.title': "We'd love to hear from you.",
    'help.email': 'Email',
    'help.phone': 'Phone',
    'help.orSend': 'Or send feedback',
    'help.thanksTitle': 'Thank you for the feedback!',
    'help.thanksMessage': "We've received your message and will read it personally.",
    'help.nameLabel': 'Your name (optional)',
    'help.namePlaceholder': 'Jane Doe',
    'help.emailLabel': 'Reply email (optional)',
    'help.emailPlaceholder': 'you@example.com',
    'help.messageLabel': 'Message',
    'help.messagePlaceholder': "What's on your mind? Bug, idea, or compliment — all welcome.",
    'help.fineprint': 'Your message goes straight to the team — no email client needed.',
    'help.send': 'Send feedback',
    'help.sending': 'Sending…',
    'profile.nameRequired': 'Tell Vogue your name first.',
    'profile.photoRequired': 'Add a photo so Vogue can dress you up.',
    'profile.tryOnPhotoRequired': 'Add or update your profile photo before generating a try-on.',
    'profile.emptyPhotos': 'No photos yet — upload one to start trying on outfits.',
    'profile.zoom': 'Click to zoom',
    'profile.primary': 'PRIMARY',
    'profile.setPrimary': 'Use',
    'profile.active': 'Active',
    'profile.settingPrimary': 'Setting primary…',
    'profile.deletePhoto': 'Delete photo',
    'profile.displayNameLabel': 'NAME',
    'profile.displayNamePlaceholder': 'Name',
    'profile.onboardTitle': 'Create your signature look',
    'profile.onboardSubtitle': 'A clear photo of you helps your AI stylist see how outfits will look on you.',
    'profile.replacePhoto': 'Tap to replace',
    'profile.uploadBest': 'Upload your best photo',
    'profile.uploadHint': 'Front-facing, good lighting works best.',
    'profile.takeSelfie': 'Take Selfie',
    'profile.choosePhoto': 'Add your photo',
    'profile.takePhoto': 'Take Photo',
    'profile.fromGallery': 'Choose from Gallery',
    'profile.nameHint': "This is how we'll greet you in the app.",
    'profile.cta': 'Meet Your AI Stylist',
    'profile.edit': 'Edit',
    'profile.delete': 'Delete',
    'profile.save': 'Save',
    'profile.cancel': 'Cancel',
    'common.saving': 'SAVING\u2026',
    'common.saveChanges': 'SAVE CHANGES',
    'common.signOut': 'Sign out',
    'common.uploading': 'Uploading\u2026',
    'common.cancel': 'Cancel',
    'common.confirm': 'Confirm',
    'common.delete': 'Delete',
    'common.apply': 'Apply',
    'common.clearAll': 'CLEAR ALL',
    'common.clear': 'Clear',
    'common.all': 'All',
    'common.more': 'More',
    'common.filters': 'Filters',
    'common.openFilters': 'Open filters',
    'common.upload': 'Upload',
    'feedback.good': 'Good result',
    'feedback.bad': 'Bad result',
    'shops.noShopsFound': 'No shops found',
    'shops.tryDifferent': 'Try a different search term.',
    'shops.beFirst': 'Be the first \u2014 open a shop and start adding pieces.',
    'shops.clearSearch': 'Clear search',
    'shops.searchPlaceholder': 'Search shops, locations, vibes\u2026',
    'shops.noMatch': 'No shops match your filters',
    'shops.tryDifferentFilter': 'Try a different filter or clear them to see all shops.',
    'shops.clearFilters': 'Clear filters',
    'shops.explore': 'Explore \u2192',
    'shop.searchPlaceholder': 'Search outfits, colors, styles\u2026',
    'shop.shareAria': 'Share this shop',
    'shop.eyebrow': 'SHOP',
    'shop.pickDesignFabric': 'Pick one design and at least one fabric.',
    'shop.noPiecesYet': 'No pieces yet',
    'shop.checkBackSoon': "This shop hasn't added any pieces. Check back soon.",
    'shop.noMatchingPieces': 'No pieces match your filters.',
    'shop.noMatchingDesigns': 'No designs match your filters.',
    'shop.noMatchingFabrics': 'No fabrics match your filters.',
    'shop.notFound': 'Shop not found.',
    'shop.saveFirst': 'Save your shop first.',
    'shop.confirmRemoveBanner': 'Remove banner photo?',
    'shop.nameRequired': 'Shop name is required.',
    'shop.categoryRequired': 'Choose a shop type before publishing your shop.',
    'shop.uploadBanner': 'Upload a banner',
    'shop.shopName': 'Shop name',
    'shop.phone': 'Phone',
    'shop.address': 'Address',
    'shop.description': 'Description',
    'shop.shopType': 'Shop Type',
    'shop.public': 'Public',
    'shop.private': 'Private',
    'shop.locate': 'Locate',
    'shop.locating': 'Locating…',
    'shop.banner': 'Shop banner',
    'shop.bannerSubtitle': 'Shown behind your shop on the Shops grid',
    'shop.bannerLockedTitle': 'Save your shop first',
    'shop.bannerLockedHint': 'Create your shop below, then add a banner here',
    'shop.change': '✎ Change',
    'shop.remove': '🗑 Remove',
    'shop.working': 'Working…',
    'shop.shopDetailsTitle': 'Shop',
    'shop.shopDetailsTitleGrad': 'Details',
    'shop.share': '🔗 Share',
    'shop.copied': '✓ Copied',
    'shop.saved': '✓ Saved',
    'shop.saving': 'Saving…',
    'shop.eyebrowStudio': 'AI Studio',
    'shop.noPiecesUpload': 'No pieces yet \u2014 drop some photos in the upload box on the left.',
    'shop.titleRequired': 'Title cannot be empty.',
    'shop.confirmRemovePiece': 'Remove this piece from your shop?',
    'shop.save': 'Save Changes \u2728',
    'shop.publish': 'Save Shop',
    'shop.open': 'Open shop',
    'shop.noMatchingSearch': 'No pieces match "{search}".',
    'shop.bulkDrop': 'Click or drop photos',
    'shop.bulkMulti': 'Multiple files supported',
    'shop.bulkTailorHint': 'Tailor shop: upload each design and fabric as separate items so buyers can combine one of each.',
    'shop.bulkPending': '{count} pending',
    'shop.bulkReview': 'Review and upload',
    'tailor.tapFlip': 'Tap to flip',
    'tailor.zoom': 'Zoom',
    'tailor.quickPreview': 'Quick preview',
    'tailor.original': 'Original',
    'looks.empty': 'No looks yet',
    'looks.emptyHint': 'Pick a piece from a shop or chat with the stylist to create your first try-on.',
    'looks.noMatching': 'No looks match your filters.',
    'looks.browseShops': 'Browse shops',
    'looks.chatStylist': 'Chat with stylist',
    'looks.deleteOne': 'Delete this look?',
    'looks.deleteMany': 'Delete {count} look{plural}?',
    'looks.couldNotDelete': 'Could not delete {count} look{plural}.',
    'looks.searchPlaceholder': 'Search outfits, styles...',
    'looks.filters': 'FILTERS',
    'looks.clearAll': 'CLEAR ALL',
    'looks.source': 'SOURCE',
    'looks.all': 'All',
    'looks.shops': '🛍 Shops',
    'looks.chat': '💬 Chat',
    'looks.shopsHeader': 'SHOPS',
    'looks.sort': 'SORT',
    'looks.newestFirst': 'Newest first',
    'looks.oldestFirst': 'Oldest first',
    'looks.apply': 'Apply',
    'looks.selected': 'selected',
    'looks.share': 'Share',
    'looks.delete': 'Delete',
    'looks.compare': 'Compare',
    'looks.copyLink': 'Copy link',
    'looks.tryon': 'Try-on',
    'looks.tapToSelect': 'Tap to select',
    'looks.clickToView': 'Click to view',
    'common.shop': 'Shop',
    'common.chat': 'Chat',
    'wardrobe.confirmRemove': 'Remove this item from your wardrobe?',
    'wardrobe.pickOne': 'Pick at least one item from your wardrobe.',
    'wardrobe.uploading': 'Uploading…',
    'wardrobe.addClothing': 'Add clothing or accessories',
    'wardrobe.uploadSubtitle': 'Camera or gallery · auto-categorized by AI',
    'wardrobe.category': 'Category',
    'wardrobe.all': 'All',
    'wardrobe.loading': 'Loading your wardrobe…',
    'wardrobe.empty': 'Your wardrobe is empty',
    'wardrobe.emptyHint': 'Add your first item to start mixing & matching looks.',
    'wardrobe.uploadNow': 'Upload Now',
    'wardrobe.stylekro': 'Stylekro',
    'wardrobe.loadingBtn': 'Loading…',
    'wardrobe.dressingUp': 'Dressing you up',
    'wardrobe.download': 'Download',
    'wardrobe.items': 'item',
    'wardrobe.itemsPlural': 'items',
    'privacy.statement': 'We do not sell your personal data, and we do not show third-party advertising.',
    'common.back': '← Back',
    'privacy.title': 'Privacy Policy',
    'deleteAcct.title': 'Delete your Stylekro account',
    'deleteAcct.subtitle': 'Permanently remove your account and all associated data.',
    'boot.loading': 'Loading\u2026',
    // --- Shops filter rail (desktop)
    'shops.categoryRail': 'Category',
    'shops.inventoryRail': 'Inventory',
    'shops.anySize': 'Any size',
    'shops.onePlus': '1+ piece',
    'shops.fivePlus': '5+ pieces',
    'shops.twentyPlus': '20+ pieces',
    // --- Login extras
    'login.eyebrow': 'Stylekro AI try-on',
    'login.stepLabel': 'Step',
    'login.googleBtn': 'Continue with Google',
    'login.googleNotConfigured': 'Google sign-in not configured.',
    // --- Filter sheet
    'filter.title': 'Filter',
    'filter.category': 'Category',
    'filter.subcategory': 'Sub-category',
    // --- Tailor picker
    'tailor.chooseType': 'Choose garment type',
    'tailor.outfitSetup': 'Outfit setup',
    'tailor.customizePieces': 'Customize each piece of your outfit',
    'tailor.selectDesignFabric': 'Select design and fabric',
    'tailor.selectHint': 'Choose garment type, pick one design, then assign fabric for each piece.',
    'tailor.exploreDesigns': 'Explore designs',
    'tailor.searchDesigns': 'Search designs',
    'tailor.fabricCollection': 'Fabric collection',
    'tailor.searchFabrics': 'Search fabrics',
    'tailor.pieceLabel': 'piece',
    'tailor.piecesLabel': 'pieces',
    'tailor.designLabel': 'design',
    'tailor.designsLabel': 'designs',
    'tailor.fabricLabel': 'fabric',
    'tailor.fabricsLabel': 'fabrics',
    'tailor.customizeOutfit': 'Customize your outfit',
    'tailor.chooseFabricEach': 'Choose fabric for each piece',
    'tailor.less': 'Less',
    'tailor.selectFabric': 'Select fabric',
    'tailor.fabricWord': 'Fabric',
    'tailor.chooseOneDesign': 'Choose one design',
    'tailor.selectedDesign': 'Selected design',
    'tailor.selectPiecesFirst': 'Select garment pieces first',
    'tailor.untitledFabric': 'Untitled fabric',
    // --- Room sheet
    'room.styleYour': 'Style your',
    'room.room': 'room',
    'room.leaveUnchanged': 'Leave unchanged',
    'room.refining': 'Refining…',
    'room.stylingRoom': 'Styling your room…',
    'room.change': 'Change',
    'room.selectMaterial': 'Select material',
    'room.noChange': 'No change',
    'room.untitled': 'Untitled',
    'room.untitledItem': 'Untitled item',
    'room.resetAll': 'Reset All',
    'room.stylingShort': 'Styling…',
    'room.styleRoom': 'StyleKro',
    // --- Create / open shop + settings
    'shop.openYour': 'Open your',
    'shop.shopWord': 'shop',
    'shop.location': 'Location',
    'shop.googleHint': 'Pick from Google suggestions to auto-fill shop name, phone, category & description.',
    'shop.whatSell': 'What do you sell?',
    'shop.chooseCategory': 'Choose a category',
    'shop.aboutShop': 'About your shop',
    'shop.namePlaceholder': 'e.g. Anaya Couture',
    'shop.descPlaceholder': 'A short description of your shop and vibe.',
    'shop.stitchingOptions': 'Stitching options offered',
    'shop.genderFocus': 'Who do you stitch for?',
    'shop.genderBoth': 'Men & Women',
    'shop.genderMen': 'Men',
    'shop.genderWomen': 'Women',
    'shop.genderRequired': 'Choose Men or Women for your tailor shop.',
    'shop.stitchingHint': 'Pick which garment pieces customers can ask you to stitch. Leave all unchecked to offer every option.',
    'shop.itemType': 'Item type',
    'shop.loadingDots': 'Loading…',
    'shop.placesLoading': 'Loading places…',
    'shop.searchMapsFull': 'Search your shop on Google Maps to auto-fill the rest',
    'shop.searchMapsShort': 'Start typing your shop name on Google Maps',
    'shop.useMyLocation': 'Use my current location',
    'shop.placeUnavailable': 'Place search unavailable:',
    'shop.chooseShopType': '— Choose a shop type —',
    'shop.openShop': 'Open shop',
    'shop.opening': 'Opening…',
    // --- Manage shop filters
    'mng.type': 'Type',
    'mng.gender': 'Gender',
    'mng.categories': 'Categories',
    'mng.subcategories': 'Subcategories',
    'mng.color': 'Color',
    'mng.status': 'Status',
    // --- Manage shop inventory
    'mng.dropPhotos': 'Click or drop photos',
    'mng.multiSupported': 'Multiple photos supported',
    'mng.readyUpload': 'Ready to upload',
    'mng.title': 'Title',
    'mng.priceOptional': 'Price (optional)',
    'mng.itemCodeOptional': 'Item code (optional)',
    'mng.shortDescription': 'Short description',
    'mng.inventory': 'Inventory',
    'mng.grid': 'Grid',
    'mng.list': 'List',
    'mng.searchInventory': 'Search title, price, color\u2026',
    'mng.selected': 'Selected',
    'mng.selectAll': 'Select all',
    'mng.share': 'Share',
    'mng.changePhoto': 'Change photo',
    'mng.price': 'Price',
    'mng.description': 'Description',
    'mng.photo': 'Photo',
    'mng.open': 'Open',
    'mng.tailorUploadHint': 'Tailor shop: upload each design and fabric as separate items so buyers can combine one of each.',
    'mng.pending': 'pending',
    'mng.adding': 'Adding…',
    'mng.addItem': 'Add Item',
    'mng.hideDetails': 'Hide details',
    'mng.editDetails': 'Edit details',
    'mng.addMoreDetails': 'Add more details',
    'mng.uploading': 'Uploading…',
    'mng.uploaded': 'Uploaded',
    'mng.remove': 'Remove',
    'mng.itemLabel': 'piece',
    'mng.itemsLabel': 'pieces',
    'mng.shown': 'shown',
    'mng.hidden': 'Hidden',
    'mng.genderDots': 'Gender…',
    'mng.groupDots': 'Group…',
    'mng.subDots': 'Subcategory…',
    'mng.applyTo': 'Apply to',
    'mng.shareNext': 'Share next item',
    'mng.noMatch': 'No pieces match',
    // --- Compare / looks
    'looks.stylistChat': 'stylist chat',
    'looks.savedLook': 'Saved look',
    'compare.sideBySide': 'Side-by-side',
    'compare.compareLooks': 'Compare looks',
    'compare.showingPrefix': 'Showing first 4 of',
    'compare.showingSuffix': 'selected.',
    // --- Item detail
    'item.noImage': 'No image',
    'item.showProduct': 'Show product',
    'item.onYou': 'On you',
    'item.usesProfile': 'Uses your profile photo to show this on you.',
    'item.details': 'Details',
    'item.color': 'Color',
    'item.code': 'Code',
    'item.type': 'Type',
    'item.gender': 'Gender',
    'item.about': 'About',
    'item.browseMore': 'Browse more items from this shop',
    'item.tryingOn': 'Trying it on you…',
    'item.tryingOnShort': 'Trying on…',
    'item.view360': '360° view',
    'item.colour': 'Colour',
    'item.viewShop': 'View shop',
    'item.garment': 'Garment',
    // --- Garment edit
    'edit.editItem': 'Edit item',
    'edit.product': 'Product',
    'edit.enterTitle': 'Enter title',
    'edit.productCategory': 'Product category',
    'edit.gender': 'Gender',
    'edit.noTaxonomy': 'No taxonomy loaded.',
    'edit.group': 'Group',
    'edit.pickGenderFirst': 'Pick a gender first.',
    'edit.subcategory': 'Subcategory',
    'edit.price': 'Price',
    'edit.untitledItem': 'Untitled item',
    'edit.deleteItem': 'Delete item',
    'edit.addPhotoSlot': 'Add a photo for slot',
    'edit.change': 'Change',
    'edit.addPhoto': 'Add photo',
    'edit.visible': 'Visible',
    'edit.hidden': 'Hidden',
    'edit.chooseSubcategory': 'Choose subcategory…',
    'edit.pickGroupFirst': 'Pick a group first',
    'edit.color': 'Color',
    'edit.colour': 'Colour',
    'edit.aboutItem': 'About this item',
    'edit.shortDescription': 'Short description',
    'edit.codeSku': 'Code / SKU',
    'edit.visibleToBuyers': 'Visible to buyers',
    'edit.colorVariants': 'Color variants',
    'edit.sameItemColour': 'Same item in another colour.',
    'edit.addColour': 'Add colour',
    'edit.sameAsBase': 'Same as base',
    'edit.adding': 'Adding',
    'edit.saving': 'Saving',
    'edit.saveChanges': 'Save changes',
  },
  hi: {
    'nav.chat': 'चैट',
    'nav.shops': 'दुकानें',
    'nav.looks': 'मेरे लुक्स',
    'nav.wardrobe': 'अलमारी',
    'nav.profile': 'प्रोफाइल',
    'nav.myShop': 'मेरी दुकान',
    'nav.shopSettings': 'दुकान विवरण',
    'menu.profile': 'मेरी प्रोफाइल',
    'menu.switchToBuyer': 'खरीदार मोड में जाएँ',
    'menu.switchToSeller': 'विक्रेता मोड में जाएँ',
    'menu.chooseExperience': 'अनुभव चुनें',
    'menu.experience': 'अनुभव',
    'menu.buyer': 'खरीदार',
    'menu.seller': 'विक्रेता',
    'menu.help': 'सहायता व प्रतिक्रिया',
    'menu.privacy': 'गोपनीयता नीति',
    'menu.deleteAccount': 'खाता हटाएं',
    'menu.signOut': 'साइन आउट',
    'menu.language': 'भाषा',
    'login.continue': 'जारी रखें',
    'login.signingIn': 'साइन इन हो रहा है…',
    'login.hero': 'खुद को किसी भी पोशाक में देखें',
    'login.tagline': 'आपका निजी AI स्टाइलिस्ट। एक फोटो अपलोड करें और कुछ भी खरीदने से पहले ट्राई करें।',
    'login.heading': 'जारी रखने के लिए साइन इन करें',
    'login.subheading': 'साइन इन करने का एक आसान तरीका चुनें',
    'login.emailFallback': 'या ईमेल से जारी रखें',
    'login.namePlaceholder': 'आपका नाम (नए उपयोगकर्ता)',
    'login.emailPlaceholder': 'you@example.com',
    'login.disclaimer': 'जारी रखकर आप हमारी सेवा की शर्तें और गोपनीयता नीति से सहमत होते हैं।',
    'login.helpLink': 'मदद चाहिए? हमसे संपर्क करें',
    'login.demoLabel': 'लाइव ट्राई-ऑन',
    'login.step1': 'अपनी फोटो अपलोड करें',
    'login.step2': 'कोई भी पोशाक चुनें',
    'login.step3': 'खुद को स्टाइल में देखें',
    'login.googleError': 'Google साइन-इन शुरू नहीं हो सका:',
    'login.googleSignInFailed': 'Google साइन-इन विफल रहा',
    'login.androidAppTitle': 'Stylekro Android ऐप पाएं',
    'login.androidAppBody': 'आपके फ़ोन के लिए बेहतर ट्राई-ऑन, तेज़ अपलोड और नेटिव अनुभव।',
    'login.androidAppCta': 'Play Store से इंस्टॉल करें',
    'login.androidAppDismiss': 'ब्राउज़र में जारी रखें',
    'chat.placeholder': 'वोग से कुछ भी पूछें…',
    'chat.send': 'भेजें',
    'chat.agentName': 'वोग',
    'chat.agentTitle': 'आपकी AI स्टाइलिस्ट',
    'chat.profilePhotoHint': 'प्रोफाइल फोटो उपलब्ध',
    'chat.loadingOlder': 'पुराने संदेश लोड हो रहे हैं…',
    'chat.scrollOlder': 'पुराने संदेश देखने के लिए ऊपर स्क्रॉल करें',
    'chat.refiningLook': 'Styling 360',
    'chat.copy': 'कॉपी',
    'chat.openImage': 'इमेज खोलें',
    'chat.dropHint': 'ट्राई करने के लिए कपड़े की इमेज यहाँ छोड़ें',
    'chat.remove': 'हटाएं',
    'chat.uploadOutfit': 'पोशाक की फोटो अपलोड करें',
    'chat.composerPlaceholder': 'कुछ भी पूछें। प्रोडक्ट का स्क्रीनशॉट या प्रोडक्ट लिंक पेस्ट करें।',
    'chat.sendAria': 'संदेश भेजें',
    'chat.composerHint': 'टिप: प्रोडक्ट URL पेस्ट करें, कपड़े की इमेज ड्रॉप करें, या बस कहें "इसे ट्राई करो"। खुद को देखने के लिए स्क्रीनशॉट शेयर करें।',
    'chat.download': 'डाउनलोड',
    'chat.loading': 'लोड हो रहा है…',
    'chat.generatingTryOn': 'आपका ट्राई-ऑन बनाया जा रहा है…',
    'chat.confirmClearHistory': 'सारा चैट इतिहास साफ़ करें?',
    'chat.clearHistoryError': 'इतिहास साफ़ नहीं हो सका:',
    'chat.welcome.hi': 'नमस्ते',
    'chat.welcome.body': 'मैं वोग हूँ, आपकी स्टाइलिस्ट ✨ बताइए वार्डरोब पर क्या प्लान है, या कपड़े की फोटो छोड़िए और मैं आपको स्टाइल कर दूँगी।',
    'chat.welcome.fresh': 'नई शुरुआत ✦ आज क्या स्टाइल करें?',
    'chat.suggestion1': 'डेट-नाइट आइडिया',
    'chat.suggestion1Prompt': 'पहली डेट के लिए मुझ पर कौन सी पोशाक जँचेगी?',
    'chat.suggestion2': 'मेरे फिट को रोस्ट करो',
    'chat.suggestion2Prompt': 'मेरी मौजूदा पोशाक को रोस्ट करो और बताओ कैसे सुधारूँ।',
    'chat.suggestion3': 'ऑटम कैप्सूल',
    'chat.suggestion3Prompt': 'ऑटम के लिए 7-पीस कैप्सूल बनाओ।',
    'chat.suggestion4': 'मेरे लिए बेस्ट रंग',
    'chat.suggestion4Prompt': 'मुझ पर कौन से रंग असल में जँचते हैं?',
    'chat.suggestion5': 'कौन सा लुक मुझ पर जँचता है?',
    'chat.suggestion5Prompt': 'मेरी प्रोफाइल फोटो और तुम्हारे बनाए आख़िरी ट्राई-ऑन को देखकर बताओ कौन सा लुक बेहतर लगता है और क्यों? सिल्हूट, रंग और अनुपात पर सटीक राय दो।',
    'chooseExp.title': 'आप Stylekro का उपयोग कैसे करेंगे?',
    'chooseExp.sub': 'अपने लिए सबसे अच्छा अनुभव चुनें। आप कभी भी प्रोफाइल मेनू से बदल सकते हैं।',
    'chooseExp.buyer': 'मैं एक खरीदार हूँ',
    'chooseExp.seller': 'मैं एक विक्रेता हूँ',
    'chooseExp.welcome': 'स्वागत है',
    'chooseExp.buyerHead': 'खरीदार',
    'chooseExp.buyerDesc': 'Vogue के साथ कपड़े ट्राई करें।',
    'chooseExp.sellerHead': 'विक्रेता',
    'chooseExp.sellerDesc': 'अपनी दुकान सूचीबद्ध करें और बेचें।',
    'chooseExp.continue': 'जारी रखें',
    'chooseExp.getStarted': 'शुरू करें',
    'help.aria': 'सहायता और प्रतिक्रिया',
    'help.close': 'बंद करें',
    'help.eyebrow': 'सहायता · प्रतिक्रिया',
    'help.title': 'हमें आपकी राय जानकर खुशी होगी।',
    'help.email': 'ईमेल',
    'help.phone': 'फ़ोन',
    'help.orSend': 'या प्रतिक्रिया भेजें',
    'help.thanksTitle': 'प्रतिक्रिया के लिए धन्यवाद!',
    'help.thanksMessage': 'हमें आपका संदेश मिल गया है और हम इसे ध्यान से पढ़ेंगे।',
    'help.nameLabel': 'आपका नाम (वैकल्पिक)',
    'help.namePlaceholder': 'जैसे, अंजली',
    'help.emailLabel': 'जवाब के लिए ईमेल (वैकल्पिक)',
    'help.emailPlaceholder': 'you@example.com',
    'help.messageLabel': 'संदेश',
    'help.messagePlaceholder': 'क्या मन में है? बग, आइडिया या तारीफ़ — सब स्वीकार है।',
    'help.fineprint': 'आपका संदेश सीधे टीम तक पहुँचेगा — ईमेल ऐप ज़रूरी नहीं।',
    'help.send': 'प्रतिक्रिया भेजें',
    'help.sending': 'भेजा जा रहा है…',
    'profile.nameRequired': 'पहले वोग को अपना नाम बताएँ।',
    'profile.photoRequired': 'फोटो जोड़ें ताकि वोग आपको स्टाइल कर सके।',
    'profile.tryOnPhotoRequired': 'ट्राई-ऑन बनाने से पहले अपनी प्रोफाइल फोटो जोड़ें या अपडेट करें।',
    'profile.emptyPhotos': 'अभी कोई फोटो नहीं — एक अपलोड करें और पोशाकें ट्राई करना शुरू करें।',
    'profile.zoom': 'ज़ूम करने के लिए क्लिक करें',
    'profile.primary': 'मुख्य',
    'profile.setPrimary': 'चुनें',
    'profile.active': 'सक्रिय',
    'profile.settingPrimary': 'मुख्य सेट हो रही है…',
    'profile.deletePhoto': 'फोटो हटाएं',
    'profile.displayNameLabel': 'नाम',
    'profile.displayNamePlaceholder': 'नाम',
    'profile.onboardTitle': 'अपना सिग्नेचर लुक बनाएँ',
    'profile.onboardSubtitle': 'आपकी साफ़ फोटो से आपका AI स्टाइलिस्ट देख पाता है कि आप पर कौन-सा आउटफिट कैसा लगेगा।',
    'profile.replacePhoto': 'बदलने के लिए टैप करें',
    'profile.uploadBest': 'अपनी सबसे अच्छी फोटो अपलोड करें',
    'profile.uploadHint': 'सामने का चेहरा और अच्छी रोशनी सबसे बेहतर है।',
    'profile.takeSelfie': 'सेल्फी लें',
    'profile.choosePhoto': 'अपनी फोटो जोड़ें',
    'profile.takePhoto': 'फोटो लें',
    'profile.fromGallery': 'गैलरी से चुनें',
    'profile.nameHint': 'ऐप में हम आपको इसी नाम से संबोधित करेंगे।',
    'profile.cta': 'अपने AI स्टाइलिस्ट से मिलें',
    'profile.edit': 'संपादित करें',
    'profile.delete': 'हटाएं',
    'profile.save': 'सहेजें',
    'profile.cancel': 'रद्द करें',
    'common.saving': 'सहेजा जा रहा है…',
    'common.saveChanges': 'बदलाव सहेजें',
    'common.signOut': 'साइन आउट',
    'common.uploading': 'अपलोड हो रहा है…',
    'common.cancel': 'रद्द करें',
    'common.confirm': 'पुष्टि करें',
    'common.delete': 'हटाएं',
    'common.apply': 'लागू करें',
    'common.clearAll': 'सब साफ़ करें',
    'common.clear': 'साफ़ करें',
    'common.all': 'सभी',
    'common.more': 'और',
    'common.filters': 'फ़िल्टर',
    'common.openFilters': 'फ़िल्टर खोलें',
    'common.upload': 'अपलोड',
    'feedback.good': 'बढ़िया परिणाम',
    'feedback.bad': 'अच्छा नहीं',
    'shops.noShopsFound': 'कोई दुकान नहीं मिली',
    'shops.tryDifferent': 'कोई अलग खोज शब्द आज़माएँ।',
    'shops.beFirst': 'पहले बनें — एक दुकान खोलें और पीस जोड़ना शुरू करें।',
    'shops.clearSearch': 'खोज साफ़ करें',
    'shops.searchPlaceholder': 'दुकानें, जगहें, स्टाइल खोजें…',
    'shops.noMatch': 'आपके फ़िल्टर से कोई दुकान मेल नहीं खाती',
    'shops.tryDifferentFilter': 'दूसरा फ़िल्टर आज़माएँ या सभी दुकानें देखने के लिए साफ़ करें।',
    'shops.clearFilters': 'फ़िल्टर साफ़ करें',
    'shops.explore': 'देखें →',
    'shop.searchPlaceholder': 'पोशाकें, रंग, स्टाइल खोजें…',
    'shop.shareAria': 'यह दुकान शेयर करें',
    'shop.eyebrow': 'दुकान',
    'shop.pickDesignFabric': 'एक डिज़ाइन और कम-से-कम एक कपड़ा चुनें।',
    'shop.noPiecesYet': 'अभी कोई पीस नहीं',
    'shop.checkBackSoon': 'इस दुकान ने अभी कोई पीस नहीं जोड़ा। बाद में देखें।',
    'shop.noMatchingPieces': 'आपके फ़िल्टर से कोई पीस मेल नहीं खाता।',
    'shop.noMatchingDesigns': 'आपके फ़िल्टर से कोई डिज़ाइन मेल नहीं खाता।',
    'shop.noMatchingFabrics': 'आपके फ़िल्टर से कोई कपड़ा मेल नहीं खाता।',
    'shop.notFound': 'दुकान नहीं मिली।',
    'shop.saveFirst': 'पहले अपनी दुकान सहेजें।',
    'shop.confirmRemoveBanner': 'बैनर फोटो हटाएं?',
    'shop.nameRequired': 'दुकान का नाम ज़रूरी है।',
    'shop.categoryRequired': 'अपनी दुकान प्रकाशित करने से पहले दुकान का प्रकार चुनें।',
    'shop.uploadBanner': 'बैनर अपलोड करें',
    'shop.shopName': 'दुकान का नाम',
    'shop.phone': 'फ़ोन',
    'shop.address': 'पता',
    'shop.description': 'विवरण',
    'shop.shopType': 'दुकान का प्रकार',
    'shop.public': 'सार्वजनिक',
    'shop.private': 'निजी',
    'shop.locate': 'स्थान खोजें',
    'shop.locating': 'खोजा जा रहा है…',
    'shop.banner': 'दुकान बैनर',
    'shop.bannerSubtitle': 'दुकानों की ग्रिड पर आपकी दुकान के पीछे दिखाई देगा',
    'shop.bannerLockedTitle': 'पहले अपनी दुकान सहेजें',
    'shop.bannerLockedHint': 'नीचे अपनी दुकान बनाएं, फिर यहाँ बैनर जोड़ें',
    'shop.change': '✎ बदलें',
    'shop.remove': '🗑 हटाएं',
    'shop.working': 'चल रहा है…',
    'shop.shopDetailsTitle': 'दुकान',
    'shop.shopDetailsTitleGrad': 'विवरण',
    'shop.share': '🔗 शेयर',
    'shop.copied': '✓ कॉपी हो गया',
    'shop.saved': '✓ सहेजा गया',
    'shop.saving': 'सहेजा जा रहा है…',
    'shop.eyebrowStudio': 'AI स्टूडियो',
    'shop.noPiecesUpload': 'अभी कोई पीस नहीं — बाईं ओर के अपलोड बॉक्स में फोटो छोड़ें।',
    'shop.titleRequired': 'शीर्षक खाली नहीं हो सकता।',
    'shop.confirmRemovePiece': 'यह पीस अपनी दुकान से हटाएं?',
    'shop.save': 'बदलाव सहेजें ✨',
    'shop.publish': 'दुकान सहेजें',
    'shop.open': 'दुकान खोलें',
    'shop.noMatchingSearch': '"{search}" से कोई पीस मेल नहीं खाता।',
    'shop.bulkDrop': 'फोटो क्लिक या ड्रॉप करें',
    'shop.bulkMulti': 'कई फ़ाइलें समर्थित',
    'shop.bulkTailorHint': 'टेलर दुकान: हर डिज़ाइन और कपड़ा अलग-अलग आइटम के रूप में अपलोड करें ताकि खरीदार एक-एक मिला सकें।',
    'shop.bulkPending': '{count} लंबित',
    'shop.bulkReview': 'समीक्षा और अपलोड करें',
    'tailor.tapFlip': 'पलटने के लिए टैप करें',
    'tailor.zoom': 'ज़ूम',
    'tailor.quickPreview': 'त्वरित पूर्वावलोकन',
    'tailor.original': 'मूल',
    'looks.empty': 'अभी कोई लुक नहीं',
    'looks.emptyHint': 'किसी दुकान से पीस चुनें या स्टाइलिस्ट से बात करके अपना पहला ट्राई-ऑन बनाएँ।',
    'looks.noMatching': 'आपके फ़िल्टर से कोई लुक मेल नहीं खाता।',
    'looks.browseShops': 'दुकानें देखें',
    'looks.chatStylist': 'स्टाइलिस्ट से चैट करें',
    'looks.deleteOne': 'इस लुक को हटाएं?',
    'looks.deleteMany': '{count} लुक हटाएं?',
    'looks.couldNotDelete': '{count} लुक हटाई नहीं जा सकी।',
    'looks.searchPlaceholder': 'आउटफिट, स्टाइल खोजें...',
    'looks.filters': 'फ़िल्टर',
    'looks.clearAll': 'सभी हटाएं',
    'looks.source': 'स्रोत',
    'looks.all': 'सभी',
    'looks.shops': '🛍 दुकानें',
    'looks.chat': '💬 चैट',
    'looks.shopsHeader': 'दुकानें',
    'looks.sort': 'क्रम',
    'looks.newestFirst': 'नई पहले',
    'looks.oldestFirst': 'पुरानी पहले',
    'looks.apply': 'लागू करें',
    'looks.selected': 'चुने गए',
    'looks.share': 'शेयर',
    'looks.delete': 'हटाएं',
    'looks.compare': 'तुलना करें',
    'looks.copyLink': 'लिंक कॉपी करें',
    'looks.tryon': 'ट्राई-ऑन',
    'looks.tapToSelect': 'चुनने के लिए टैप करें',
    'looks.clickToView': 'देखने के लिए क्लिक करें',
    'common.shop': 'दुकान',
    'common.chat': 'चैट',
    'wardrobe.confirmRemove': 'यह आइटम अपनी अलमारी से हटाएं?',
    'wardrobe.pickOne': 'अपनी अलमारी से कम-से-कम एक आइटम चुनें।',
    'wardrobe.uploading': 'अपलोड हो रहा है…',
    'wardrobe.addClothing': 'कपड़े या ऐक्सेसरीज़ जोड़ें',
    'wardrobe.uploadSubtitle': 'कैमरा या गैलरी · AI द्वारा स्वतः श्रेणीबद्ध',
    'wardrobe.category': 'श्रेणी',
    'wardrobe.all': 'सभी',
    'wardrobe.loading': 'आपकी अलमारी लोड हो रही है…',
    'wardrobe.empty': 'आपकी अलमारी खाली है',
    'wardrobe.emptyHint': 'लुक मिक्स एंड मैच के लिए अपना पहला आइटम जोड़ें।',
    'wardrobe.uploadNow': 'अभी अपलोड करें',
    'wardrobe.stylekro': 'Stylekro',
    'wardrobe.loadingBtn': 'लोड हो रहा है…',
    'wardrobe.dressingUp': 'आपको तैयार किया जा रहा है',
    'wardrobe.download': 'डाउनलोड',
    'wardrobe.items': 'आइटम',
    'wardrobe.itemsPlural': 'आइटम',
    'privacy.statement': 'हम आपका निजी डेटा नहीं बेचते, और तीसरे-पक्ष के विज्ञापन नहीं दिखाते।',
    'common.back': '← वापस',
    'privacy.title': 'गोपनीयता नीति',
    'deleteAcct.title': 'अपना Stylekro खाता हटाएं',
    'deleteAcct.subtitle': 'अपना खाता और सभी संबंधित डेटा स्थायी रूप से हटाएं।',
    'boot.loading': 'लोड हो रहा है…',
    // --- Shops filter rail (desktop)
    'shops.categoryRail': 'श्रेणी',
    'shops.inventoryRail': 'इन्वेंटरी',
    'shops.anySize': 'कोई भी संख्या',
    'shops.onePlus': '1+ आइटम',
    'shops.fivePlus': '5+ आइटम',
    'shops.twentyPlus': '20+ आइटम',
    // --- Login extras
    'login.eyebrow': 'Stylekro AI ट्राय-ऑन',
    'login.stepLabel': 'चरण',
    'login.googleBtn': 'Google से जारी रखें',
    'login.googleNotConfigured': 'Google साइन-इन कॉन्फ़िगर नहीं है।',
    // --- Filter sheet
    'filter.title': 'फ़िल्टर',
    'filter.category': 'श्रेणी',
    'filter.subcategory': 'उप-श्रेणी',
    // --- Tailor picker
    'tailor.chooseType': 'परिधान का प्रकार चुनें',
    'tailor.outfitSetup': 'आउटफिट सेटअप',
    'tailor.customizePieces': 'अपने आउटफिट के हर हिस्से को कस्टमाइज़ करें',
    'tailor.selectDesignFabric': 'डिज़ाइन और फ़ैब्रिक चुनें',
    'tailor.selectHint': 'परिधान का प्रकार चुनें, एक डिज़ाइन चुनें, फिर हर हिस्से के लिए फ़ैब्रिक तय करें।',
    'tailor.exploreDesigns': 'डिज़ाइन देखें',
    'tailor.searchDesigns': 'डिज़ाइन खोजें',
    'tailor.fabricCollection': 'फ़ैब्रिक कलेक्शन',
    'tailor.searchFabrics': 'फ़ैब्रिक खोजें',
    'tailor.pieceLabel': 'पीस',
    'tailor.piecesLabel': 'पीस',
    'tailor.designLabel': 'डिज़ाइन',
    'tailor.designsLabel': 'डिज़ाइन',
    'tailor.fabricLabel': 'फ़ैब्रिक',
    'tailor.fabricsLabel': 'फ़ैब्रिक',
    'tailor.customizeOutfit': 'अपना आउटफिट कस्टमाइज़ करें',
    'tailor.chooseFabricEach': 'हर हिस्से के लिए फ़ैब्रिक चुनें',
    'tailor.less': 'कम',
    'tailor.selectFabric': 'फ़ैब्रिक चुनें',
    'tailor.fabricWord': 'फ़ैब्रिक',
    'tailor.chooseOneDesign': 'एक डिज़ाइन चुनें',
    'tailor.selectedDesign': 'चयनित डिज़ाइन',
    'tailor.selectPiecesFirst': 'पहले परिधान के हिस्से चुनें',
    'tailor.untitledFabric': 'बिना शीर्षक फ़ैब्रिक',
    // --- Room sheet
    'room.styleYour': 'सजाएँ अपना',
    'room.room': 'कमरा',
    'room.leaveUnchanged': 'अपरिवर्तित रखें',
    'room.refining': 'सुधारा जा रहा है…',
    'room.stylingRoom': 'आपका कमरा सजाया जा रहा है…',
    'room.change': 'बदलें',
    'room.selectMaterial': 'सामग्री चुनें',
    'room.noChange': 'कोई बदलाव नहीं',
    'room.untitled': 'बिना शीर्षक',
    'room.untitledItem': 'बिना शीर्षक आइटम',
    'room.resetAll': 'सब रीसेट करें',
    'room.stylingShort': 'सजाया जा रहा है…',
    'room.styleRoom': 'StyleKro',
    // --- Create / open shop + settings
    'shop.openYour': 'खोलें अपनी',
    'shop.shopWord': 'दुकान',
    'shop.location': 'स्थान',
    'shop.googleHint': 'दुकान का नाम, फ़ोन, श्रेणी और विवरण अपने-आप भरने के लिए Google सुझावों में से चुनें।',
    'shop.whatSell': 'आप क्या बेचते हैं?',
    'shop.chooseCategory': 'एक श्रेणी चुनें',
    'shop.aboutShop': 'आपकी दुकान के बारे में',
    'shop.namePlaceholder': 'जैसे आन्या कॉउचर',
    'shop.descPlaceholder': 'आपकी दुकान और उसके अंदाज़ का संक्षिप्त विवरण।',
    'shop.stitchingOptions': 'दी जाने वाली सिलाई विकल्प',
    'shop.genderFocus': 'आप किसके लिए सिलाई करते हैं?',
    'shop.genderBoth': 'पुरुष और महिला',
    'shop.genderMen': 'पुरुष',
    'shop.genderWomen': 'महिला',
    'shop.genderRequired': 'अपनी टेलर दुकान के लिए पुरुष या महिला चुनें।',
    'shop.stitchingHint': 'चुनें कि ग्राहक आपसे कौन-से हिस्से सिलवा सकते हैं। हर विकल्प देने के लिए सभी अनचेक छोड़ दें।',
    'shop.itemType': 'आइटम प्रकार',
    'shop.loadingDots': 'लोड हो रहा है…',
    'shop.placesLoading': 'स्थान लोड हो रहे हैं…',
    'shop.searchMapsFull': 'बाकी अपने-आप भरने के लिए Google Maps पर अपनी दुकान खोजें',
    'shop.searchMapsShort': 'Google Maps पर अपनी दुकान का नाम टाइप करना शुरू करें',
    'shop.useMyLocation': 'मेरा वर्तमान स्थान उपयोग करें',
    'shop.placeUnavailable': 'स्थान खोज उपलब्ध नहीं:',
    'shop.chooseShopType': '— दुकान का प्रकार चुनें —',
    'shop.openShop': 'दुकान खोलें',
    'shop.opening': 'खोला जा रहा है…',
    // --- Manage shop filters
    'mng.type': 'प्रकार',
    'mng.gender': 'लिंग',
    'mng.categories': 'श्रेणियाँ',
    'mng.subcategories': 'उप-श्रेणियाँ',
    'mng.color': 'रंग',
    'mng.status': 'स्थिति',
    // --- Manage shop inventory
    'mng.dropPhotos': 'फ़ोटो क्लिक करें या यहाँ छोड़ें',
    'mng.multiSupported': 'एक से अधिक फ़ोटो समर्थित',
    'mng.readyUpload': 'अपलोड के लिए तैयार',
    'mng.title': 'शीर्षक',
    'mng.priceOptional': 'कीमत (वैकल्पिक)',
    'mng.itemCodeOptional': 'आइटम कोड (वैकल्पिक)',
    'mng.shortDescription': 'संक्षिप्त विवरण',
    'mng.inventory': 'इन्वेंटरी',
    'mng.grid': 'ग्रिड',
    'mng.list': 'सूची',
    'mng.searchInventory': 'शीर्षक, कीमत, रंग खोजें…',
    'mng.selected': 'चयनित',
    'mng.selectAll': 'सभी चुनें',
    'mng.share': 'शेयर करें',
    'mng.changePhoto': 'फ़ोटो बदलें',
    'mng.price': 'कीमत',
    'mng.description': 'विवरण',
    'mng.photo': 'फ़ोटो',
    'mng.open': 'खोलें',
    'mng.tailorUploadHint': 'टेलर शॉप: हर डिज़ाइन और फ़ैब्रिक को अलग आइटम के रूप में अपलोड करें ताकि खरीदार हर एक चुन सकें।',
    'mng.pending': 'लंबित',
    'mng.adding': 'जोड़ा जा रहा है…',
    'mng.addItem': 'आइटम जोड़ें',
    'mng.hideDetails': 'विवरण छिपाएं',
    'mng.editDetails': 'विवरण संपादित करें',
    'mng.addMoreDetails': 'और विवरण जोड़ें',
    'mng.uploading': 'अपलोड हो रहा है…',
    'mng.uploaded': 'अपलोड हो गया',
    'mng.remove': 'हटाएं',
    'mng.itemLabel': 'आइटम',
    'mng.itemsLabel': 'आइटम',
    'mng.shown': 'दिखाए गए',
    'mng.hidden': 'छिपा',
    'mng.genderDots': 'लिंग…',
    'mng.groupDots': 'समूह…',
    'mng.subDots': 'उप-श्रेणी…',
    'mng.applyTo': 'इन पर लागू करें',
    'mng.shareNext': 'अगला आइटम शेयर करें',
    'mng.noMatch': 'कोई आइटम मेल नहीं खाता',
    // --- Compare / looks
    'looks.stylistChat': 'स्टाइलिस्ट चैट',
    'looks.savedLook': 'सेव किया लुक',
    'compare.sideBySide': 'साथ-साथ',
    'compare.compareLooks': 'लुक की तुलना करें',
    'compare.showingPrefix': 'चुने गए',
    'compare.showingSuffix': 'में से पहले 4 दिखाए जा रहे हैं।',
    // --- Item detail
    'item.noImage': 'कोई छवि नहीं',
    'item.showProduct': 'उत्पाद दिखाएँ',
    'item.onYou': 'आप पर',
    'item.usesProfile': 'यह आप पर दिखाने के लिए आपकी प्रोफ़ाइल फ़ोटो का उपयोग करता है।',
    'item.details': 'विवरण',
    'item.color': 'रंग',
    'item.code': 'कोड',
    'item.type': 'प्रकार',
    'item.gender': 'लिंग',
    'item.about': 'के बारे में',
    'item.browseMore': 'इस दुकान के और आइटम देखें',
    'item.tryingOn': 'आप पर आज़माया जा रहा है…',
    'item.tryingOnShort': 'आज़माया जा रहा है…',
    'item.view360': '360° व्यू',
    'item.colour': 'रंग',
    'item.viewShop': 'दुकान देखें',
    'item.garment': 'गारमेंट',
    // --- Garment edit
    'edit.editItem': 'आइटम संपादित करें',
    'edit.product': 'उत्पाद',
    'edit.enterTitle': 'शीर्षक दर्ज करें',
    'edit.productCategory': 'उत्पाद श्रेणी',
    'edit.gender': 'लिंग',
    'edit.noTaxonomy': 'कोई वर्गीकरण लोड नहीं हुआ।',
    'edit.group': 'समूह',
    'edit.pickGenderFirst': 'पहले लिंग चुनें।',
    'edit.subcategory': 'उप-श्रेणी',
    'edit.price': 'कीमत',
    'edit.untitledItem': 'बिना शीर्षक आइटम',
    'edit.deleteItem': 'आइटम हटाएं',
    'edit.addPhotoSlot': 'स्लॉट के लिए फ़ोटो जोड़ें',
    'edit.change': 'बदलें',
    'edit.addPhoto': 'फ़ोटो जोड़ें',
    'edit.visible': 'दिखाई दे',
    'edit.hidden': 'छिपा',
    'edit.chooseSubcategory': 'उप-श्रेणी चुनें…',
    'edit.pickGroupFirst': 'पहले समूह चुनें',
    'edit.color': 'रंग',
    'edit.colour': 'रंग',
    'edit.aboutItem': 'इस आइटम के बारे में',
    'edit.shortDescription': 'संक्षिप्त विवरण',
    'edit.codeSku': 'कोड / SKU',
    'edit.visibleToBuyers': 'खरीदारों को दिखाई दे',
    'edit.colorVariants': 'रंग वैरिएंट',
    'edit.sameItemColour': 'वही आइटम दूसरे रंग में।',
    'edit.addColour': 'रंग जोड़ें',
    'edit.sameAsBase': 'बेस के समान',
    'edit.adding': 'जोड़ा जा रहा है',
    'edit.saving': 'सेव हो रहा है',
    'edit.saveChanges': 'बदलाव सेव करें',
  },
};
function makeT(lang) {
  return (key) => (STRINGS[lang] && STRINGS[lang][key]) || STRINGS.en[key] || key;
}

// ===== API helper =====
async function api(path, opts = {}, token) {
  const headers = opts.headers || {};
  if (token) headers['Authorization'] = `Bearer ${token}`;
  const res = await fetch(`${API}${path}`, { ...opts, headers });
  if (!res.ok) {
    const text = await res.text();
    throw new Error(text || `${res.status} ${res.statusText}`);
  }
  const ct = res.headers.get('content-type') || '';
  return ct.includes('application/json') ? res.json() : res;
}

// ===== Streaming chat helper (SSE over POST) =====
async function streamSSE(path, { body, token, onEvent, headers: extraHeaders } = {}) {
  const headers = { ...(extraHeaders || {}) };
  // A string body is always JSON for our endpoints. If no caller set a
  // Content-Type, the browser would default to text/plain, which FastAPI
  // ignores (body -> None). Force JSON here so every JSON path is parsed,
  // regardless of caller. FormData bodies (chat, tattoo) are objects, not
  // strings, so they keep their own multipart type (browser sets the boundary).
  if (typeof body === 'string' && !Object.keys(headers).some((k) => k.toLowerCase() === 'content-type')) {
    headers['Content-Type'] = 'application/json';
  }
  const authToken = token || localStorage.getItem('styleyou_token');
  if (authToken) headers['Authorization'] = `Bearer ${authToken}`;
  else console.warn('[streamSSE] no auth token for', path);
  const res = await fetch(`${API}${path}`, { method: 'POST', body, headers });
  if (!res.ok) {
    const text = await res.text();
    throw new Error(text || `${res.status} ${res.statusText}`);
  }
  const reader = res.body.getReader();
  const decoder = new TextDecoder();
  let buf = '';
  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    buf += decoder.decode(value, { stream: true });
    let idx;
    while ((idx = buf.indexOf('\n\n')) !== -1) {
      const block = buf.slice(0, idx);
      buf = buf.slice(idx + 2);
      let evt = 'message';
      let dataStr = '';
      for (const line of block.split('\n')) {
        if (line.startsWith('event:')) evt = line.slice(6).trim();
        else if (line.startsWith('data:')) dataStr += line.slice(5).trim();
      }
      let data = {};
      try { data = dataStr ? JSON.parse(dataStr) : {}; } catch { /* ignore */ }
      if (evt === 'done') return;
      onEvent(evt, data);
    }
  }
}

async function streamChat(formData, token, onEvent) {
  return streamSSE('/chat/stream', { body: formData, token, onEvent });
}

// Poll a backend job until it's done or errors. `apiCall` is the auth-bound
// helper from useAuth (so we don't have to thread the token through).
// Resolves with the job's `result`; rejects on `error` status or network fail.
async function pollJob(apiCall, jobId, { intervalMs = 1500, signal, onUpdate } = {}) {
  while (true) {
    if (signal?.aborted) throw new Error('cancelled');
    const j = await apiCall(`/jobs/${jobId}`);
    onUpdate?.(j);
    if (j.status === 'done') return j.result;
    if (j.status === 'error') throw new Error(j.error || 'job failed');
    await new Promise((r) => setTimeout(r, intervalMs));
  }
}

// ===== Auth context =====
const AuthCtx = createContext(null);

function AuthProvider({ children }) {
  const [token, setToken] = useState(() => localStorage.getItem('styleyou_token'));
  const [user, setUser] = useState(null);
  const [profile, setProfile] = useState(null);
  const [ready, setReady] = useState(false);
  const [photoVersion, setPhotoVersion] = useState(() => Date.now());
  const [experience, setExperienceState] = useState(() => {
    const v = localStorage.getItem('stylekro_experience');
    return v === 'seller' || v === 'buyer' ? v : null;
  });

  const setExperience = useCallback((next) => {
    if (next === 'seller' || next === 'buyer') {
      localStorage.setItem('stylekro_experience', next);
      setExperienceState(next);
    } else {
      localStorage.removeItem('stylekro_experience');
      setExperienceState(null);
    }
  }, []);

  const [lang, setLangState] = useState(() => {
    const v = localStorage.getItem('stylekro_lang');
    return LANGS.some((l) => l.code === v) ? v : 'en';
  });
  const setLang = useCallback((next) => {
    if (LANGS.some((l) => l.code === next)) {
      localStorage.setItem('stylekro_lang', next);
      setLangState(next);
      try { document.documentElement.setAttribute('lang', next); } catch {}
    }
  }, []);
  useEffect(() => { try { document.documentElement.setAttribute('lang', lang); } catch {} }, [lang]);
  const t = useCallback(makeT(lang), [lang]);

  // Hydrate user from token on first load.
  useEffect(() => {
    let cancelled = false;
    (async () => {
      if (!token) { setReady(true); return; }
      try {
        const me = await api('/auth/me', {}, token);
        if (cancelled) return;
        setUser(me);
        try {
          const p = await api('/profiles/me', {}, token);
          if (!cancelled) setProfile(p);
        } catch { /* no profile yet */ }
      } catch {
        localStorage.removeItem('styleyou_token');
        if (!cancelled) { setToken(null); setUser(null); }
      } finally {
        if (!cancelled) setReady(true);
      }
    })();
    return () => { cancelled = true; };
  }, []); // run once

  const signIn = useCallback((data) => {
    localStorage.setItem('styleyou_token', data.token);
    setToken(data.token);
    setUser(data);
  }, []);

  const signOut = useCallback(() => {
    localStorage.removeItem('styleyou_token');
    localStorage.removeItem('stylekro_experience');
    setToken(null);
    setUser(null);
    setProfile(null);
    setExperienceState(null);
  }, []);

  const refreshProfile = useCallback(async () => {
    if (!token) return null;
    const p = await api('/profiles/me', {}, token);
    setProfile(p);
    setPhotoVersion(Date.now());
    return p;
  }, [token]);

  const apiCall = useCallback((path, opts) => api(path, opts, token), [token]);

  return (
    <AuthCtx.Provider value={{ token, user, profile, ready, signIn, signOut, refreshProfile, apiCall, setProfile, experience, setExperience, lang, setLang, t, photoVersion, bumpPhotoVersion: () => setPhotoVersion(Date.now()) }}>
      {children}
    </AuthCtx.Provider>
  );
}

const useAuth = () => useContext(AuthCtx);

const ConfirmCtx = createContext(async (message) => {
  if (typeof window !== 'undefined' && typeof window.confirm === 'function') return window.confirm(message);
  return false;
});

function ConfirmProvider({ children }) {
  const { t } = useAuth();
  const resolverRef = useRef(null);
  const [dialog, setDialog] = useState({ open: false, message: '', confirmLabel: '', tone: 'default' });

  const closeDialog = useCallback((answer) => {
    const resolve = resolverRef.current;
    resolverRef.current = null;
    setDialog((cur) => ({ ...cur, open: false }));
    if (resolve) resolve(answer);
  }, []);

  const confirmAction = useCallback((message, options = {}) => {
    if (resolverRef.current) {
      resolverRef.current(false);
      resolverRef.current = null;
    }
    setDialog({
      open: true,
      message,
      confirmLabel: options.confirmLabel || t('common.confirm'),
      tone: options.tone || 'default',
    });
    return new Promise((resolve) => {
      resolverRef.current = resolve;
    });
  }, [t]);

  useEffect(() => {
    if (!dialog.open) return undefined;
    function onKeyDown(e) {
      if (e.key === 'Escape') {
        e.preventDefault();
        closeDialog(false);
      }
    }
    document.addEventListener('keydown', onKeyDown);
    return () => document.removeEventListener('keydown', onKeyDown);
  }, [dialog.open, closeDialog]);

  return (
    <ConfirmCtx.Provider value={confirmAction}>
      {children}
      {dialog.open && typeof document !== 'undefined' && ReactDOM.createPortal(
        <div className="confirm-modal" role="dialog" aria-modal="true" aria-labelledby="confirm-dialog-title">
          <div className="confirm-backdrop" onClick={() => closeDialog(false)} />
          <div className={`confirm-card${dialog.tone === 'danger' ? ' danger' : ''}`}>
            <h3 id="confirm-dialog-title">{t('common.confirm')}</h3>
            <p className="confirm-message">{dialog.message}</p>
            <div className="confirm-actions">
              <button type="button" className="btn btn-ghost" onClick={() => closeDialog(false)}>
                {t('common.cancel')}
              </button>
              <button
                type="button"
                className={`btn btn-primary${dialog.tone === 'danger' ? ' confirm-danger' : ''}`}
                onClick={() => closeDialog(true)}
              >
                {dialog.confirmLabel}
              </button>
            </div>
          </div>
        </div>,
        document.body,
      )}
    </ConfirmCtx.Provider>
  );
}

const useConfirmDialog = () => useContext(ConfirmCtx);

function useRequireProfilePhoto() {
  const { profile, user, refreshProfile, t } = useAuth();
  const confirmAction = useConfirmDialog();
  const navigate = useNavigate();

  return useCallback(async () => {
    let currentProfile = profile;
    if (!currentProfile && user?.has_profile) {
      try {
        currentProfile = await refreshProfile();
      } catch {}
    }
    if (currentProfile?.photo_url) return true;
    const openProfile = await confirmAction(t('profile.tryOnPhotoRequired'), {
      confirmLabel: t('profile.choosePhoto'),
    });
    if (openProfile) navigate('/profile');
    return false;
  }, [confirmAction, navigate, profile, refreshProfile, t, user?.has_profile]);
}

// ===== Protected route =====
function Protected({ children, experience: requiredExp }) {
  const { token, user, profile, ready, experience, t } = useAuth();
  if (!ready) return <div className="boot-splash">{t('boot.loading')}</div>;
  if (!token || !user) return <Navigate to="/login" replace />;
  if (requiredExp && !experience) {
    return <Navigate to="/choose-experience" replace />;
  }
  if (requiredExp && experience && experience !== requiredExp) {
    return <Navigate to={experience === 'seller' ? '/shop/manage' : '/shops'} replace />;
  }
  return children;
}

// ===== Help & Feedback drawer (shared) =====
function HelpDrawer({ open, onClose }) {
  const { token, t } = useAuth();
  const [fbName, setFbName] = useState('');
  const [fbEmail, setFbEmail] = useState('');
  const [fbMsg, setFbMsg] = useState('');
  const [fbState, setFbState] = useState('idle'); // 'idle' | 'sending' | 'sent' | 'error'
  const [fbError, setFbError] = useState('');

  useEffect(() => {
    if (!open) return;
    function onKey(e) { if (e.key === 'Escape') onClose(); }
    document.addEventListener('keydown', onKey);
    return () => document.removeEventListener('keydown', onKey);
  }, [open, onClose]);

  if (!open) return null;
  const drawer = (
    <>
      <div className="help-backdrop" onClick={onClose} />
      <aside className="help-drawer" role="dialog" aria-label={t('help.aria')}>
        <button
          type="button"
          className="help-close"
          aria-label={t('help.close')}
          onClick={onClose}
        >×</button>
        <div className="help-eyebrow">{t('help.eyebrow')}</div>
        <h2 className="help-title">{t('help.title')}</h2>
        <p className="help-sub">
          Reach the team directly, or drop a note below — we read every message.
        </p>

        <div className="help-contacts">
          <a className="help-contact" href="mailto:stylekrohelp@gmail.com">
            <span className="help-contact-icon" aria-hidden>
              <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
                <rect x="3" y="5" width="18" height="14" rx="2"/>
                <path d="m3 7 9 6 9-6"/>
              </svg>
            </span>
            <div>
              <span className="help-contact-label">{t('help.email')}</span>
              <strong>stylekrohelp@gmail.com</strong>
            </div>
          </a>
          <a className="help-contact" href="tel:+918796596946">
            <span className="help-contact-icon" aria-hidden>
              <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
                <path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92Z"/>
              </svg>
            </span>
            <div>
              <span className="help-contact-label">{t('help.phone')}</span>
              <strong>+91-87965-96946</strong>
            </div>
          </a>
        </div>

        <div className="help-divider"><span>{t('help.orSend')}</span></div>

        {fbState === 'sent' ? (
          <div className="help-thanks" role="status" aria-live="polite">
            <div className="help-thanks-icon" aria-hidden>✓</div>
            <h3 className="help-thanks-title">{t('help.thanksTitle')}</h3>
            <p className="help-thanks-sub">{t('help.thanksMessage')}</p>
          </div>
        ) : (
        <form
          className="help-form"
          onSubmit={async (e) => {
            e.preventDefault();
            if (!fbMsg.trim() || fbState === 'sending') return;
            setFbState('sending');
            setFbError('');
            try {
              await api('/feedback', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({
                  name: fbName.trim() || null,
                  email: fbEmail.trim() || null,
                  message: fbMsg.trim(),
                  page: typeof location !== 'undefined' ? (location.hash || location.pathname) : null,
                }),
              }, token);
              setFbState('sent');
              setFbName(''); setFbEmail(''); setFbMsg('');
              setTimeout(() => setFbState('idle'), 4000);
            } catch (err) {
              setFbState('error');
              setFbError(errMsg(err));
            }
          }}
        >
          <div className="field">
            <label className="field-label">{t('help.nameLabel')}</label>
            <input
              type="text"
              value={fbName}
              onChange={(e) => setFbName(e.target.value)}
              placeholder={t('help.namePlaceholder')}
            />
          </div>
          <div className="field">
            <label className="field-label">{t('help.emailLabel')}</label>
            <input
              type="email"
              value={fbEmail}
              onChange={(e) => setFbEmail(e.target.value)}
              placeholder={t('help.emailPlaceholder')}
            />
          </div>
          <div className="field">
            <label className="field-label">{t('help.messageLabel')}</label>
            <textarea
              rows={5}
              required
              value={fbMsg}
              onChange={(e) => setFbMsg(e.target.value)}
              placeholder={t('help.messagePlaceholder')}
            />
          </div>
          <button
            type="submit"
            className="btn btn-primary btn-lg"
            disabled={fbState === 'sending' || !fbMsg.trim()}
          >
            {fbState === 'sending' ? t('help.sending') : fbState === 'sent' ? t('help.thanksTitle') : t('help.send')}
          </button>
          {fbError && <p className="error" style={{ marginTop: 8 }}>{fbError}</p>}
          <p className="help-fineprint">{t('help.fineprint')}</p>
        </form>
        )}
      </aside>
    </>
  );
  return ReactDOM.createPortal(drawer, document.body);
}

// ===== UserChip =====
function UserChip() {
  const { user, profile, signOut, experience, setExperience, lang, setLang, t, photoVersion } = useAuth();
  const navigate = useNavigate();
  const [open, setOpen] = useState(false);
  const [helpOpen, setHelpOpen] = useState(false);
  const ref = useRef(null);

  useEffect(() => {
    if (!open) return;
    function onDoc(e) {
      if (ref.current && !ref.current.contains(e.target)) setOpen(false);
    }
    function onKey(e) { if (e.key === 'Escape') setOpen(false); }
    document.addEventListener('mousedown', onDoc);
    document.addEventListener('keydown', onKey);
    return () => {
      document.removeEventListener('mousedown', onDoc);
      document.removeEventListener('keydown', onKey);
    };
  }, [open]);

  if (!user) return null;
  const initial = (user.name || user.email || '?')[0].toUpperCase();
  const photoSrc = profile?.photo_url ? bust(imgUrl(profile.photo_url), photoVersion) : null;
  return (
    <div className={`user-chip${open ? ' open' : ''}`} ref={ref}>
      <button type="button" className="user-chip-btn" onClick={() => setOpen((v) => !v)} aria-haspopup="menu" aria-expanded={open}>
        <div className="avatar">{photoSrc ? <img src={photoSrc} alt="" /> : initial}</div>
        <span>{user.name || user.email}</span>
      </button>
      {open && (
        <div className="user-menu user-menu-v2" role="menu">
          <div className="user-menu-arrow" aria-hidden />
          <button type="button" role="menuitem" className="user-menu-header"
            onClick={() => { setOpen(false); navigate('/profile'); }}>
            <div className="user-menu-header-avatar">{photoSrc ? <img src={photoSrc} alt="" /> : initial}</div>
            <div className="user-menu-header-text">
              <strong>{t('menu.profile')}</strong>
              <small>{user.email}</small>
            </div>
            <span className="user-menu-header-chev" aria-hidden>›</span>
          </button>

          <div className="user-menu-section" role="none">
            <span className="user-menu-label">{t('menu.experience')}</span>
            <div className="user-menu-exp">
              <button
                type="button"
                role="menuitemradio"
                aria-checked={experience === 'buyer'}
                className={`exp-card${experience === 'buyer' ? ' active' : ''}`}
                onClick={() => {
                  setExperience('buyer');
                  setOpen(false);
                  navigate('/shops', { replace: true });
                }}
              >
                <span className="exp-icon">🛍️</span>
                <span className="exp-label">{t('menu.buyer')}</span>
                {experience === 'buyer' && <span className="exp-check" aria-hidden>✓</span>}
              </button>
              <button
                type="button"
                role="menuitemradio"
                aria-checked={experience === 'seller'}
                className={`exp-card${experience === 'seller' ? ' active' : ''}`}
                onClick={() => {
                  setExperience('seller');
                  setOpen(false);
                  navigate('/shop/manage', { replace: true });
                }}
              >
                <span className="exp-icon">🏪</span>
                <span className="exp-label">{t('menu.seller')}</span>
                {experience === 'seller' && <span className="exp-check" aria-hidden>✓</span>}
              </button>
            </div>
          </div>

          <div className="user-menu-section" role="none">
            <span className="user-menu-label">{t('menu.language')}</span>
            <div className="user-menu-lang">
              {LANGS.map((l) => (
                <button
                  key={l.code}
                  type="button"
                  role="menuitemradio"
                  aria-checked={lang === l.code}
                  className={`lang-pill${lang === l.code ? ' active' : ''}`}
                  onClick={() => setLang(l.code)}
                >{l.native}</button>
              ))}
            </div>
          </div>

          <div className="user-menu-links" role="none">
            <button type="button" role="menuitem" className="user-menu-link"
              onClick={() => { setOpen(false); setHelpOpen(true); }}>
              <span className="user-menu-link-icon" aria-hidden>💬</span>
              <span>{t('menu.help')}</span>
            </button>
            <button type="button" role="menuitem" className="user-menu-link"
              onClick={() => { setOpen(false); navigate('/privacy'); }}>
              <span className="user-menu-link-icon" aria-hidden>🔒</span>
              <span>{t('menu.privacy')}</span>
            </button>
            <button type="button" role="menuitem" className="user-menu-link danger"
              onClick={() => { setOpen(false); navigate('/delete-account'); }}>
              <span className="user-menu-link-icon" aria-hidden>🗑</span>
              <span>{t('menu.deleteAccount')}</span>
            </button>
          </div>

          <div className="user-menu-divider" role="none" />
          <button type="button" role="menuitem" className="user-menu-link signout"
            onClick={() => { setOpen(false); signOut(); navigate('/login'); }}>
            <span className="user-menu-link-icon" aria-hidden>↪</span>
            <span>{t('menu.signOut')}</span>
          </button>
        </div>
      )}
      <HelpDrawer open={helpOpen} onClose={() => setHelpOpen(false)} />
    </div>
  );
}

// ===== Login page =====
function LoginPage() {
  const { signIn, token, user, ready, t } = useAuth();
  const navigate = useNavigate();
  const [cfg, setCfg] = useState({ google_client_id: '', allow_dev_login: false });
  const [error, setError] = useState('');
  const [devName, setDevName] = useState('');
  const [devEmail, setDevEmail] = useState('');
  const [submitting, setSubmitting] = useState(false);
  const [helpOpen, setHelpOpen] = useState(false);
  const gBtnRef = useRef(null);

  // Already authed → let HomeRedirect decide the next landing page.
  useEffect(() => {
    if (ready && token && user) {
      navigate('/', { replace: true });
    }
  }, [ready, token, user, navigate]);

  // Detect APK shell (UA suffix set in MainActivity.java).
  const inAppWebView = typeof navigator !== 'undefined' && /StyleYouMobile/.test(navigator.userAgent);
  const hasNativeGoogle = inAppWebView && typeof window !== 'undefined' && window.AndroidAuth && typeof window.AndroidAuth.requestGoogleSignIn === 'function';

  // Mobile-Android browser (NOT the in-app WebView) → nudge to install the app.
  const isAndroidBrowser = typeof navigator !== 'undefined'
    && /Android/i.test(navigator.userAgent)
    && !inAppWebView;
  const [appPromoDismissed, setAppPromoDismissed] = useState(() => {
    try { return typeof sessionStorage !== 'undefined' && sessionStorage.getItem('styleyou_app_promo_dismissed') === '1'; }
    catch { return false; }
  });
  const showAndroidAppPromo = isAndroidBrowser && !appPromoDismissed;
  const dismissAppPromo = () => {
    setAppPromoDismissed(true);
    try { sessionStorage.setItem('styleyou_app_promo_dismissed', '1'); } catch {}
  };

  // Load auth config and render Google button.
  useEffect(() => {
    let cancelled = false;
    (async () => {
      let c = { google_client_id: '', allow_dev_login: false };
      try { c = await api('/auth/config'); } catch (e) { console.warn(e); }
      if (cancelled) return;
      setCfg(c);

      // In the Android WebView, GSI is blocked — use the native bridge instead.
      if (hasNativeGoogle) {
        if (gBtnRef.current) {
          gBtnRef.current.innerHTML = '';
          const btn = document.createElement('button');
          btn.type = 'button';
          btn.className = 'native-google-btn';
          btn.innerHTML = '<svg width="18" height="18" viewBox="0 0 18 18" aria-hidden="true"><path fill="#4285F4" d="M17.64 9.2c0-.64-.06-1.25-.16-1.84H9v3.49h4.84a4.14 4.14 0 0 1-1.8 2.72v2.26h2.91c1.7-1.57 2.69-3.88 2.69-6.63z"/><path fill="#34A853" d="M9 18c2.43 0 4.47-.8 5.96-2.18l-2.91-2.26a5.4 5.4 0 0 1-3.05.86 5.36 5.36 0 0 1-5.04-3.71H.96v2.33A9 9 0 0 0 9 18z"/><path fill="#FBBC05" d="M3.96 10.71A5.41 5.41 0 0 1 3.68 9c0-.59.1-1.16.28-1.71V4.96H.96A9 9 0 0 0 0 9c0 1.45.35 2.83.96 4.04l3-2.33z"/><path fill="#EA4335" d="M9 3.58c1.32 0 2.5.46 3.44 1.35l2.58-2.58A8.99 8.99 0 0 0 9 0 9 9 0 0 0 .96 4.96l3 2.33A5.36 5.36 0 0 1 9 3.58z"/></svg><span>' + t('login.googleBtn') + '</span>';
          btn.addEventListener('click', () => {
            try { window.AndroidAuth.requestGoogleSignIn(); }
            catch (e) { setError(t('login.googleError') + ' ' + e.message); }
          });
          gBtnRef.current.appendChild(btn);
        }
        return;
      }

      const renderG = () => {
        if (!gBtnRef.current) return;
        if (c.google_client_id && window.google?.accounts?.id) {
          window.google.accounts.id.initialize({
            client_id: c.google_client_id,
            callback: handleGoogle,
          });
          gBtnRef.current.innerHTML = '';
          const availW = Math.round(gBtnRef.current.getBoundingClientRect().width) || 360;
          const gWidth = Math.max(200, Math.min(400, availW));
          window.google.accounts.id.renderButton(gBtnRef.current, {
            theme: 'outline', size: 'large', shape: 'pill', text: 'continue_with', width: gWidth, logo_alignment: 'center',
          });
          window.google.accounts.id.prompt();
        } else if (gBtnRef.current) {
          gBtnRef.current.innerHTML = '<small style="color:var(--muted)">' + t('login.googleNotConfigured') + '</small>';
        }
      };
      // GSI script loads async; poll briefly.
      // Inject GSI on demand (removed from index.html so it doesn't cost
      // every page a third-party round-trip on first load).
      if (!window.google?.accounts?.id && !document.getElementById('gsi-client-script')) {
        const s = document.createElement('script');
        s.id = 'gsi-client-script';
        s.src = 'https://accounts.google.com/gsi/client';
        s.async = true; s.defer = true;
        document.head.appendChild(s);
      }
      if (window.google?.accounts?.id) renderG();
      else {
        let tries = 0;
        const t = setInterval(() => {
          if (window.google?.accounts?.id || tries++ > 30) { clearInterval(t); renderG(); }
        }, 100);
      }
    })();
    return () => { cancelled = true; };
  }, [hasNativeGoogle]);

  // Native Google sign-in callbacks from MainActivity.
  useEffect(() => {
    if (!hasNativeGoogle) return;
    window.__styleyouOnGoogleIdToken = (idToken) => handleGoogle({ credential: idToken });
    window.__styleyouOnGoogleIdTokenError = (msg) => setError(msg || t('login.googleSignInFailed'));
    return () => {
      delete window.__styleyouOnGoogleIdToken;
      delete window.__styleyouOnGoogleIdTokenError;
    };
  }, [hasNativeGoogle]);

  async function handleGoogle(response) {
    setError('');
    try {
      const data = await api('/auth/google', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ credential: response.credential }),
      });
      signIn(data);
      navigate('/', { replace: true });
    } catch (e) {
      setError(e.message);
    }
  }

  async function handleDev(e) {
    e.preventDefault();
    setError('');
    if (!devEmail) return;
    setSubmitting(true);
    try {
      const data = await api('/auth/google', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email: devEmail, name: devName || devEmail.split('@')[0] }),
      });
      signIn(data);
      navigate('/', { replace: true });
    } catch (e) {
      setError(e.message);
    } finally {
      setSubmitting(false);
    }
  }

  // Show the email fallback only when explicitly allowed and Google sign-in
  // isn't configured. (Google sign-in works in the APK via the native bridge.)
  const showFallback = cfg.allow_dev_login && !cfg.google_client_id;

  return (
    <section className="screen">
      <div className="hero">
        <div className="login-copy">
          <a className="logo" href="#/" aria-label="Stylekro"><img src="/static/logo.svg" alt="Stylekro" /></a>
          <div className="login-copy-main">
            <span className="login-eyebrow">{t('login.eyebrow')}</span>
            <h1>{t('login.hero')}</h1>
            <p className="lede">{t('login.tagline')}</p>
          </div>
        </div>
        {showAndroidAppPromo && (
          <div className="android-app-promo" role="region" aria-label={t('login.androidAppTitle')}>
            <div className="android-app-promo-icon" aria-hidden="true">
              <img src="/static/logo.svg" alt="" />
            </div>
            <div className="android-app-promo-body">
              <strong>{t('login.androidAppTitle')}</strong>
              <span>{t('login.androidAppBody')}</span>
              <div className="android-app-promo-actions">
                <a
                  className="btn btn-primary"
                  href="https://play.google.com/store/apps/details?id=com.stylekro.app"
                  target="_blank"
                  rel="noopener noreferrer"
                >
                  {t('login.androidAppCta')}
                </a>
                <button type="button" className="link-btn" onClick={dismissAppPromo}>
                  {t('login.androidAppDismiss')}
                </button>
              </div>
            </div>
          </div>
        )}
        <div className="login-card">
          <div className="login-card-header">
            <h2 className="login-heading">{t('login.heading')}</h2>
          </div>
          <div ref={gBtnRef} className="google-btn-wrap"></div>
          {showFallback && (
            <div className="dev-fallback">
              <div className="divider"><span>{t('login.emailFallback')}</span></div>
              <form onSubmit={handleDev} className="dev-login">
                <input type="text" placeholder={t('login.namePlaceholder')} value={devName} onChange={(e) => setDevName(e.target.value)} />
                <input type="email" placeholder={t('login.emailPlaceholder')} required value={devEmail} onChange={(e) => setDevEmail(e.target.value)} />
                <button type="submit" disabled={submitting} className="btn btn-primary">
                  {submitting ? t('login.signingIn') : t('login.continue')}
                </button>
              </form>
            </div>
          )}
          {/* PHONE-AUTH: mobile-number sign-in (renders only when backend enables it) */}
          {_PhoneAuth && React.createElement(_PhoneAuth.LoginBlock, {
            onSignedIn: (data) => { setError(''); signIn(data); navigate('/', { replace: true }); },
          })}
          {error && <p className="error">{error}</p>}
          <button type="button" className="help-link help-row" onClick={() => setHelpOpen(true)}>
            <span className="help-row-icon" aria-hidden>
              <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
                <path d="M3 18v-6a9 9 0 0 1 18 0v6" />
                <path d="M21 19a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3zM3 19a2 2 0 0 0 2 2h1a2 2 0 0 0 2-2v-3a2 2 0 0 0-2-2H3z" />
              </svg>
            </span>
            <span className="help-row-text">{t('login.helpLink')}</span>
            <svg className="help-row-chev" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden>
              <path d="m9 18 6-6-6-6" />
            </svg>
          </button>
          <p className="fineprint">{t('login.disclaimer')}</p>
        </div>
      </div>
      <HelpDrawer open={helpOpen} onClose={() => setHelpOpen(false)} />
      <aside className="hero-art">
        <div className="tryon-stage">
          <div className="tryon-meta">
            <span className="tryon-meta-label">{t('login.demoLabel')}</span>
            <span className="tryon-meta-index">
              <span className="tryon-idx tryon-idx-1">01</span>
              <span className="tryon-idx tryon-idx-2">02</span>
              <span className="tryon-idx tryon-idx-3">03</span>
              <span className="tryon-idx-total">/ 03</span>
            </span>
          </div>
          <div className="tryon-frame">
            <div className="tryon-slide tryon-slide-1" style={{ backgroundImage: "url('/static/2.png')" }} />
            <div className="tryon-slide tryon-slide-2" style={{ backgroundImage: "url('/static/3.png')" }} />
            <div className="tryon-slide tryon-slide-3" style={{ backgroundImage: "url('/static/4.png')" }} />
            <div className="tryon-vignette" />
            <div className="tryon-grain" />
            <div className="tryon-caption">
              <div className="tryon-cap tryon-cap-1">
                <span className="tryon-cap-eyebrow">{t('login.stepLabel')} 01</span>
                <span className="tryon-cap-title">{t('login.step1')}</span>
              </div>
              <div className="tryon-cap tryon-cap-2">
                <span className="tryon-cap-eyebrow">{t('login.stepLabel')} 02</span>
                <span className="tryon-cap-title">{t('login.step2')}</span>
              </div>
              <div className="tryon-cap tryon-cap-3">
                <span className="tryon-cap-eyebrow">{t('login.stepLabel')} 03</span>
                <span className="tryon-cap-title">{t('login.step3')}</span>
              </div>
            </div>
          </div>
          <div className="tryon-progress" aria-hidden="true">
            <span className="tryon-bar tryon-bar-1" />
            <span className="tryon-bar tryon-bar-2" />
            <span className="tryon-bar tryon-bar-3" />
          </div>
        </div>
      </aside>
    </section>
  );
}

// ===== Profile page =====
function ProfilePage() {
  const { apiCall, refreshProfile, profile, user, t, photoVersion, token } = useAuth();
  const confirmAction = useConfirmDialog();
  const navigate = useNavigate();
  const fileRef = useRef(null);
  const cameraRef = useRef(null);
  const galleryRef = useRef(null);
  const galleryCameraRef = useRef(null);
  const [name, setName] = useState('');
  const [preview, setPreview] = useState('');
  const [photo, setPhoto] = useState(null);
  const [error, setError] = useState('');
  const [saving, setSaving] = useState(false);
  const [editingName, setEditingName] = useState(false);
  const [photos, setPhotos] = useState([]);          // [{id, url, is_primary}]
  const [photosBusy, setPhotosBusy] = useState(false);
  const [photosLoading, setPhotosLoading] = useState(true);
  const [uploadingPhotos, setUploadingPhotos] = useState([]);
  const [settingPrimaryId, setSettingPrimaryId] = useState(null);
  const [lightbox, setLightbox] = useState(null);    // url string when zoomed
  const [pickerOpen, setPickerOpen] = useState(false);
  const [verifyingProfile, setVerifyingProfile] = useState(() => !profile && !!user?.has_profile);

  const isEdit = !!profile;
  const primaryPhoto = photos.find((p) => p.is_primary) || photos[0] || null;

  useEffect(() => {
    if (profile) {
      setName(profile.name || '');
      setPreview(bust(imgUrl(profile.photo_url), photoVersion));
    } else if (user?.name) {
      // Pre-fill from Google sign-in for first-time users
      setName(user.name);
    }
  }, [profile?.id, user?.name]);

  // If the server says the user has a profile but the hydration fetch failed
  // (transient 5xx / network), refetch before falling back to the onboarding UI.
  useEffect(() => {
    let cancelled = false;
    if (!profile && user?.has_profile) {
      setVerifyingProfile(true);
      (async () => {
        try { await refreshProfile(); } catch {}
        finally { if (!cancelled) setVerifyingProfile(false); }
      })();
    } else {
      setVerifyingProfile(false);
    }
    return () => { cancelled = true; };
  }, [profile, user?.has_profile, refreshProfile]);

  const loadPhotos = useCallback(async () => {
    if (!isEdit) { setPhotosLoading(false); return; }
    setPhotosLoading(true);
    try {
      const list = await apiCall('/profiles/me/photos');
      setPhotos(Array.isArray(list) ? list : []);
    } catch (e) {
      setError(errMsg(e));
    } finally {
      setPhotosLoading(false);
    }
  }, [apiCall, isEdit]);

  useEffect(() => { loadPhotos(); }, [loadPhotos]);

  useEffect(() => () => {
    uploadingPhotos.forEach((item) => {
      try { URL.revokeObjectURL(item.preview_url); } catch {}
    });
  }, [uploadingPhotos]);

  async function addPhotos(fileList) {
    const files = Array.from(fileList || []);
    if (!files.length) return;
    // Diagnostic: report what the WebView actually delivered so we can tell
    // apart "0-byte File", "arrayBuffer threw", and "backend rejected".
    let shellVer = 'web';
    try {
      if (typeof window !== 'undefined' && window.AndroidShell && window.AndroidShell.versionName) {
        shellVer = `apk-${window.AndroidShell.versionName()}-${window.AndroidShell.versionCode && window.AndroidShell.versionCode()}`;
      }
    } catch {}
    const diag = `[${shellVer}] ` + files.map((f, i) => `f${i}:name=${f && f.name}|type=${f && f.type}|size=${f && f.size}`).join(' ; ');    setPhotosBusy(true);
    setError('');
    let materialized;
    try {
      materialized = await Promise.all(files.map(async (f) => {
        const buf = await f.arrayBuffer();
        return {
          name: f.name || 'photo.jpg',
          type: f.type || 'image/jpeg',
          blob: new Blob([buf], { type: f.type || 'image/jpeg' }),
          size: buf.byteLength,
        };
      }));
    } catch (e) {
      setPhotosBusy(false);
      setError(`[client] arrayBuffer failed: ${errMsg(e)} | ${diag}`);
      return;
    }
    const bufSizes = materialized.map((m, i) => `b${i}=${m.size}`).join(',');
    const usable = materialized.filter((m) => m.size > 0);
    if (!usable.length) {
      setPhotosBusy(false);
      setError(`[client] empty bytes after read | ${diag} | ${bufSizes}`);
      return;
    }
    const pending = usable.map((m, index) => ({
      key: `pending-${Date.now()}-${index}`,
      preview_url: URL.createObjectURL(m.blob),
    }));
    setUploadingPhotos(pending);
    try {
      const fd = new FormData();
      usable.forEach((m) => fd.append('files', m.blob, m.name));
      await apiCall('/profiles/me/photos', { method: 'POST', body: fd });
      await loadPhotos();
      await refreshProfile();
    } catch (e) {
      setError(`[server] ${errMsg(e)} | ${diag} | ${bufSizes}`);
    } finally {
      setPhotosBusy(false);
      setUploadingPhotos([]);
    }
  }

  async function setPrimary(id) {
    setPhotosBusy(true);
    setSettingPrimaryId(id);
    setError('');
    try {
      await apiCall(`/profiles/me/photos/${id}/primary`, { method: 'PUT' });
      await loadPhotos();
      await refreshProfile();
    } catch (e) {
      setError(errMsg(e));
    } finally {
      setPhotosBusy(false);
      setSettingPrimaryId(null);
    }
  }

  async function removePhoto(id) {
    if (!(await confirmAction(t('profile.deletePhoto') + '?', { confirmLabel: t('common.delete'), tone: 'danger' }))) return;
    setPhotosBusy(true);
    setError('');
    try {
      await apiCall(`/profiles/me/photos/${id}`, { method: 'DELETE' });
      await loadPhotos();
      await refreshProfile();
    } catch (e) {
      setError(errMsg(e));
    } finally {
      setPhotosBusy(false);
    }
  }

  function onFile(e) {
    const f = e.target.files[0];
    if (!f) return;
    setPhoto(f);
    setPreview(URL.createObjectURL(f));
  }

  async function save() {
    setError('');
    if (!name.trim()) return setError(t('profile.nameRequired'));
    if (!isEdit && !photo) return setError(t('profile.photoRequired'));
    setSaving(true);
    try {
      const fd = new FormData();
      fd.append('name', name.trim());
      if (photo) fd.append('photo', photo);
      if (isEdit) {
        await apiCall('/profiles/me', { method: 'PUT', body: fd });
      } else {
        await apiCall('/profiles', { method: 'POST', body: fd });
      }
      await refreshProfile();
      setEditingName(false);
      if (!isEdit) navigate('/shops', { replace: true });
    } catch (e) {
      setError(e.message);
    } finally {
      setSaving(false);
    }
  }

  return (
    <section className="screen">
      {isEdit ? <NavBar active="profile" /> : (
        <header className="topbar">
          <Link className="logo small" to="/" aria-label="Stylekro"><img src="/static/logo.svg" alt="Stylekro" /></Link>
          <UserChip />
        </header>
      )}
      <div className="profile-wrap">
        {isEdit ? (
          <div className="profile-v2-card">
            <div className="profile-v2-hero">
              <div className="profile-v2-avatar" aria-hidden="true">
                {primaryPhoto ? (
                  <img src={imgUrl(primaryPhoto.url)} alt="" loading="eager" decoding="async" />
                ) : preview ? (
                  <img src={preview} alt="" loading="eager" decoding="async" />
                ) : (
                  <span>{(name || user?.name || 'S').trim().charAt(0).toUpperCase()}</span>
                )}
              </div>
              <div className="profile-v2-heading">
                <span className="profile-v2-label">{t('nav.profile')}</span>
                {editingName ? (
                  <form className="profile-v2-inline-edit" onSubmit={(e) => { e.preventDefault(); save(); }}>
                    <input
                      className="profile-v2-title-input"
                      type="text"
                      aria-label={t('profile.displayNameLabel')}
                      placeholder={t('profile.displayNamePlaceholder')}
                      value={name}
                      autoFocus
                      onChange={(e) => setName(e.target.value)}
                    />
                    <button type="submit" disabled={saving} className="profile-v2-inline-save">
                      {saving ? t('common.saving') : t('profile.save')}
                    </button>
                    <button
                      type="button"
                      disabled={saving}
                      className="profile-v2-inline-cancel"
                      aria-label={t('common.cancel')}
                      onClick={() => { setName(profile?.name || user?.name || ''); setEditingName(false); setError(''); }}
                    >×</button>
                  </form>
                ) : (
                  <div className="profile-v2-title-row">
                    <h1 className="profile-v2-title">{name || user?.name || t('menu.profile')}</h1>
                    <button
                      type="button"
                      className="profile-v2-edit-name"
                      aria-label={t('profile.edit')}
                      onClick={() => setEditingName(true)}
                    >
                      <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
                        <path d="M12 20h9" />
                        <path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4 12.5-12.5z" />
                      </svg>
                    </button>
                  </div>
                )}
              </div>
            </div>

            {/* PHONE-AUTH: link/verify a mobile number to this account (renders only when enabled) */}
            {_PhoneAuth && (
              <div className="profile-v2-details">
                {React.createElement(_PhoneAuth.LinkBlock, { token })}
              </div>
            )}
            {error && <p className="error">{error}</p>}

            <div className="profile-v2-actions">
              <input
                ref={galleryRef}
                id="profile-gallery-input"
                type="file"
                accept="image/*"
                multiple
                className="profile-v2-file"
                onChange={(e) => { addPhotos(e.target.files); e.target.value = ''; }}
              />
              <label
                htmlFor="profile-gallery-input"
                className={`profile-v2-btn profile-v2-btn-primary${photosBusy ? ' is-disabled' : ''}`}
                aria-disabled={photosBusy}
                onClick={(e) => {
                  try {
                    if (window.AndroidShell && typeof window.AndroidShell.pickImage === 'function') {
                      e.preventDefault();
                      nativePickImages(true).then((items) => {
                        if (!items || !items.length) return;
                        const files = items.map((it) => {
                          const raw = atob(it.b64);
                          const arr = new Uint8Array(raw.length);
                          for (let i = 0; i < raw.length; i++) arr[i] = raw.charCodeAt(i);
                          return new File([arr], it.name || 'photo.jpg', { type: it.mime || 'image/jpeg' });
                        });
                        addPhotos(files);
                      }).catch((err) => {
                        setError(`[native-pick] ${errMsg(err)}`);
                      });
                    }
                  } catch (err) {
                    setError(`[native-pick] ${errMsg(err)}`);
                  }
                }}
              >
                {photosBusy ? t('common.uploading') : t('common.upload')}
              </label>
            </div>

            {photosLoading && photos.length === 0 && uploadingPhotos.length === 0 ? (
              <div className="profile-v2-loading" role="status" aria-live="polite">
                <span className="profile-v2-spinner-ring" />
                <span className="profile-v2-loading-label">{t('boot.loading')}</span>
              </div>
            ) : photos.length === 0 && uploadingPhotos.length === 0 ? (
              <p className="profile-v2-empty">{t('profile.emptyPhotos')}</p>
            ) : (
              <div className="profile-v2-gallery">
                {uploadingPhotos.map((item) => (
                  <div key={item.key} className="profile-v2-photo uploading" aria-hidden="true">
                    <img src={item.preview_url} alt="" loading="eager" decoding="async" />
                    <div className="profile-v2-overlay" />
                    <div className="profile-v2-spinner" aria-label={t('common.uploading')}>
                      <span className="profile-v2-spinner-ring" />
                    </div>
                    <div className="profile-v2-uploading-badge">{t('common.uploading')}</div>
                  </div>
                ))}
                {photos.map((p, i) => (
                  <div
                    key={p.id}
                    className={`profile-v2-photo${p.is_primary ? ' primary' : ''}`}
                    style={{ cursor: 'zoom-in' }}
                    onClick={() => setLightbox(imgUrl(p.url))}
                    title={t('profile.zoom')}
                  >
                    <img src={imgUrl(p.url)} alt="" loading="lazy" decoding="async" />
                    <div className="profile-v2-overlay" />
                    {p.is_primary && <div className="profile-v2-tag">{t('profile.primary')}</div>}
                    {settingPrimaryId === p.id && (
                      <div className="profile-v2-spinner" aria-label={t('profile.settingPrimary')}>
                        <span className="profile-v2-spinner-ring" />
                      </div>
                    )}
                    <button
                      type="button"
                      className="profile-v2-remove"
                      aria-label={t('profile.deletePhoto')}
                      onClick={(e) => { e.stopPropagation(); removePhoto(p.id); }}
                    >×</button>
                    <div className="profile-v2-footer">
                      <button
                        type="button"
                        className={`profile-v2-pill${p.is_primary ? ' active' : ''}`}
                        disabled={photosBusy || p.is_primary}
                        onClick={(e) => { e.stopPropagation(); if (!p.is_primary) setPrimary(p.id); }}
                      >
                        {settingPrimaryId === p.id ? t('profile.settingPrimary') : (p.is_primary ? t('profile.active') : t('profile.setPrimary'))}
                      </button>
                    </div>
                  </div>
                ))}
              </div>
            )}

          </div>
        ) : verifyingProfile ? (
          <div className="onboard onboard-lux">
            <div className="onboard-lux-card" style={{ minHeight: 320, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
              <div className="profile-v2-spinner" aria-label={t('boot.loading')}>
                <span className="profile-v2-spinner-ring" />
              </div>
            </div>
          </div>
        ) : (
          <div className="onboard onboard-lux">
            <div className="onboard-lux-card">
              <div className="lux-step">
                <span className="lux-step-label">{t('login.stepLabel')} 01 / 02</span>
                <div className="lux-step-bar"><div className="lux-step-bar-fill" /></div>
              </div>

              <h1 className="lux-title">{t('profile.onboardTitle')}</h1>
              <p className="lux-sub">{t('profile.onboardSubtitle')}</p>

              <button
                type="button"
                className="lux-dropzone"
                onClick={() => setPickerOpen(true)}
                aria-haspopup="dialog"
              >
                {preview ? (
                  <div className="lux-preview">
                    <img src={preview} alt="Your photo" />
                    <div className="lux-preview-overlay"><span>{t('profile.replacePhoto')}</span></div>
                  </div>
                ) : (
                  <div className="lux-drop-empty">
                    <div className="lux-drop-icon" aria-hidden>
                      <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                        <path d="M12 16V4M12 4l-5 5M12 4l5 5"/>
                        <path d="M4 16v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2"/>
                      </svg>
                    </div>
                    <strong>{t('profile.uploadBest')}</strong>
                    <small>{t('profile.uploadHint')}</small>
                  </div>
                )}
              </button>

              {/* Hidden inputs triggered by the picker sheet */}
              <input ref={cameraRef} type="file" accept="image/*" capture="user" hidden onChange={(e) => { setPickerOpen(false); onFile(e); }} />
              <input ref={galleryRef} type="file" accept="image/*" hidden onChange={(e) => { setPickerOpen(false); onFile(e); }} />

              {pickerOpen && (
                <div className="lux-picker-backdrop" onClick={() => setPickerOpen(false)}>
                  <div className="lux-picker-sheet" role="dialog" aria-label={t('profile.choosePhoto')} onClick={(e) => e.stopPropagation()}>
                    <div className="lux-picker-handle" aria-hidden />
                    <h3 className="lux-picker-title">{t('profile.choosePhoto')}</h3>
                    <div className="lux-picker-options">
                      <button type="button" className="lux-picker-option" onClick={() => cameraRef.current?.click()}>
                        <span className="lux-picker-icon" aria-hidden>
                          <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                            <path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/>
                            <circle cx="12" cy="13" r="4"/>
                          </svg>
                        </span>
                        <span className="lux-picker-label">{t('profile.takePhoto')}</span>
                      </button>
                      <button type="button" className="lux-picker-option" onClick={() => galleryRef.current?.click()}>
                        <span className="lux-picker-icon" aria-hidden>
                          <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                            <rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
                            <circle cx="8.5" cy="8.5" r="1.5"/>
                            <path d="M21 15l-5-5L5 21"/>
                          </svg>
                        </span>
                        <span className="lux-picker-label">{t('profile.fromGallery')}</span>
                      </button>
                    </div>
                    <button type="button" className="lux-picker-cancel" onClick={() => setPickerOpen(false)}>{t('profile.cancel')}</button>
                  </div>
                </div>
              )}

              <div className="lux-field">
                <label className="lux-field-label" htmlFor="lux-name-input">{t('profile.displayNameLabel')}</label>
                <input
                  id="lux-name-input"
                  className="lux-input"
                  type="text"
                  placeholder={t('profile.displayNamePlaceholder')}
                  value={name}
                  onChange={(e) => setName(e.target.value)}
                />
                <p className="lux-field-hint">{t('profile.nameHint')}</p>
              </div>

              <button
                onClick={save}
                disabled={saving || !photo || !name.trim()}
                className="lux-cta"
              >
                {saving ? t('common.saving') : t('profile.cta')}
              </button>
              {error && <p className="error">{error}</p>}
            </div>
          </div>
        )}
      </div>
      {lightbox && (
        <div className="lightbox" onClick={() => setLightbox(null)}>
          <button className="lightbox-close" title="Close">×</button>
          <ZoomableImage src={lightbox} />
          <LightboxActions url={lightbox} filename="stylekro-image.png" />
        </div>
      )}
    </section>
  );
}

function NavBar({ active }) {
  const { experience, t } = useAuth();
  return (
    <>
      <header className="topbar">
        <Link className="logo small" to="/" aria-label="Stylekro"><img src="/static/logo.svg" alt="Stylekro" /></Link>
        <UserChip />
      </header>
      <nav className="bottom-nav" aria-label="Main navigation">
        {experience === 'seller' ? (
          <>
            <NavLink to="/shop/manage" className={({isActive}) => `bottom-nav-item${isActive || active === 'manage' ? ' active' : ''}`}>
              <svg className="bottom-nav-icon" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M3 3h18v4H3z"/><path d="M5 7v13a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V7"/><path d="M10 12h4"/></svg>
              <span>{t('nav.myShop')}</span>
            </NavLink>
            <NavLink to="/shop/settings" className={({isActive}) => `bottom-nav-item${isActive || active === 'settings' ? ' active' : ''}`}>
              <svg className="bottom-nav-icon" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
              <span>{t('nav.shopSettings')}</span>
            </NavLink>
          </>
        ) : (
          <>
            <NavLink to="/chat" className={({isActive}) => `bottom-nav-item${isActive || active === 'chat' ? ' active' : ''}`}>
              <svg className="bottom-nav-icon" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
              <span>{t('nav.chat')}</span>
            </NavLink>
            <NavLink to="/wardrobe" className={({isActive}) => `bottom-nav-item${isActive || active === 'wardrobe' ? ' active' : ''}`}>
              <svg className="bottom-nav-icon" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M3 7l9-4 9 4-9 4-9-4z"/><path d="M3 7v10l9 4 9-4V7"/><path d="M12 11v10"/></svg>
              <span>{t('nav.wardrobe')}</span>
            </NavLink>
            <NavLink to="/shops" className={({isActive}) => `bottom-nav-item${isActive || active === 'shops' ? ' active' : ''}`}>
              <svg className="bottom-nav-icon" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/><path d="M16 10a4 4 0 0 1-8 0"/></svg>
              <span>{t('nav.shops')}</span>
            </NavLink>
            <NavLink to="/looks" className={({isActive}) => `bottom-nav-item${isActive || active === 'looks' ? ' active' : ''}`}>
              <svg className="bottom-nav-icon" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21,15 16,10 5,21"/></svg>
              <span>{t('nav.looks')}</span>
            </NavLink>
            <NavLink to="/profile" className={({isActive}) => `bottom-nav-item${isActive || active === 'profile' ? ' active' : ''}`}>
              <svg className="bottom-nav-icon" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
              <span>{t('nav.profile')}</span>
            </NavLink>
          </>
        )}
      </nav>
    </>
  );
}

function HomeRedirect() {
  const { token, user, ready, experience } = useAuth();
  if (!ready) return <div className="boot-splash">Loading…</div>;
  if (!token || !user) return <Navigate to="/login" replace />;
  if (!experience) return <Navigate to="/choose-experience" replace />;
  return <Navigate to={experience === 'seller' ? '/shop/manage' : '/shops'} replace />;
}

// ===== Choose experience page =====
function ChooseExperiencePage() {
  const { token, user, ready, experience, setExperience, t } = useAuth();
  const navigate = useNavigate();
  if (!ready) return <div className="boot-splash">{t('boot.loading')}</div>;
  if (!token || !user) return <Navigate to="/login" replace />;

  function pick(role) {
    setExperience(role);
    navigate(role === 'seller' ? '/shop/manage' : '/shops', { replace: true });
  }

  return (
    <section className="page-wrap choose-exp-page">
      <header className="choose-exp-hero">
        <Link className="logo small" to="/" aria-label="Stylekro"><img src="/static/logo.svg" alt="Stylekro" /></Link>
        <p className="choose-exp-eyebrow">{t('chooseExp.welcome')}{user.name ? `, ${user.name.split(' ')[0]}` : ''}</p>
        <h1 className="choose-exp-title" dangerouslySetInnerHTML={{ __html: t('chooseExp.title').replace('Stylekro', '<em>Stylekro</em>') }} />
        <p className="choose-exp-sub">{t('chooseExp.sub')}</p>
      </header>
      <div className="choose-exp-grid">
        <button type="button" className={`choose-exp-card${experience === 'buyer' ? ' current' : ''}`} onClick={() => pick('buyer')}>
          <div className="choose-exp-icon" aria-hidden>✨</div>
          <h2>{t('chooseExp.buyerHead')}</h2>
          <p>{t('chooseExp.buyerDesc')}</p>
          <span className="choose-exp-cta">{experience === 'buyer' ? t('chooseExp.continue') : t('chooseExp.getStarted')} →</span>
        </button>
        <button type="button" className={`choose-exp-card${experience === 'seller' ? ' current' : ''}`} onClick={() => pick('seller')}>
          <div className="choose-exp-icon" aria-hidden>🛍️</div>
          <h2>{t('chooseExp.sellerHead')}</h2>
          <p>{t('chooseExp.sellerDesc')}</p>
          <span className="choose-exp-cta">{experience === 'seller' ? t('chooseExp.continue') : t('chooseExp.getStarted')} →</span>
        </button>
      </div>
    </section>
  );
}

function PrivacyPage() {
  const { t } = useAuth();
  const navigate = useNavigate();
  return (
    <section className="screen">
      <main className="page-wrap privacy-page" style={{ maxWidth: 760, margin: '0 auto', padding: '24px 20px 80px', lineHeight: 1.6 }}>
        <button type="button" className="btn btn-ghost small" onClick={() => (window.history.length > 1 ? navigate(-1) : navigate('/'))} style={{ marginBottom: 16 }}>{t('common.back')}</button>
        <h1 style={{ fontFamily: 'Fraunces, serif', fontSize: 32, marginBottom: 4 }}>{t('privacy.title')}</h1>
        <p className="muted" style={{ marginTop: 0 }}>Last updated: November 2025</p>

        <p>Stylekro ("we", "us") respects your privacy. This policy explains what data the Stylekro app and website collect, how we use it, and your choices.</p>

        <h2>1. Information we collect</h2>
        <ul>
          <li><strong>Account info</strong> — when you sign in with Google we receive your name, email and profile picture.</li>
          <li><strong>Profile photo</strong> — the selfie/portrait you upload to enable virtual try-on.</li>
          <li><strong>Chat & try-on content</strong> — your messages to the AI stylist and the garments you try on, used to generate try-on images.</li>
          <li><strong>Saved looks</strong> — try-on results you choose to save to "My Looks".</li>
          <li><strong>Usage logs</strong> — basic technical logs (request times, errors, device type) used to keep the service running.</li>
        </ul>

        <h2>2. How we use your data</h2>
        <ul>
          <li>To authenticate your account and remember your preferences.</li>
          <li>To process virtual try-on requests by sending your portrait + selected garment to Microsoft Azure OpenAI image-generation services.</li>
          <li>To respond to your AI stylist chats.</li>
          <li>To improve reliability, debug issues, and protect against abuse.</li>
        </ul>

        <h2>3. Where your data is stored</h2>
        <p>Account data, saved looks and try-on history are stored on Microsoft Azure (Azure SQL and Azure Blob Storage) in the Central India region. Generated images and uploaded photos are kept only as long as needed to provide the feature.</p>

        <h2>4. Third parties</h2>
        <ul>
          <li><strong>Google Sign-In</strong> — used only for authentication.</li>
          <li><strong>Microsoft Azure OpenAI</strong> — used to generate try-on images and chat responses. Inputs are processed under Microsoft's enterprise data protections and are not used to train public models.</li>
        </ul>
        <p>We do <strong>not</strong> sell your personal data, and we do not show third-party advertising.</p>

        <h2>5. Your choices</h2>
        <ul>
          <li>You can delete saved looks from the "My Looks" page at any time.</li>
          <li>You can sign out from the profile menu.</li>
          <li>To delete your account and associated data, email <a href="mailto:stylekrohelp@gmail.com">stylekrohelp@gmail.com</a> from your registered address.</li>
        </ul>

        <h2>6. Children</h2>
        <p>Stylekro is not directed to children under 13. We do not knowingly collect personal information from children under 13.</p>

        <h2>7. Changes</h2>
        <p>We may update this policy from time to time. Material changes will be reflected in the "Last updated" date above.</p>

        <h2>8. Contact</h2>
        <p>Questions or requests? Email <a href="mailto:stylekrohelp@gmail.com">stylekrohelp@gmail.com</a>.</p>
      </main>
    </section>
  );
}

function DeleteAccountPage() {
  const { t } = useAuth();
  const navigate = useNavigate();
  const subject = encodeURIComponent('Delete my Stylekro account');
  const body = encodeURIComponent('Please delete my Stylekro account and all associated data.\n\nMy registered email (Google sign-in): \n');
  return (
    <section className="screen">
      <main className="page-wrap" style={{ maxWidth: 760, margin: '0 auto', padding: '24px 20px 80px', lineHeight: 1.6 }}>
        <button type="button" className="btn btn-ghost small" onClick={() => (window.history.length > 1 ? navigate(-1) : navigate('/'))} style={{ marginBottom: 16 }}>{t('common.back')}</button>
        <h1 style={{ fontFamily: 'Fraunces, serif', fontSize: 32, marginBottom: 4 }}>{t('deleteAcct.title')}</h1>
        <p className="muted" style={{ marginTop: 0 }}>{t('deleteAcct.subtitle')}</p>

        <h2>What gets deleted</h2>
        <ul>
          <li>Your Stylekro account (name, email, profile picture from Google sign-in)</li>
          <li>Your uploaded portrait / selfie photo</li>
          <li>All saved looks in “My Looks”</li>
          <li>Your chat history with the AI stylist</li>
          <li>Your style preferences and profile settings</li>
          <li>If you are a seller: your shop, garment listings and shop photos</li>
        </ul>

        <h2>What may be retained</h2>
        <ul>
          <li>Anonymous, aggregated usage logs that no longer identify you (kept for service reliability and abuse prevention).</li>
          <li>Records we are required to keep by law (e.g., tax/audit records for paid transactions, if any).</li>
        </ul>

        <h2>How to request deletion</h2>
        <ol>
          <li>From the email address you used to sign in (your Google account), send an email to <a href={`mailto:stylekrohelp@gmail.com?subject=${subject}&body=${body}`}>stylekrohelp@gmail.com</a>.</li>
          <li>Subject: <strong>Delete my Stylekro account</strong></li>
          <li>We will verify the request and complete deletion within <strong>30 days</strong>, and reply to confirm.</li>
        </ol>

        <p>
          <a className="btn btn-primary" href={`mailto:stylekrohelp@gmail.com?subject=${subject}&body=${body}`}>Email deletion request</a>
        </p>

        <h2>Questions</h2>
        <p>For any privacy questions, see our <a href="#/privacy">Privacy policy</a> or email <a href="mailto:stylekrohelp@gmail.com">stylekrohelp@gmail.com</a>.</p>
      </main>
    </section>
  );
}
