/* eslint-disable */
;(function(){
/* eslint-disable */
// יפן 2026 — the generic guide screen (DL-057). Konbini is its first guide.
//
// A guide is sections holding rows, which is the shape tips.jsx already renders. This one
// adds a photo, a Japanese product name and a label-watch list, and groups by section into
// horizontal rails.
//
// Store-backed with a literal fallback, exactly as tips.jsx does it: `guides` /
// `guide_items` / `label_terms` come from Supabase, and FALLBACK_GUIDE below is the
// offline/dev copy used when the tables are empty (local sample backend). The literal is
// NOT the source of truth — the CSVs in export/ are.
//
// The rails are deliberately plain scroll containers, not ARIA carousels: the tiles are
// real <button>s, so keyboard reach, focus and scroll-into-view are native, and nothing
// auto-advances (so WCAG 2.2.2 does not apply). No JS reads the rail's scroll offset —
// `scrollLeft` is negative in RTL per spec, Chrome historically disagreed, and Safari
// pushes it past the maximum during overscroll.
const ASSET_G = './assets';
const { useStore: useStoreG, resolveDocUrl: resolveDocUrlG, revokeDocUrl: revokeDocUrlG } = window.JP2026Store;
const { ChibiLightbox: ChibiLightboxG, TapChibi: TapChibiG } = window.JP2026Detail || {};
const FONT_JP = "'Noto Sans JP', var(--font-ui)";

const FALLBACK_TERMS = [
  { key:'milk', jp:'乳', he:'חלב' },
  { key:'egg', jp:'卵', he:'ביצה' },
  { key:'gelatin', jp:'ゼラチン', he:"ג'לטין" },
  { key:'dashi', jp:'かつおだし', he:'ציר דגים' },
];

const FALLBACK_GUIDE = {
  guide:'konbini', title:'מה קונים בקונביני', subtitle:'מה באמת טבעוני במכולת של יפן 🏪',
  crumb:'קונביני', icon:'🏪', tint:'var(--sky-tint)', deep:'var(--sky-deep)', chibi:'konbini',
};

const FALLBACK_ITEMS = [
  { id:'f1', guide:'konbini', section:'7-Eleven', section_mark:'7', section_tint:'var(--sky-tint)', section_deep:'var(--sky-deep)', sort_order:1,
    he:'לחמניית שעועית אדומה', jp:'あんパン', chip:'vegan', note:'מגיעה חמה מהמדף — הפריט הכי מומלץ בקונביני.', watch:['milk','egg'] },
  { id:'f2', guide:'konbini', section:'7-Eleven', sort_order:2,
    he:'אוניגירי אורז חלק', jp:'塩むすび', chip:'vegan', note:'אורז ומלח בלבד. הבחירה שתמיד בטוחה, בכל שעה.', watch:['dashi'] },
  { id:'f3', guide:'konbini', section:'Lawson', section_mark:'L', section_tint:'var(--lavender-tint)', section_deep:'var(--lavender-deep)', sort_order:1,
    he:'חלב סויה בטעמים', jp:'豆乳', chip:'check', note:'סאקורה הכי טעים. בגרסאות בטעמים כדאי לעבור על התווית.', watch:['milk'] },
];

// Prose rules that sit between sections. Content, not chrome — but they belong to the guide
// substrate rather than to any one item, so they live here until a second guide needs its own.
const RULES = {
  konbini: [
    { after:'7-Eleven', emoji:'🚶', tint:'var(--sun-tint)',
      text:'לא אוכלים תוך כדי הליכה — אוכלים ליד החנות, לא בדרך ולא ברכבת' },
    { after:'Lawson', emoji:'🏷️', tint:'var(--blossom-tint)',
      text:'חוק התוויות ביפן מחייב לסמן רק אלרגנים עיקריים, אז שאריות מרכיבים מן החי עלולות לא להופיע ברשימה' },
    { after:'בכל הרשתות', emoji:'🔍', tint:'var(--matcha-tint)',
      text:'ヴィーガン על האריזה = טבעוני. ベジタリアン או プラントベース = צמחוני בלבד, ולרוב יש בפנים ביצה או חלב' },
  ],
};

// A chip value with no entry here renders nothing at all, so a typo in the CSV would drop a
// row's status silently. tests/guides-csv.test.mjs asserts every chip in export/ is defined.
const CHIPS = {
  vegan: { label:'טבעוני',      fg:'var(--vegan-full)',    bg:'var(--vegan-full-tint)' },
  check: { label:'לבדוק תווית', fg:'var(--vegan-partial)', bg:'var(--vegan-partial-tint)' },
  avoid: { label:'לא טבעוני',   fg:'var(--chip-closed)',   bg:'var(--chip-closed-tint)' },
};

const isLatin = (s) => /^[A-Za-z0-9]/.test(s || '');

function Chip({ kind, size }) {
  const c = CHIPS[kind];
  if (!c) return null;
  return (
    <span style={{ display:'inline-block', fontFamily:'var(--font-ui)', fontSize:size || 10.5,
      color:c.fg, background:c.bg, borderRadius:'var(--r-pill)', padding: size ? '4px 11px' : '2.5px 8px',
      lineHeight:1.35, whiteSpace:'nowrap' }}>{c.label}</span>
  );
}

// Photo asset slot. `photo_key` is a path in the private document bucket, resolved the same
// way quickcards.jsx resolves an uploaded card. Until a photo exists the slot renders as a
// photo well rather than an icon, so an empty guide still reads as deliberate.
function PhotoSlot({ photoKey, alt, height, radius }) {
  const [url, setUrl] = React.useState(null);
  React.useEffect(() => {
    if (!photoKey) { setUrl(null); return undefined; }
    let cancelled = false, owned = null;
    resolveDocUrlG({ path:photoKey, contentType:'image/jpeg' }).then((resolved) => {
      if (cancelled) { revokeDocUrlG(resolved); return; }
      owned = resolved; setUrl(resolved);
    }).catch(() => { if (!cancelled) setUrl(null); });
    return () => { cancelled = true; revokeDocUrlG(owned); };
  }, [photoKey]);

  if (url) return <img src={url} alt={alt || ''} style={{ display:'block', width:'100%', height,
    objectFit:'cover', borderRadius:radius || 'var(--r-photo)' }} />;
  return (
    <div role="img" aria-label={`מקום לתמונת מדף של ${alt || 'המוצר'}`} style={{ width:'100%', height,
      borderRadius:radius || 'var(--r-photo)', border:'1px dashed var(--hairline)', display:'grid',
      placeItems:'center', color:'var(--ink-300)',
      background:'repeating-linear-gradient(135deg, var(--surface-card-2) 0 7px, var(--paper-cream-deep) 7px 14px)' }}>
      <span className="ltr" style={{ fontFamily:'var(--font-mono)', fontSize:9.5, letterSpacing:0.2 }}>shelf photo</span>
    </div>
  );
}

function ProductTile({ p, onOpen, pressed }) {
  const [down, setDown] = React.useState(false);
  const isDown = pressed || down;
  return (
    <button type="button" onClick={()=>onOpen && onOpen(p)}
      onPointerDown={()=>setDown(true)} onPointerUp={()=>setDown(false)} onPointerLeave={()=>setDown(false)}
      style={{ appearance:'none', textAlign:'start', flex:'0 0 auto', width:118, padding:8, cursor:'pointer',
        scrollSnapAlign:'start', background:'var(--surface-card)', border:'var(--border-card)',
        borderRadius:'var(--r-md)', boxShadow:'var(--shadow-sm)',
        transform:isDown ? 'scale(var(--press-scale))' : 'none',
        transition:'transform var(--dur-fast) var(--ease-soft)' }}>
      <PhotoSlot photoKey={p.photo_key} alt={p.he} height={78} />
      <span style={{ marginTop:7, fontFamily:'var(--font-ui)', fontSize:11.5,
        lineHeight:1.3, color:'var(--ink-900)', display:'-webkit-box', WebkitLineClamp:2,
        WebkitBoxOrient:'vertical', overflow:'hidden' }}>{p.he}</span>
      {p.chip && <span style={{ display:'block', marginTop:5 }}><Chip kind={p.chip} /></span>}
    </button>
  );
}

function RuleCard({ rule }) {
  return (
    <div style={{ display:'flex', gap:11, alignItems:'flex-start', margin:'0 20px', padding:'13px 15px',
      background:rule.tint, borderRadius:'var(--r-lg)', boxShadow:'var(--shadow-sm)', border:'var(--border-card)' }}>
      <span aria-hidden="true" style={{ fontSize:19, lineHeight:1.3, flex:'0 0 auto' }}>{rule.emoji}</span>
      <span style={{ fontFamily:'var(--font-ui)', fontSize:13.5, lineHeight:'var(--lh-body)',
        color:'var(--ink-700)', textAlign:'start' }}>{rule.text}</span>
    </div>
  );
}

function SectionRail({ section, onOpen }) {
  const id = `guide-sec-${section.key}`;
  return (
    <section aria-labelledby={id}>
      <div style={{ display:'flex', alignItems:'center', gap:8, padding:'0 20px 8px' }}>
        <span aria-hidden="true" style={{ width:22, height:22, flex:'0 0 auto', borderRadius:7,
          background:section.tint || 'var(--paper-cream-deep)', color:section.deep || 'var(--ink-700)',
          display:'grid', placeItems:'center', fontFamily:'var(--font-ui)', fontSize:12 }}>
          <span className={isLatin(section.mark) ? 'ltr' : undefined}>{section.mark}</span>
        </span>
        <h2 id={id} style={{ fontFamily:'var(--font-display)', fontSize:23,
          color:'var(--ink-700)', margin:0, letterSpacing:0.4 }}>
          {isLatin(section.name) ? <span className="ltr">{section.name}</span> : section.name}
        </h2>
      </div>
      {/* Plain scroll container. `overscroll-behavior-x: contain` stops the swipe chaining
          out to the day pager once the rail hits its end (DL-006's lesson, other axis).
          `proximity`, NOT `mandatory`: measured at 390px, a 3-tile rail overflows by 24px
          and mandatory snapping pinned it at the first snap position — the last tile stayed
          clipped and could not be brought fully into view, by touch or by focus. Proximity
          still snaps when the scroll ends near a tile and always lets the end be reached.
          `scroll-padding-inline-start` keeps a snapped tile clear of the rail's own inset
          instead of tucking it under the edge. */}
      <div style={{ display:'flex', gap:10, overflowX:'auto', overflowY:'hidden',
        scrollSnapType:'x proximity', scrollPaddingInlineStart:20,
        overscrollBehaviorX:'contain', scrollbarWidth:'none',
        paddingInlineStart:20, paddingInlineEnd:20, paddingBottom:4 }}>
        {section.items.map(p => <ProductTile key={p.id} p={p} onOpen={onOpen} />)}
      </div>
    </section>
  );
}

function DetailSheet({ p, terms, onClose }) {
  if (!p) return null;
  const watch = (p.watch || []).map((k) => terms[k]).filter(Boolean);
  return (
    <div role="dialog" aria-modal="true" aria-label={p.he}
      style={{ position:'fixed', inset:0, zIndex:40, display:'flex', flexDirection:'column',
        justifyContent:'flex-end' }}>
      <button type="button" aria-label="סגירה" onClick={onClose}
        style={{ position:'absolute', inset:0, appearance:'none', border:'none', cursor:'pointer',
          background:'rgba(74,64,58,0.34)' }} />
      <div style={{ position:'relative', background:'var(--paper-cream)', borderTopLeftRadius:'var(--r-xl)',
        borderTopRightRadius:'var(--r-xl)', boxShadow:'var(--shadow-sheet)', padding:'10px 20px 26px',
        maxHeight:'86%', overflowY:'auto',
        animation:'jp-sheet-up var(--dur-base) var(--ease-soft) both' }}>
        <div aria-hidden="true" style={{ width:42, height:4, borderRadius:999, background:'var(--hairline)',
          margin:'0 auto 12px' }} />
        <PhotoSlot photoKey={p.photo_key} alt={p.he} height={188} radius="var(--r-lg)" />
        <h2 style={{ fontFamily:'var(--font-display)', fontSize:38, lineHeight:0.92, color:'var(--ink-900)',
          margin:'14px 0 0' }}>{p.he}</h2>
        {p.jp && (
          <div className="ltr" style={{ marginTop:6, fontFamily:FONT_JP, fontSize:20, color:'var(--ink-700)',
            userSelect:'text' }}>{p.jp}</div>
        )}
        {p.note && <p style={{ fontFamily:'var(--font-ui)', fontSize:14.5, lineHeight:'var(--lh-body)',
          color:'var(--ink-700)', margin:'10px 0 0' }}>{p.note}</p>}
        {p.chip && <div style={{ marginTop:12 }}><Chip kind={p.chip} size={12} /></div>}

        {watch.length > 0 && (
          <div style={{ marginTop:18, padding:'14px 15px', background:'var(--surface-card)',
            border:'var(--border-card)', borderRadius:'var(--r-lg)', boxShadow:'var(--shadow-sm)' }}>
            <div style={{ fontFamily:'var(--font-ui)', fontSize:12.5, color:'var(--ink-500)' }}>
              מה לחפש על התווית
            </div>
            <div style={{ display:'flex', flexWrap:'wrap', gap:8, marginTop:9 }}>
              {watch.map((w) => (
                <span key={w.key} style={{ display:'block', textAlign:'center', padding:'7px 12px',
                  background:'var(--surface-card-2)', border:'1px solid var(--hairline)',
                  borderRadius:'var(--r-sm)' }}>
                  {/* Selectable on purpose: this is the string you compare against a package. */}
                  <span className="ltr" style={{ display:'block', fontFamily:FONT_JP, fontSize:17,
                    color:'var(--ink-900)', userSelect:'text' }}>{w.jp}</span>
                  <span style={{ display:'block', fontFamily:'var(--font-ui)', fontSize:11,
                    color:'var(--ink-500)', marginTop:2 }}>{w.he}</span>
                </span>
              ))}
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

// Rows -> ordered sections. Section-level fields are denormalised onto every row by the
// seeder, but only the first row of a section is guaranteed to carry them, so the first
// non-empty value wins (a later blank must not erase the section's colour).
// Sections order by `section_sort`, items inside them by `sort_order`. Ordering on
// `sort_order` alone put every section's first row at rank 1, so the sections themselves came
// out in whatever order PostgREST happened to return — stable at three sections, visibly
// shuffled at six. Section order is data, not arrival order.
function toSections(items) {
  const order = [];
  const byName = new Map();
  const rank = (x) => (x.section_sort || 0) * 1000 + (x.sort_order || 0);
  for (const it of items.slice().sort((a, b) => rank(a) - rank(b))) {
    const name = it.section || '';
    if (!byName.has(name)) {
      byName.set(name, { key:String(order.length), name, mark:'', tint:'', deep:'', items:[] });
      order.push(name);
    }
    const sec = byName.get(name);
    sec.mark = sec.mark || it.section_mark || '';
    sec.tint = sec.tint || it.section_tint || '';
    sec.deep = sec.deep || it.section_deep || '';
    sec.items.push(it);
  }
  return order.map((n) => byName.get(n));
}

function GuideScreen({ guide = 'konbini' }) {
  const [open, setOpen] = React.useState(null);
  const [lightbox, setLightbox] = React.useState(null);

  const guideRows = useStoreG('guides');
  const itemRows = useStoreG('guide_items');
  const termRows = useStoreG('label_terms');

  // A tombstoned row stays in the table so guides-sync.js can carry the deletion to the
  // desk; it must never render (reminders.jsx:65 filters the same way).
  const meta = guideRows.find((g) => g.guide === guide && !g.deleted_at) || FALLBACK_GUIDE;
  const mine = itemRows.filter((it) => it.guide === guide && !it.deleted_at);
  const sections = toSections(mine.length ? mine : FALLBACK_ITEMS);
  const terms = Object.fromEntries((termRows.length ? termRows : FALLBACK_TERMS).map((t) => [t.key, t]));
  const rules = RULES[guide] || [];

  const CHIBI_G = `${ASSET_G}/chibi/chibi-${meta.chibi || 'wave'}.png`;
  // Breadcrumb and alt text read from the guide row, not from literals: this screen renders
  // every guide, and a hardcoded "קונביני" would follow guide #2 onto its own screen.
  const crumb = meta.crumb || meta.title;
  const chibiAlt = `משה וליעוז · ${meta.title}`;
  const chibiStyle = { position:'absolute', top:0, insetInlineEnd:12, width:66, height:66,
    borderRadius:'var(--r-md)' };

  return (
    <div style={{ paddingBottom:28 }}>
      <header style={{ position:'relative', padding:'16px 20px 8px' }}>
        {TapChibiG
          ? <TapChibiG src={CHIBI_G} alt={chibiAlt} onOpen={setLightbox} style={chibiStyle} />
          : <img src={CHIBI_G} alt={chibiAlt} style={{ ...chibiStyle, objectFit:'contain' }} />}
        <div style={{ fontFamily:'var(--font-ui)', fontSize:13, color:'var(--ink-500)' }}>{`עוד · ${crumb}`}</div>
        <h1 style={{ fontFamily:'var(--font-display)', fontSize:44, lineHeight:0.9, color:'var(--ink-900)',
          margin:'2px 0 0' }}>{meta.title}</h1>
        {meta.subtitle && (
          <div style={{ fontFamily:'var(--font-ui)', fontSize:13, color:'var(--ink-500)', marginTop:3 }}>
            {meta.subtitle}
          </div>
        )}
      </header>

      <div style={{ display:'flex', flexDirection:'column', gap:20, paddingTop:12 }}>
        {sections.map((section) => (
          <React.Fragment key={section.key}>
            <SectionRail section={section} onOpen={setOpen} />
            {rules.filter((r) => r.after === section.name).map((r, i) => <RuleCard key={i} rule={r} />)}
          </React.Fragment>
        ))}
      </div>

      <DetailSheet p={open} terms={terms} onClose={()=>setOpen(null)} />
      {lightbox && ChibiLightboxG && <ChibiLightboxG src={lightbox} alt={chibiAlt} onClose={()=>setLightbox(null)} />}
    </div>
  );
}

window.JP2026Guide = GuideScreen;
window.JP2026GuideKit = { GuideScreen, ProductTile, RuleCard, DetailSheet, Chip, PhotoSlot, toSections,
  FALLBACK_GUIDE, FALLBACK_ITEMS, FALLBACK_TERMS };

})();
