// ─────────────────────────────────────────────────────────────────────
// i18n runtime — drop-in equivalent of i18next + react-i18next surface.
// The real production setup lives in i18n.js (shown as a deliverable).
// At runtime we use this lightweight implementation so the prototype
// works without external CDN scripts.
// ─────────────────────────────────────────────────────────────────────

// Translation dictionaries (embedded — production loads from locales/*.json
// via the http backend, but inlined here for prototype reliability).
const TRANSLATIONS = {
  en: {
    common: {
      save: 'Save', cancel: 'Cancel', continue: 'Continue', back: 'Back', next: 'Next',
      edit: 'Edit', delete: 'Delete', search: 'Search', view_all: 'See all',
      publish: 'Publish changes', discard: 'Discard', export: 'Export', new: 'New',
      branch_location: 'Branch', currency_unit: 'THB', currency_symbol: '฿',
      points: 'points', points_short: 'pts', member: 'Member', since: 'since', or: 'or',
    },
    nav: { home: 'Home', orders: 'Rewards', points: 'Points', courses: 'Courses', profile: 'Profile' },
    auth: {
      tagline: 'Aesthetic · Longevity',
      welcome_back: 'Welcome back.',
      subtitle: 'Sign in to view your treatments, points and remaining courses.',
      continue_line: 'Continue with LINE',
      continue_google: 'Continue with Google',
      continue_phone: 'Continue with phone number',
      magic_link: 'Email me a magic link',
      terms: 'By continuing you agree to our Terms\nand the storage of medical records under PDPA.',
      your_number: 'Your number',
      phone_sub: "We'll text a 6-digit code. No password to remember.",
      send_code: 'Send code',
      enter_code: 'Enter code',
      otp_sub: 'Sent to +66 {{phone}}. Try {{code}}.',
      verify: 'Verify',
      verifying: 'Verifying…',
      resend_in: 'Resend code in {{s}}s',
      resend_now: 'Tap to resend code',
      code_error: 'Code did not match. Try again.',
      link_sent: 'Link on the way',
      link_sent_sub: 'Check your inbox for a single-tap sign-in link.',
      link_opening: 'Opening link…',
      link_verifying_device: 'Verifying your device…',
      signed_in: 'Signed in',
      signed_in_sub: 'Welcome back to The Clover Clinic.',
      merge_tag: 'Legacy data found',
      merge_title: 'We found your records',
      merge_sub: 'Phone {{phone}} matches a customer file from before our app launch. Confirm to merge points, courses and visit history into your new account.',
      merge_points: 'Points', merge_visits: 'Visits', merge_spend: 'Lifetime', merge_last_visit: 'Last visit',
      merge_courses_label: 'Active courses to import',
      merge_safe_note: 'No data is overwritten. We pause for 30 days so you can opt out.',
      merge_confirm: 'Merge into my account',
      merge_not_me: "That isn’t me — skip",
      pdpa_tag: 'Privacy · PDPA',
      pdpa_title: 'Your data, your choice',
      pdpa_sub: 'Under Thailand’s Personal Data Protection Act, we need your consent to collect and use the data below. Required items are needed to deliver care.',
      pdpa_req_records: 'Medical records',
      pdpa_req_records_desc: 'Treatment history, dosages and consent forms — retained 10 years per Ministry guidelines.',
      pdpa_req_treatment: 'Treatment delivery & member identity',
      pdpa_req_treatment_desc: 'Identify you across our 8 branches and deliver the courses you have purchased.',
      pdpa_opt_marketing: 'Marketing & member offers',
      pdpa_opt_marketing_desc: 'Send tier promotions, birthday gifts and new-treatment announcements via LINE or email.',
      pdpa_opt_research: 'De-identified clinical research',
      pdpa_opt_research_desc: 'Use anonymised outcomes data to improve treatment protocols. Never shared with third parties.',
      pdpa_rights_note: 'You may withdraw consent or request export/erasure at any time from Profile · Privacy. Required consents can’t be withdrawn while you remain a patient.',
      pdpa_agreement: 'I have read and agree to the Clover Clinic Privacy Notice and consent to the items above.',
      pdpa_continue: 'Continue',
    },
    home: {
      tier_member: '{{tier}} Member',
      points_available: 'points available',
      to_next_tier: '{{amount}} more lifetime spend to {{tier}}',
      top_tier_achieved: 'Highest tier achieved',
      my_qr: 'My QR', book: 'Book', rewards: 'Rewards',
      next_visit: 'Next visit', active_courses: 'Active courses',
      for_diamond: 'For Diamond members',
      banner_title: 'Complimentary Glow IV with any Profhilo session',
      banner_tag: 'Exclusive · May',
      session_n: 'Session {{n}}',
    },
    orders: {
      title: 'Treatment history', this_year: 'This year', visits: 'Visits',
      lifetime: 'Lifetime', filter_all: 'All', filter_90: '90 days',
    },
    points: {
      clover_points: 'Clover Points', in_spend_value: '≈ {{value}} in spend value',
      earn_rule_full: 'Earn rule: {{rate}} = 1 point · {{toNext}} more to {{tier}}',
      earn_rule_simple: '{{currency}}{{rate}} = 1 point',
      redeem: 'Redeem', activity: 'Activity', tiers: 'Tiers', current: 'Current',
      diamond_perks: 'Private consultation, dedicated concierge, 2× points multipliers',
      platinum_perks: '15% off all packages, complimentary IV upgrade quarterly',
      gold_perks: '10% off facials, birthday gift, priority booking',
      silver_perks: 'Standard earn rate, member-only events',
    },
    courses: {
      remaining_courses: 'Remaining Courses',
      your_courses: 'Your courses',
      your_courses_sub: 'Prepaid treatments and reserved units, across all branches.',
      sessions_left: 'Sessions left', units_reserved: 'Units reserved',
      of_x_sessions: 'of {{total}} sessions remaining',
      of_x_units: 'of {{total}} {{unit}} left',
      next_label: 'Next:', session_log: 'Session log', usage_log: 'Usage log',
      book_next: 'Book next session', all_courses: 'All courses',
      across_n: 'across {{n}} treatments',
      tox_balance: 'Botulinum balance',
      purchased_at: 'Purchased {{date}}',
    },
    profile: {
      account: 'Account', phone: 'Phone', email: 'Email', birthday: 'Birthday',
      home_branch: 'Home branch', preferences: 'Preferences',
      treatment_reminders: 'Treatment reminders', promo_offers: 'Promotional offers',
      line_receipts: 'Receipts via LINE', language: 'Language', sign_out: 'Sign out',
    },
    qr: {
      title: 'Show this at the counter',
      subtitle: 'Generated from your member profile. Staff scan this to pull up your courses, points and treatment plan.',
      show_qr: 'Show my member QR',
      refresh_in: 'Refreshes in {{s}}s',
      scan_note: 'Auto-rotates · safe to screenshot, but the code expires every 30 seconds.',
    },
    admin: {
      console: 'Operator console',
      overview: 'Overview', branches: 'Branches', customers: 'Customers',
      import_data: 'Import data', cms_rewards: 'CMS · Rewards', settings: 'Settings',
      group_overview: 'Group overview',
      group_overview_sub: 'Week of May 11 – 17, 2026  ·  All 8 branches  ·  THB',
      last_7_days: 'Last 7 days',
      revenue: 'Revenue', visits: 'Visits', avg_ticket: 'Avg ticket', new_members: 'New members',
      vs_last_wk: 'vs last wk',
      performance_by_branch: 'Performance by branch', sorted_by_revenue: 'Sorted by revenue',
      import_title: 'Import legacy data',
      import_sub: 'Migrate customers, courses, and point balances from Excel or CSV',
      content_rules: 'Content & rules',
      content_sub: 'Customer-app banners, rewards catalogue, point economy',
      home_banners: 'Home banners', rewards_catalog: 'Rewards catalog', point_rules: 'Point rules',
      bilingual_note: 'Bilingual content · Thai and English versions are both required before publish.',
      title_th: 'Title · Thai', title_en: 'Title · English',
      desc_th: 'Description · Thai', desc_en: 'Description · English',
      image: 'Image', audience: 'Audience', point_cost: 'Point cost',
      accounting: 'Accounting',
    },
    acc: {
      title: 'Accounting export',
      subtitle: 'Finance · transactions with doctor fee (DF%) and staff commission (COMM%) breakdown',
      date_range: 'Date range', branch: 'Branch',
      doctor: 'Doctor', staff: 'Staff',
      all_branches: 'All branches', all_doctors: 'All doctors',
      all_staff: 'All staff', all_categories: 'All categories',
      treatment_category: 'Category',
      reset: 'Reset',
      gross_revenue: 'Gross revenue', doctor_fee: 'Doctor fees (DF)',
      staff_comm: 'Staff commission', net_after: 'Net after costs',
      transactions: 'Transactions',
      txn_id: 'Txn ID', date: 'Date',
      treatment: 'Treatment', revenue: 'Revenue', gross: 'Net',
      csv: 'CSV', export_xlsx: 'Export to Excel',
      exporting: 'Building workbook…',
      export_done: 'Exported {{rows}} rows to clover_accounting_2026-05.xlsx',
      rbac_scope: 'RBAC · scoped to your branch: {{branch}}. Super-admins can switch branches.',
    },
    reward: {
      title: 'New reward', subtitle: 'Bilingual content + treatment-linked grants',
      step_grant: 'Reward type',
      kind_session: 'Treatment session', kind_session_desc: 'Adds 1+ session to a course balance',
      kind_voucher: 'Voucher / discount', kind_voucher_desc: 'THB credit applied at checkout',
      kind_physical: 'Physical gift', kind_physical_desc: 'Skincare, accessory or product',
      kind_upgrade: 'Service upgrade', kind_upgrade_desc: 'Tier-up an existing treatment',
      step_link: 'Link to treatment',
      treatment_template: 'Treatment template',
      qty_sessions: 'Sessions to grant', qty_units: 'Units to grant', qty_cc: 'CC to grant',
      rule_preview: 'Rule preview',
      rule_preview_text: 'On redemption, the customer instantly receives {{qty}} {{unit}} of {{name}} as a fresh course balance.',
      step_voucher: 'Voucher details', discount_amount: 'Discount amount', valid_for: 'Validity',
      step_physical: 'Product details', sku: 'SKU', inventory: 'Inventory',
      step_upgrade: 'Upgrade details', upgrade_target: 'From → to',
      step_copy: 'Bilingual copy',
      step_publish: 'Cost & audience',
      point_cost: 'Point cost', spend_equivalent: 'in spend value',
      live_preview: 'Live preview',
    },
  },
  th: {
    common: {
      save: 'บันทึก', cancel: 'ยกเลิก', continue: 'ดำเนินการต่อ', back: 'ย้อนกลับ', next: 'ถัดไป',
      edit: 'แก้ไข', delete: 'ลบ', search: 'ค้นหา', view_all: 'ดูทั้งหมด',
      publish: 'เผยแพร่การเปลี่ยนแปลง', discard: 'ยกเลิกการเปลี่ยนแปลง', export: 'ส่งออก', new: 'สร้างใหม่',
      branch_location: 'สาขา', currency_unit: 'บาท', currency_symbol: '฿',
      points: 'คะแนน', points_short: 'คะแนน', member: 'สมาชิก', since: 'ตั้งแต่', or: 'หรือ',
    },
    nav: { home: 'หน้าหลัก', orders: 'ของรางวัล', points: 'คะแนน', courses: 'คอร์ส', profile: 'โปรไฟล์' },
    auth: {
      tagline: 'ความงาม · สุขภาพระยะยาว',
      welcome_back: 'ยินดีต้อนรับกลับ',
      subtitle: 'เข้าสู่ระบบเพื่อดูทรีตเมนต์ คะแนนสะสม และคอร์สคงเหลือของคุณ',
      continue_line: 'ดำเนินการต่อด้วย LINE',
      continue_google: 'ดำเนินการต่อด้วย Google',
      continue_phone: 'ดำเนินการต่อด้วยเบอร์โทรศัพท์',
      magic_link: 'ส่งลิงก์เข้าสู่ระบบทางอีเมล',
      terms: 'การดำเนินการต่อถือว่าคุณยอมรับข้อกำหนด\nและการจัดเก็บประวัติทางการแพทย์ตาม PDPA',
      your_number: 'เบอร์โทรศัพท์ของคุณ',
      phone_sub: 'เราจะส่งรหัส 6 หลักทาง SMS ไม่ต้องจำรหัสผ่าน',
      send_code: 'ส่งรหัส',
      enter_code: 'กรอกรหัส',
      otp_sub: 'ส่งไปยัง +66 {{phone}} ทดลองใช้รหัส {{code}}',
      verify: 'ยืนยัน',
      verifying: 'กำลังตรวจสอบ…',
      resend_in: 'ส่งรหัสใหม่ได้ในอีก {{s}} วินาที',
      resend_now: 'แตะเพื่อส่งรหัสใหม่',
      code_error: 'รหัสไม่ถูกต้อง กรุณาลองอีกครั้ง',
      link_sent: 'กำลังส่งลิงก์',
      link_sent_sub: 'กรุณาตรวจสอบกล่องอีเมลเพื่อเข้าสู่ระบบ',
      link_opening: 'กำลังเปิดลิงก์…',
      link_verifying_device: 'กำลังยืนยันอุปกรณ์ของคุณ…',
      signed_in: 'เข้าสู่ระบบแล้ว',
      signed_in_sub: 'ยินดีต้อนรับสู่ The Clover Clinic',
      merge_tag: 'พบข้อมูลเดิม',
      merge_title: 'เราพบประวัติของคุณ',
      merge_sub: 'เบอร์ {{phone}} ตรงกับประวัติลูกค้าก่อนเปิดตัวแอป ยืนยันเพื่อเชื่อมต่อคะแนน คอร์ส และประวัติการเข้ารับบริการมายังบัญชีใหม่ของคุณ',
      merge_points: 'คะแนน', merge_visits: 'จำนวนครั้ง', merge_spend: 'ยอดรวม', merge_last_visit: 'มาล่าสุด',
      merge_courses_label: 'คอร์สที่จะนำเข้า',
      merge_safe_note: 'ไม่มีข้อมูลถูกลบ ระบบจะรอ 30 วัน หากไม่ต้องการยกเลิกได้',
      merge_confirm: 'ยืนยันรวมบัญชี',
      merge_not_me: 'ไม่ใช่ตัวฉัน · ข้าม',
      pdpa_tag: 'ความเป็นส่วนตัว · PDPA',
      pdpa_title: 'ข้อมูลของคุณ คุณเป็นผู้ตัดสิน',
      pdpa_sub: 'ตาม พ.ร.บ. คุ้มครองข้อมูลส่วนบุคคลฯ เราขอความยินยอมจากคุณในการเก็บและใช้ข้อมูลข้างล่าง รายการที่จำเป็นสำหรับการรักษา',
      pdpa_req_records: 'บันทึกประวัติทางการแพทย์',
      pdpa_req_records_desc: 'ประวัติการรักษา ปริมาณยา และใบยินยอม จัดเก็บ 10 ปีตามข้อกำหนดของกระทรวงฯ',
      pdpa_req_treatment: 'การให้บริการและยืนยันตัวตนสมาชิก',
      pdpa_req_treatment_desc: 'ยืนยันตัวตนข้ามทั้ง 8 สาขา และให้บริการคอร์สที่คุณซื้อไว้',
      pdpa_opt_marketing: 'ข่าวสารการตลาดและสิทธิพิเศษ',
      pdpa_opt_marketing_desc: 'ส่งโปรโมชั่นของระดับสมาชิก ของขวัญวันเกิด และทรีตเมนต์ใหม่ ผ่าน LINE หรืออีเมล',
      pdpa_opt_research: 'งานวิจัยทางคลินิกแบบไม่ระบุตัวตน',
      pdpa_opt_research_desc: 'นำข้อมูลผลลัพธ์แบบนิรนามมาพัฒนาโปรโตคอลการรักษา ไม่มีการแบ่งปันกับบุคคลที่สาม',
      pdpa_rights_note: 'คุณสามารถถอนความยินยอม ขอสำเนาข้อมูล หรือขอลบได้ตลอดเวลาจากหน้าโปรไฟล์ · ความเป็นส่วนตัว',
      pdpa_agreement: 'ฉันได้อ่านและยินยอมประกาศความเป็นส่วนตัวของ The Clover Clinic',
      pdpa_continue: 'ดำเนินการต่อ',
    },
    home: {
      tier_member: 'สมาชิกระดับ{{tier}}',
      points_available: 'คะแนนสะสม',
      to_next_tier: 'ใช้จ่ายอีก {{amount}} เพื่อเลื่อนเป็น{{tier}}',
      top_tier_achieved: 'ระดับสูงสุดแล้ว',
      my_qr: 'QR ของฉัน', book: 'นัดหมาย', rewards: 'ของรางวัล',
      next_visit: 'นัดหมายครั้งต่อไป', active_courses: 'คอร์สที่ใช้งานอยู่',
      for_diamond: 'สำหรับสมาชิกระดับไดมอนด์',
      banner_title: 'รับ Glow IV ฟรี เมื่อทำ Profhilo ทุกครั้ง',
      banner_tag: 'สิทธิพิเศษ · พฤษภาคม',
      session_n: 'ครั้งที่ {{n}}',
    },
    orders: {
      title: 'ประวัติการรักษา', this_year: 'ปีนี้', visits: 'จำนวนครั้ง',
      lifetime: 'ตลอดอายุสมาชิก', filter_all: 'ทั้งหมด', filter_90: '90 วัน',
    },
    points: {
      clover_points: 'คะแนนสะสม Clover',
      in_spend_value: '≈ {{value}} เทียบมูลค่าการใช้จ่าย',
      earn_rule_full: 'เงื่อนไขสะสม: {{rate}} = 1 คะแนน · อีก {{toNext}} เพื่อเลื่อนเป็น{{tier}}',
      earn_rule_simple: '{{currency}}{{rate}} = 1 คะแนน',
      redeem: 'แลกของรางวัล', activity: 'ประวัติคะแนน', tiers: 'ระดับสมาชิก', current: 'ปัจจุบัน',
      diamond_perks: 'ปรึกษาส่วนตัว · คอนเซียร์จเฉพาะ · คะแนนคูณ 2 เท่า',
      platinum_perks: 'ส่วนลด 15% ทุกแพ็กเกจ · อัปเกรด IV ฟรีรายไตรมาส',
      gold_perks: 'ส่วนลด 10% เฟเชียล · ของขวัญวันเกิด · จองคิวก่อนใคร',
      silver_perks: 'อัตราสะสมคะแนนปกติ · กิจกรรมเฉพาะสมาชิก',
    },
    courses: {
      remaining_courses: 'คอร์สคงเหลือ',
      your_courses: 'คอร์สของคุณ',
      your_courses_sub: 'ทรีตเมนต์ที่ชำระล่วงหน้าและยูนิตที่จองไว้ทุกสาขา',
      sessions_left: 'เซสชันคงเหลือ', units_reserved: 'ยูนิตคงเหลือ',
      of_x_sessions: 'จากทั้งหมด {{total}} เซสชัน',
      of_x_units: 'จาก {{total}} {{unit}}',
      next_label: 'ครั้งต่อไป:', session_log: 'บันทึกการรักษา', usage_log: 'บันทึกการใช้',
      book_next: 'จองเซสชันถัดไป', all_courses: 'คอร์สทั้งหมด',
      across_n: 'จาก {{n}} ทรีตเมนต์',
      tox_balance: 'ยูนิตโบทูลินั่มคงเหลือ',
      purchased_at: 'ซื้อเมื่อ {{date}}',
    },
    profile: {
      account: 'บัญชี', phone: 'เบอร์โทรศัพท์', email: 'อีเมล', birthday: 'วันเกิด',
      home_branch: 'สาขาประจำ', preferences: 'การตั้งค่า',
      treatment_reminders: 'แจ้งเตือนการรักษา', promo_offers: 'โปรโมชันและสิทธิพิเศษ',
      line_receipts: 'รับใบเสร็จผ่าน LINE', language: 'ภาษา', sign_out: 'ออกจากระบบ',
    },
    qr: {
      title: 'แสดง QR ที่เคาน์เตอร์',
      subtitle: 'สร้างจากข้อมูลสมาชิกของคุณโดยอัตโนมัติ พนักงานสแกนเพื่อดึงคอร์ส คะแนน และแผนการรักษา',
      show_qr: 'แสดง QR สมาชิก',
      refresh_in: 'รหัสจะรีเฟรชใน {{s}} วิ',
      scan_note: 'รหัสหมุนเวียนทุก 30 วินาที · บันทึกภาพได้แต่จะใช้ไม่ได้หลังหมดเวลา',
    },
    admin: {
      console: 'ระบบจัดการสำหรับผู้ดูแล',
      overview: 'ภาพรวม', branches: 'สาขา', customers: 'ลูกค้า',
      import_data: 'นำเข้าข้อมูล', cms_rewards: 'เนื้อหา · ของรางวัล', settings: 'ตั้งค่า',
      group_overview: 'ภาพรวมเครือ',
      group_overview_sub: 'สัปดาห์ที่ 11 – 17 พ.ค. 2026  ·  ทั้ง 8 สาขา  ·  บาท',
      last_7_days: '7 วันที่ผ่านมา',
      revenue: 'รายได้', visits: 'จำนวนครั้ง', avg_ticket: 'เฉลี่ยต่อบิล', new_members: 'สมาชิกใหม่',
      vs_last_wk: 'เทียบสัปดาห์ก่อน',
      performance_by_branch: 'ผลประกอบการรายสาขา', sorted_by_revenue: 'เรียงตามรายได้',
      import_title: 'นำเข้าข้อมูลเดิม',
      import_sub: 'ย้ายข้อมูลลูกค้า คอร์ส และคะแนนสะสมจากไฟล์ Excel หรือ CSV',
      content_rules: 'เนื้อหาและกฎเกณฑ์',
      content_sub: 'แบนเนอร์แอป · รายการของรางวัล · ระบบคะแนน',
      home_banners: 'แบนเนอร์หน้าหลัก', rewards_catalog: 'รายการของรางวัล', point_rules: 'กฎคะแนน',
      bilingual_note: 'เนื้อหาสองภาษา · ต้องกรอกทั้งภาษาไทยและอังกฤษก่อนเผยแพร่',
      title_th: 'ชื่อ · ภาษาไทย', title_en: 'ชื่อ · ภาษาอังกฤษ',
      desc_th: 'คำอธิบาย · ภาษาไทย', desc_en: 'คำอธิบาย · ภาษาอังกฤษ',
      image: 'ภาพประกอบ', audience: 'กลุ่มเป้าหมาย', point_cost: 'คะแนนที่ใช้แลก',
      accounting: 'บัญชี',
    },
    acc: {
      title: 'ส่งออกรายงานบัญชี',
      subtitle: 'ฝ่ายการเงิน · รายการพร้อมการแยกค่าแพทย์ (DF%) และคอมมิชชั่นพนักงาน (COMM%)',
      date_range: 'ช่วงวันที่', branch: 'สาขา',
      doctor: 'แพทย์', staff: 'พนักงาน',
      all_branches: 'ทุกสาขา', all_doctors: 'แพทย์ทั้งหมด',
      all_staff: 'พนักงานทั้งหมด', all_categories: 'ทุกประเภท',
      treatment_category: 'ประเภท',
      reset: 'รีเซ็ต',
      gross_revenue: 'รายได้รวม', doctor_fee: 'ค่าแพทย์ (DF)',
      staff_comm: 'คอมมิชชั่นพนักงาน', net_after: 'กำไรสุทธิหลังหักต้นทุน',
      transactions: 'รายการ',
      txn_id: 'รหัสรายการ', date: 'วันที่',
      treatment: 'ทรีตเมนต์', revenue: 'รายได้', gross: 'สุทธิ',
      csv: 'CSV', export_xlsx: 'ส่งออกเป็น Excel',
      exporting: 'กำลังสร้างไฟล์…',
      export_done: 'ส่งออก {{rows}} รายการไปยัง clover_accounting_2026-05.xlsx',
      rbac_scope: 'RBAC · จำกัดขอบเขตสาขา {{branch}} Super Admin สามารถสลับสาขาได้',
    },
    reward: {
      title: 'สร้างของรางวัล', subtitle: 'เนื้อหาสองภาษา + สิทธิผูกกับทรีตเมนต์',
      step_grant: 'ประเภทของรางวัล',
      kind_session: 'เซสชันทรีตเมนต์', kind_session_desc: 'เพิ่มเซสชันลงในคอร์ส',
      kind_voucher: 'คูปอง / ส่วนลด', kind_voucher_desc: 'เครดิตบาทใช้ตอนชำระเงิน',
      kind_physical: 'ของรางวัล', kind_physical_desc: 'ผลิตภัณฑ์สกินแคร์ ของขวัญ',
      kind_upgrade: 'อัพเกรดบริการ', kind_upgrade_desc: 'ยกระดับทรีตเมนต์ที่มี',
      step_link: 'เชื่อมโยงกับทรีตเมนต์',
      treatment_template: 'เทมเพลตทรีตเมนต์',
      qty_sessions: 'จำนวนเซสชัน', qty_units: 'จำนวนยูนิต', qty_cc: 'จำนวน CC',
      rule_preview: 'ตัวอย่างกฎเกณฑ์',
      rule_preview_text: 'เมื่อแลก ลูกค้าจะได้รับ {{qty}} {{unit}} ของ {{name}} เพิ่มลงในคอร์สทันที',
      step_voucher: 'รายละเอียดคูปอง', discount_amount: 'จำนวนส่วนลด', valid_for: 'ระยะเวลา',
      step_physical: 'รายละเอียดสินค้า', sku: 'SKU', inventory: 'สต็อก',
      step_upgrade: 'รายละเอียดอัพเกรด', upgrade_target: 'จาก → ไป',
      step_copy: 'เนื้อหาสองภาษา',
      step_publish: 'ค่าคะแนนและกลุ่มเป้าหมาย',
      point_cost: 'ค่าคะแนน', spend_equivalent: 'เทียบมูลค่าการใช้จ่าย',
      live_preview: 'ตัวอย่างสด',
    },
  },
};

