// ============================================
// Reliq — Policy Library
// ============================================

const POLICY_CATEGORIES = [
  { id:"play",     label:"Google Play",          icon:"android", count:12 },
  { id:"app",      label:"Apple App Store",       icon:"apple",   count:11 },
  { id:"privacy",  label:"Privacy",               icon:"eye",     count:8 },
  { id:"permissions", label:"Permissions",        icon:"shield",  count:7 },
  { id:"sdks",     label:"SDKs & Trackers",       icon:"cpu",     count:5 },
  { id:"security", label:"Security",              icon:"lock",    count:7 },
  { id:"metadata", label:"Metadata",              icon:"package", count:5 },
  { id:"listing",  label:"Store Listing",         icon:"sparkles",count:4 },
  { id:"platform", label:"Platform Requirements", icon:"layers",  count:4 },
  { id:"content",  label:"Content Policy",        icon:"book",    count:4 },
  { id:"upcoming", label:"Upcoming Changes",      icon:"calendar",count:4 },
];

const POLICY_CHECKS = {
  play: [
    { code:"DATA_SAFETY_COMPLETENESS", name:"Data Safety Form Completeness", sev:"critical",
      desc:"The Data Safety section in Play Console must accurately declare all data types collected, shared, and processed — including data collected via third-party SDKs. Mismatches trigger automated rejection.",
      updatedAt:"Aug 2025", stores:["play"],
      check:"Decompiles the APK/AAB and identifies all third-party SDKs. Cross-references SDK known-data footprints against your Data Safety form declaration.",
      trigger:"Finding raised when detected SDK data collection (Device ID, IP, App events) is not reflected in the Data Safety form, or when the form declares 'no sharing' but trackers are present.",
      why:"Data Safety mismatches are the #1 Google Play rejection reason. Automated review flags the discrepancy within hours of submission.",
      fix:"Open Play Console → Store presence → Data Safety. For each detected SDK, add entries under 'Data shared with third parties' with the correct data types and purposes.",
      relatedChecks:["DATA_SAFETY_COMPLETENESS","ADVERTISING_ID_DECLARATION","TRACKER_SDK_DISCLOSURE"] },
    { code:"TARGET_SDK_REQUIREMENT", name:"Target SDK Level Requirements", sev:"critical",
      desc:"All new apps and updates must target API level 34 (Android 14) or higher. Apps targeting API 33 or below are rejected at submission. Apps published before Aug 2024 require an update by Nov 2024.",
      updatedAt:"Aug 2024", stores:["play"],
      check:"Reads targetSdkVersion from the compiled AndroidManifest.xml inside the APK/AAB.",
      trigger:"Finding raised when targetSdkVersion < 34.",
      why:"Google Play rejects all new submissions and updates targeting below API 34 since 31 Aug 2024. Existing non-updated apps are hidden from users on newer Android versions.",
      fix:"In app/build.gradle, set targetSdkVersion 34. Test scoped storage, photo picker, foreground service types, and POST_NOTIFICATIONS permission before re-submitting.",
      relatedChecks:["TARGET_SDK_ANDROID"] },
    { code:"ADVERTISING_ID_DECLARATION", name:"Advertising ID Declaration", sev:"warning",
      desc:"Apps using the Advertising ID (GAID) must declare this in the Data Safety form under 'Device or other IDs'. Apps targeting children are prohibited from using the Advertising ID.",
      updatedAt:"Jan 2024", stores:["play"] },
    { code:"BACKGROUND_LOCATION_POLICY", name:"Background Location Access", sev:"warning",
      desc:"Apps requesting ACCESS_BACKGROUND_LOCATION must submit a Permissions Declaration form and demonstrate a core use case that requires background access. The declaration is reviewed manually.",
      updatedAt:"May 2023", stores:["play"] },
    { code:"DANGEROUS_PERMISSIONS_AUDIT", name:"Dangerous Permissions Declaration", sev:"warning",
      desc:"Dangerous permissions must have a clear, user-facing purpose consistent with the app category. Unused dangerous permissions must be removed.",
      updatedAt:"Mar 2023", stores:["play"] },
    { code:"PRIVACY_POLICY_REQUIREMENT", name:"Privacy Policy Requirement", sev:"warning",
      desc:"All apps must link a valid, publicly accessible privacy policy URL that is reachable via HTTPS without redirects. The policy must describe data collection and use comprehensively.",
      updatedAt:"Jan 2023", stores:["play"] },
    { code:"MISLEADING_METADATA", name:"Misleading Metadata Policy", sev:"warning",
      desc:"App titles, descriptions, icons, and screenshots must not mislead users about the app's functionality or impersonate other apps or brands. AI-generated content in listings must be disclosed.",
      updatedAt:"Sep 2024", stores:["play"] },
    { code:"FAMILIES_POLICY", name:"Families Policy (Kids Apps)", sev:"critical",
      desc:"Apps targeting children must comply with the Families Policy, which prohibits advertising IDs, behavioural advertising, and many third-party SDKs. Content must be age-appropriate.",
      updatedAt:"Apr 2025", stores:["play"] },
    { code:"FINANCIAL_FEATURES_POLICY", name:"Financial Features Policy", sev:"warning",
      desc:"Apps offering financial services (loans, investment, insurance) require specific declarations and may require government licensing proof in certain regions.",
      updatedAt:"Jun 2024", stores:["play"] },
    { code:"CONTENT_RATING_CONSISTENCY", name:"Content Rating Consistency", sev:"info",
      desc:"The declared content rating must be consistent with signals detected in the app — screenshots, strings, and UI. Inaccurate ratings trigger manual review.",
      updatedAt:"Jan 2023", stores:["play"] },
    { code:"CLONED_APP_POLICY", name:"Cloned App Policy", sev:"critical",
      desc:"Apps must not copy or closely replicate another app's functionality, UI, or brand without authorisation. Clone detection uses visual fingerprinting and code similarity analysis.",
      updatedAt:"Mar 2024", stores:["play"] },
    { code:"GOOGLE_PLAY_BILLING", name:"Google Play Billing Requirement", sev:"critical",
      desc:"Apps distributing on Google Play must use Google Play Billing for all digital goods and subscriptions sold within the app. Alternative payment methods are prohibited for in-app purchases.",
      updatedAt:"Nov 2023", stores:["play"] },
  ],
  app: [
    { code:"PRIVACY_MANIFEST_REQUIRED", name:"Privacy Manifest (PrivacyInfo.xcprivacy)", sev:"critical",
      desc:"Since Spring 2024, all apps must include a PrivacyInfo.xcprivacy file declaring required reason APIs (NSUserDefaults, FileManager, etc.) and third-party SDK privacy manifests must be bundled.",
      updatedAt:"Mar 2024", stores:["app"],
      check:"Extracts PrivacyInfo.xcprivacy from the IPA and validates NSPrivacyAccessedAPITypes entries for all required reason APIs used by the app and bundled SDKs.",
      trigger:"Finding raised when PrivacyInfo.xcprivacy is missing, when required API reason entries are absent, or when bundled SDK privacy manifests are not included.",
      why:"Apple rejects submissions missing required Privacy Manifest entries. As of Spring 2024, this is an automated check that blocks submission before App Review.",
      fix:"Add PrivacyInfo.xcprivacy to your Xcode target. For each required reason API (NSUserDefaults, NSFileManager, etc.), add the appropriate reason code. Validate with Xcode's privacy report.",
      relatedChecks:["PRIVACY_MANIFEST_INCOMPLETE","PRIVACY_MANIFEST_REQUIRED"] },
    { code:"NUTRITION_LABEL_ACCURACY", name:"Privacy Nutrition Label Accuracy", sev:"warning",
      desc:"The App Store Connect Privacy Nutrition Label must accurately reflect all data types collected by the app and its third-party SDKs. Inaccurate labels result in App Review rejection.",
      updatedAt:"Dec 2022", stores:["app"],
      check:"Detects location API usage precision (kCLLocationAccuracyBest vs. reduced), advertising frameworks, and SDK data footprints. Compares against declared nutrition label data.",
      trigger:"Finding raised when precise location is used but only approximate location is declared, or when a tracked SDK collects data types absent from the label.",
      why:"App Review manually verifies nutrition labels for high-risk apps. Inaccurate labels cause rejection and repeat submissions delay app updates.",
      fix:"In App Store Connect → App Privacy, update each data type section to match what your app and its SDKs actually collect. Set collection purpose accurately.",
      relatedChecks:["NUTRITION_LABEL_MISMATCH","ATT_REQUIREMENT"] },
    { code:"ATT_REQUIREMENT", name:"App Tracking Transparency (ATT)", sev:"warning",
      desc:"Apps must request user permission via ATT before accessing the IDFA or tracking users across apps and websites. ATT usage description strings must be clear and accurate.",
      updatedAt:"Apr 2021", stores:["app"] },
    { code:"ATS_POLICY", name:"App Transport Security (ATS)", sev:"warning",
      desc:"Apps must use HTTPS for all network connections. Exceptions require justification via NSExceptionDomains with documented reasons. Cleartext HTTP is disallowed by default.",
      updatedAt:"Jan 2017", stores:["app"] },
    { code:"ENTITLEMENTS_POLICY", name:"Entitlements Usage Requirement", sev:"warning",
      desc:"All sensitive entitlements (HealthKit, HomeKit, NFC, etc.) must be actively used by the app. Unused entitlements cause App Review rejection. Entitlements must match declared functionality.",
      updatedAt:"Jun 2023", stores:["app"] },
    { code:"MINORS_PRIVACY", name:"Privacy for Minors (COPPA / Kids Category)", sev:"critical",
      desc:"Apps in the Kids category or apps directed at children must not include advertising networks, third-party analytics, or external links without parental gateway. COPPA compliance is required.",
      updatedAt:"Apr 2024", stores:["app"] },
    { code:"APPLE_SIGN_IN", name:"Sign in with Apple Requirement", sev:"warning",
      desc:"Apps that support third-party sign-in options (Google, Facebook, etc.) must also support Sign in with Apple as an equivalent option.",
      updatedAt:"Jun 2020", stores:["app"] },
    { code:"APP_REVIEW_GUIDELINES_4_0", name:"Design — Copycat Apps", sev:"critical",
      desc:"Apps that copy another app's ideas and present them without meaningful differentiation will be rejected. The app must provide unique functionality or content.",
      updatedAt:"Sep 2024", stores:["app"] },
    { code:"METADATA_ACCURACY", name:"Metadata Accuracy", sev:"warning",
      desc:"App metadata (name, subtitle, description, keywords, screenshots) must accurately represent the app. Keyword stuffing, misleading screenshots, and irrelevant keywords cause rejection.",
      updatedAt:"Jan 2023", stores:["app"] },
    { code:"IN_APP_PURCHASE", name:"In-App Purchase Requirement", sev:"critical",
      desc:"Apps must use Apple's In-App Purchase system for all digital goods and subscriptions. External payment links or calls-to-action to purchase outside the app are prohibited.",
      updatedAt:"Nov 2023", stores:["app"] },
    { code:"NOTCH_SAFEAREA", name:"Display Cutout & Safe Area", sev:"info",
      desc:"Apps must support all current iPhone and iPad screen sizes including Dynamic Island, notch, and rounded corners. Letterboxing or black-barring is a rejection reason.",
      updatedAt:"Sep 2023", stores:["app"] },
  ],
  privacy: [
    { code:"DATA_MINIMISATION", name:"Data Minimisation Principle", sev:"warning",
      desc:"Apps should collect only the data strictly necessary for their declared functionality. Collecting data 'just in case' violates both store policies and GDPR/CCPA principles.",
      updatedAt:"Jan 2024", stores:["play","app"] },
    { code:"GDPR_CONSENT", name:"GDPR Consent Mechanism", sev:"warning",
      desc:"Apps distributed to EU/EEA users must implement a GDPR-compliant consent mechanism before processing personal data for advertising or analytics. Pre-ticked boxes are invalid.",
      updatedAt:"May 2023", stores:["play","app"] },
    { code:"CCPA_OPT_OUT", name:"CCPA / CPRA Opt-Out Requirement", sev:"info",
      desc:"Apps serving California users must provide a 'Do Not Sell or Share My Personal Information' opt-out mechanism accessible from the app's settings.",
      updatedAt:"Jan 2023", stores:["play","app"] },
    { code:"CHILDREN_PRIVACY", name:"Children's Privacy (COPPA)", sev:"critical",
      desc:"Apps knowingly directed at children under 13 must comply with COPPA: no behavioural advertising, no persistent identifiers, parental consent for data collection.",
      updatedAt:"Apr 2024", stores:["play","app"] },
    { code:"DATA_DELETION", name:"Data Deletion Mechanism", sev:"warning",
      desc:"Apps that allow account creation must offer an in-app mechanism for users to request account and data deletion. Both stores have enforced this since 2022.",
      updatedAt:"Jun 2022", stores:["play","app"] },
    { code:"PRIVACY_POLICY_CONTENT", name:"Privacy Policy Content Requirements", sev:"warning",
      desc:"Privacy policies must disclose: what data is collected, how it's used, with whom it's shared, and how users can exercise their rights. Vague or incomplete policies are rejected.",
      updatedAt:"Jan 2024", stores:["play","app"] },
    { code:"LOCATION_TRANSPARENCY", name:"Location Data Transparency", sev:"warning",
      desc:"Apps using location data must clearly communicate why it's needed, at what precision, and when it's collected. Precise location must not be collected when approximate suffices.",
      updatedAt:"Mar 2023", stores:["play","app"] },
    { code:"SENSITIVE_DATA_HANDLING", name:"Sensitive Data Handling", sev:"critical",
      desc:"Health data, biometric data, financial data, and authentication credentials must be stored encrypted at rest and transmitted only over HTTPS. Leaking sensitive data to logs or analytics is a critical violation.",
      updatedAt:"Jun 2024", stores:["play","app"] },
  ],
  permissions: [
    { code:"CONTACTS_POLICY", name:"READ_CONTACTS / Contacts Access", sev:"warning",
      desc:"Access to the user's contacts requires a clear, user-facing purpose. Apps must implement a Permissions Declaration (Google Play) and Privacy Nutrition Label entry (App Store). Bulk contact upload to servers requires explicit user consent.",
      updatedAt:"Mar 2023", stores:["play","app"] },
    { code:"LOCATION_BACKGROUND", name:"Background Location Access", sev:"critical",
      desc:"Background location is a sensitive permission requiring manual review on both platforms. Core use case must justify always-on location. Foreground-only apps must not request background location.",
      updatedAt:"May 2023", stores:["play","app"] },
    { code:"MICROPHONE_POLICY", name:"Microphone Access", sev:"warning",
      desc:"Microphone access must be justified by a clear in-app feature. Recording without UI indication violates both store policies. Audio must not be transmitted or stored without explicit user disclosure.",
      updatedAt:"Jan 2023", stores:["play","app"] },
    { code:"CAMERA_POLICY", name:"Camera Access", sev:"info",
      desc:"Camera access is common but must be scoped correctly. Apps must not activate the camera without user-visible indication. Background camera use is prohibited.",
      updatedAt:"Jan 2023", stores:["play","app"] },
    { code:"STORAGE_SCOPED", name:"Storage — Scoped Storage (Android 10+)", sev:"warning",
      desc:"Apps targeting Android 10+ must use scoped storage APIs. MANAGE_EXTERNAL_STORAGE requires manual review and a declared use case. READ_EXTERNAL_STORAGE is deprecated in API 33+.",
      updatedAt:"Aug 2023", stores:["play"] },
    { code:"HEALTH_DATA_ENTITLEMENT", name:"HealthKit / Health Data Entitlement", sev:"warning",
      desc:"HealthKit entitlement must be actively used. Health data must not be shared with advertisers or analytics providers without explicit user consent. Apple reviews all HealthKit apps manually.",
      updatedAt:"Jun 2023", stores:["app"] },
    { code:"SENSITIVE_PERMISSION_DECLARATION", name:"Sensitive Permissions Declaration Form", sev:"warning",
      desc:"Google Play requires a Permissions Declaration form for apps requesting certain sensitive permissions (background location, accessibility services, device admin, etc.). Failure to complete the form causes rejection.",
      updatedAt:"Feb 2024", stores:["play"] },
  ],
  sdks: [
    { code:"TRACKER_SDK_DISCLOSURE", name:"Third-Party Tracker SDK Disclosure", sev:"critical",
      desc:"All SDKs that collect or share user data must be declared in the Google Play Data Safety form and the Apple App Store Privacy Nutrition Label. Undisclosed trackers are the most common rejection reason.",
      updatedAt:"Jun 2023", stores:["play","app"],
      check:"Decompiles the binary and identifies embedded SDKs by fingerprint, class names, and entrypoint signatures. Cross-references against Reliq's tracker database of 400+ known SDKs.",
      trigger:"Finding raised when an SDK with a known data footprint is detected in the binary but not declared in the applicable store privacy form.",
      why:"Both stores use automated scanners to detect undisclosed trackers. Google Play can auto-reject within minutes; Apple App Review raises it during manual review.",
      fix:"For each flagged SDK, add the correct data types and purposes to your Data Safety form (Play Console) and Privacy Nutrition Label (App Store Connect). Remove unused SDKs.",
      relatedChecks:["DATA_SAFETY_COMPLETENESS","NUTRITION_LABEL_ACCURACY","OUTDATED_SDK_VERSION"] },
    { code:"SDK_FAMILIES_COMPLIANCE", name:"SDK Compliance — Families / Kids Policy", sev:"critical",
      desc:"SDKs used in apps targeting children must be approved for use with minors. Google's Families Self-Certified SDK programme and Apple's App Store Kids Category restrictions apply.",
      updatedAt:"Apr 2024", stores:["play","app"] },
    { code:"OUTDATED_SDK_VERSION", name:"Outdated SDK Version Risk", sev:"warning",
      desc:"Significantly outdated SDK versions may contain unpatched vulnerabilities or non-compliant behaviour. Both stores actively monitor for known-vulnerable SDK versions.",
      updatedAt:"Jan 2024", stores:["play","app"] },
    { code:"ADVERTISING_SDK_POLICY", name:"Advertising SDK Policy", sev:"warning",
      desc:"Advertising SDKs must comply with each store's advertising policies. Mediation networks must only include store-approved ad SDKs. Rewarded ads must not be used in Kids apps.",
      updatedAt:"Mar 2024", stores:["play","app"] },
    { code:"ANALYTICS_SDK_GDPR", name:"Analytics SDK & GDPR Consent", sev:"warning",
      desc:"Analytics SDKs (Firebase, Amplitude, Mixpanel, etc.) must be initialised only after obtaining valid GDPR consent for EU users. SDKs must be configured to respect consent signals.",
      updatedAt:"May 2023", stores:["play","app"] },
  ],
  security: [
    { code:"CLEARTEXT_TRAFFIC", name:"Cleartext Traffic Policy", sev:"critical",
      desc:"Apps must not transmit sensitive data over unencrypted HTTP. Android Network Security Config must set cleartextTrafficPermitted=false. iOS ATS enforces HTTPS by default.",
      updatedAt:"Jan 2017", stores:["play","app"],
      check:"Parses Android Network Security Config XML and iOS Info.plist for ATS exceptions. Performs static analysis on URL strings in the binary for HTTP endpoints.",
      trigger:"Finding raised when usesCleartextTraffic=true in manifest, when an NSExceptionAllowsInsecureHTTPLoads exception is present without justification, or when http:// URLs are found in network call sites.",
      why:"Cleartext traffic exposes user data to interception. Both stores reject or flag apps that transmit sensitive data over HTTP.",
      fix:"Android: set android:usesCleartextTraffic=\"false\" in AndroidManifest.xml or Network Security Config. iOS: remove ATS exceptions and ensure all endpoints use HTTPS.",
      relatedChecks:["CLEARTEXT_TRAFFIC","ATS_POLICY"] },
    { code:"HARDCODED_SECRETS", name:"Hardcoded Secrets & API Keys", sev:"critical",
      desc:"API keys, secrets, passwords, and tokens must not be embedded in the app binary. Use secure secrets management (environment variables, key stores, or secure enclave).",
      updatedAt:"Jan 2023", stores:["play","app"],
      check:"Scans decompiled source code for high-entropy strings matching patterns for API keys, auth tokens, private keys, and connection strings. Uses Reliq's regex + entropy analysis.",
      trigger:"Finding raised when strings matching known secret patterns (AWS keys, Stripe keys, private SSH keys, JWT secrets, etc.) are found in the compiled binary.",
      why:"Hardcoded secrets are extractable from any APK or IPA in minutes. Exposed keys lead to API abuse, data breaches, and store removal.",
      fix:"Move all secrets to environment variables injected at build time, or use a secure vault (AWS Secrets Manager, HashiCorp Vault). For Android, use the Android Keystore for runtime key storage.",
      relatedChecks:["HARDCODED_SECRETS_SCAN"] },
    { code:"CERTIFICATE_TRANSPARENCY", name:"Certificate Transparency & Pinning", sev:"info",
      desc:"Certificate pinning is recommended for apps handling sensitive data. Certificate Transparency logs help detect mis-issued certificates. Both can be validated via Reliq's security checks.",
      updatedAt:"Jun 2023", stores:["play","app"] },
    { code:"DEBUGGABLE_RELEASE", name:"Debuggable Release Build", sev:"critical",
      desc:"Release builds must not have android:debuggable=true in the manifest or DEBUG flag enabled. Debuggable builds expose the app to runtime manipulation and are rejected by Google Play.",
      updatedAt:"Jan 2017", stores:["play"] },
    { code:"EXPORTED_COMPONENTS", name:"Exported Component Security", sev:"warning",
      desc:"Android Activities, Services, and BroadcastReceivers with android:exported=true must enforce appropriate permissions. Exported components without permission checks are exploitable.",
      updatedAt:"May 2022", stores:["play"] },
    { code:"ROOT_EMULATOR_DETECTION", name:"Root & Emulator Detection", sev:"info",
      desc:"For apps handling financial data, health records, or authentication, root detection and emulator detection are recommended. Both reduce fraud and reverse-engineering risk.",
      updatedAt:"Jan 2024", stores:["play","app"] },
    { code:"OBFUSCATION_PROGUARD", name:"Code Obfuscation (ProGuard/R8)", sev:"info",
      desc:"Release builds should use ProGuard or R8 to obfuscate class names, method names, and string constants. Unobfuscated code makes reverse-engineering trivial.",
      updatedAt:"Jan 2023", stores:["play"] },
  ],
  metadata: [
    { code:"TITLE_LENGTH_PLAY", name:"Title Length — Google Play", sev:"info",
      desc:"App titles must be 30 characters or fewer. Keyword stuffing in titles (e.g. 'App Name — Best Free Tool 2024') violates Google Play metadata policy.",
      updatedAt:"Jan 2024", stores:["play"] },
    { code:"TITLE_LENGTH_IOS", name:"App Name & Subtitle — Apple App Store", sev:"info",
      desc:"App name (30 chars max) and subtitle (30 chars max). Names may not include generic descriptions like 'app' or category names. Subtitles must describe a key feature.",
      updatedAt:"Jan 2023", stores:["app"] },
    { code:"DESCRIPTION_KEYWORD_SPAM", name:"Description Keyword Spam", sev:"warning",
      desc:"Long descriptions must not repeat keywords excessively. Both stores use automated classifiers to detect keyword stuffing. Descriptions should be written for humans, not search algorithms.",
      updatedAt:"Mar 2024", stores:["play","app"] },
    { code:"ICON_REQUIREMENTS", name:"Icon Requirements", sev:"info",
      desc:"Google Play: 512×512 PNG, no pre-rounded corners, no alpha transparency. Apple App Store: 1024×1024 PNG, no transparency, no App Store badge or Apple imagery.",
      updatedAt:"Jan 2024", stores:["play","app"] },
    { code:"SCREENSHOT_REQUIREMENTS", name:"Screenshot Requirements", sev:"info",
      desc:"Google Play requires at least 2 phone screenshots. Apple requires 6.7\" and 5.5\" screenshots. Screenshots must reflect the app's actual UI, not mock-ups or third-party content.",
      updatedAt:"Jun 2024", stores:["play","app"] },
  ],
  listing: [
    { code:"FEATURE_GRAPHIC", name:"Feature Graphic — Google Play", sev:"info",
      desc:"A 1024×500 feature graphic is required for Google Play store listings. It must not include device frames or simulated user interfaces.",
      updatedAt:"Jan 2023", stores:["play"] },
    { code:"PROMO_TEXT", name:"Promotional Text — Apple App Store", sev:"info",
      desc:"Promotional text (170 chars max) appears above the description and can be updated without a new app submission. Use it for time-sensitive messaging.",
      updatedAt:"Jan 2023", stores:["app"] },
    { code:"STORE_LISTING_ASSETS", name:"Store Listing Assets Quality", sev:"warning",
      desc:"Store listing assets (screenshots, previews, feature graphics) must be high quality and accurately represent the app. Blurry, outdated, or misleading assets cause rejection.",
      updatedAt:"Sep 2024", stores:["play","app"] },
    { code:"APP_PREVIEW_VIDEOS", name:"App Preview Videos", sev:"info",
      desc:"App preview videos must show only the app's UI — no simulated device interactions, no marketing claims, no 'Best App' language. Apple rejects videos with third-party content.",
      updatedAt:"Jun 2023", stores:["play","app"] },
  ],
  platform: [
    { code:"TARGET_SDK_ANDROID", name:"Target SDK — Android", sev:"critical",
      desc:"API 34 required for all new submissions since Aug 31, 2024. Existing apps must update by Nov 1, 2024. Apps will be hidden from users on newer Android versions if they target below API 24.",
      updatedAt:"Aug 2024", stores:["play"] },
    { code:"IOS_VERSION_MINIMUM", name:"Minimum iOS Version Support", sev:"info",
      desc:"Apple recommends supporting the current and two previous major iOS versions. Apps targeting iOS versions below Apple's threshold may be flagged during App Review.",
      updatedAt:"Sep 2024", stores:["app"] },
    { code:"IPADOS_SUPPORT", name:"iPadOS Support", sev:"info",
      desc:"Apps submitted to the App Store must be compatible with iPadOS unless specifically excluded. Universal binaries (arm64) must run on both iPhone and iPad.",
      updatedAt:"Jan 2023", stores:["app"] },
    { code:"64BIT_REQUIREMENT", name:"64-bit Architecture Requirement", sev:"critical",
      desc:"Both stores require 64-bit builds. 32-bit-only apps are rejected. Android: no armeabi-v7a-only APKs. iOS: arm64 is required for all submissions.",
      updatedAt:"Jan 2020", stores:["play","app"] },
  ],
  content: [
    { code:"CONTENT_RATING", name:"Content Rating Accuracy", sev:"warning",
      desc:"Declared content ratings must match the actual content. Both stores use automated and manual checks to verify content rating consistency.",
      updatedAt:"Jan 2024", stores:["play","app"] },
    { code:"USER_GENERATED_CONTENT", name:"User-Generated Content Moderation", sev:"warning",
      desc:"Apps with UGC features must have clear reporting mechanisms and moderation processes. Failure to moderate harmful UGC results in removal.",
      updatedAt:"Mar 2024", stores:["play","app"] },
    { code:"ILLEGAL_CONTENT_POLICY", name:"Illegal Content Policy", sev:"critical",
      desc:"Apps must not facilitate access to, or distribution of, illegal content including CSAM, illegal weapons, counterfeit goods, or controlled substances.",
      updatedAt:"Jan 2023", stores:["play","app"] },
    { code:"GAMBLING_POLICY", name:"Real-Money Gambling Policy", sev:"critical",
      desc:"Real-money gambling apps require explicit approval from both stores and valid gambling licences for each jurisdiction they operate in.",
      updatedAt:"Jun 2024", stores:["play","app"] },
  ],
  upcoming: [
    { code:"UPCOMING_EU_DMA", name:"EU Digital Markets Act — Sideloading (iOS)", sev:"warning",
      desc:"Under the EU DMA, Apple must allow alternative app distribution in the EU. Apps may need to certify compliance with Apple's new notarisation requirements for alternative marketplaces.",
      updatedAt:"Mar 2024", stores:["app"] },
    { code:"UPCOMING_AI_DISCLOSURE", name:"AI-Generated Content Disclosure", sev:"info",
      desc:"Both stores are developing policies requiring disclosure of AI-generated content (images, descriptions, conversations). Reliq will add this check when policies are finalised.",
      updatedAt:"Expected Q3 2025", stores:["play","app"] },
    { code:"UPCOMING_PRIVACY_SANDBOX", name:"Android Privacy Sandbox — Topics API", sev:"info",
      desc:"Google's Privacy Sandbox replaces the Advertising ID for behavioural advertising on Android. Ad SDK updates required. Data Safety form updates expected.",
      updatedAt:"Expected H1 2026", stores:["play"] },
    { code:"UPCOMING_APPLE_EU_CORE_TECH", name:"Apple Core Technology Fee (EU)", sev:"warning",
      desc:"Large apps in the EU exceeding 1M downloads may be subject to the €0.50 per install Core Technology Fee. Distribution strategy may require adjustment.",
      updatedAt:"Mar 2024", stores:["app"] },
  ],
};

