// planner-v6-tokens.jsx — v6 foundation: design tokens, shared atoms, the tier
// system (Free / Pro / Premium) and lock/gate primitives, plus small date/time
// helpers shared by the v6 modules.
//
// MERGE NOTE: everything here is NEW in v6 (register these in COMPONENTS.md).
// Nothing re-declares shared.jsx globals. All top-level names carry V6_/v6.

// ── type + surface tokens ───────────────────────────────────────────
// Runtime values are GENERATED from design-system/cla-design-tokens.json.
// Keep the fallback only as a fail-soft boot path for stale cached HTML; new
// design values belong in the JSON source, never here.
const V6_DESIGN = (typeof window !== 'undefined' && window.CLA_DESIGN) || {
  font: {
    sans: '"Geist", -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", system-ui, sans-serif',
    display: '"Geist", -apple-system, BlinkMacSystemFont, "SF Pro Display", "Helvetica Neue", system-ui, sans-serif',
    mono: '"Geist Mono", ui-monospace, "SF Mono", "JetBrains Mono", monospace',
    serif: '"Iowan Old Style", "Hoefler Text", Georgia, serif',
  },
  color: {
    toolLight: {
      canvas:'#f4f4f1', surface:'#ffffff', surfaceSubtle:'#f8f8f6',
      hairline:'#dfe1e4', outline:'#c8cbd0',
      text:'#15171a', textSubtle:'#626872', textFaint:'#858b94',
      neutralAction:'#111317', accent:'#1f5bb5', accentSoft:'#edf3ff',
      hover:'rgba(21, 23, 26, .045)',
      warning:'#9a5c00', danger:'#a83a38',
    },
  },
  radius: { compact:8, control:10, panel:14, card:20, pill:999 },
  control: {
    button: {
      xs:{ height:22, padding:'0 9px', fontSize:10.5, fontWeight:700, borderRadius:999 },
      sm:{ height:28, padding:'0 10px', fontSize:11.5, fontWeight:600, borderRadius:7 },
      md:{ height:32, padding:'0 13px', fontSize:12.5, fontWeight:600, borderRadius:9 },
      lg:{ height:40, padding:'0 16px', fontSize:13.5, fontWeight:600, borderRadius:10 },
      xl:{ height:44, padding:'0 18px', fontSize:14.5, fontWeight:600, borderRadius:11 },
      icon:{ height:26, width:26, padding:0, fontSize:12, fontWeight:600, borderRadius:7 },
    },
  },
  motion: { fast:120, standard:160 },
  state: { disabledOpacity:.35 },
};
const V6_FONT = V6_DESIGN.font.sans;
const V6_MONO = V6_DESIGN.font.mono;
const V6_INK   = V6_DESIGN.color.toolLight.text;
const V6_SUB   = V6_DESIGN.color.toolLight.textSubtle;
const V6_FAINT = V6_DESIGN.color.toolLight.textFaint;
const V6_HAIR  = V6_DESIGN.color.toolLight.hairline;
const V6_BG    = V6_DESIGN.color.toolLight.canvas;
const V6_GOLD  = V6_DESIGN.color.toolLight.warning;
const V6_SURFACE = V6_DESIGN.color.toolLight.surface;
const V6_SURFACE_2 = V6_DESIGN.color.toolLight.surfaceSubtle;
const V6_OUTLINE = V6_DESIGN.color.toolLight.outline;
const V6_ACCENT_SOFT = V6_DESIGN.color.toolLight.accentSoft;
const V6_RED = V6_DESIGN.color.toolLight.danger;
// Serif display face shared by the v6 warm-paper surfaces (home/dashboard,
// calendar game header, rotation maker). Lives HERE (loaded on every v6 page)
// so pages that load calendar-v6/rotation-v6 without home-v6 — e.g. /app — don't
// hit "H6_SERIF is not defined" (flag f5eda01b, 2026-07-09).
const H6_SERIF = V6_DESIGN.font.serif;