// Tier name lookup table — used by both customer and admin views.
const TIER_NAMES = {
  en: { silver: 'Silver', gold: 'Gold', platinum: 'Platinum', diamond: 'Diamond' },
  th: { silver: 'ซิลเวอร์', gold: 'โกลด์', platinum: 'แพลทินัม', diamond: 'ไดมอนด์' },
};

// Names of branches in Thai — for true bilingual experience
const BRANCH_NAMES_TH = {
  thonglor: 'ทองหล่อ (สำนักงานใหญ่)', phromphong: 'พร้อมพงษ์',
  siam: 'สยามพารากอน', 'central-wd': 'เซ็นทรัลเวิลด์',
  asoke: 'อโศก', chiangmai: 'นิมมาน · เชียงใหม่',
  phuket: 'ลากูน่า · ภูเก็ต', pattaya: 'ริมหาด · พัทยา',
};

// ──────────────────────────────────────────────────────────────
// Locale resolution — get(dict, "courses.remaining_courses") → string
// ──────────────────────────────────────────────────────────────
function lookup(dict, key) {
  return key.split('.').reduce((acc, k) => (acc && acc[k] != null ? acc[k] : null), dict);
}

function interpolate(str, vars = {}) {
  return String(str).replace(/\{\{(\w+)\}\}/g, (_, k) => (vars[k] != null ? vars[k] : ''));
}

