<!DOCTYPE html>

<html lang="zh-TW">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Jubo 官網資訊架構 Sitemap</title>

    <style>

        body { margin: 0; padding: 0; background: #f4f6f8; font-family: "Microsoft JhengHei", "Heiti TC", sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; overflow: hidden; }

        #container { width: 100%; height: 100%; max-width: 1400px; max-height: 900px; box-shadow: 0 4px 20px rgba(0,0,0,0.1); background: white; border-radius: 12px; position: relative; }

        

        /* --- Share Button Styles --- */

        #share-widget { position: absolute; bottom: 30px; right: 30px; z-index: 100; }

        #share-btn { 

            background: white; color: #555; border: 1px solid #ddd; 

            padding: 12px 20px; border-radius: 50px; font-size: 14px; font-weight: 600; 

            cursor: pointer; box-shadow: 0 4px 10px rgba(0,0,0,0.1); 

            transition: all 0.2s; display: flex; align-items: center; gap: 8px;

        }

        #share-btn:hover { transform: translateY(-2px); box-shadow: 0 6px 15px rgba(0,0,0,0.15); color: #333; }

        #share-btn:active { transform: translateY(0); }

        

        /* --- Toast Notification --- */

        #toast {

            position: absolute; bottom: 80px; right: 30px;

            background: rgba(0,0,0,0.8); color: white;

            padding: 10px 20px; border-radius: 8px;

            font-size: 14px; opacity: 0; transition: opacity 0.3s;

            pointer-events: none; z-index: 101;

        }

        #toast.show { opacity: 1; }


        /* --- Detail Modal Styles --- */

        #detail-modal-overlay {

            position: absolute; top: 0; left: 0; width: 100%; height: 100%;

            background: rgba(0,0,0,0.4);

            display: none; justify-content: center; align-items: center;

            z-index: 200;

            backdrop-filter: blur(2px);

            opacity: 0; transition: opacity 0.3s;

        }

        #detail-modal {

            background: white; width: 400px; max-width: 90%; padding: 25px;

            border-radius: 16px; box-shadow: 0 10px 40px rgba(0,0,0,0.2);

            transform: scale(0.9); transition: transform 0.3s;

            position: relative;

            display: flex; flex-direction: column; gap: 15px;

        }

        #detail-modal.active { transform: scale(1); }

        #modal-close { position: absolute; top: 15px; right: 15px; cursor: pointer; font-size: 20px; color: #999; }

        #modal-title { font-size: 22px; font-weight: bold; color: #333; margin: 0; }

        #modal-desc { font-size: 15px; color: #555; line-height: 1.6; background: #f5f7fa; padding: 15px; border-radius: 8px; border-left: 4px solid #667eea; }

        #modal-children-title { font-size: 14px; font-weight: bold; color: #777; margin-top: 10px; text-transform: uppercase; letter-spacing: 1px; }

        #modal-children-list { display: flex; flex-wrap: wrap; gap: 10px; }

        .child-btn {

            background: white; border: 1px solid #667eea; color: #667eea;

            padding: 8px 16px; border-radius: 20px; font-size: 14px; font-weight: 600;

            cursor: pointer; transition: all 0.2s;

        }

        .child-btn:hover { background: #667eea; color: white; }

    </style>

</head>

<body>


<div id="container">

    <!-- SVG 容器 -->

    <svg id="sitemapSvg" width="100%" height="100%" viewBox="0 0 1300 800" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg">

        <defs>

            <filter id="dropShadow" x="-20%" y="-20%" width="140%" height="140%">

                <feGaussianBlur in="SourceAlpha" stdDeviation="3"/>

                <feOffset dx="2" dy="2" result="offsetblur"/>

                <feComponentTransfer>

                    <feFuncA type="linear" slope="0.3"/>

                </feComponentTransfer>

                <feMerge>

                    <feMergeNode/>

                    <feMergeNode in="SourceGraphic"/>

                </feMerge>

            </filter>

            <marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">

                <polygon points="0 0, 10 3.5, 0 7" fill="#ccc"/>

            </marker>

            <marker id="arrowhead-active" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">

                <polygon points="0 0, 10 3.5, 0 7" fill="#FFD700"/>

            </marker>

        </defs>


        <style>

            .node-item { cursor: pointer; transition: transform 0.3s ease; }

            .node-item:hover { transform: scale(1.1); } 

            .node-rect { stroke-width: 2px; transition: all 0.3s ease; filter: url(#dropShadow); }

            .node-text { font-size: 14px; font-weight: bold; pointer-events: none; user-select: none; fill: #333; }

            .link { fill: none; stroke: #e0e0e0; stroke-width: 2px; transition: stroke 0.3s; }

            .active .node-rect { fill: #fffde7 !important; stroke: #F5A623 !important; stroke-width: 3px; }

            .active-link { stroke: #F5A623; stroke-width: 3px; stroke-dasharray: 10; animation: dash 1s linear infinite; }

            @keyframes dash { to { stroke-dashoffset: -20; } }

            #info-bar { transition: opacity 0.3s; pointer-events: none; }

            #info-bg { fill: rgba(0, 0, 0, 0.8); rx: 20; }

            #info-text { fill: white; font-size: 16px; text-anchor: middle; font-weight: bold; }

        </style>


        <g id="links-layer"></g>

        <g id="nodes-layer"></g>


        <!-- 底部簡單提示 -->

        <g id="info-bar" transform="translate(650, 750)" style="opacity: 0;">

            <rect id="info-bg" x="-200" y="-20" width="400" height="40"></rect>

            <text id="info-text" x="0" y="6">點擊節點查看詳細介紹</text>

        </g>

    </svg>


    <!-- Detail Modal -->

    <div id="detail-modal-overlay" onclick="closeModal(event)">

        <div id="detail-modal">

            <span id="modal-close" onclick="closeModal(event)">×</span>

            <h2 id="modal-title">標題</h2>

            <div id="modal-desc">描述內容...</div>

            

            <div id="modal-children-container" style="display:none;">

                <div id="modal-children-title">下一層項目</div>

                <div id="modal-children-list"></div>

            </div>

        </div>

    </div>


    <!-- Share Widget (Replaces AI) -->

    <div id="share-widget">

        <button id="share-btn" onclick="copyLink()">

            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">

                <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"></path>

                <polyline points="16 6 12 2 8 6"></polyline>

                <line x1="12" y1="2" x2="12" y2="15"></line>

            </svg>

            <span>分享連結</span>

        </button>

    </div>

    <div id="toast">連結已複製!</div>


    <script>

        // --- 資料結構 (最新版) ---

        const data = {

            id: "root", label: "Jubo 官網", desc: "全方位智慧照護科技平台", detailedDesc: "Jubo 智齡科技提供從機構到居家的全方位照護解決方案,串聯 IoT 設備與 AI 數據分析。", color: "#2c3e50", width: 160, height: 50,

            children: [

                {

                    id: "products", label: "產品 (Products)", desc: "機構與個人軟硬體", detailedDesc: "涵蓋照護機構管理系統、個人健康 App 以及智慧照護推車等核心產品。", color: "#4A90E2", width: 140,

                    children: [

                        {

                            id: "inst", label: "照護機構", desc: "機構管理核心系統", detailedDesc: "專為住宿型、日照型與居服單位設計的數位化管理系統。", color: "#5DADE2", width: 110,

                            children: [

                                { id: "p1", label: "住宿型系統", desc: "24h 機構管理", detailedDesc: "• 系統截圖與功能介紹<br>• Navattic 互動 Demo<br>• Learn More 連結<br>專為住宿型長照機構打造,整合護理紀錄與行政管理。", color: "#AED6F1" },

                                { id: "p2", label: "日照型系統", desc: "日照排程管理", detailedDesc: "• 系統截圖與功能介紹<br>• Navattic 互動 Demo<br>• Learn More 連結<br>提供個案管理、交通車排程與活動記錄功能。", color: "#AED6F1" },

                                { id: "p3", label: "居服系統", desc: "居服派遣核銷", detailedDesc: "• 系統截圖與功能介紹<br>• Navattic 互動 Demo<br>• Learn More 連結<br>Hi 居服 App 整合,優化派遣效率與核銷流程。", color: "#AED6F1" },

                                { id: "p4", label: "智慧推車", desc: "Smart Cart", detailedDesc: "• IoT 量測整合<br>• 生命徵象自動上傳<br>減少抄寫錯誤,提升護理工作效率。", color: "#AED6F1" }

                            ]

                        },

                        {

                            id: "pers", label: "個人健康", desc: "家屬與長輩連結", detailedDesc: "連結家屬與長輩的數位橋樑,提供即時健康資訊。", color: "#5DADE2", width: 110,

                            children: [

                                { id: "p5", label: "Jubo健康APP", desc: "家屬聯繫與紀錄", detailedDesc: "• Jubo 健康 APP 介紹<br>• App Store / Google Play 連結<br>讓家屬即時掌握長輩健康狀況。", color: "#AED6F1" },

                                { id: "p6", label: "智齡照顧網", desc: "照護知識平台", detailedDesc: "• 智齡照顧網介紹<br>• 外部網站連結<br>提供專業照護知識文章與資源。", color: "#AED6F1" }

                            ]

                        }

                    ]

                },

                {

                    id: "solutions", label: "解決方案", desc: "場景驅動整合服務", detailedDesc: "針對不同照護場景提供的完整解決方案包,整合軟體、硬體與 AI。", color: "#50E3C2", width: 140,

                    children: [

                        { id: "s_res", label: "住宿型", desc: "住宿機構方案", detailedDesc: "• 模組介紹連結回住宿產品頁面<br>• 客戶成功案例<br>• Demo 連結<br>一站式住宿機構數位轉型方案。", color: "#A2D9CE" },

                        { id: "s_day", label: "日照型", desc: "日照中心方案", detailedDesc: "• 模組介紹連結回日照產品頁面<br>• 客戶成功案例<br>• Demo 連結<br>提升日照中心營運效率與個案互動。", color: "#A2D9CE" },

                        { id: "s_home", label: "居服型", desc: "居家服務方案", detailedDesc: "• 模組介紹連結回居服產品頁面<br>• 客戶成功案例<br>• Demo 連結<br>解決居服單位派案與管理痛點。", color: "#A2D9CE" },

                        { id: "s_iot", label: "Jubo IoT生態圈", desc: "硬體聯網生態", detailedDesc: "• 照護推車 + IoT<br>• 合作廠商設備整合<br>打造互聯互通的智慧照護環境。", color: "#A2D9CE" },

                        { id: "s_ai", label: "N-Copilot", desc: "護理 AI 夥伴", detailedDesc: "• N-copilot 功能展示<br>• Navattic Demo 整合<br>專屬於護理人員的 AI 智能助理,輔助紀錄與決策。", color: "#A2D9CE" }

                    ]

                },

                {

                    id: "resources", label: "資源 (Resources)", desc: "活動與知識", detailedDesc: "匯集活動花絮、教育資源與客戶成功案例。", color: "#F5A623", width: 130,

                    children: [

                        { 

                            id: "r1", label: "活動花絮", desc: "線上線下活動", detailedDesc: "• 線上線下活動清單<br>• 活動詳情頁 (CMS)<br>• Hubspot 表單整合", color: "#FAD7A0",

                            children: [

                                { id: "r1_1", label: "三三分享會", desc: "產業知識交流", detailedDesc: "定期舉辦的產業知識分享會,邀請專家與業者交流。", color: "#FEF9E7" }

                            ]

                        },

                        { id: "r3", label: "教育平台", desc: "卓越教育中心", detailedDesc: "• 卓越教育平台介紹<br>• 外部網站連結<br>提供系統操作教學與長照專業課程。", color: "#FAD7A0" },

                        { id: "r4", label: "客戶案例", desc: "成功導入故事", detailedDesc: "• 成功案例展示 (CMS)<br>• 文章列表<br>見證合作夥伴的數位轉型成效。", color: "#FAD7A0" },

                        { id: "r5", label: "媒合平台", desc: "長照資源網", detailedDesc: "• 長照資源網介紹<br>• 機構媒合功能<br>連結需求者與照護資源。", color: "#FAD7A0" }

                    ]

                },

                {

                    id: "partners", label: "夥伴 (Partners)", desc: "跨領域聯盟", detailedDesc: "IoT 夥伴與策略合作夥伴。", color: "#BD10E0", width: 120,

                    children: [

                        { id: "pt1", label: "IoT夥伴", desc: "硬體生態", detailedDesc: "合作廠商名單與產品整合說明。", color: "#D7BDE2" },

                        { id: "pt2", label: "策略夥伴", desc: "投資人與公司", detailedDesc: "公司的投資人、投資公司與策略合作單位。", color: "#D7BDE2" }

                    ]

                },

                {

                    id: "company", label: "關於我們", desc: "品牌與團隊", detailedDesc: "Jubo 品牌故事、團隊介紹與人才招募。", color: "#9013FE", width: 120,

                    children: [

                        { id: "c1", label: "品牌故事", desc: "願景與使命", detailedDesc: "品牌願景:以科技溫暖照護。", color: "#D2B4DE" },

                        { id: "c2", label: "團隊介紹", desc: "專業團隊", detailedDesc: "結合醫療、資料科學與設計的跨領域團隊。", color: "#D2B4DE" },

                        { id: "c3", label: "人才招募", desc: "加入我們", detailedDesc: "職缺、文化與福利介紹。<br>連結至 104 人力銀行。", color: "#D2B4DE" }

                    ]

                },

                {

                    id: "contact", label: "聯絡我們", desc: "業務洽詢", detailedDesc: "業務名單 + 聯絡表單 (Hubspot)。<br>我們將盡快與您聯繫。", color: "#E91E63", width: 120,

                    children: []

                }

            ]

        };


        // --- 佈局計算 ---

        const svg = document.getElementById('sitemapSvg');

        const nodesLayer = document.getElementById('nodes-layer');

        const linksLayer = document.getElementById('links-layer');

        

        const startY = 50;

        const levelHeight = 150;

        const leafGap = 120; 

        const nodesMap = {}; 

        const linksArr = []; 


        function calcWeight(node) {

            if (!node.children || node.children.length === 0) {

                node.weight = 1;

            } else {

                node.weight = node.children.reduce((sum, child) => sum + calcWeight(child), 0);

            }

            return node.weight;

        }


        function assignPositions(node, x, y, level, parentId) {

            const nodeWidth = node.width || 100;

            const nodeHeight = node.height || 40;


            nodesMap[node.id] = {

                x: x,

                y: y,

                w: nodeWidth,

                h: nodeHeight,

                data: node,

                parent: parentId,

                level: level

            };


            if (parentId) {

                linksArr.push({ source: parentId, target: node.id });

            }


            if (node.children && node.children.length > 0) {

                // 特別處理:若子節點是 "三三分享會" 

                if (node.id === "r1") {

                    assignPositions(node.children[0], x, y + 60, level + 1, node.id);

                    return;

                }


                let currentX = x - (node.weight * leafGap) / 2; 

                if(level === 0) currentX = x - (node.weight * (leafGap + 10)) / 2;


                node.children.forEach(child => {

                    const childWeight = child.weight;

                    const childSpan = childWeight * leafGap;

                    const childX = currentX + childSpan / 2;

                    let childY = y + levelHeight;

                    

                    assignPositions(child, childX, childY, level + 1, node.id);

                    currentX += childSpan;

                });

            }

        }


        calcWeight(data);

        // 根節點 X 設為中心點 (1300/2 = 650)

        assignPositions(data, 650, startY, 0, null);


        // --- 修正垂直列表佈局 ---

        function adjustLayoutToVerticalList(parentId) {

            const parent = nodesMap[parentId];

            if (!parent) return;

            const childrenIds = Object.keys(nodesMap).filter(k => nodesMap[k].parent === parentId);

            

            childrenIds.forEach((childId, index) => {

                nodesMap[childId].x = parent.x; 

                nodesMap[childId].y = parent.y + 60 + (index * 50);

            });

        }


        // 調整特定分支為垂直排列

        adjustLayoutToVerticalList("inst");

        adjustLayoutToVerticalList("pers");

        adjustLayoutToVerticalList("solutions");

        adjustLayoutToVerticalList("resources");

        adjustLayoutToVerticalList("partners");

        adjustLayoutToVerticalList("company");


        // --- 手動微調第一層的位置 (X軸) ---

        nodesMap["products"].x  = 180;  // 左移

        nodesMap["solutions"].x = 420;  // 緊接

        nodesMap["resources"].x = 640;  // 接近中間

        nodesMap["partners"].x  = 840;  

        nodesMap["company"].x   = 1000; 

        nodesMap["contact"].x   = 1160; // 新增在最右側


        // 重新套用垂直佈局以更新子節點位置

        nodesMap["inst"].x = nodesMap["products"].x - 60; nodesMap["inst"].y = 250;

        nodesMap["pers"].x = nodesMap["products"].x + 60; nodesMap["pers"].y = 250;

        

        adjustLayoutToVerticalList("inst");

        adjustLayoutToVerticalList("pers");

        

        adjustLayoutToVerticalList("solutions");

        adjustLayoutToVerticalList("resources");

        adjustLayoutToVerticalList("partners");

        adjustLayoutToVerticalList("company");


        // 特別調整:三三分享會

        if (nodesMap["r1"] && nodesMap["r1_1"]) {

             nodesMap["r1_1"].x = nodesMap["r1"].x + 20; 

             nodesMap["r1_1"].y = nodesMap["r1"].y + 45; 

             

             const resChildren = ["r3", "r4", "r5"]; 

             resChildren.forEach((id) => {

                 if(nodesMap[id]) nodesMap[id].y += 30; 

             });

        }



        // --- 繪圖 ---

        function drawTree() {

            // 1. Draw Links

            linksArr.forEach(link => {

                const s = nodesMap[link.source];

                const t = nodesMap[link.target];

                const path = document.createElementNS("http://www.w3.org/2000/svg", "path");

                path.setAttribute("class", "link");

                path.setAttribute("id", `link-${link.source}-${link.target}`);

                

                let d = "";

                if (Math.abs(s.x - t.x) < 40) {

                     if (link.target === "r1_1") {

                         d = `M ${s.x} ${s.y + s.h/2} L ${s.x} ${t.y} L ${t.x - t.w/2} ${t.y}`; 

                     } else {

                         d = `M ${s.x} ${s.y + s.h/2} L ${t.x} ${t.y - t.h/2}`;

                     }

                } else {

                     d = `M ${s.x} ${s.y + s.h/2} C ${s.x} ${s.y + s.h/2 + 50}, ${t.x} ${t.y - t.h/2 - 50}, ${t.x} ${t.y - t.h/2}`;

                }

                path.setAttribute("d", d);

                linksLayer.appendChild(path);

            });


            // 2. Draw Nodes

            Object.keys(nodesMap).forEach(key => {

                const n = nodesMap[key];

                

                const container = document.createElementNS("http://www.w3.org/2000/svg", "g");

                container.setAttribute("class", "node-container");

                container.setAttribute("transform", `translate(${n.x}, ${n.y})`);

                

                const item = document.createElementNS("http://www.w3.org/2000/svg", "g");

                item.setAttribute("class", "node-item");

                item.setAttribute("id", `node-${key}`);

                

                item.onclick = (e) => handleClick(key, e);

                item.onmouseover = () => handleHover(key);

                item.onmouseout = () => handleOut();


                const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");

                rect.setAttribute("class", "node-rect");

                rect.setAttribute("x", -n.w / 2);

                rect.setAttribute("y", -n.h / 2);

                rect.setAttribute("width", n.w);

                rect.setAttribute("height", n.h);

                rect.setAttribute("rx", 8);

                rect.setAttribute("ry", 8);

                rect.setAttribute("fill", "white");

                rect.setAttribute("stroke", n.data.color || "#999");

                

                const text = document.createElementNS("http://www.w3.org/2000/svg", "text");

                text.setAttribute("class", "node-text");

                text.setAttribute("x", 0);

                text.setAttribute("y", 5); 

                text.setAttribute("text-anchor", "middle");

                text.textContent = n.data.label;


                item.appendChild(rect);

                item.appendChild(text);

                container.appendChild(item);

                nodesLayer.appendChild(container);

            });

        }


        // --- Interaction Logic ---


        function handleHover(id) {

            const node = nodesMap[id];

            const infoBar = document.getElementById('info-bar');

            const infoText = document.getElementById('info-text');

            const infoBg = document.getElementById('info-bg');

            

            infoBar.style.opacity = 1;

            infoText.textContent = node.data.label;

            infoBg.setAttribute("width", node.data.label.length * 20 + 40);

            infoBg.setAttribute("x", -(node.data.label.length * 20 + 40) / 2);

        }


        function handleOut() {

            document.getElementById('info-bar').style.opacity = 0;

        }


        function handleClick(id, e) {

            if(e) e.stopPropagation();


            document.querySelectorAll('.active').forEach(el => el.classList.remove('active'));

            document.querySelectorAll('.active-link').forEach(el => el.classList.remove('active-link'));


            const nodeEl = document.getElementById(`node-${id}`);

            if(nodeEl) nodeEl.classList.add('active');


            tracePath(id);

            showDetailModal(id);

        }


        function tracePath(currentId) {

            const node = nodesMap[currentId];

            if (!node || !node.parent) return;

            const parentId = node.parent;

            

            const linkId = `link-${parentId}-${currentId}`;

            const linkEl = document.getElementById(linkId);

            if (linkEl) {

                linkEl.classList.add('active-link');

                linkEl.parentNode.appendChild(linkEl); 

            }


            const parentEl = document.getElementById(`node-${parentId}`);

            if(parentEl) parentEl.classList.add('active');

            tracePath(parentId);

        }


        // --- Detail Modal Logic ---

        function showDetailModal(id) {

            const node = nodesMap[id];

            const modalOverlay = document.getElementById('detail-modal-overlay');

            const modal = document.getElementById('detail-modal');

            const title = document.getElementById('modal-title');

            const desc = document.getElementById('modal-desc');

            const childContainer = document.getElementById('modal-children-container');

            const childList = document.getElementById('modal-children-list');


            title.textContent = node.data.label;

            desc.innerHTML = node.data.detailedDesc || node.data.desc; 


            childList.innerHTML = '';

            if (node.data.children && node.data.children.length > 0) {

                childContainer.style.display = 'block';

                node.data.children.forEach(child => {

                    const btn = document.createElement('button');

                    btn.className = 'child-btn';

                    btn.textContent = child.label;

                    btn.onclick = () => {

                        handleClick(child.id, null);

                    };

                    childList.appendChild(btn);

                });

            } else {

                childContainer.style.display = 'none';

            }


            modalOverlay.style.display = 'flex';

            setTimeout(() => {

                modalOverlay.style.opacity = 1;

                modal.classList.add('active');

            }, 10);

        }


        function closeModal(e) {

            if (e.target.id === 'detail-modal-overlay' || e.target.id === 'modal-close') {

                const modalOverlay = document.getElementById('detail-modal-overlay');

                const modal = document.getElementById('detail-modal');

                modalOverlay.style.opacity = 0;

                modal.classList.remove('active');

                setTimeout(() => {

                    modalOverlay.style.display = 'none';

                }, 300);

            }

        }


        // --- Share Functionality ---

        function copyLink() {

            const url = window.location.href;

            navigator.clipboard.writeText(url).then(() => {

                const toast = document.getElementById('toast');

                toast.classList.add('show');

                setTimeout(() => {

                    toast.classList.remove('show');

                }, 2000);

            });

        }


        drawTree();


    </script>

</div>

</body>

</html>