// ── date/time helpers (v6 renames of the v5 helpers; same behavior) ─
function v6TodayDate(){
  return new Date().toLocaleDateString('en-US', { weekday:'short', month:'short', day:'numeric' });
}
function v6DateDisplay(d){ return d.toLocaleDateString('en-US', { weekday:'short', month:'short', day:'numeric' }); }
function v6DisplayToISO(display){
  if (!display) return '';
  const now = new Date();
  let parsed = new Date(`${display} ${now.getFullYear()}`);
  if (isNaN(parsed.getTime())) return '';
  if (parsed.getTime() - now.getTime() > 1000*60*60*24*183) parsed.setFullYear(now.getFullYear()-1);
  const y = parsed.getFullYear(), m = String(parsed.getMonth()+1).padStart(2,'0'), d = String(parsed.getDate()).padStart(2,'0');
  return `${y}-${m}-${d}`;
}
function v6ISOToDisplay(iso){
  if (!iso) return '';
  const [y,m,d] = iso.split('-').map(Number);
  if (!y || !m || !d) return '';
  return v6DateDisplay(new Date(y, m-1, d));
}
function v6StartToTimeInput(start){
  const m = ((Number(start)||0) % 1440 + 1440) % 1440;
  return `${String(Math.floor(m/60)).padStart(2,'0')}:${String(m%60).padStart(2,'0')}`;
}
function v6TimeInputToStart(val){
  if (!val) return null;
  const [h,mn] = val.split(':').map(Number);
  if (!Number.isFinite(h) || !Number.isFinite(mn)) return null;
  return ((h*60 + mn) % 1440 + 1440) % 1440;
}

