PageSourceSearch

https://www.ingredicheck.app/download/ios?source=%2F

html ingredicheck.app collected 2026-10-01 23:44:17 UTC 43,870 bytes, 401 lines download raw bytes

1<!DOCTYPE html><html lang="en" data-critters-container><head>
2    <meta charset="UTF-8">
3    <link rel="icon" type="image/svg+xml" href="/images/logo.svg">
4    <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
5    <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
6    <link rel="icon" href="/favicon.ico" sizes="any">
7    <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
8    <meta name="viewport" content="width=device-width, initial-scale=1.0">
9    <meta name="color-scheme" content="only light">
10    <meta name="theme-color" content="#ffffff">
11    <meta name="darkreader-lock">
12    <link rel="preload" as="image" href="/images/phone-base-frame-no-lookup-mobile.webp" fetchpriority="high" media="(max-width: 639px)">
13    <link rel="preload" as="image" href="/images/phone-base-frame-no-lookup.webp" fetchpriority="high" media="(min-width: 640px)">
14    <link rel="preconnect" href="https://fonts.googleapis.com">
15    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
16    <link rel="dns-prefetch" href="https://va.vercel-scripts.com">
17    <!-- Critical above-fold fonts: preloaded. Figtree/Protest are used everywhere;
18         Plus Jakarta Sans and Nunito are used above the fold in HeroSection/MatchCard. -->
19    <link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Figtree:wght@400;600&amp;family=Nunito:wght@500;700&amp;
19family=Plus+Jakarta+Sans:wght@300;500;600;800&amp;family=Protest+Strike&amp;display=swap" onload="this.onload=null;this.rel='stylesheet'">
20    <noscript>
21      <link href="https://fonts.googleapis.com/css2?family=Figtree:wght@400;600&family=Nunito:wght@500;700&family=Plus+Jakarta+Sans:wght@300;500;600;800&family=Protest+Strike&display=swap" rel="stylesheet">
22    </noscript>
23    <!-- Below-fold fonts: deferred (no preload, just async stylesheet). Inter/Manrope
24         only appear in lazy-loaded sections and non-home pages. -->
25    <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&amp;family=Manrope:wght@400;500;600;700;800&amp;display=swap">
26    <noscript>
27      <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&family=Manrope:wght@400;500;600;700;800&display=swap" rel="stylesheet">
28    </noscript>
29  <style>nav.site-navbar{position:sticky;top:0;z-index:100;width:100%}.download-nav-cta{background:linear-gradient(143.74deg, hsl(82 85% 44%) 21.16%, hsl(75 92% 29%) 68.87%);box-shadow:0 4px 11px hsl(0 0% 77% / 0.57), inset 0 4px 5.7px hsl(77 87% 31%), inset 2px 9px 7.5px hsl(0 0% 93% / 0.25)}body{transition:opacity 0.25s ease}@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-content:""}}}@layer theme{:root{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-gray-50:oklch(98.5% .002 247.839);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-300:oklch(87.2% .01 258.338);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-3xl:48rem;--text-3xl:1.875rem;--text-3xl--line-height:1.2 ;--font-weight-light:300;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-normal:0em;--tracking-wide:.025em;--tracking-widest:.1em;--leading-tight:1.25;--leading-snug:1.375;--drop-shadow-2xl:0 25px 25px #00000026;--ease-out:cubic-bezier(0,0,.2,1);--ease-in-out:cubic-bezier(.4,0,.2,1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--blur-md:12px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-brand-green-light:#8db511;--color-badge-green:#91b640;--color-card-green:#eef5e3;--color-text-dark:#161616;--color-text-body:#303030;--color-text-muted:#949494;--color-text-subtitle:#a6a6a6;--font-bradley:"Bradley Hand","Bradley Hand ITC",cursive;--font-jakarta:"Plus Jakarta Sans",sans-serif;--font-protest:"Protest Strike",system-ui;
29--font-figtree:"Figtree",sans-serif;--font-inter:"Inter",sans-serif;--font-nunito:"Nunito",sans-serif;--font-manrope:"Manrope",sans-serif}}@layer base{*,:after,:before{box-sizing:border-box;border:0 solid;margin:0;padding:0}html{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}h1{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}img,svg{vertical-align:middle;display:block}img{max-width:100%;height:auto}button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}button{appearance:button}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing)*0)}.top-0{top:calc(var(--spacing)*0)}.z-10{z-index:10}.z-\[5\]{z-index:5}.z-\[100\]{z-index:100}.mx-auto{margin-inline:auto}.mt-\[16px\]{margin-top:16px}.mt-\[28px\]{margin-top:28px}.-mb-\[17px\]{margin-bottom:-17px}.ml-3{margin-left:calc(var(--spacing)*3)}.block{display:block}.flex{display:flex}.hidden{display:none}.inline-flex{display:inline-flex}.h-0\.5{height:calc(var(--spacing)*.5)}.h-\[18px\]{height:18px}.h-\[20px\]{height:20px}.h-\[28px\]{height:28px}.h-\[32px\]{height:32px}.h-\[40px\]{height:40px}.h-\[52px\]{height:52px}.min-h-\[60vh\]{min-height:60vh}.min-h-screen{min-height:100vh}.w-6{width:calc(var(--spacing)*6)}.w-\[20px\]{width:20px}.w-\[32px\]{width:32px}.w-\[40px\]{width:40px}.w-\[150px\]{width:150px}.w-\[158px\]{width:158px}.w-auto{width:auto}.w-full{width:100%}.max-w-0{max-width:calc(var(--spacing)*0)}.max-w-\[480px\]{max-width:480px}.max-w-\[1440px\]{max-width:1440px}.flex-shrink-0{flex-shrink:0}.cursor-pointer{cursor:pointer}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-1\.5{gap:calc(var(--spacing)*1.5)}.gap-3{gap:calc(var(--spacing)*3)}.gap-\[46px\]{gap:46px}.overflow-hidden{overflow:hidden}.overflow-x-clip{overflow-x:clip}.rounded-\[26px\]{border-radius:26px}.rounded-full{border-radius:3.40282e38px}.bg-text-body{background-color:var(--color-text-body)}.bg-transparent{background-color:#0000}.bg-white{background-color:var(--color-white)}.p-2{padding:calc(var(--spacing)*2)}.px-\[10px\]{padding-inline:10px}.px-\[20px\]{padding-inline:20px}.px-\[24px\]{padding-inline:24px}.py-\[16px\]{padding-block:16px}.py-\[100px\]{padding-block:100px}.pt-\[16px\]{padding-top:16px}.pb-\[17px\]{padding-bottom:17px}.text-center{text-align:center}.font-figtree{font-family:var(--font-figtree)}.font-protest{font-family:var(--font-protest)}.text-\[16px\]{font-size:16px}.text-\[18px\]{font-size:18px}.text-\[42px\]{font-size:42px}.leading-\[1\.05\]{--tw-leading:1.05;line-height:1.05}.leading-\[1\.6\]{--tw-leading:1.6;line-height:1.6}.leading-\[18px\]{--tw-leading:18px;line-height:18px}.leading-\[24px\]{--tw-leading:24px;line-height:24px}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.whitespace-nowrap{white-space:nowrap}.text-text-dark{color:var(--color-text-dark)}.text-text-muted{color:var(--color-text-muted)}.text-text-subtitle{color:var(--color-text-subtitle)}.text-white{color:var(--color-white)}.opacity-0{opacity:0}.opacity-100{opacity:1}.shadow-\[0_4px_11px_rgba\(0\,0\,0\,0\.12\)\]{--tw-shadow:0 4px 11px var(--tw-sha
29dow-color,#0000001f);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.brightness-0{--tw-brightness:brightness(0%);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}@media(hover:hover){.group-hover\/apple\:opacity-100:is(:where(.group\/apple):hover *){opacity:1}.group-hover\/apple\:invert:is(:where(.group\/apple):hover *){--tw-invert:invert(100%);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.group-hover\/cta\:ml-0:is(:where(.group\/cta):hover *){margin-left:calc(var(--spacing)*0)}.group-hover\/cta\:ml-3:is(:where(.group\/cta):hover *){margin-left:calc(var(--spacing)*3)}.group-hover\/cta\:w-0:is(:where(.group\/cta):hover *){width:calc(var(--spacing)*0)}.group-hover\/cta\:max-w-\[100px\]:is(:where(.group\/cta):hover *){max-width:100px}.group-hover\/cta\:opacity-0:is(:where(.group\/cta):hover *){opacity:0}.group-hover\/cta\:opacity-100:is(:where(.group\/cta):hover *){opacity:1}.group-hover\/play\:opacity-0:is(:where(.group\/play):hover *){opacity:0}.group-hover\/play\:opacity-100:is(:where(.group\/play):hover *){opacity:1}}@media(hover:hover){.hover\:text-text-body:hover{color:var(--color-text-body)}.hover\:brightness-110:hover{--tw-brightness:brightness(110%);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}}@media(min-width:40rem){.sm\:h-\[33px\]{height:33px}.sm\:w-\[190px\]{width:190px}.sm\:px-6{padding-inline:calc(var(--spacing)*6)}.sm\:pt-\[17px\]{padding-top:17px}.sm\:text-\[56px\]{font-size:56px}}@media(min-width:64rem){.lg\:px-10{padding-inline:calc(var(--spacing)*10)}}@media(min-width:80rem){.xl\:block{display:block}.xl\:flex{display:flex}.xl\:hidden{display:none}.xl\:px-\[150px\]{padding-inline:150px}.xl\:pt-\[17px\]{padding-top:17px}}}:root{color-scheme:light only}html{-ms-overflow-style:none;scrollbar-width:none;filter:none!important}body{font-family:var(--font-figtree);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:var(--color-text-body)!important;background-color:#fff!important}.download-nav-cta{background:linear-gradient(143.74deg,#8ad011 21.16%,#6c8e06 68.87%);box-shadow:0 4px 11px #c4c4c491,inset 0 4px 5.7px #6d940a,inset 2px 9px 7.5px #ededed40}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-gradient-position{syntax:"*";inherits:false}@property --tw-gradient-from{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-stops{syntax:"*";inherits:false}@property --tw-gradient-via-stops{syntax:"*";inherits:false}@property --tw-gradient-from-position{syntax:"<length-percentage>";inherits:false;initial-value:0%}
29@property --tw-gradient-via-position{syntax:"<length-percentage>";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:"<length-percentage>";inherits:false;initial-value:100%}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-content{syntax:"*";inherits:false;initial-value:""}</style>
30  
30<script>
31    // Inject the hide rule at runtime (survives the critical-CSS prune), disable the
32    // browser's native scroll restoration (it jumps to a stale, clamped position before
33    // lazy chunks load), and — on a full load/refresh with a saved position — hide the
34    // page before first paint. Safety timeout reveals regardless if JS stalls.
35    (function () {
36      try {
37        var s = document.createElement('style');
38        s.textContent = 'html.restoring-scroll body{opacity:0!important}';
39        document.head.appendChild(s);
40        if ('scrollRestoration' in history) history.scrollRestoration = 'manual';
41        var y = sessionStorage.getItem('homeScrollY');
42        if (location.pathname === '/' && y && parseInt(y, 10) > 0) {
43          document.documentElement.classList.add('restoring-scroll');
44          setTimeout(function () {
45            document.documentElement.classList.remove('restoring-scroll');
46          }, 2500);
47        }
48      } catch (e) {}
49    })();
50  </script>
50
51  
51<script>
52    // PostHog — production-only, pageview-scoped, deferred past window.load,
53    // attribution-parity across the deferral window.
54    //   Hostname gate: matches the App.jsx / DownloadRedirectPage.jsx idiom.
55    //     Keeps localhost, Vercel preview URLs, and the ReactSnap prerender
56    //     crawl (runs against a local file server) out of the prod project.
57    //   Landing snapshot: captures location.href / pathname / referrer sync
58    //     in <head>, before any client-side navigation can rewrite them.
59    //     After the deferred SDK loads, we emit the initial $pageview using
60    //     this snapshot so the report shows the true landing URL even when
61    //     the user has already navigated to another SPA route by the time
62    //     init completes. Every emitted pageview also explicitly passes
63    //     $session_entry_url / $session_entry_pathname from the snapshot so
64    //     PostHog's auto-derived (post-nav) values never override them.
65    //   Pre-init history queue: we wrap history.pushState / replaceState
66    //     and listen for popstate synchronously in <head>. Before init the
67    //     handler appends the current route to a queue; after init the
68    //     queue is flushed as $pageview events (deduped by path+search)
69    //     and the handler switches to direct capture. This means SPA
70    //     navigations that happen during the deferral window (e.g. / to
71    //     /about before the SDK loaded) are still captured, in order.
72    //   Deferred load: window.load THEN requestIdleCallback (setTimeout
73    //     fallback, 3s timeout) — RIC alone can fire mid-parse before LCP,
74    //     so we wait for load first, then let the browser pick an idle
75    //     slice. array.js (~90 kB gzip) never competes with the app module.
76    //   Locked-down init: every remotely-configurable event source is
77    //     explicitly disabled — autocapture, heatmaps, web vitals
78    //     (capture_performance), page leave, session replay, surveys.
79    //     capture_pageview: false suppresses the SDK's own init-time
80    //     pageview so it can't fire against the post-navigation URL.
81    //   before_send: the SDK's calculateEventProperties() runs its
82    //     sessionPropsManager AFTER caller-supplied capture properties,
83    //     so passing $session_entry_url/$session_entry_pathname in the
84    //     capture call gets overwritten with the post-navigation URL
85    //     before the event leaves the client. before_send runs after
86    //     that enrichment, so we set the session-entry fields here from
87    //     the snapshot — this is the final wire value. The override is
88    //     scoped by session boundaries: we only apply it when this page
89    //     load actually started the current PostHog session. Ownership
90    //     is determined from the session id attached to the FIRST
91    //     captured event, because PostHog's capture() runs the
92    //     non-read-only session path (which does inactivity rotation)
93    //     while ph.get_session_id() uses the read-only path and skips
94    //     it. On the first event we snapshot its $session_id as
95    //     `initialSessionId` and compute
96    //     `hadPriorSession = (preInitSessionId === initialSessionId)`.
97    //     If PostHog rotated the persisted session at init because it
98    //     was idle-expired, the ids differ and we own the fresh
99    //     session (override applies). If PostHog kept the persisted
100    //     id, we joined an existing session (override skipped). Later
101    //     events check parity against `initialSessionId`; a subsequent
102    //     rotation makes the ids diverge and we stop overriding. The
103    //     handler then generic-scans every string property for
104    //     /invite/<code> and redacts it unconditionally, defense-in-depth
105    //     against family invite codes leaking via any URL-shaped
106    //     property (existing or future). Mirrored in
107    //     src/utils/posthogBeforeSend.js and covered by
108    //     scripts/posthog-before-send.test.js, which tests both the
109    //     module and this inline copy (via string extraction) so drift
110    //     between them fails a test.
111    //   See PrivacyPolicyPage.jsx §"Analytics and Session Replay" for the
112    //   disclosure that mirrors this configuration.
113    (function () {
114      var h = window.location.hostname;
115      if (h !== 'ingredicheck.app' && h !== 'www.ingredicheck.app') return;
116      var POSTHOG_KEY = 'phc_BFYelq2GeyigXBP3MgML57wKoWfLe5MW7m6HMYhtX8m';
117      var initialHref = window.location.href;
118      var initialPathname = window.location.pathname;
119      var initialSearch = window.location.search || '';
120      var initialReferrer = document.referrer;
121      var INVITE_RE = /\/invite\/[^/?#]+/g;
122      // Mirror of redactInvites in src/utils/posthogBeforeSend.js. Walks a
123      // properties bag and rewrites any string containing /invite/<code>
124      // in place. Called in before_send on event.properties, event.$set,
125      // event.$set_once, and the nested properties.$set / $set_once — with
126      // person_profiles: 'always', the SDK writes $initial_current_url /
127      // $initial_pathname into $set_once on the first event of a fresh
128      // person, sitting outside event.properties. Without this the invite
129      // code leaks in person properties even when pageview properties are
130      // clean.
131      var redactInvites = function (bag) {
132        if (!bag || typeof bag !== 'object') return;
133        for (var key in bag) {
134          var v = bag[key];
135          if (typeof v === 'string' && v.indexOf('/invite/') !== -1) {
136            bag[key] = v.replace(INVITE_RE, '/invite/[redacted]');
137          }
138        }
139      };
140      // Mirror of extractPersistedSessionId in src/utils/posthogBeforeSend.js
141      // (this snippet must run synchronously in <head> and therefore can't
142      // import). Reads the raw stored value from `ph_<token>_posthog`,
143      // returns the persisted session id string, or null if no valid
144      // session state is present. $sesid layout is
145      // [lastActivityTimestamp, sessionId, sessionStartTimestamp].
146      var extractPersistedSessionId = function (storedValue) {
147        if (typeof storedValue !== 'string') return null;
148        try {
149          var parsed = JSON.parse(storedValue);
150          if (parsed && Array.isArray(parsed.$sesid) && typeof parsed.$sesid[1] === 'string') {
151            return parsed.$sesid[1];
152          }
153        } catch (e) {}
154        return null;
155      };
156      // Capture the persisted session id from localStorage BEFORE
157      // posthog.init runs. In the loaded callback we compare it to
158      // ph.get_session_id() to determine whether PostHog continued the
159      // pre-existing session (hadPriorSession = true → skip the
160      // session-entry override) or started a fresh one (hadPriorSession
161      // stays false → override is safe until the session rotates again).
162      // Delegating the validity check to PostHog avoids hard-coding its
163      // inactivity + max-lifetime timeout semantics here.
164      var preInitSessionId = null;
165      try {
166        preInitSessionId = extractPersistedSessionId(window.localStorage.getItem('ph_' + POSTHOG_KEY + '_posthog'));
167      } catch (e) {}
168      var hadPriorSession = false;
169      // Populated in the loaded callback via ph.get_session_id(). before_send
170      // reads this at event time — if PostHog later rotates the session
171      // due to inactivity, event.$session_id will differ from this value
172      // and we stop overriding the session-entry fields.
173      var initialSessionId = null;
174      var lastPath = initialPathname + initialSearch;
175      var pendingNavs = [];
176      var phReady = false;
177      // Tracking-parameter keys stripped by CanonicalUrlNormalizer
178      // (mirror of CRAWL_NOISE_PARAMS + the 'utm_' prefix rule in
179      // src/utils/urlNormalization.js). When the ONLY difference
180      // between lastPath and the current URL is these params being
181      // removed (or added), the "navigation" is the normalizer running,
182      // not a real route change — skip the $pageview so campaign-tagged
183      // landings don't get double-counted.
184      var TRACKING_PARAM_KEYS = ['utm_source','utm_medium','utm_campaign','utm_term','utm_content','fbclid','from','gbraid','gclid','mc_cid','mc_eid','ref','wbraid'];
185      var isTrackingParam = function (name) {
186        var lower = String(name).toLowerCase();
187        if (lower.indexOf('utm_') === 0) return true;
188        for (var ti = 0; ti < TRACKING_PARAM_KEYS.length; ti++) {
189          if (TRACKING_PARAM_KEYS[ti] === lower) return true;
190        }
191        return false;
192      };
193      var isTrackingOnlyDiff = function (prevSearch, nextSearch) {
194        // Return true when every param that differs between the two
195        // search strings is a tracking param. Same pathname + this diff
196        // == the normalizer stripping crawl noise, not a real nav.
197        try {
198          var prev = new URLSearchParams(prevSearch || '');
199          var next = new URLSearchParams(nextSearch || '');
200          var seen = {};
201          var keys = [];
202          var addKey = function (k) { if (!seen[k]) { seen[k] = true; keys.push(k); } };
203          var pk = prev.keys();
204          for (var pStep = pk.next(); !pStep.done; pStep = pk.next()) addKey(pStep.value);
205          var nk = next.keys();
206          for (var nStep = nk.next(); !nStep.done; nStep = nk.next()) addKey(nStep.value);
207          for (var ki = 0; ki < keys.length; ki++) {
208            var key = keys[ki];
209            if (prev.get(key) !== next.get(key) && !isTrackingParam(key)) return false;
210          }
211          return true;
212        } catch (e) { return false; }
213      };
214      var splitPath = function (p) {
215        var qi = p.indexOf('?');
216        return qi === -1 ? { pathname: p, search: '' } : { pathname: p.substring(0, qi), search: p.substring(qi) };
217      };
218      var handleNav = function () {
219        if (!phReady) {
220          if (pendingNavs && pendingNavs.length < 50) {
221            pendingNavs.push({
222              href: window.location.href,
223              pathname: window.location.pathname,
224              search: window.location.search || ''
225            });
226          }
227          return;
228        }
229        var currentPathname = window.location.pathname;
230        var currentSearch = window.location.search || '';
231        var current = currentPathname + currentSearch;
232        if (current === lastPath) return;
233        var last = splitPath(lastPath);
234        if (last.pathname === currentPathname && isTrackingOnlyDiff(last.search, currentSearch)) {
235          // Absorb the URL change without emitting — same page, just the
236          // crawl-noise stripper running.
237          lastPath = current;
238          return;
239        }
240        lastPath = current;
241        window.posthog.capture('$pageview');
242      };
243      var _push = history.pushState;
244      var _replace = history.replaceState;
245      history.pushState = function () {
246        var r = _push.apply(this, arguments);
247        handleNav();
248        return r;
249      };
250      history.replaceState = function () {
251        var r = _replace.apply(this, arguments);
252        handleNav();
253        return r;
254      };
255      window.addEventListener('popstate', handleNav);
256      var loadPh = function () {
257        var s = document.createElement('script');
258        s.src = 'https://us-assets.i.posthog.com/static/array.js';
259        s.async = true;
260        s.crossOrigin = 'anonymous';
261        s.onerror = function () { pendingNavs = null; };
262        s.onload = function () {
263          window.posthog.init(POSTHOG_KEY, {
264            api_host: 'https://us.i.posthog.com',
265            autocapture: false,
266            capture_pageview: false,
267            capture_pageleave: false,
268            capture_heatmaps: false,
269            capture_performance: false,
270            disable_session_recording: true,
271            disable_surveys: true,
272            person_profiles: 'always',
273            before_send: function (event) {
274              if (!event || !event.properties) return event;
275              var currentSid = event.properties.$session_id;
276              // Snapshot ownership on the first event with a session id.
277              // PostHog's non-read-only session path runs inside capture(),
278              // so the id on the outbound event reflects any rotation
279              // triggered at init (idle-expired persisted session). We
280              // trust the event over ph.get_session_id(), which uses the
281              // read-only path and skips rotation.
282              if (initialSessionId == null && currentSid != null) {
283                initialSessionId = currentSid;
284                hadPriorSession = (preInitSessionId != null && preInitSessionId === currentSid);
285              }
286              var isOwnSession = !hadPriorSession && initialSessionId != null && currentSid === initialSessionId;
287              if (isOwnSession) {
288                event.properties.$session_entry_url = initialHref;
289                event.properties.$session_entry_pathname = initialPathname;
290              }
291              redactInvites(event.properties);
292              redactInvites(event.$set);
293              redactInvites(event.$set_once);
294              redactInvites(event.properties.$set);
295              redactInvites(event.properties.$set_once);
296              return event;
297            },
298            loaded: function (ph) {
299              // Snapshot campaign params from the landing URL BEFORE any React
300              // effects run — CanonicalUrlNormalizer strips utm_*/fbclid/gclid
301              // etc. from the visible URL as crawl noise, so we capture them
302              // here (from initialSearch, taken sync in <head>).
303              //
304              // Persistence scope matters: ph.register() persists forever, so
305              // registering utm_source on visit A would carry over into
306              // visit B (different or no campaign) and pollute attribution.
307              // We (1) explicitly unregister every campaign key first — clears
308              // any residue from a prior visit — then (2) register the
309              // current-URL params for the CURRENT SESSION only via
310              // register_for_session (available since posthog-js 1.152.0).
311              // Fallback path uses register() when register_for_session
312              // isn't available; the explicit unregister above still keeps
313              // it visit-scoped in practice because the next visit will
314              // clear it before registering anything new.
315              try {
316                var campaignKeys = ['utm_source','utm_medium','utm_campaign','utm_term','utm_content','fbclid','gclid','gbraid','wbraid','mc_cid','mc_eid','ref'];
317                var campaignProps = {};
318                var hasCampaign = false;
319                if (initialSearch) {
320                  var params = new URLSearchParams(initialSearch);
321                  for (var ci = 0; ci < campaignKeys.length; ci++) {
322                    var ck = campaignKeys[ci];
323                    var cv = params.get(ck);
324                    if (cv) {
325                      campaignProps[ck] = cv;
326                      hasCampaign = true;
327                    }
328                  }
329                }
330                if (typeof ph.unregister === 'function') {
331                  for (var ucIdx = 0; ucIdx < campaignKeys.length; ucIdx++) {
332                    ph.unregister(campaignKeys[ucIdx]);
333                  }
334                }
335                if (hasCampaign) {
336                  if (typeof ph.register_for_session === 'function') {
337                    ph.register_for_session(campaignProps);
338                  } else if (typeof ph.register === 'function') {
339                    ph.register(campaignProps);
340                  }
341                }
342              } catch (e) {}
343              ph.capture('$pageview', {
344                $current_url: initialHref,
345                $pathname: initialPathname,
346                $referrer: initialReferrer
347              });
348              for (var i = 0; i < pendingNavs.length; i++) {
349                var n = pendingNavs[i];
350                var pathKey = n.pathname + n.search;
351                if (pathKey === lastPath) continue;
352                var lastForFlush = splitPath(lastPath);
353                if (lastForFlush.pathname === n.pathname && isTrackingOnlyDiff(lastForFlush.search, n.search)) {
354                  // Queued replaceState from CanonicalUrlNormalizer stripping
355                  // tracking params — same page, absorb without emitting.
356                  lastPath = pathKey;
357                  continue;
358                }
359                lastPath = pathKey;
360                ph.capture('$pageview', {
361                  $current_url: n.href,
362                  $pathname: n.pathname
363                });
364              }
365              pendingNavs = null;
366              // Drain the pending-events queue populated by utils/analytics.js
367              // captureEvent() calls that fired before window.posthog existed
368              // (user clicks between page paint and deferred SDK init).
369              try {
370                var pendingEvents = window.__phPendingEvents;
371                if (pendingEvents && pendingEvents.length) {
372                  for (var pi = 0; pi < pendingEvents.length; pi++) {
373                    var ev = pendingEvents[pi];
374                    if (ev && ev.name) ph.capture(ev.name, ev.props);
375                  }
376                }
377              } catch (e) {}
378              window.__phPendingEvents = null;
379              phReady = true;
380            }
381          });
382        };
383        document.head.appendChild(s);
384      };
385      window.addEventListener('load', function () {
386        if ('requestIdleCallback' in window) {
387          window.requestIdleCallback(loadPh, { timeout: 3000 });
388        } else {
389          setTimeout(loadPh, 1000);
390        }
391      });
392    })();
393  </script>
393
394    
394<script type="module" crossorigin src="/assets/main-CNs67Yuc.js"></script>
394
395    <link rel="stylesheet" crossorigin href="/assets/main-Cg1r_LRd.css"><noscript><link rel="stylesheet" crossorigin href="/assets/main-Cg1r_LRd.css"></noscript>
396  <title>Redirecting to App Store | IngrediCheck</title><meta data-ingredicheck-seo="true" name="description" content="Redirecting to IngrediCheck on App Store."><meta data-ingredicheck-seo="true" property="og:type" content="website"><meta data-ingredicheck-seo="true" property="og:title" content="Redirecting to App Store | IngrediCheck"><meta data-ingredicheck-seo="true" property="og:description" content="Redirecting to IngrediCheck on App Store."><meta data-ingredicheck-seo="true" property="og:site_name" content="IngrediCheck"><meta data-ingredicheck-seo="true" property="og:locale" content="en_US"><meta data-ingredicheck-seo="true" name="twitter:card" content="summary"><meta data-ingredicheck-seo="true" name="twitter:title" content="Redirecting to App Store | IngrediCheck"><meta data-ingredicheck-seo="true" name="twitter:description" content="Redirecting to IngrediCheck on App Store."><meta data-ingredicheck-seo="true" name="twitter:site" content="@ingredicheck"><meta data-ingredicheck-seo="true" name="robots" content="noindex, nofollow"></head>
397  <body>
398    <div id="root"><div class="min-h-screen bg-white relative overflow-x-clip"><div class="xl:hidden fixed inset-0 pointer-events-none" style="z-index: 0; background-image: repeating-linear-gradient(to right, transparent, transparent 47px, rgb(224, 224, 224) 47px, rgb(224, 224, 224) 48px), repeating-linear-gradient(transparent, transparent 47px, rgb(224, 224, 224) 47px, rgb(224, 224, 224) 48px); opacity: 0.5;"></div><div class="hidden xl:block fixed inset-0 pointer-events-none" style="z-index: 2; background-image: repeating-linear-gradient(to right, transparent, transparent 47px, rgb(224, 224, 224) 47px, rgb(224, 224, 224) 48px), repeating-linear-gradient(transparent, transparent 47px, rgb(224, 224, 224) 47px, rgb(224, 224, 224) 48px); opacity: 0; transition: opacity 0.3s ease 0s;"></div><div class="relative z-[5]"><nav class="site-navbar w-full pt-[16px] sm:pt-[17px] xl:pt-[17px] sticky top-0 z-[100] pb-[17px] -mb-[17px] transition-all duration-300 bg-transparent"><div class="max-w-[1440px] mx-auto px-[10px] sm:px-6 lg:px-10 xl:px-[150px]"><div class="flex items-center justify-between h-[52px]"><a href="/" class="flex-shrink-0" aria-label="IngrediCheck home"><img alt="IngrediCheck" width="190" height="33" class="h-[28px] w-[150px] sm:h-[33px] sm:w-[190px]" src="/images/ingredicheck.svg"></a><div class="hidden xl:flex items-center gap-[46px]"><a class="font-figtree text-[16px] leading-[24px] transition-colors duration-200 font-normal text-text-muted hover:text-text-body" href="/" data-discover="true">Home</a><a href="/#about" class="font-figtree text-[16px] leading-[24px] transition-colors duration-200 font-normal text-text-muted hover:text-text-body">About</a><a class="font-figtree text-[16px] leading-[24px] transition-colors duration-200 font-normal text-text-muted hover:text-text-body" href="/blog" data-discover="true">Blog</a><a href="/#why-ingredicheck" class="font-figtree text-[16px] leading-[24px] transition-colors duration-200 font-normal text-text-muted hover:text-text-body">Features</a><a href="/#faqs" class="font-figtree text-[16px] leading-[24px] transition-colors duration-200 font-normal text-text-muted hover:text-text-body">FAQs</a></div><div class="hidden xl:block opacity-100 transition-opacity duration-300 w-[158px] flex-shrink-0"><div class="download-nav-cta group/cta inline-flex items-center rounded-[26px] h-[52px] px-[24px] py-[16px] text-white font-figtree text-[16px] font-semibold leading-[18px] cursor-pointer hover:brightness-110"><span class="whitespace-nowrap">Get the app</span><img alt class="ml-3 flex-shrink-0 w-[20px] h-[20px] transition-all duration-300 group-hover/cta:opacity-0 group-hover/cta:w-0 group-hover/cta:ml-0" src="/images/download-icon.svg"><div class="flex items-center gap-1.5 max-w-0 opacity-0 overflow-hidden group-hover/cta:max-w-[100px] group-hover/cta:opacity-100 group-hover/cta:ml-3 transition-all duration-300"><a href="/download/ios?source=%2Fdownload%2Fios" target="_blank" rel="noopener noreferrer" class="group/apple relative flex items-center justify-center w-[32px] h-[32px] rounded-full overflow-hidden flex-shrink-0 "><div class="absolute inset-0 rounded-full bg-white opacity-0 group-hover/apple:opacity-100 transition-opacity duration-300"></div><img alt="App Store" class="relative z-10 h-[18px] w-auto transition-all duration-300 group-hover/apple:invert" src="/images/apple.webp"></a>
398<a href="/download/android?source=%2Fdownload%2Fios" target="_blank" rel="noopener noreferrer" class="group/play relative flex items-center justify-center w-[32px] h-[32px] rounded-full overflow-hidden flex-shrink-0 "><div class="absolute inset-0 rounded-full bg-white opacity-0 group-hover/play:opacity-100 transition-opacity duration-300"></div><svg viewBox="0 0 24 24" width="18" height="18" class="relative z-10 transition-opacity duration-300 group-hover/play:opacity-0" aria-hidden="true"><path d="M3.6 1.8C3.2 2.1 3 2.6 3 3.2v17.6c0 .6.2 1.1.6 1.4l10-10.1L3.6 1.8z" fill="#ffffff"/><path d="M17 8.8l-2.6 2.6-10.4-10c.5-.3 1.1-.3 1.6 0L17 8.8z" fill="#ffffff" fill-opacity="0.85"/><path d="M17 15.2L5.6 22.6c-.5.3-1.1.3-1.6 0l10.4-10 2.6 2.6z" fill="#ffffff" fill-opacity="0.7"/><path d="M20.8 10.9c.7.4.7 1.4 0 1.8L17.4 14.6l-2.8-2.6 2.8-2.6 3.4 1.5z" fill="#ffffff" fill-opacity="0.55"/></svg><img alt="Google Play" class="absolute z-10 h-[18px] w-auto opacity-0 group-hover/play:opacity-100 transition-opacity duration-300" src="/images/playstore.webp"></a></div></div></div><div class="xl:hidden flex items-center gap-3"><a href="/download/ios?source=%2Fdownload%2Fios" target="_blank" rel="noopener noreferrer" aria-label="Download on the App Store" class="flex items-center justify-center w-[40px] h-[40px] rounded-full flex-shrink-0 bg-white shadow-[0_4px_11px_rgba(0,0,0,0.12)]"><img alt class="h-[20px] w-auto brightness-0" src="/images/apple.webp"></a><button class="flex flex-col gap-1.5 p-2" aria-label="Toggle menu"><span class="block w-6 h-0.5 bg-text-body transition-transform "></span><span class="block w-6 h-0.5 bg-text-body transition-opacity "></span><span class="block w-6 h-0.5 bg-text-body transition-transform "></span></button></div></div></div></nav><main class="min-h-[60vh] flex items-center justify-center px-[20px] py-[100px]"><div class="max-w-[480px] text-center"><h1 class="font-protest text-[42px] sm:text-[56px] leading-[1.05] text-text-dark">Opening App Store</h1><p class="mt-[16px] font-figtree text-[18px] leading-[1.6] text-text-subtitle">If the store does not open, use the button below to keep going.</p><a href="https://apps.apple.com/app/id6477521615" class="inline-flex items-center justify-center mt-[28px] px-[24px] h-[52px] rounded-full text-white font-figtree text-[16px] font-semibold" style="background: linear-gradient(143.74deg, rgb(138, 208, 17) 21.16%, rgb(108, 142, 6) 68.87%);">Get the app on App Store</a></div></main></div></div></div>
399  
400
401</body></html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.