// BRAWL BALA — shared React components (images: local -> Brawlify CDN -> initials)

// Build a rarity-tinted gradient (preferred) or fall back to a hue-based palette
// when the rarity color is unknown (e.g. before data.js finishes loading).
function brawlerGradient(name) {
  const rarity = BB.brawlerRarityColor(name);
  if (rarity) {
    const c1 = BB.hexToRgba(rarity, 0.95);
    const c2 = BB.hexToRgba(rarity, 0.55);
    const c3 = BB.hexToRgba(rarity, 0.18);
    return { c1, c2, c3, ring: rarity };
  }
  const hue = BB.brawlerHue(name);
  return {
    c1: `oklch(0.75 0.22 ${hue})`,
    c2: `oklch(0.40 0.20 ${(hue + 30) % 360})`,
    c3: `oklch(0.20 0.10 ${(hue + 200) % 360})`,
    ring: `oklch(0.75 0.22 ${hue})`,
  };
}

function BrawlerImg({ name, size = 56, rounded = 0.28, ring = false, badge = null }) {
  const candidates = React.useMemo(() => {
    const arr = [BB.brawlerImg(name)];
    const remote = BB.brawlerImgRemote(name, 'border');
    if (remote) arr.push(remote);
    return arr;
  }, [name]);
  const [idx, setIdx] = React.useState(0);
  const current = candidates[idx];
  const initials = String(name || '?').split(/\s|&|-/).filter(Boolean).slice(0, 2).map(s => s[0] || '').join('').toUpperCase() || '?';
  const { c1, c2, c3, ring: ringColor } = brawlerGradient(name);
  return (
    <div style={{position:'relative',width:size,height:size,borderRadius:size*rounded,overflow:'hidden',flexShrink:0,background:`radial-gradient(circle at 30% 25%, ${c1} 0%, ${c2} 55%, ${c3} 100%)`,boxShadow: ring ? `0 0 0 2px ${ringColor}, 0 8px 24px -6px ${c2}` : 'inset 0 -8px 16px rgba(0,0,0,0.35), 0 6px 20px -8px rgba(0,0,0,0.6)'}} title={name}>
      {current ? (
        <img src={current} alt={name||''} loading="lazy" onError={()=>setIdx(i=>i+1)}
          style={{position:'absolute',inset:0,width:'100%',height:'100%',objectFit:'cover',objectPosition:'center 30%'}}/>
      ) : (<>
        <div style={{position:'absolute',inset:0,background:'repeating-linear-gradient(135deg, rgba(255,255,255,0.06) 0 2px, transparent 2px 8px)',mixBlendMode:'overlay'}}/>
        <div style={{position:'absolute',inset:0,display:'flex',alignItems:'flex-end',justifyContent:'center',fontFamily:'var(--font-display)',fontWeight:800,fontSize:size*0.42,color:'rgba(255,255,255,0.95)',textShadow:'0 2px 8px rgba(0,0,0,0.4)',paddingBottom:size*0.08,letterSpacing:'-0.04em'}}>{initials}</div>
      </>)}
      {badge && <div style={{position:'absolute',top:4,right:4,background:'rgba(0,0,0,0.5)',backdropFilter:'blur(6px)',fontSize:9,padding:'2px 5px',borderRadius:6,fontWeight:700,color:'var(--gold)',letterSpacing:'0.05em'}}>{badge}</div>}
    </div>
  );
}