// ── tier system ─────────────────────────────────────────────────────
// FUNCTIONALITY LADDER (Tim's Doc 03 answers, REFILED 2026-08-04 — flag
// 77fe0de8 supersedes the 07-25 filing): every rung is a capability.
// D1 Adjust — Pro Team lists NO public price: "tell them to contact sales".
// D2 Load data = Pro Team ONLY. D3 assistant on Coach, metered.
// D4 YES founding — "first 100...then probably scaling it up for all - to
//   $20/month coach and 150/month for team": first 100 coaches lock $12/mo
//   for life; Staff STAYS $100 until Tim confirms the hedged $150.
// D5 sell now — assistant went LIVE 2026-08-04 (OpenAI key in prod), so Coach
//   lists $20 with the founding $12 as the offer line (V6_COACH_AI_LIVE=true).
// D6 YES — season prices $169 Coach / $899 Staff (≈2 months free; reverses
//   the 07-25 "no i want to pay monthly" ruling).
// D7 grandfathering PERMANENT — auth.jsx sets window.__claGrandfathered from
//   account age; see v6Allows.
// The tier SIGNAL is wired production-side (useClaTier in auth.jsx); v6 only
// reads it. Legacy cla_user_data.tier values 'pro'/'premium' map via
// V6_TIER_ALIAS — never rewrite stored tiers, normalize at read.
const V6_COACH_AI_LIVE = true; // flipped 2026-08-04 — OpenAI key live in prod, assistant answers
const V6_TIERS = {
  free:    { key:'free',    label:'Free',     price:'$0',      blurb:'The full pro workflow, one team.' },
  coach:   { key:'coach',   label:'Coach',    price:'$20',     per:'/ month', season:'$169 / season',
             founding:'First 100 coaches — $12/mo, locked for life',
             blurb:'Your film works for you.' },
  staff:   { key:'staff',   label:'Staff',    price:'$100',    per:'/ month', season:'$899 / season',
             blurb:'Your staff on one team — 3 logins, +$20/seat.' },
  proteam: { key:'proteam', label:'Pro Team', price:'Contact sales', contact:true,
             blurb:'Everything, for one professional team.' },
};
const V6_TIER_ORDER = ['free','coach','staff','proteam'];
const V6_TIER_ALIAS = { pro:'coach', premium:'staff' }; // legacy stored values
function v6TierKey(t){ return V6_TIER_ALIAS[t] || t; }
// feature key → minimum tier
const V6_GATES = {
  multiTeam:     'coach',    // 2nd+ team
  import:        'coach',    // cross-team import (practices + drills)
  templates:     'coach',    // save-as-template / use-template
  printModes:    'coach',    // Activity overview + Manager view print modes
  breakdownSave: 'coach',    // save/rename/duplicate breakdown versions
  vocabCategory: 'coach',    // custom vocab categories
  clipDownload:  'coach',    // ⬇ clip/playlist MP4 downloads (tagger) — NEW rung-1 gate
  vocabClipSync: 'coach',    // vocabulary ⇄ video library sync — NEW rung-1 gate
  assistant:     'coach',    // AI assistant, metered — NEW rung-1 gate (D3)
  scoutBuilder:  'coach',    // scout builder — free keeps 2 scouts, Coach+ unlimited (v8.67)
  whiteLabel:    'staff',    // remove the print footer credit line
  sharedTeams:   'staff',    // multi-user teams — 3 logins incl., +$20/seat
  loadData:      'proteam',  // Kinexon / Smartabase — Pro Team ONLY (Tim D2)
};
// New-capability gates existing accounts keep for free, forever (D7 permanent).
const V6_GRANDFATHER_KEYS = ['clipDownload','vocabClipSync','assistant','scoutBuilder'];
// Enforcement of the NEW gates stays OFF until checkout exists — defining the
// map ≠ walling users with no way to pay. Landing/upgrade UI shows the ladder.
const V6_NEW_GATES_ENFORCED = false;
// human copy for the upgrade sheet + lock tooltips
const V6_GATE_COPY = {
  multiTeam:     ['Multiple teams', 'Run every squad you coach from one account.'],
  import:        ['Cross-team import', 'Pull practices and drill banks in from your other teams.'],
  templates:     ['Practice templates', 'Save any plan as a reusable template.'],
  printModes:    ['All print modes', 'Activity overview and Manager view sheets.'],
  breakdownSave: ['Saved breakdowns', 'Keep named team-split versions per squad size.'],
  vocabCategory: ['Custom vocab categories', 'Add your own buckets to the team vocabulary.'],
  clipDownload:  ['Clip downloads', 'Take your clips with you — single, each, stitched or burned-in MP4s.'],
  vocabClipSync: ['Vocabulary ⇄ video sync', 'Your calls and concepts carry their own film everywhere.'],
  assistant:     ['AI assistant', 'A coaching assistant that knows your practices, film and vocabulary.'],
  scoutBuilder:  ['Unlimited scouts', 'Opponent scouting reports without the two-scout limit.'],
  whiteLabel:    ['White-label printing', 'Remove the CLAP credit line from printed sheets.'],
  sharedTeams:   ['Shared teams', 'Invite your staff by link — shared practices, calendar & vocabulary.'],
  loadData:      ['Load data', 'Kinexon / Smartabase connections wired for your program.'],
};
function v6TierRank(t){ const i = V6_TIER_ORDER.indexOf(v6TierKey(t)); return i < 0 ? 0 : i; }
function v6CurrentTier(){
  const t = (typeof window !== 'undefined' && typeof window.__claTier === 'string') ? window.__claTier : 'free';
  return v6TierKey(t);
}
function useV6Tier(){
  const [tier, setTier] = React.useState(v6CurrentTier);
  React.useEffect(()=>{
    const on = ()=> setTier(v6CurrentTier());
    window.addEventListener('cla:tier-changed', on);
    return ()=> window.removeEventListener('cla:tier-changed', on);
  }, []);
  return tier;
}
function v6Allows(tier, key){
  const need = V6_GATES[key];
  if (!need) return true;
  if (V6_GRANDFATHER_KEYS.includes(key)){
    if (!V6_NEW_GATES_ENFORCED) return true;                    // no checkout yet — never wall
    if (typeof window !== 'undefined' && window.__claGrandfathered) return true; // D7 permanent
  }
  return v6TierRank(tier) >= v6TierRank(need);
}
// open the Upgrade sheet, highlighting a feature (core listens for this)
function v6OpenUpgrade(feature){
  try { window.dispatchEvent(new CustomEvent('cla:open-upgrade', { detail: { feature } })); } catch(_){}
}