// Detect from navigator with localStorage override
function detectLanguage() {
  try {
    const stored = window.localStorage.getItem('clover.lang');
    if (stored === 'en' || stored === 'th') return stored;
  } catch (_) {}
  const nav = (navigator.language || 'en').toLowerCase();
  return nav.startsWith('th') ? 'th' : 'en';
}

// ──────────────────────────────────────────────────────────────
// Context + Provider + hook (drop-in for react-i18next surface)
// ──────────────────────────────────────────────────────────────
const I18nContext = React.createContext(null);

function I18nProvider({ children, defaultLang }) {
  const [lang, setLangState] = React.useState(defaultLang || detectLanguage());

  const setLang = React.useCallback((next) => {
    setLangState(next);
    try { window.localStorage.setItem('clover.lang', next); } catch (_) {}
    if (typeof document !== 'undefined') {
      document.documentElement.setAttribute('lang', next);
    }
  }, []);

  React.useEffect(() => {
    if (typeof document !== 'undefined') document.documentElement.setAttribute('lang', lang);
  }, [lang]);

  const t = React.useCallback((key, params) => {
    const dict = TRANSLATIONS[lang] || TRANSLATIONS.en;
    const fallback = TRANSLATIONS.en;
    const found = lookup(dict, key) || lookup(fallback, key) || key;
    return interpolate(found, params);
  }, [lang]);

  // tierName / branchName helpers — convenience for non-keyed content
  const tierName = React.useCallback((id) => (TIER_NAMES[lang] || TIER_NAMES.en)[id] || id, [lang]);
  const branchName = React.useCallback((id) => {
    if (lang === 'th') return BRANCH_NAMES_TH[id] || id;
    const b = (window.BRANCHES || []).find((x) => x.id === id);
    return b ? b.name : id;
  }, [lang]);

  const value = React.useMemo(() => ({
    lang, setLang, t, tierName, branchName,
    i18n: { language: lang, changeLanguage: setLang },
  }), [lang, setLang, t, tierName, branchName]);

  return <I18nContext.Provider value={value}>{children}</I18nContext.Provider>;
}