function BrawlerSplash({ name, height = 260 }) {
  const candidates = React.useMemo(() => {
    const arr = [BB.brawlerImg(name)];
    const remote = BB.brawlerImgRemote(name, 'borderless');
    if (remote) arr.push(remote);
    return arr;
  }, [name]);
  const [idx, setIdx] = React.useState(0);
  const current = candidates[idx];
  const initials = String(name || '?').split(/\s|&|-/).filter(Boolean).slice(0, 2).map(s => s[0] || '').join('').toUpperCase() || '?';
  const { c1, c2, c3 } = brawlerGradient(name);
  const isBorderless = current && current.indexOf('borderless') >= 0;
  return (
    <div style={{position:'relative',height,borderRadius:16,overflow:'hidden',background:`radial-gradient(80% 90% at 60% 30%, ${c1} 0%, ${c2} 50%, ${c3} 100%)`}}>
      <div style={{position:'absolute',inset:0,background:'repeating-linear-gradient(120deg, rgba(255,255,255,0.05) 0 1px, transparent 1px 14px)'}}/>
      <div style={{position:'absolute',left:-40,top:-40,width:200,height:200,background:'radial-gradient(closest-side, rgba(255,255,255,0.35), transparent)',filter:'blur(20px)'}}/>
      {current ? (
        <img src={current} alt={name||''} onError={()=>setIdx(i=>i+1)}
          style={{position:'absolute',right:isBorderless?'50%':0,bottom:0,transform:isBorderless?'translateX(50%)':'none',height:'105%',width:'auto',objectFit:'contain',objectPosition:isBorderless?'center bottom':'right bottom',filter:'drop-shadow(0 12px 30px rgba(0,0,0,0.45))'}}/>
      ) : (
        <div style={{position:'absolute',right:-20,bottom:-40,fontFamily:'var(--font-display)',fontWeight:900,fontSize:height*1.4,lineHeight:1,color:'rgba(0,0,0,0.35)',letterSpacing:'-0.08em',userSelect:'none'}}>{initials}</div>
      )}
      <div style={{position:'absolute',inset:0,background:'linear-gradient(180deg, transparent 50%, rgba(0,0,0,0.55))'}}/>
    </div>
  );
}

function TeamLogo({ name, img, imgRemote, size = 44, rounded = 12, accent = 'var(--magenta)' }) {
  const candidates = React.useMemo(() => {
    const arr = [];
    const reject = /\/default\.png(\?|$)/i;
    if (img && !reject.test(img)) arr.push(img);
    if (imgRemote && !reject.test(imgRemote)) arr.push(BB.encodeUrlFilename(imgRemote));
    return arr;
  }, [img, imgRemote]);
  const [idx, setIdx] = React.useState(0);
  const current = candidates[idx];
  const initials = String(name || '?').split(/\s+/).filter(Boolean).slice(0, 2).map(s => s[0]).join('').toUpperCase();
  return (
    <div style={{width:size,height:size,borderRadius:rounded,background:'rgba(255,255,255,0.04)',border:'1px solid var(--line-1)',display:'flex',alignItems:'center',justifyContent:'center',overflow:'hidden',flexShrink:0,position:'relative'}} title={name}>
      {current
        ? <img src={current} alt={name||''} loading="lazy" onError={()=>setIdx(i=>i+1)} style={{width:'82%',height:'82%',objectFit:'contain'}}/>
        : <span className="display" style={{fontSize:size*0.36,color:accent,fontWeight:800}}>{initials}</span>}
    </div>
  );
}

// MapThumb — local /assets/maps/<id>.png with Brawlify CDN fallback.
function MapThumb({ mapId, name, width = 92, height = 110, rounded = 10 }) {
  const candidates = React.useMemo(() => {
    const arr = [];
    const local = BB.mapImg(mapId);
    const remote = BB.mapImgRemote(mapId);
    if (local) arr.push(local);
    if (remote) arr.push(remote);
    return arr;
  }, [mapId]);
  const [idx, setIdx] = React.useState(0);
  const current = candidates[idx];
  return (
    <div style={{width,height,borderRadius:rounded,overflow:'hidden',flexShrink:0,position:'relative',background:'rgba(255,255,255,0.04)',border:'1px solid var(--line-1)'}} title={name || ''}>
      {current
        ? <img src={current} alt={name || ''} loading="lazy" onError={()=>setIdx(i=>i+1)}
            style={{width:'100%',height:'100%',objectFit:'cover',objectPosition:'center'}}/>
        : (<div className="display" style={{position:'absolute',inset:0,display:'flex',alignItems:'center',justifyContent:'center',fontSize:11,color:'var(--text-2)',textAlign:'center',padding:6,letterSpacing:'0.04em'}}>{(name||'?').slice(0,16)}</div>)}
    </div>
  );
}