// small gold lock — sits inside gated buttons/rows
function V6_Lock({ size = 11, color = V6_GOLD }){
  return (
    <svg width={size} height={size} viewBox="0 0 12 12" fill="none" stroke={color} strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}>
      <rect x="2.5" y="5.2" width="7" height="5" rx="1.2"/>
      <path d="M4 5V3.8a2 2 0 014 0V5"/>
    </svg>
  );
}
// "PRO" / "PREMIUM" pill shown next to a locked feature.
// v9 micro-floor (V9-TOKEN-NOTES): ONE size — 9.5px, the negotiated
// uppercase+tracked exception. `dense` is kept as a NO-OP alias so the many
// call sites keep working; the 8.5px dense variant is retired.
function V6_TierPill({ need = 'coach', dense }){ // eslint-disable-line no-unused-vars
  const t = V6_TIERS[v6TierKey(need)] || V6_TIERS.coach;
  return (
    <span style={{ display:'inline-flex', alignItems:'center', gap: 4, height: 18, padding:'0 7px',
      borderRadius: 999, background:`color-mix(in srgb, ${V6_GOLD} 12%, #fff)`, boxShadow:`inset 0 0 0 .5px ${V6_GOLD}66`,
      fontSize: 9.5, fontWeight: 800, letterSpacing:'.08em', textTransform:'uppercase', color:'#8a5f10',
      fontFamily: V6_FONT, whiteSpace:'nowrap' }}>
      <V6_Lock size={9} /> {t.label}
    </span>
  );
}