function useTranslation() {
  const ctx = React.useContext(I18nContext);
  if (!ctx) {
    // graceful fallback when used outside provider (e.g. raw artboard preview)
    const t = (k, p) => interpolate(lookup(TRANSLATIONS.en, k) || k, p);
    return { t, lang: 'en', setLang: () => {}, tierName: (id) => TIER_NAMES.en[id] || id, branchName: (id) => {
      const b = (window.BRANCHES || []).find((x) => x.id === id);
      return b ? b.name : id;
    }, i18n: { language: 'en', changeLanguage: () => {} } };
  }
  return ctx;
}

// ──────────────────────────────────────────────────────────────
// Premium Language Switcher — used inside customer header,
// admin topbar, and the Profile screen.
// Variants: "pill" (small, for chrome) and "full" (settings row).
// ──────────────────────────────────────────────────────────────
function LanguageSwitcher({ variant = 'pill', dark = false }) {
  const { lang, setLang, t } = useTranslation();
  const isDark = dark;

  if (variant === 'full') {
    return (
      <div style={{ padding: '14px 16px', display: 'flex', alignItems: 'center',
        justifyContent: 'space-between', borderTop: '1px solid #E6E2D7' }}>
        <span style={{ color: '#14140F', fontSize: 13.5 }}>{t('profile.language')}</span>
        <SegmentedLang lang={lang} setLang={setLang} />
      </div>
    );
  }

  // pill variant — compact, sits in chrome
  return <SegmentedLang lang={lang} setLang={setLang} compact dark={isDark} />;
}