function Sparkline({ data, width = 80, height = 28, color, fillOpacity = 0.18 }) {
  if (!data || data.length < 2) return null;
  const min = Math.min(...data), max = Math.max(...data);
  const range = max - min || 1;
  const pts = data.map((v, i) => [(i / (data.length - 1)) * width, height - ((v - min) / range) * (height - 4) - 2]);
  const stroke = color || (data[data.length-1] >= data[0] ? 'var(--lime)' : 'var(--red)');
  const path = 'M ' + pts.map(p => `${p[0].toFixed(1)} ${p[1].toFixed(1)}`).join(' L ');
  const gid = 'sg-' + Math.random().toString(36).slice(2, 8);
  return (
    <svg width={width} height={height} style={{display:'block'}}>
      <defs><linearGradient id={gid} x1="0" x2="0" y1="0" y2="1">
        <stop offset="0%" stopColor={stroke} stopOpacity={fillOpacity}/>
        <stop offset="100%" stopColor={stroke} stopOpacity={0}/>
      </linearGradient></defs>
      <path d={`${path} L ${width} ${height} L 0 ${height} Z`} fill={`url(#${gid})`}/>
      <path d={path} fill="none" stroke={stroke} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
      <circle cx={pts[pts.length-1][0]} cy={pts[pts.length-1][1]} r="2.2" fill={stroke}/>
    </svg>
  );
}

function TierBadge({ tier = 'A' }) {
  const map = {
    S: { bg: 'var(--magenta)', fg: '#1A0010', glow: '0 0 14px rgba(255,46,154,0.6)' },
    A: { bg: 'var(--gold)', fg: '#1A1500', glow: '0 0 12px rgba(255,203,5,0.45)' },
    B: { bg: 'var(--cyan)', fg: '#00131A', glow: '0 0 10px rgba(34,211,238,0.4)' },
    C: { bg: 'rgba(255,255,255,0.12)', fg: 'var(--text-1)', glow: 'none' },
    D: { bg: 'rgba(255,255,255,0.08)', fg: 'var(--text-2)', glow: 'none' },
  };
  const s = map[tier] || map.C;
  return (<div className="display" style={{width:28,height:28,borderRadius:8,background:s.bg,color:s.fg,display:'flex',alignItems:'center',justifyContent:'center',fontWeight:800,fontSize:14,boxShadow:s.glow,flexShrink:0}}>{tier}</div>);
}

function ModeIcon({ mode, active = false, size = 44 }) {
  const url = BB.modeIconUrl(mode);
  const [errored, setErrored] = React.useState(false);
  return (
    <div style={{width:size,height:size,borderRadius:12,display:'flex',alignItems:'center',justifyContent:'center',background: active ? 'linear-gradient(180deg, rgba(255,46,154,0.25), rgba(255,46,154,0.08))' : 'linear-gradient(180deg, rgba(255,255,255,0.06), rgba(255,255,255,0.02))',border: `1px solid ${active ? 'var(--line-magenta)' : 'var(--line-1)'}`,color: active ? 'var(--magenta-soft)' : 'var(--text-1)',boxShadow: active ? '0 0 18px -4px rgba(255,46,154,0.5)' : 'none',transition:'all 200ms',overflow:'hidden'}}>
      {url && !errored ? <img src={url} alt={mode} onError={()=>setErrored(true)} style={{width:'70%',height:'70%',objectFit:'contain'}}/>
        : <span style={{fontSize:size*0.42,fontFamily:'var(--font-display)',fontWeight:700}}>{(BB.modeDisplay(mode) || '?').slice(0,1).toUpperCase()}</span>}
    </div>
  );
}

function LiveTag({ label = 'LIVE' }) {
  return (<div style={{display:'inline-flex',alignItems:'center',gap:6,padding:'5px 10px',borderRadius:999,background:'rgba(163,230,53,0.12)',border:'1px solid rgba(163,230,53,0.35)'}}>
    <div className="pulse-dot"/><span className="display" style={{fontSize:11,fontWeight:700,letterSpacing:'0.1em',color:'var(--lime)'}}>{label}</span>
  </div>);
}

function freshnessAgeLabel(hours) {
  if (hours == null || isNaN(hours)) return '';
  const h = Number(hours);
  if (h < 1) return '<1h';
  if (h < 48) return Math.round(h) + 'h';
  return Math.round(h / 24) + 'd';
}

function sourceIso(source) {
  if (!source) return null;
  return source.data_updated_at || source.modified_at || source.generated_at || null;
}

function sourceAge(source) {
  if (!source) return null;
  if (source.data_age_hours != null) return source.data_age_hours;
  if (source.age_hours != null) return source.age_hours;
  return null;
}

function FreshnessChip({ label, value, sub, tone = 'neutral', title }) {
  return (
    <span className="freshness-chip" data-tone={tone} title={title || ''}>
      <strong>{label}</strong>
      <span>{value || '-'}</span>
      {sub && <span className="num" style={{opacity:0.85}}>{sub}</span>}
    </span>
  );
}