// ── shared atoms ────────────────────────────────────────────────────
// KIT-SPEC §1.1 button scale (v8.52 kit slice 1). `size` takes a named token —
// 'xs' | 'sm' | 'md' (default) | 'lg' | 'xl' | 'icon' — or a legacy NUMBER
// (height only; md padding/type/radius), which pre-kit call sites still use and
// which renders pixel-identical to the old numeric behavior. 'icon' is square:
// ALWAYS pass title= with it.
const V6_BTN_SIZES = V6_DESIGN.control.button;
// KIT-SPEC §1.2 state conventions — hover = bg step (+4% ink on light fills),
// disabled = .35 opacity + default cursor. `busy` convention: NO spinner — swap
// the LABEL at the call site ("Save" → "Saving…") and pass disabled while the
// write is in flight (see V6_BrandEditor / V7_DataConnections for the pattern).
// Kinds: primary (ONE per view) / outline (workhorse) / quiet / danger;
// 'dark' stays for now — it retires into the Film-Dark skin (run-mode slice).
function V6_Btn({ children, onClick, kind = 'outline', size = 'md', title, disabled, className = '', style, accent = V6_DESIGN.color.toolLight.neutralAction, ...rest }){
  const s = (typeof size === 'number')
    ? { ...V6_BTN_SIZES.md, height: size }   // legacy numeric call sites: height only
    : (V6_BTN_SIZES[size] || V6_BTN_SIZES.md);
  const base = {
    display:'inline-flex', alignItems:'center', justifyContent:'center', gap: 6,
    cursor: disabled ? 'default' : 'pointer',
    fontFamily: V6_FONT, fontWeight: 600, whiteSpace:'nowrap',
    transition:`background ${V6_DESIGN.motion.fast}ms, box-shadow ${V6_DESIGN.motion.fast}ms`, border: 0, ...s,
  };
  const kinds = {
    primary: { background: accent, color:'#fff', fontWeight: 700 },
    outline: { background:V6_SURFACE, color: V6_INK, boxShadow:`inset 0 0 0 .5px ${V6_OUTLINE}` },
    quiet:   { background:'transparent', color: V6_SUB },
    danger:  { background:V6_SURFACE, color:V6_RED, boxShadow:`inset 0 0 0 .5px color-mix(in srgb, ${V6_RED} 35%, transparent)` },
    dark:    { background: V6_INK, color:'#fff' },
  };
  const merged = { ...base, ...(kinds[kind] || kinds.outline), ...(disabled ? { opacity: V6_DESIGN.state.disabledOpacity } : {}), ...(style || {}) };
  const hoverBg = disabled ? null : ({
    primary: `color-mix(in srgb, #000 6%, ${String(merged.background)})`,
    outline: `color-mix(in srgb, #000 4%, ${V6_SURFACE})`,
    quiet:   V6_DESIGN.color.toolLight.hover || 'rgba(0,0,0,.05)',
    danger:  `color-mix(in srgb, #000 4%, ${V6_SURFACE})`,
    dark:    `color-mix(in srgb, #fff 8%, ${V6_INK})`,
  })[kinds[kind] ? kind : 'outline'];
  return (
    <button type="button" title={title} disabled={disabled} className={`cla-control cla-button cla-button-${kind} ${className}`.trim()} {...rest}
      onClick={disabled ? undefined : onClick}
      onMouseEnter={(e)=>{ if (hoverBg) e.currentTarget.style.background = hoverBg; }}
      onMouseLeave={(e)=>{ e.currentTarget.style.background = merged.background != null ? merged.background : ''; }}
      style={merged}>
      {children}
    </button>
  );
}
function V6_IconBtn({ children, onClick, title, size = 28, active, className = '', disabled, style, ...rest }){
  return (
    <button type="button" onClick={disabled ? undefined : onClick} title={title} disabled={disabled}
      className={`cla-control cla-icon-button ${className}`.trim()} aria-pressed={active ? true : undefined} {...rest}
      style={{ width: size, height: size, border: 0, borderRadius: V6_DESIGN.radius.compact, cursor:disabled?'default':'pointer',
        background: active ? V6_ACCENT_SOFT : V6_SURFACE_2, color:V6_SUB,
        display:'flex', alignItems:'center', justifyContent:'center', flexShrink: 0, ...(style || {}) }}>
      {children}
    </button>
  );
}
function V6_Toggle({ on, onChange, accent = '#16110e', disabled }){
  return (
    <button type="button" role="switch" aria-checked={!!on} disabled={disabled}
      className="cla-control cla-toggle" onClick={()=>{ if (!disabled && onChange) onChange(!on); }}
      style={{ width: 36, height: 21, padding:0, border:0, borderRadius: 999, flexShrink: 0, position:'relative', display:'inline-block',
        cursor: disabled ? 'default' : 'pointer', transition:`background ${V6_DESIGN.motion.standard}ms`,
        background: (on && !disabled) ? accent : V6_OUTLINE }}>
      <span style={{ position:'absolute', top: 2, left: (on && !disabled) ? 17 : 2, width: 17, height: 17, borderRadius:'50%',
        background:V6_SURFACE, boxShadow:'0 1px 3px rgba(0,0,0,.3)', transition:`left ${V6_DESIGN.motion.standard}ms` }} />
    </button>
  );
}
function V6_Kicker({ children, style }){
  // D9 (v6.1): courtside micro-type floor.
  return <div style={{ fontSize: 10.5, fontWeight: 800, letterSpacing:'.12em', textTransform:'uppercase',
    color: V6_FAINT, fontFamily: V6_FONT, ...(style || {}) }}>{children}</div>;
}