const PolicyCard = ({ c, go, sevColor, sevBg, defaultOpen }) => {
  const [open, setOpen] = React.useState(defaultOpen || false);
  return (
    <div id={"policy-" + c.code} className="card" style={{overflow:"hidden"}}>
      <div style={{padding:"16px 18px", cursor:"pointer"}} onClick={() => setOpen(!open)}>
        <div style={{display:"flex", alignItems:"flex-start", gap:12, marginBottom:10}}>
          <span style={{
            fontFamily:"var(--font-mono)", fontSize:10.5, fontWeight:600, letterSpacing:"0.06em",
            textTransform:"uppercase", padding:"3px 8px", borderRadius:4, flexShrink:0,
            color:sevColor[c.sev], background:sevBg[c.sev], border:`1px solid ${sevColor[c.sev]}40`
          }}>{c.sev}</span>
          <div style={{flex:1}}>
            <div style={{fontWeight:600, fontSize:14, letterSpacing:"-0.005em", marginBottom:4}}>{c.name}</div>
            <span className="mono" style={{fontSize:11, color:"var(--fg-3)"}}>{c.code}</span>
          </div>
          <div style={{display:"flex", gap:6, flexShrink:0, alignItems:"center"}}>
            {c.stores.map(s => <StoreChip key={s} store={s} />)}
            <Icon name="chevron-down" size={14} style={{color:"var(--fg-3)", transform:open?"rotate(180deg)":"none", transition:"transform .15s"}} />
          </div>
        </div>
        <p style={{margin:"0 0 10px", fontSize:13.5, color:"var(--fg-1)", lineHeight:1.6}}>{c.desc}</p>
        <div style={{display:"flex", alignItems:"center", gap:12}}>
          <span className="mono" style={{fontSize:11, color:"var(--fg-3)"}}>Updated {c.updatedAt}</span>
          <a className="policy-link" style={{marginTop:0}}>
            <Icon name="external-link" size={11} /> View store policy
          </a>
          <button className="btn btn-sm btn-ghost" style={{marginLeft:"auto"}} onClick={e => { e.stopPropagation(); go("/scan/new"); }}>
            Run scan to check →
          </button>
        </div>
      </div>

      {open && (
        <div style={{borderTop:"1px solid var(--line-1)", padding:"16px 18px", background:"var(--bg-2)", display:"grid", gridTemplateColumns:"1fr 1fr", gap:16}}>
          {c.check && (
            <div>
              <div className="mono" style={{fontSize:10, letterSpacing:"0.1em", textTransform:"uppercase", color:"var(--fg-3)", marginBottom:6}}>What Reliq checks</div>
              <div style={{fontSize:13, color:"var(--fg-1)", lineHeight:1.55}}>{c.check}</div>
            </div>
          )}
          {c.trigger && (
            <div>
              <div className="mono" style={{fontSize:10, letterSpacing:"0.1em", textTransform:"uppercase", color:"var(--fg-3)", marginBottom:6}}>What triggers a finding</div>
              <div style={{fontSize:13, color:"var(--fg-1)", lineHeight:1.55}}>{c.trigger}</div>
            </div>
          )}
          {c.why && (
            <div>
              <div className="mono" style={{fontSize:10, letterSpacing:"0.1em", textTransform:"uppercase", color:"var(--fg-3)", marginBottom:6}}>Why it matters</div>
              <div style={{fontSize:13, color:"var(--fg-1)", lineHeight:1.55}}>{c.why}</div>
            </div>
          )}
          {c.fix && (
            <div>
              <div className="mono" style={{fontSize:10, letterSpacing:"0.1em", textTransform:"uppercase", color:"var(--fg-3)", marginBottom:6}}>Recommended fix</div>
              <div style={{fontSize:13, color:"var(--fg-1)", lineHeight:1.55}}>{c.fix}</div>
            </div>
          )}
          {c.relatedChecks && c.relatedChecks.length > 0 && (
            <div style={{gridColumn:"1 / -1"}}>
              <div className="mono" style={{fontSize:10, letterSpacing:"0.1em", textTransform:"uppercase", color:"var(--fg-3)", marginBottom:6}}>Related scan checks</div>
              <div style={{display:"flex", gap:6, flexWrap:"wrap"}}>
                {c.relatedChecks.map(rc => (
                  <span key={rc} style={{fontFamily:"var(--font-mono)", fontSize:11, color:"var(--blue)", background:"var(--blue-bg)", border:"1px solid rgba(91,157,255,0.25)", borderRadius:4, padding:"2px 8px"}}>{rc}</span>
                ))}
              </div>
            </div>
          )}
          {!c.check && !c.trigger && !c.why && !c.fix && (
            <div style={{gridColumn:"1 / -1", color:"var(--fg-3)", fontSize:13}}>Detailed guidance available in the full Reliq documentation.</div>
          )}
        </div>
      )}
    </div>
  );
};