function SegmentedLang({ lang, setLang, compact = false, dark = false }) {
  const bg = dark ? 'rgba(255,255,255,0.08)' : '#fff';
  const border = dark ? '1px solid rgba(255,255,255,0.12)' : '1px solid #E6E2D7';
  const activeBg = dark ? '#C9A66B' : '#1F4435';
  const activeFg = dark ? '#1F4435' : '#fff';
  const idleFg = dark ? 'rgba(255,255,255,0.65)' : '#7A7A72';

  const padding = compact ? '3px' : '4px';
  const itemPad = compact ? '4px 12px' : '7px 16px';
  const fontSize = compact ? 11.5 : 13;

  return (
    <div role="group" aria-label="Language"
      style={{
        display: 'inline-flex', alignItems: 'center', background: bg,
        border, borderRadius: 999, padding,
        boxShadow: dark ? 'none' : '0 1px 2px rgba(0,0,0,0.04)',
      }}
    >
      {[
        { id: 'en', label: 'EN', sub: 'English' },
        { id: 'th', label: 'TH', sub: 'ไทย' },
      ].map((o) => {
        const active = lang === o.id;
        return (
          <button
            key={o.id}
            onClick={() => setLang(o.id)}
            title={o.sub}
            style={{
              padding: itemPad, borderRadius: 999, border: 'none',
              background: active ? activeBg : 'transparent',
              color: active ? activeFg : idleFg,
              fontSize, fontWeight: active ? 700 : 600,
              letterSpacing: 0.6, cursor: 'pointer',
              fontFamily: 'DM Sans, sans-serif',
              transition: 'background .15s, color .15s',
              display: 'inline-flex', alignItems: 'center', gap: 6,
            }}
          >
            <span style={{
              width: 5, height: 5, borderRadius: 3,
              background: active ? (dark ? '#1F4435' : '#C9A66B') : 'transparent',
              transition: 'background .15s',
            }} />
            {o.label}
          </button>
        );
      })}
    </div>
  );
}