// Shared chrome for the left-rail panels — one consistent header (title, sub,
// full-screen + collapse) so every panel breathes the same at narrow widths.
function V6_PanelShell({ title, sub, dotColor, onClose, onOpenFull, width = 340, children, headExtra }){
  return (
    <div style={{ width, flexShrink: 0, height:'100%', background:V6_SURFACE, borderRight:`.5px solid ${V6_HAIR}`,
      display:'flex', flexDirection:'column', fontFamily: V6_FONT, minWidth: 0 }}>
      <div style={{ padding:'14px 16px 12px', borderBottom:`.5px solid ${V6_HAIR}`, flexShrink: 0 }}>
        <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', gap: 8 }}>
          <div style={{ minWidth: 0, display:'flex', alignItems:'center', gap: 8 }}>
            {dotColor && <span style={{ width: 8, height: 8, borderRadius:'50%', background: dotColor, flexShrink: 0 }} />}
            <div style={{ minWidth: 0 }}>
              <div style={{ fontSize: 14.5, fontWeight: 700, letterSpacing:'-0.01em', color: V6_INK, whiteSpace:'nowrap' }}>{title}</div>
              {sub && <div style={{ fontSize: 11, color: V6_FAINT, marginTop: 1, whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>{sub}</div>}
            </div>
          </div>
          <div style={{ display:'flex', alignItems:'center', gap: 5, flexShrink: 0 }}>
            {headExtra}
            {onOpenFull && (
              <V6_IconBtn onClick={onOpenFull} title="Open full screen" size={26}>
                <svg width="12" height="12" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M5 1.5H1.5V5M9 1.5h3.5V5M5 12.5H1.5V9M9 12.5h3.5V9"/></svg></V6_IconBtn>
            )}
            {onClose && (
              <V6_IconBtn onClick={onClose} title="Collapse" size={26}>
                <svg width="12" height="12" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"><path d="M9 2.5L4.5 7 9 11.5"/></svg></V6_IconBtn>
            )}
          </div>
        </div>
      </div>
      <div style={{ flex: 1, minHeight: 0, display:'flex', flexDirection:'column' }}>{children}</div>
    </div>
  );
}

// Default drill-type icons (flags 8ae7b899 + c72e9e71) — used when the badge
// style is 'icons'; per-team overrides live in printOpts.catIcons.
const V6_CAT_ICONS = { offense:'🏀', defense:'🛡️', scrimmage:'⚔️', shooting:'🎯',
  playerdev:'💪', conditioning:'🏃', film:'🎬', other:'📋' };

// Version chip (Tim 2026-07-10): every shipped update bumps +0.1 (v7.1 → v7.2 …);
// a full design generation jumps to the next whole number. Shown bottom-LEFT of
// SCHEME (Tim 2026-08-04, RULING): ONE decimal place only — v8.2 → v8.3 → …,
// and .9 rolls to the next integer (v8.9 → v9.0). The chip had drifted to
// three digits (v8.114); renumbered to v8.2 on his instruction. Never write
// v8.10+ as a two-digit minor again.
// the planner tab strip + landing footer. Set V6_VERSION_UPDATED (ISO) at EVERY
// ship — it drives the hover: "Updated 10m ago" under an hour, "4h ago" under a
// day, full date + time after that.
const V6_VERSION = 'v9.0';
// The Tagger source owns its build number. This mirror is protected by the
// UX-map release gate so the neighboring product tab can state the real build
// without loading a second application bundle into the Planner.
const V6_TAGGER_VERSION = 'v40.6';
const V6_VERSION_UPDATED = '2026-08-10T03:23:19Z';
function v6VersionTitle(){
  const t = new Date(V6_VERSION_UPDATED).getTime();
  const mins = Math.round((Date.now() - t) / 60000);
  if (!Number.isFinite(mins) || mins < 0) return 'Updated ' + new Date(V6_VERSION_UPDATED).toLocaleString();
  if (mins < 60) return 'Updated ' + Math.max(1, mins) + 'm ago';
  if (mins < 1440) return 'Updated ' + Math.round(mins / 60) + 'h ago';
  return 'Updated ' + new Date(V6_VERSION_UPDATED).toLocaleString();
}

// Clickable version chip (Tim 2026-07-30: "clicking on the version can you have
// that refresh and update the app if an update is available"). Click = probe
// prod for the shipped version (this file, no-store, ~20KB) and compare against
// the one running here. Newer → nudge the SW registration and reload this
// window (the network-first SW serves the fresh bundle on reload). Same →
// a quiet "✓ latest" flash, no reload, nothing to re-mount. Offline/unreadable
// probe reports honestly instead of pretending to be current.
async function v6CheckPlannerUpdate(setState){
  setState('checking');
  try {
    const res = await fetch('/planner-v6-tokens.jsx?update-check=' + Date.now(), { cache:'no-store' });
    if (!res.ok) throw new Error('probe ' + res.status);
    const m = (await res.text()).match(/V6_VERSION = '(v[\d.]+)'/);
    if (!m) throw new Error('no version marker');
    if (m[1] !== V6_VERSION){
      setState('found');
      try { const reg = await navigator.serviceWorker?.getRegistration?.(); await reg?.update?.(); } catch(_){}
      setTimeout(()=>window.location.reload(), 650);   // long enough to read "updating…"
      return;
    }
    setState('current');
  } catch(_){ setState('offline'); }
  setTimeout(()=>setState('idle'), 2200);
}

function v6PlannerVersionLabel(state){
  return state === 'checking' ? 'checking…'
    : state === 'found' ? 'updating…'
    : state === 'current' ? V6_VERSION + ' ✓ latest'
    : state === 'offline' ? V6_VERSION + ' · offline'
    : V6_VERSION;
}

function V6_VersionChip({ flat } = {}){
  // `flat` (family-mark ruling 2026-08-05): the chip's home is now Settings ▸
  // Account ▸ About — flat drops the tab-strip margins and reads a step larger.
  const [st, setSt] = React.useState('idle');   // idle | checking | current | offline | found
  const check = async ()=>{
    if (st === 'checking' || st === 'found') return;
    await v6CheckPlannerUpdate(setSt);
  };
  const label = v6PlannerVersionLabel(st);
  return (
    <button onClick={check} data-ux="version-chip-update-check"
      title={v6VersionTitle() + ' — click to check for updates'}
      style={{ marginBottom: flat ? 0 : 9, marginRight: flat ? 0 : 10, padding: 0, border: 0, background:'transparent',
        fontSize: flat ? 11.5 : 10, fontFamily: V6_MONO, color:'rgba(0,0,0,.35)', flexShrink: 0,
        userSelect:'none', cursor:'pointer' }}
      onMouseEnter={(e)=>{ e.currentTarget.style.color = 'rgba(0,0,0,.6)'; }}
      onMouseLeave={(e)=>{ e.currentTarget.style.color = 'rgba(0,0,0,.35)'; }}>
      {label}
    </button>
  );
}

Object.assign(window, {
  V6_VERSION, V6_TAGGER_VERSION, V6_VERSION_UPDATED, v6VersionTitle, v6CheckPlannerUpdate, v6PlannerVersionLabel, V6_VersionChip, V6_CAT_ICONS,
  V6_DESIGN, V6_FONT, V6_MONO, V6_INK, V6_SUB, V6_FAINT, V6_HAIR, V6_BG, V6_GOLD,
  V6_SURFACE, V6_SURFACE_2, V6_OUTLINE, V6_ACCENT_SOFT, V6_RED,
  v6TodayDate, v6DateDisplay, v6DisplayToISO, v6ISOToDisplay, v6StartToTimeInput, v6TimeInputToStart,
  V6_TIERS, V6_TIER_ORDER, V6_TIER_ALIAS, v6TierKey, V6_GATES, V6_GATE_COPY, V6_GRANDFATHER_KEYS, V6_NEW_GATES_ENFORCED, V6_COACH_AI_LIVE, v6TierRank, v6CurrentTier, useV6Tier, v6Allows, v6OpenUpgrade,
  V6_Lock, V6_TierPill, V6_Btn, V6_BTN_SIZES, V6_IconBtn, V6_Toggle, V6_Kicker, V6_PanelShell,
});