const PolicyLibrary = ({ go }) => {
  // Deep-link: if global search navigated here for a specific policy, jump to it
  const _focusPolicy = React.useRef(null);
  const [activeCategory, setActiveCategory] = React.useState(() => {
    const t = window.__reliq_pending_focus;
    if (t && t.type === "policy" && t.category) {
      window.__reliq_pending_focus = null;
      _focusPolicy.current = t;
      return t.category;
    }
    return "play";
  });
  const [platformFilter, setPlatformFilter] = React.useState("all");
  const [search, setSearch] = React.useState("");

  // Scroll to and open the focused policy card after mount
  React.useEffect(() => {
    const t = _focusPolicy.current;
    if (!t?.code) return;
    setTimeout(() => {
      const el = document.getElementById("policy-" + t.code);
      if (el) el.scrollIntoView({ behavior: "smooth", block: "center" });
    }, 80);
  }, []);

  const checks = POLICY_CHECKS[activeCategory] || [];
  const filtered = checks.filter(c => {
    if (platformFilter === "play" && !c.stores.includes("play")) return false;
    if (platformFilter === "app" && !c.stores.includes("app")) return false;
    if (platformFilter === "both" && !(c.stores.includes("play") && c.stores.includes("app"))) return false;
    if (search && !c.name.toLowerCase().includes(search.toLowerCase()) &&
        !c.code.toLowerCase().includes(search.toLowerCase()) &&
        !c.desc.toLowerCase().includes(search.toLowerCase())) return false;
    return true;
  });

  const sevColor = { critical:"var(--red)", warning:"var(--amber)", info:"var(--blue)", advisory:"var(--fg-2)" };
  const sevBg    = { critical:"var(--red-bg)", warning:"var(--amber-bg)", info:"var(--blue-bg)", advisory:"var(--bg-3)" };

  const tabBtn = (id, label, icon) => (
    <button
      className={`btn btn-sm ${platformFilter === id ? "" : "btn-ghost"}`}
      style={{gap:6}}
      onClick={() => setPlatformFilter(id)}
    >
      {icon && <Icon name={icon} size={13} />}{label}
    </button>
  );

  return (
    <AuthLayout route="policy" go={go} crumbs={["Workspace", "Policy Library"]}>
        <div style={{display:"grid", gridTemplateColumns:"220px 1fr", gap:24, alignItems:"flex-start"}}>

          {/* Category sidebar */}
          <div style={{position:"sticky", top:72}}>
            <div className="mono" style={{fontSize:10, letterSpacing:"0.14em", textTransform:"uppercase", color:"var(--fg-3)", padding:"0 8px 8px"}}>Categories</div>
            {POLICY_CATEGORIES.map(cat => (
              <div
                key={cat.id}
                onClick={() => { setActiveCategory(cat.id); setSearch(""); }}
                style={{
                  display:"flex", alignItems:"center", gap:10,
                  padding:"9px 10px", borderRadius:"var(--r-sm)",
                  cursor:"pointer", marginBottom:2,
                  background:activeCategory===cat.id?"var(--bg-3)":"transparent",
                  color:activeCategory===cat.id?"var(--fg-0)":"var(--fg-1)",
                  border:`1px solid ${activeCategory===cat.id?"var(--line-2)":"transparent"}`,
                  transition:"background .1s",
                }}
              >
                <Icon name={cat.icon} size={14} style={{color:activeCategory===cat.id?"var(--teal)":"var(--fg-3)"}} />
                <span style={{fontSize:13.5, flex:1}}>{cat.label}</span>
                <span style={{fontFamily:"var(--font-mono)", fontSize:10.5, color:"var(--fg-3)", background:"var(--bg-3)", padding:"1px 5px", borderRadius:4}}>{cat.count}</span>
              </div>
            ))}
          </div>

          {/* Main content */}
          <div>
            <div style={{marginBottom:16}}>
              <div style={{display:"flex", alignItems:"center", gap:12, marginBottom:12}}>
                <Icon name={POLICY_CATEGORIES.find(c=>c.id===activeCategory)?.icon||"book"} size={18} style={{color:"var(--fg-1)"}} />
                <h1 style={{margin:0, fontFamily:"var(--font-display)", fontSize:20, fontWeight:600, letterSpacing:"-0.01em"}}>
                  {POLICY_CATEGORIES.find(c=>c.id===activeCategory)?.label}
                </h1>
                <span className="chip">{filtered.length} check{filtered.length!==1?"s":""}</span>
                {activeCategory==="upcoming" && (
                  <span style={{fontFamily:"var(--font-mono)", fontSize:10.5, color:"var(--amber)", background:"var(--amber-bg)", border:"1px solid rgba(245,158,11,0.25)", borderRadius:4, padding:"2px 8px", letterSpacing:"0.06em", textTransform:"uppercase"}}>Upcoming</span>
                )}
              </div>

              {/* Platform + search row */}
              <div style={{display:"flex", gap:8, alignItems:"center", flexWrap:"wrap"}}>
                <div style={{display:"flex", gap:4, background:"var(--bg-1)", border:"1px solid var(--line-2)", borderRadius:"var(--r-sm)", padding:3}}>
                  {tabBtn("all", "All platforms")}
                  {tabBtn("play", "Google Play", "android")}
                  {tabBtn("app", "App Store", "apple")}
                  {tabBtn("both", "Both stores")}
                </div>
                <div style={{position:"relative", flex:"1 1 220px"}}>
                  <Icon name="search" size={14} style={{position:"absolute", left:10, top:"50%", transform:"translateY(-50%)", color:"var(--fg-3)", pointerEvents:"none"}} />
                  <input
                    value={search} onChange={e => setSearch(e.target.value)}
                    placeholder="Search checks…"
                    style={{width:"100%", background:"var(--bg-1)", border:"1px solid var(--line-2)", borderRadius:"var(--r-sm)",
                      padding:"7px 12px 7px 34px", fontSize:13, color:"var(--fg-0)", outline:"none"}}
                  />
                </div>
              </div>
            </div>

            {filtered.length === 0 ? (
              <div style={{padding:"60px 32px", textAlign:"center", color:"var(--fg-3)", background:"var(--bg-1)", border:"1px solid var(--line-1)", borderRadius:"var(--r-lg)"}}>
                <Icon name="search" size={24} />
                <div style={{fontFamily:"var(--font-display)", fontSize:16, fontWeight:600, marginTop:12}}>No checks match</div>
                <div style={{fontSize:13, marginTop:6}}>Try adjusting the platform filter or search.</div>
              </div>
            ) : (
              <div style={{display:"flex", flexDirection:"column", gap:10}}>
                {filtered.map((c) => (
                  <PolicyCard key={c.code} c={c} go={go} sevColor={sevColor} sevBg={sevBg}
                    defaultOpen={_focusPolicy.current?.code === c.code} />
                ))}
              </div>
            )}
          </div>
        </div>
    </AuthLayout>
  );
};

Object.assign(window, { PolicyLibrary });