function DataFreshness({ data, updatedAt }) {
  const health = (data && data.health) || {};
  const sources = health.sources || {};
  const warnings = health.warnings || [];
  const errors = health.errors || [];
  const ranked = sources.meta_atual || {};
  const refresh = sources.meta_atual_refresh || {};
  const scrims = sources.meta_pro || sources.scrims_raw || {};
  const rankedAge = sourceAge(ranked);
  const scrimsAge = sourceAge(scrims);
  const rankedFailed = refresh.status === 'failed' || errors.indexOf('meta_atual_refresh_falhou_persistente') >= 0;
  const rankedStale = rankedAge != null && rankedAge > 24;
  const scrimsStale = scrimsAge != null && scrimsAge > 6;
  const issueCount = warnings.length + errors.length;
  return (
    <div className="freshness-strip" aria-label={BB.t('fresh.label','Data freshness')}>
      <FreshnessChip label={BB.t('fresh.site','SITE')} value={BB.fmtTimestamp(updatedAt)} />
      <FreshnessChip
        label={BB.t('fresh.ranked','RANKED')}
        value={BB.fmtTimestamp(sourceIso(ranked))}
        sub={freshnessAgeLabel(rankedAge)}
        tone={rankedFailed ? 'error' : rankedStale ? 'warn' : 'ok'}
        title={rankedFailed ? BB.t('fresh.rankedFail','Ranked refresh failed; showing last good snapshot') : BB.t('fresh.rankedOk','Ranked snapshot age')}
      />
      <FreshnessChip
        label={BB.t('fresh.scrims','SCRIMS')}
        value={BB.fmtTimestamp(sourceIso(scrims))}
        sub={freshnessAgeLabel(scrimsAge)}
        tone={scrimsStale ? 'warn' : 'ok'}
        title={BB.t('fresh.scrimsTitle','Competitive scrims source age')}
      />
      {issueCount > 0 && (
        <FreshnessChip
          label={BB.t('fresh.health','HEALTH')}
          value={errors.length ? errors.length + ' ' + BB.t(errors.length === 1 ? 'fresh.error' : 'fresh.errors', errors.length === 1 ? 'erro' : 'erros') : warnings.length + ' ' + BB.t(warnings.length === 1 ? 'fresh.warning' : 'fresh.warnings', warnings.length === 1 ? 'aviso' : 'avisos')}
          tone={errors.length ? 'error' : 'warn'}
          title={(errors.concat(warnings)).join(', ')}
        />
      )}
    </div>
  );
}

function Chip({ children, active = false, accent = 'var(--magenta)', onClick, style = {} }) {
  const baseStyle = {display:'inline-flex',alignItems:'center',gap:6,padding:'7px 12px',borderRadius:12,background: active ? `color-mix(in oklab, ${accent} 18%, transparent)` : 'rgba(255,255,255,0.04)',border: `1px solid ${active ? accent : 'var(--line-1)'}`,color: active ? accent : 'var(--text-1)',fontSize:12,fontWeight:600,letterSpacing:'0.02em',cursor: onClick ? 'pointer' : 'default',transition:'all 160ms',...style};
  if (onClick) return (<button type="button" onClick={onClick} style={baseStyle}>{children}</button>);
  return (<span style={baseStyle}>{children}</span>);
}

function CountUp({ value, format, duration = 900, className = '', style = {} }) {
  const [v, setV] = React.useState(0);
  React.useEffect(() => {
    let raf, start;
    const step = (t) => {
      if (!start) start = t;
      const p = Math.min(1, (t - start) / duration);
      setV(value * (1 - Math.pow(1 - p, 3)));
      if (p < 1) raf = requestAnimationFrame(step);
    };
    raf = requestAnimationFrame(step);
    return () => cancelAnimationFrame(raf);
  }, [value, duration]);
  const fn = format || ((x) => Math.round(x).toString());
  return <span className={className} style={style}>{fn(v)}</span>;
}

function BBLogo({ size = 22 }) {
  return (<a href="/" style={{display:'flex',alignItems:'center',gap:10}}>
    <div style={{width:size*1.4,height:size*1.4,borderRadius:size*0.32,background:'conic-gradient(from 220deg, #FF2E9A, #22D3EE, #FF2E9A)',display:'flex',alignItems:'center',justifyContent:'center',boxShadow:'0 0 18px -4px rgba(255,46,154,0.6)',position:'relative'}}>
      <div style={{position:'absolute',inset:2,borderRadius:size*0.28,background:'var(--bg-0)',display:'flex',alignItems:'center',justifyContent:'center',fontFamily:'var(--font-display)',fontWeight:900,fontSize:size*0.72,color:'var(--magenta-soft)',letterSpacing:'-0.05em'}}>B</div>
    </div>
    <div className="display" style={{fontWeight:800,fontSize:size,letterSpacing:'-0.02em',lineHeight:1}}>BRAWL <span style={{color:'var(--magenta)'}}>BALA</span></div>
  </a>);
}