Object.assign(window, {
  I18nProvider, useTranslation, LanguageSwitcher, SegmentedLang,
  TRANSLATIONS, TIER_NAMES, BRANCH_NAMES_TH,
});

// ─── LangScope ─────────────────────────────────────────────────────
// Pin a subtree to a fixed language regardless of the parent provider.
// Used by the bilingual-showcase artboards (side-by-side EN/TH cards).
function LangScope({ lang, children }) {
  const fixed = React.useMemo(() => ({
    lang,
    setLang: () => {},                  // no-op inside scope
    t: (key, params) => {
      const dict = TRANSLATIONS[lang] || TRANSLATIONS.en;
      const found = lookup(dict, key) || lookup(TRANSLATIONS.en, key) || key;
      return interpolate(found, params);
    },
    tierName: (id) => (TIER_NAMES[lang] || TIER_NAMES.en)[id] || id,
    branchName: (id) => {
      if (lang === 'th') return BRANCH_NAMES_TH[id] || id;
      const b = (window.BRANCHES || []).find((x) => x.id === id);
      return b ? b.name : id;
    },
    i18n: { language: lang, changeLanguage: () => {} },
  }), [lang]);
  return (
    <div lang={lang} style={{ display: 'contents' }}>
      <I18nContext.Provider value={fixed}>{children}</I18nContext.Provider>
    </div>
  );
}

Object.assign(window, { LangScope });
