/* Aifit — SpaceX-style genre showcase: full-bleed panels, one statement each. Exposes window.GenreShowcase */ (function () { const Icon = window.Icon; const GENRES = [ { key: "technology", en: "Aifit Technology", title: "終活は、迷惑をかけずに旅立つ理想の自分への最高の投資。心の資産形成。", brand: "やすらぎ共済会ロードマップ", sub: "ご入会は、葬儀費用の割引だけではありません。死後の“面倒”からご自身とご家族を守る「配慮という投資」— それはやがて、予想もつかない“資産”となって還っていきます。", href: "technology.html", photo: "img/medical/promise_pic_03.webp", slot: "ph-g-technology", video: "video/tech.mp4", vposter: "img/genre-poster-tech.webp", }, { key: "funeral", en: "Funeral & Family", title: "大切な人を、最良のかたちで見送る。", sub: "直葬8.9万円〜から一般葬まで、選べる13プラン。東広島ご依頼件数 10年連続第1位。", href: "plan.html", photo: "img/plan/family/lead_pic.webp", slot: "ph-g-funeral", }, { key: "shukatsu", en: "Life Design Salon", title: "はじめて不安になった夜を支える。", sub: "終活相談ならアイフィット — 遺影撮影・エンディングノート講習・生前整理のご相談。", href: "aifit.html", photo: "img/kyosaikai/img_entry.webp", slot: "ph-g-shukatsu", video: "video/shukatsu.mp4", vposter: "img/genre-poster-shukatsu.webp", }, { key: "pet", en: "Pet Ceremony", title: "ペットも家族、心を込めて見送る。", brand: "アイフィットペット霊園", sub: "西条中心部で、ペットの火葬から納骨・永代供養まで。", href: "pet.html", photo: "img/pet/explanation_pic01.webp", slot: "ph-g-pet", video: "video/pet-light.mp4", vposter: "img/genre-poster-pet.webp", }, { key: "member", en: "Membership", title: "一生もののお守り、やすらぎ共済会。", brand: "さいきやすらぎ共済会", sub: (入会金1万円だけでご家族全員割引。当社では面倒な積み立てが本当に不要!!!葬儀費用は最大35%OFF!!!), href: "kyosaikai.html", photo: "img/hall-yasuragi.webp", slot: "ph-g-member", video: "video/kyosaikai.mp4", vposter: "img/genre-poster-member.webp", }, { key: "corporate", en: "Corporate & Large-scale", title: "1000名規模のご葬儀も。", sub: "社葬・合同葬・お別れの会。会場手配から進行・運営まで一括代行。", href: "plan-group.html", photo: "img/group/lead_pic.webp", slot: "ph-g-corporate", }, { key: "after", en: "After Support", title: "葬儀のあとの手続きまで、迷わせない。", sub: "香典返し・法事・相続・空き家・遺品整理 — 司法書士初回無料、ワンストップでサポート。", href: "after.html", photo: "img/aifit/img_service_10.webp", slot: "ph-g-after", video: "video/kataduke.mp4", vposter: "img/genre-poster-after.webp", }, { key: "ai", en: "AI Support — 24 / 365", title: "24時間、いつでも、そばに。", sub: "Aifitチャット — 自社1万件以上の事象を記憶した専属AIが、登録不要・無料で即答します。", href: "https://saiki-sousai.com/chat/", photo: "img/medical/promise_pic_01.webp", slot: "ph-g-ai", cta: "Aifitチャットを開く", chips: ["登録不要", "無料", "匿名OK", "24時間365日"], }, ]; // iOS Safari: React doesn't render the `muted` attribute, which blocks // autoplay. Force-mute + play() on mount, retry when data arrives, when // the panel scrolls into view, and on any tap. function tryPlay(el) { if (!el || !el.paused) return; el.muted = true; const p = el.play(); if (p && p.catch) p.catch(function () {}); } // Skip video downloads only when they'd be wasted bandwidth: data-saver // mode or 2G connections get the (50x smaller) poster photo instead. // NOTE: do NOT gate on prefers-reduced-motion — iPhones with 「視差効果を // 減らす」 enabled would silently lose every background video. const NOVIDEO = (function () { try { const c = navigator.connection; if (c && (c.saveData || /(^|-)2g$/.test(c.effectiveType || ""))) return true; } catch (e) {} return false; })(); function loadVid(el) { if (NOVIDEO) return Promise.resolve(); if (!el || !el.dataset.src || el.dataset.loaded) return Promise.resolve(); el.dataset.loaded = "1"; // iOS Safari needs HTTP range support to stream ; the preview // server may not provide it, so fetch the whole file and play as a blob. return fetch(el.dataset.src) .then(function (r) { if (!r.ok) throw new Error(r.status); return r.blob(); }) .then(function (b) { el.src = URL.createObjectURL(b); tryPlay(el); }) .catch(function () { el.src = el.dataset.src; tryPlay(el); }); } function vidMount(el) { if (!el) return; el.muted = true; el.defaultMuted = true; el.setAttribute("muted", ""); el.playsInline = true; el.setAttribute("playsinline", ""); el.preload = "auto"; el.addEventListener("loadeddata", function () { tryPlay(el); }); el.addEventListener("canplaythrough", function () { tryPlay(el); }); // The hero loads immediately; genre-panel videos defer until the panel // approaches the viewport (see GenreShowcase) so mobile connections // aren't saturated by ~40MB of parallel downloads at page load. if (!el.classList.contains("gpanel-video") || !("IntersectionObserver" in window)) loadVid(el); tryPlay(el); } window.aifitVidMount = vidMount; window.aifitVidLoad = loadVid; function PhoneDemo() { const { useState, useEffect } = React; const SCRIPTS = [ [ { who: "ai", text: "ご自身の終活について考えていらっしゃるんですね。とても大切な一歩です!何でもお気軽にどうぞ 😊" }, { who: "user", text: "葬儀の費用について" }, { who: "ai", text: "一番気になるところですよね。Aifitは「明朗会計」がモットー。直葬13.5万円〜、家族葬35.8万円〜(会員価格)。追加料金で驚くことはありません。" }, ], [ { who: "ai", text: "今日は、誰のためのご相談ですか?どんなご相談でも大丈夫ですよ 🤝" }, { who: "user", text: "親のことで相談したい" }, { who: "ai", text: "親御さんのことですね。気持ちの整理から費用のことまで、ご一緒に考えましょう。まずはいまの状況をお聞かせください。" }, ], ]; const [script] = useState(() => { let n = 0; try { n = (parseInt(localStorage.getItem("aifit-gphone-alt") || "0", 10) + 1) % SCRIPTS.length; localStorage.setItem("aifit-gphone-alt", String(n)); } catch (e) {} return SCRIPTS[n]; }); const [msgs, setMsgs] = useState([]); const [typing, setTyping] = useState(false); useEffect(() => { let alive = true; const timers = []; const wait = (ms) => new Promise((r) => { timers.push(setTimeout(r, ms)); }); (async () => { await wait(900); while (alive) { setMsgs([]); for (const m of script) { if (!alive) return; if (m.who === "ai") { setTyping(true); await wait(1200); setTyping(false); } else { await wait(700); } for (let i = 1; i <= m.text.length; i++) { if (!alive) return; const part = m.text.slice(0, i); setMsgs((prev) => { const next = prev.slice(); if (next.length && next[next.length - 1].who === m.who && !next[next.length - 1].done) { next[next.length - 1] = { who: m.who, text: part }; } else { next.push({ who: m.who, text: part }); } return next; }); await wait(m.who === "user" ? 70 : 34); } setMsgs((prev) => { const next = prev.slice(); next[next.length - 1] = { who: m.who, text: m.text, done: true }; return next; }); await wait(700); } await wait(4200); } })(); return () => { alive = false; timers.forEach(clearTimeout); }; }, [script]); return ( Ai Aifitチャット24時間対応中 {msgs.map((m, i) => ( {m.text} ))} {typing && } メッセージを入力…↑ ); } function GenreShowcase() { React.useEffect(() => { const all = () => Array.prototype.slice.call(document.querySelectorAll("video.gpanel-video, video.hero-video")); const kick = () => { all().forEach((v) => { loadVid(v); tryPlay(v); }); }; window.addEventListener("touchend", kick, { passive: true }); window.addEventListener("click", kick, { passive: true }); const io = ("IntersectionObserver" in window) ? new IntersectionObserver((es) => { es.forEach((e) => { if (e.isIntersecting) { loadVid(e.target); tryPlay(e.target); } else if (!e.target.paused) e.target.pause(); }); }, { threshold: 0.15 }) : null; if (io) all().forEach((v) => io.observe(v)); // start loading a panel video well before it scrolls into view const ioLoad = ("IntersectionObserver" in window) ? new IntersectionObserver((es) => { es.forEach((e) => { if (e.isIntersecting) { loadVid(e.target); ioLoad.unobserve(e.target); } }); }, { rootMargin: "150% 0px" }) : null; if (ioLoad) all().forEach((v) => ioLoad.observe(v)); // idle prefetch: after the page settles, fetch any remaining videos one // at a time so they're ready before the user reaches them. const idleTimer = setTimeout(() => { all().reduce((p, v) => p.then(() => loadVid(v)), Promise.resolve()); }, 2500); return () => { window.removeEventListener("touchend", kick); window.removeEventListener("click", kick); if (io) io.disconnect(); if (ioLoad) ioLoad.disconnect(); clearTimeout(idleTimer); }; }, []); return ( {GENRES.map((g) => { const ext = /^https?:/.test(g.href); return ( {g.photo && ( )} {g.video && ( )} {g.inset && ( )} {g.key === "ai" && } {g.en} {g.title} {g.brand && {g.brand}} {g.sub} {g.chips && ( {g.chips.map((c, i) => ({c}))} )} {g.cta || "詳しく見る"} ); })} ); } window.GenreShowcase = GenreShowcase; })();
{g.sub}