function NavTab({ href, label, active }) {
  return (<a href={href} className="display" style={{padding:'8px 14px',borderRadius:10,fontSize:12,fontWeight:700,letterSpacing:'0.08em',color: active ? 'var(--magenta)' : 'var(--text-1)',background: active ? 'rgba(255,46,154,0.10)' : 'transparent',border: active ? '1px solid var(--line-magenta)' : '1px solid transparent',transition:'all 160ms'}}>{label}</a>);
}

function LangToggle() {
  const cur = BB.lang();
  return (<div style={{display:'flex',gap:4,padding:'3px',borderRadius:8,background:'rgba(255,255,255,0.04)',border:'1px solid var(--line-1)'}}>
    {['pt','en'].map(l => (
      <button key={l} onClick={() => BB.setLang(l)} className="display" style={{padding:'3px 8px',borderRadius:6,fontSize:10,fontWeight:700,letterSpacing:'0.1em',background: cur === l ? 'var(--magenta)' : 'transparent',color: cur === l ? '#1A0010' : 'var(--text-2)',border:'none',cursor:'pointer'}}>{l.toUpperCase()}</button>
    ))}
  </div>);
}

function PageHeader({ active = 'meta', updatedAt, data }) {
  const status = data && data.health && data.health.status;
  const liveLabel = status === 'error' ? BB.t('header.check','CHECK') : status === 'warn' ? BB.t('header.watch','WATCH') : 'LIVE';
  return (
    <header className="app-header" style={{display:'flex',alignItems:'center',justifyContent:'space-between',gap:16,padding:'8px 12px',borderRadius:16,background:'linear-gradient(180deg, rgba(255,255,255,0.04), rgba(255,255,255,0.01))',border:'1px solid var(--line-1)',marginBottom:20,flexWrap:'wrap'}}>
      <div style={{display:'flex',alignItems:'center',gap:14,flexWrap:'wrap',minWidth:0}}>
        <BBLogo size={20}/>
        <div className="nav-tabs" style={{display:'flex',gap:4,marginLeft:10}}>
          <NavTab href="/" label={BB.t('nav.meta', 'META')} active={active === 'meta'}/>
          <NavTab href="/meta-ranked-3v3/" label={BB.t('nav.ranking', 'RANKING')} active={active === 'ranking'}/>
          <NavTab href="/bsc-2026/" label={BB.t('nav.esports', 'E-SPORTS')} active={active === 'esports' || active === 'team'}/>
        </div>
      </div>
      <div style={{display:'flex',alignItems:'center',gap:12,flexWrap:'wrap',justifyContent:'flex-end',minWidth:0}}>
        <DataFreshness data={data} updatedAt={updatedAt}/>
        <LangToggle/>
        <LiveTag label={liveLabel}/>
      </div>
    </header>
  );
}

function SectionTitle({ children, hint }) {
  return (<div style={{display:'flex',alignItems:'baseline',gap:12,margin:'8px 4px 14px',flexWrap:'wrap'}}><h2 className="display" style={{margin:0,fontSize:22,letterSpacing:'-0.02em'}}>{children}</h2>{hint && <span style={{color:'var(--text-2)',fontSize:12}}>{hint}</span>}</div>);
}

function PageLoading() {
  return (
    <div style={{display:'grid',gap:16}}>
      <div className="skel" style={{height:64,borderRadius:16}}/>
      <div className="grid-bento">
        <div className="skel" style={{height:320,borderRadius:20,gridRow:'span 2'}}/>
        <div className="skel" style={{height:140,borderRadius:20,gridColumn:'span 2'}}/>
        <div className="skel" style={{height:160,borderRadius:20}}/>
        <div className="skel" style={{height:160,borderRadius:20}}/>
      </div>
      <div className="skel" style={{height:240,borderRadius:20}}/>
    </div>
  );
}
function PageError({ error }) {
  return (
    <div className="glass" style={{padding:30,textAlign:'center'}}>
      <h2 className="display" style={{marginTop:0}}>{BB.t('error.title','Falha ao carregar dados')}</h2>
      <p style={{color:'var(--text-2)',fontSize:13}}>{String(error && error.message || error || 'Erro desconhecido')}</p>
      <button onClick={() => location.reload()} style={{marginTop:12,padding:'10px 18px',borderRadius:10,background:'var(--magenta)',color:'#1A0010',border:'none',fontWeight:700,cursor:'pointer'}}>{BB.t('error.retry','Tentar novamente')}</button>
    </div>
  );
}

function usePageData() {
  const [state, setState] = React.useState({ data: null, error: null, loading: true });
  React.useEffect(() => {
    BB.loadData()
      .then(d => setState({ data: d, error: null, loading: false }))
      .catch(e => setState({ data: null, error: e, loading: false }));
  }, []);
  return state;
}

BB.STR.en = {
  'nav.meta':'META','nav.ranking':'RANKING','nav.esports':'E-SPORTS',
  'header.updated':'UPDATED','header.check':'CHECK','header.watch':'WATCH',
  'fresh.label':'Data freshness','fresh.site':'SITE','fresh.ranked':'RANKED','fresh.scrims':'SCRIMS','fresh.health':'HEALTH',
  'fresh.rankedFail':'Ranked refresh failed; showing last good snapshot','fresh.rankedOk':'Ranked snapshot age','fresh.scrimsTitle':'Competitive scrims source age','fresh.error':'error','fresh.errors':'errors','fresh.warning':'warning','fresh.warnings':'warnings',
  'error.title':'Failed to load data','error.retry':'Try again',
  'meta.top':'TOP META','meta.panel':'Global panel','meta.sessions':'Sessions','meta.matches':'Matches','meta.today':'Today','meta.picks':'Picks (90d)',
  'meta.topteam':'Top team','meta.topmap':'Top map','meta.topmode':'Top mode',
  'meta.rising':'RISING','meta.falling':'FALLING','meta.bymode':'Meta by mode','meta.top10':'Top 10 — now',
  'meta.seeall':'See full ranking →','meta.scope':'brawlers in scope','meta.atrank':'in overall ranking',
  'rank.title':'RANKING','rank.ranked':'Ranked 3v3','rank.pro':'Pro Friendly',
  'rank.search':'Search brawler…','rank.empty':'No brawlers match these filters.','rank.lowsample':'low sample — incomplete data',
  'rank.presence':'Presence','rank.rounds':'rounds',
  'esports.banner':'BRAWL STARS CHAMPIONSHIP','esports.circuit':'CIRCUIT',
  'esports.lead':'Top 10 regional - roster, points and scrim performance. Click any team for the full profile.',
  'esports.teams':'Teams','esports.sets':'Sets','esports.topfinishes':'Top finishes','esports.other':'Other regions','esports.other.hint':'additional tracked regions',
  'team.seed':'SEED','team.champ':'★ REGIONAL CHAMPION','team.players':'players','team.prefix':'prefix','team.back':'← Back to circuit',
  'team.points':'Points','team.matchwr':'Match WR','team.setwr':'Set WR','team.setsplayed':'Sets played',
  'team.training':'Training performance','team.training.hint':'consolidated scrim data','team.sessions':'Sessions','team.rounds':'Rounds played','team.roundwr':'Round WR',
  'team.topopps':'Top opponents','team.allopps':'All','team.filtered':'filtered','team.roster':'Roster','team.playerscount':'players','team.scrims':'Recent scrims','team.matches':'matches','team.noscrims':'No scrims tracked yet.','team.nomains':'no mains tracked','team.picks':'picks',
  'team.notfound.title':'Team not found','team.choose':'Pick a team',
  'brawler.abilities':'Abilities','brawler.abilitiesHint':'star powers, gadgets and hypercharge','brawler.goodAgainst':'Strong against','brawler.goodHint':'high observed win rate','brawler.badAgainst':'Struggles against','brawler.badHint':'low observed win rate','brawler.brawlify':'Open in Brawlify',
  'common.of':'of','common.sortedBy':'sorted by','common.showMore':'Show more','common.showLess':'Show less',
};

Object.assign(window, { BrawlerImg, BrawlerSplash, TeamLogo, MapThumb, Sparkline, TierBadge, ModeIcon, LiveTag, DataFreshness, Chip, CountUp, BBLogo, PageHeader, SectionTitle, PageLoading, PageError, usePageData, LangToggle });
