{"id":7946,"date":"2026-01-10T06:56:20","date_gmt":"2026-01-09T22:56:20","guid":{"rendered":"https:\/\/louisthai.com\/?page_id=7946"},"modified":"2026-01-15T09:40:58","modified_gmt":"2026-01-15T01:40:58","slug":"followup-card","status":"publish","type":"page","link":"https:\/\/louisthai.com\/hk\/followup-card\/","title":{"rendered":"Followup Card"},"content":{"rendered":"<div data-elementor-type=\"wp-page\" data-elementor-id=\"7946\" class=\"elementor elementor-7946\" data-elementor-post-type=\"page\">\n\t\t\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-fb96df0 elementor-section-full_width elementor-section-stretched elementor-section-height-default elementor-section-height-default\" data-id=\"fb96df0\" data-element_type=\"section\" data-settings=\"{&quot;stretch_section&quot;:&quot;section-stretched&quot;}\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-e9f4ec6\" data-id=\"e9f4ec6\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-77c5081 elementor-widget elementor-widget-html\" data-id=\"77c5081\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!doctype html>\n<html lang=\"zh-Hans\">\n\n<head>\n  <meta charset=\"utf-8\" \/>\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" \/>\n  <title>THE White Card \u2022 LOUIS THAI<\/title>\n  <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/html2canvas\/1.4.1\/html2canvas.min.js\"><\/script>\n  <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jspdf\/2.5.1\/jspdf.umd.min.js\"><\/script>\n  <style>\n    :root {\n      --bg: #ffffff;\n      --text: #0f172a;\n      --muted: #475569;\n      --line: #e2e8f0;\n      --soft: #f8fafc;\n      --chip: #f1f5f9;\n      --accent: #111827;\n      --ok: #065f46;\n      --danger: #b91c1c;\n      --shadow: 0 18px 40px rgba(2, 8, 23, .08);\n      --radius: 18px;\n      --radius2: 14px;\n      --font: ui-sans-serif, system-ui, -apple-system, \"SF Pro Display\", \"SF Pro Text\",\n        \"PingFang SC\", \"Noto Sans CJK SC\", \"Microsoft YaHei\", Arial, sans-serif;\n\n      \/* Apple system colors *\/\n      --apple-blue: #007AFF;\n      --apple-blue-hover: #0063E6;\n      --apple-blue-active: #0051C7;\n\n      --apple-red: #FF3B30;\n      --apple-red-hover: #E8352A;\n      --apple-red-active: #C62822;\n    }\n\n    * {\n      box-sizing: border-box\n    }\n\n    body {\n      margin: 0;\n      font-family: var(--font);\n      color: var(--text);\n      background: radial-gradient(1200px 400px at 10% -10%, #f1f5f9 0%, transparent 60%),\n        radial-gradient(900px 360px at 90% -20%, #eef2ff 0%, transparent 55%),\n        var(--bg);\n    }\n\n    .wrap {\n      max-width: 1440px;\n      margin: 32px auto;\n      padding: 0 18px 40px;\n    }\n\n    .topbar {\n      display: flex;\n      gap: 12px;\n      align-items: center;\n      justify-content: space-between;\n      margin-bottom: 16px;\n    }\n\n    .brand {\n      display: flex;\n      gap: 12px;\n      align-items: center;\n    }\n\n    .logo {\n      width: 44px;\n      height: 44px;\n      border-radius: 14px;\n      background: linear-gradient(135deg, #111827, #334155);\n      box-shadow: 0 10px 24px rgba(2, 8, 23, .18);\n      display: grid;\n      place-items: center;\n      color: #fff;\n      font-weight: 800;\n      letter-spacing: .08em;\n      font-size: 14px;\n      user-select: none;\n    }\n\n    .brand h1 {\n      margin: 0;\n      font-size: 18px;\n      letter-spacing: .08em;\n    }\n\n    .brand p {\n      margin: 2px 0 0;\n      color: var(--muted);\n      font-size: 14px;\n    }\n\n    .actions {\n      display: flex;\n      gap: 10px;\n      flex-wrap: wrap;\n      justify-content: flex-end;\n    }\n\n    button {\n      border: 1px solid var(--line);\n      background: #fff;\n      color: var(--text);\n      padding: 10px 12px;\n      border-radius: 12px;\n      font-weight: 600;\n      cursor: pointer;\n      box-shadow: 0 8px 20px rgba(2, 8, 23, .06);\n    }\n\n    button:active {\n      transform: translateY(1px);\n    }\n\n    button.primary {\n      background: var(--accent);\n      color: #fff;\n      border-color: transparent;\n    }\n\n    \/* === Apple-style Button System (v7) === *\/\n    button {\n      background: #ffffff;\n      color: var(--apple-blue);\n      border: 1px solid rgba(0, 122, 255, .45);\n      box-shadow: 0 6px 16px rgba(0, 122, 255, .18);\n      transition: background .15s ease, border-color .15s ease, box-shadow .15s ease, transform .12s ease;\n    }\n\n    button:hover {\n      background: rgba(0, 122, 255, .08);\n      border-color: rgba(0, 122, 255, .65);\n    }\n\n    button:active {\n      background: rgba(0, 122, 255, .14);\n      transform: translateY(1px);\n    }\n\n    \/* Primary (Most Important) \u2013 Apple Blue filled *\/\n    button.primary {\n      background: var(--apple-blue);\n      color: #ffffff;\n      border-color: var(--apple-blue);\n      box-shadow: 0 10px 26px rgba(0, 122, 255, .38);\n    }\n\n    button.primary:hover {\n      background: var(--apple-blue-hover);\n      border-color: var(--apple-blue-hover);\n    }\n\n    button.primary:active {\n      background: var(--apple-blue-active);\n      border-color: var(--apple-blue-active);\n    }\n\n    \/* Danger \u2013 Apple Red (Delete) *\/\n    button.danger {\n      background: #ffffff;\n      color: var(--apple-red);\n      border: 1px solid rgba(255, 59, 48, .55);\n      box-shadow: 0 6px 16px rgba(255, 59, 48, .22);\n    }\n\n    button.danger:hover {\n      background: rgba(255, 59, 48, .10);\n      border-color: rgba(255, 59, 48, .75);\n    }\n\n    button.danger:active {\n      background: rgba(255, 59, 48, .18);\n      transform: translateY(1px);\n    }\n\n    .card {\n      background: #fff;\n      border: 1px solid var(--line);\n      border-radius: var(--radius);\n      box-shadow: var(--shadow);\n      overflow: hidden;\n    }\n\n    .header {\n      padding: 18px 18px 16px;\n      background: linear-gradient(180deg, #ffffff, #fbfdff);\n      border-bottom: 1px solid var(--line);\n      display: flex;\n      gap: 14px;\n      align-items: flex-start;\n      justify-content: space-between;\n      flex-wrap: wrap;\n    }\n\n    .title {\n      display: flex;\n      flex-direction: column;\n      gap: 6px;\n      min-width: 260px;\n    }\n\n    .title .kicker {\n      display: flex;\n      gap: 8px;\n      align-items: center;\n      flex-wrap: wrap;\n      color: var(--muted);\n      font-size: 14px;\n    }\n\n    .pill {\n      display: inline-flex;\n      align-items: center;\n      gap: 8px;\n      padding: 6px 10px;\n      border-radius: 999px;\n      background: var(--chip);\n      border: 1px solid var(--line);\n      font-weight: 600;\n      color: #0b1220;\n    }\n\n    .dot {\n      width: 8px;\n      height: 8px;\n      border-radius: 999px;\n      background: var(--ok);\n      box-shadow: 0 0 0 3px rgba(6, 95, 70, .12);\n    }\n\n    .title h2 {\n      margin: 0;\n      font-size: 24px;\n      letter-spacing: .02em;\n    }\n\n    .subtitle {\n      margin: 0;\n      color: var(--muted);\n      font-size: 15px;\n      line-height: 1.45;\n    }\n\n    .meta {\n      display: flex;\n      gap: 10px;\n      flex-wrap: wrap;\n      align-items: center;\n    }\n\n    .meta .mini {\n      display: flex;\n      flex-direction: column;\n      gap: 2px;\n      padding: 10px 12px;\n      border: 1px solid var(--line);\n      border-radius: 14px;\n      background: var(--soft);\n      min-width: 160px;\n    }\n\n    .mini .label {\n      color: var(--muted);\n      font-size: 13px;\n    }\n\n    .mini .value {\n      font-weight: 700;\n      font-size: 16px;\n    }\n\n    .grid {\n      display: grid;\n      grid-template-columns: 1.2fr .8fr;\n      gap: 16px;\n      padding: 18px;\n    }\n\n    @media (max-width: 880px) {\n      .grid {\n        grid-template-columns: 1fr;\n      }\n    }\n\n    .panel {\n      border: 1px solid var(--line);\n      border-radius: var(--radius2);\n      overflow: hidden;\n      background: #fff;\n    }\n\n    .panel .ph {\n      padding: 12px 14px;\n      border-bottom: 1px solid var(--line);\n      background: linear-gradient(180deg, #ffffff, #f9fafb);\n      display: flex;\n      align-items: center;\n      justify-content: space-between;\n      gap: 12px;\n    }\n\n    .ph h3 {\n      margin: 0;\n      font-size: 15px;\n      letter-spacing: .06em;\n      text-transform: uppercase;\n    }\n\n    .ph small {\n      color: var(--muted);\n    }\n\n    .pc {\n      padding: 14px;\n    }\n\n    .row {\n      display: grid;\n      grid-template-columns: 1fr 1fr;\n      gap: 12px;\n      margin-bottom: 12px;\n    }\n\n    @media (max-width: 520px) {\n      .row {\n        grid-template-columns: 1fr;\n      }\n    }\n\n    label {\n      display: block;\n      font-size: 13px;\n      color: var(--muted);\n      margin-bottom: 6px;\n    }\n\n    input,\n    textarea,\n    select {\n      width: 100%;\n      padding: 10px 12px;\n      border-radius: 12px;\n      border: 1px solid var(--line);\n      background: #fff;\n      outline: none;\n      font-size: 15px;\n      color: var(--text);\n    }\n\n    textarea {\n      min-height: 96px;\n      resize: vertical;\n    }\n\n    .hint {\n      margin-top: 6px;\n      color: var(--muted);\n      font-size: 13px;\n      line-height: 1.35;\n    }\n\n    .checklist {\n      display: grid;\n      grid-template-columns: 1fr;\n      gap: 10px;\n    }\n\n    .item {\n      display: flex;\n      gap: 10px;\n      align-items: flex-start;\n      padding: 10px 12px;\n      border: 1px solid var(--line);\n      border-radius: 14px;\n      background: var(--soft);\n    }\n\n    .item input[type=\"checkbox\"] {\n      width: 18px;\n      height: 18px;\n      margin-top: 2px;\n    }\n\n    .item .t {\n      flex: 1;\n    }\n\n    .item .t b {\n      display: block;\n      font-size: 15px;\n    }\n\n    .item .t span {\n      display: block;\n      color: var(--muted);\n      font-size: 13px;\n      line-height: 1.35;\n    }\n\n    .result {\n      border: 1px dashed var(--line);\n      border-radius: 14px;\n      background: #fff;\n      padding: 12px;\n      display: flex;\n      flex-direction: column;\n      gap: 6px;\n    }\n\n    .result .big {\n      font-size: 20px;\n      font-weight: 800;\n      letter-spacing: .02em;\n    }\n\n    .result .sub {\n      color: var(--muted);\n      font-size: 14px;\n      line-height: 1.4;\n    }\n\n    .footer {\n      padding: 16px 18px 18px;\n      border-top: 1px solid var(--line);\n      display: flex;\n      gap: 12px;\n      align-items: flex-start;\n      justify-content: space-between;\n      flex-wrap: wrap;\n      background: linear-gradient(180deg, #ffffff, #fbfdff);\n    }\n\n    .sign {\n      display: grid;\n      grid-template-columns: 1fr 1fr;\n      gap: 12px;\n      width: 100%;\n    }\n\n    @media (max-width: 720px) {\n      .sign {\n        grid-template-columns: 1fr;\n      }\n    }\n\n    .sigbox {\n      border: 1px solid var(--line);\n      border-radius: 14px;\n      background: var(--soft);\n      padding: 12px;\n    }\n\n    .smallnote {\n      color: var(--muted);\n      font-size: 13px;\n      line-height: 1.45;\n    }\n\n\n    \/* TIMELINE (10 years) *\/\n    .timelineControls {\n      display: flex;\n      gap: 10px;\n      flex-wrap: wrap;\n      align-items: flex-end;\n      margin-bottom: 12px;\n    }\n\n    .timelineControls .field {\n      flex: 1;\n      min-width: 180px;\n    }\n\n    .timelineLegend {\n      display: flex;\n      gap: 8px;\n      flex-wrap: wrap;\n      align-items: center;\n      margin: 8px 0 0;\n      color: var(--muted);\n      font-size: 13px;\n    }\n\n    .legendDot {\n      width: 10px;\n      height: 10px;\n      border-radius: 999px;\n      display: inline-block;\n      border: 1px solid var(--line);\n      background: #fff;\n    }\n\n    .legendDot.ritual {\n      background: #fef2f2;\n      border-color: #fecaca;\n    }\n\n    .legendDot.reading {\n      background: #eff6ff;\n      border-color: #bfdbfe;\n    }\n\n    .legendDot.both {\n      background: #f0fdf4;\n      border-color: #bbf7d0;\n    }\n\n    .timelineTableWrap {\n      overflow: auto;\n      border: 1px solid var(--line);\n      border-radius: 14px;\n      background: #fff;\n    }\n\n    table.timeline {\n      width: 100%;\n      border-collapse: separate;\n      border-spacing: 0;\n      min-width: 1000px;\n      font-size: 14px;\n      table-layout: fixed;\n    }\n\n    .timeline th,\n    .timeline td {\n      border-bottom: 1px solid var(--line);\n      border-right: 1px solid var(--line);\n      padding: 6px 4px;\n      vertical-align: middle;\n      background: #fff;\n    }\n\n    .timeline th {\n      position: sticky;\n      top: 0;\n      background: linear-gradient(180deg, #ffffff, #f9fafb);\n      z-index: 2;\n      text-align: center;\n      font-weight: 800;\n      letter-spacing: .02em;\n      font-size: 12px;\n    }\n\n    .timeline th:first-child,\n    .timeline td:first-child {\n      position: sticky;\n      left: 0;\n      z-index: 3;\n      background: linear-gradient(180deg, #ffffff, #f9fafb);\n      width: 60px;\n      min-width: 60px;\n    }\n\n    .timeline td:first-child {\n      background: #fff;\n      font-weight: 800;\n      font-size: 12px;\n    }\n\n    .timeline td:first-child .y {\n      display: flex;\n      flex-direction: column;\n      gap: 2px;\n    }\n\n    .timeline td:first-child .y small {\n      color: var(--muted);\n      font-weight: 600;\n      letter-spacing: 0;\n    }\n\n    .monthCell {\n      cursor: pointer;\n      border-radius: 12px;\n      border: 1px solid var(--line);\n      background: var(--soft);\n      padding: 8px 8px;\n      display: flex;\n      flex-direction: column;\n      gap: 2px;\n      user-select: none;\n      transition: transform .06s ease;\n    }\n\n    .monthCell:active {\n      transform: scale(.99);\n    }\n\n    .monthCell .m {\n      font-weight: 800;\n      font-size: 14px;\n    }\n\n    .monthCell .a {\n      color: var(--muted);\n      font-size: 12px;\n    }\n\n    .monthCell .n {\n      color: var(--muted);\n      font-size: 12px;\n      white-space: nowrap;\n      overflow: hidden;\n      text-overflow: ellipsis;\n      max-width: 88px;\n    }\n\n    .monthCell.state-none {\n      background: var(--soft);\n    }\n\n    .monthCell.state-ritual {\n      background: #fef2f2;\n      border-color: #fecaca;\n    }\n\n    .monthCell.state-reading {\n      background: #eff6ff;\n      border-color: #bfdbfe;\n    }\n\n    .monthCell.state-both {\n      background: #f0fdf4;\n      border-color: #bbf7d0;\n    }\n\n\n    \/* Improved Month Cell (editable) *\/\n    .monthCell {\n      gap: 4px;\n      padding: 6px;\n    }\n\n    .monthTop {\n      display: flex;\n      align-items: flex-start;\n      justify-content: space-between;\n      gap: 8px;\n      cursor: pointer;\n      outline: none;\n    }\n\n    .monthTop:focus {\n      box-shadow: 0 0 0 3px rgba(59, 130, 246, .18);\n      border-radius: 12px;\n    }\n\n    .monthTop .m {\n      font-weight: 800;\n      font-size: 14px;\n    }\n\n    .monthTop .a {\n      color: var(--muted);\n      font-size: 12px;\n      margin-top: 2px;\n    }\n\n    .badge {\n      margin-left: auto;\n      font-size: 12px;\n      color: var(--muted);\n      padding: 4px 8px;\n      border: 1px solid var(--line);\n      border-radius: 999px;\n      background: #fff;\n      white-space: nowrap;\n      min-height: 18px;\n    }\n\n    .monthNote {\n      width: 100%;\n      border: 1px solid var(--line);\n      border-radius: 8px;\n      background: #fff;\n      padding: 4px 6px;\n      font-size: 11px;\n      line-height: 1.25;\n      color: var(--text);\n      resize: vertical;\n      min-height: 40px;\n    }\n\n    .monthNote::placeholder {\n      color: #94a3b8;\n    }\n\n\n    .timelineTip {\n      margin-top: 10px;\n      color: var(--muted);\n      font-size: 13px;\n      line-height: 1.45;\n    }\n\n    \/* PRINT *\/\n    @media print {\n      @page {\n        size: landscape;\n        margin: 8mm;\n      }\n\n      body {\n        background: #fff;\n        font-size: 12px;\n      }\n\n      .topbar,\n      .timelineControls,\n      .timelineLegend,\n      .timelineTip {\n        display: none;\n      }\n\n      .wrap {\n        margin: 0;\n        padding: 0;\n        max-width: none;\n      }\n\n      .card {\n        box-shadow: none;\n        border: 1px solid #cbd5e1;\n        border-radius: 12px;\n      }\n\n      input,\n      textarea,\n      select {\n        border: 1px solid #cbd5e1;\n        padding: 4px 6px;\n        font-size: 12px;\n      }\n\n      .panel {\n        break-inside: avoid;\n      }\n\n      .grid {\n        gap: 8px;\n        padding: 12px;\n      }\n\n      .ph {\n        padding: 8px 12px;\n      }\n\n      .pc {\n        padding: 8px 12px;\n      }\n\n      .timelineTableWrap {\n        overflow: visible !important;\n      }\n\n      table.timeline {\n        width: 100% !important;\n        min-width: 0 !important;\n        table-layout: fixed;\n      }\n\n      .timeline th,\n      .timeline td {\n        font-size: 9px;\n        padding: 2px;\n      }\n\n      .monthCell {\n        padding: 2px;\n        border-radius: 6px;\n      }\n\n      .monthTop .m {\n        font-size: 10px;\n      }\n\n      .monthTop .a {\n        font-size: 9px;\n      }\n\n      .monthNote {\n        min-height: 30px;\n        font-size: 9px;\n        border-radius: 4px;\n      }\n\n      .badge {\n        display: none;\n      }\n    }\n  <\/style>\n<\/head>\n\n<body>\n  <div class=\"wrap\">\n    <div class=\"topbar\">\n      <div class=\"brand\">\n        <div class=\"logo\">white-card.png<\/div>\n        <div>\n          <h1>LOUIS THAI<\/h1>\n          <p>THE White Card | Follow-Up & Revisit White Card<\/p>\n        <\/div>\n      <\/div>\n\n      <div class=\"actions\">\n        <button onclick=\"window.print()\">Print<\/button>\n        <button class=\"primary\" onclick=\"exportCardAsImage(this)\">Export PNG (High Res)<\/button>\n        <button class=\"primary\" onclick=\"exportCardAsPDF(this)\">Export PDF<\/button>\n        <button onclick=\"saveCard()\">Save to Browser<\/button>\n        <button onclick=\"loadCard()\">Load<\/button>\n        <button onclick=\"clearCard()\">Reset<\/button>\n      <\/div>\n    <\/div>\n\n    <div class=\"card\" id=\"card\">\n      <div class=\"header\">\n        <div class=\"title\">\n          <div class=\"kicker\">\n            <span class=\"pill\"><span class=\"dot\"><\/span> Active Follow-Up<\/span>\n            <span class=\"pill\">Stay With U<\/span>\n          <\/div>\n          <h2>THE White Card<\/h2>\n          <p class=\"subtitle\">\n            <strong>Ownership, Confidentiality & Disclaimer<\/strong> | This card <strong>(\u201cTHE White Card\u201d)<\/strong> is\n            a document and intellectual property asset of LOUIS THAI. The Client is strongly discouraged from\n            disclosing, copying, or sharing this card with any third party unless explicitly instructed or authorized by\n            LOUIS THAI. Any unauthorized disclosure, misuse, or failure to comply with this instruction may result in\n            legal action under applicable laws and regulations.\n          <\/p>\n        <\/div>\n\n        <div class=\"meta\">\n          <div class=\"mini\">\n            <div class=\"label\">Card No.<\/div>\n            <div class=\"value\"><input id=\"cardNo\" placeholder=\"\" \/><\/div>\n          <\/div>\n          <div class=\"mini\">\n            <div class=\"label\">Created Date<\/div>\n            <div class=\"value\"><input id=\"createdDate\" type=\"date\" \/><\/div>\n          <\/div>\n          <div class=\"mini\">\n            <div class=\"label\">Practitioner<\/div>\n            <div class=\"value\"><input id=\"practitioner\" placeholder=\"\" \/><\/div>\n          <\/div>\n        <\/div>\n      <\/div>\n\n      <div class=\"grid\">\n        <!-- LEFT -->\n        <div class=\"panel\">\n          <div class=\"ph\">\n            <h3>Client Profile<\/h3>\n            <small>Client Details<\/small>\n          <\/div>\n          <div class=\"pc\">\n            <div class=\"row\">\n              <div>\n                <label>Client Name<\/label>\n                <input id=\"clientName\" placeholder=\"\" \/>\n              <\/div>\n              <div>\n                <label>Contact Platform Info<\/label>\n                <input id=\"clientPhone\" placeholder=\"+60...\" \/>\n              <\/div>\n            <\/div>\n\n            <div class=\"row\">\n              <div>\n                <label>Date of Birth<\/label>\n                <input id=\"dob\" type=\"date\" \/>\n                <div class=\"hint\"><\/div>\n              <\/div>\n              <div>\n                <label>Birth Time<\/label>\n                <input id=\"birthTime\" type=\"time\" \/>\n                <div class=\"hint\"><\/div>\n              <\/div>\n            <\/div>\n\n            <div class=\"row\">\n              <div>\n                <label>Last Service Date<\/label>\n                <input id=\"lastDate\" type=\"date\" \/>\n              <\/div>\n              <div>\n                <label>Last Service Type<\/label>\n                <select id=\"lastType\">\n                  <option value=\"\u7b97\u547d\">\u7b97\u547d See Luck<\/option>\n                  <option value=\"\u6cd5\u4e8b\">\u6cd5\u4e8b Ritual<\/option>\n                  <option value=\"\u7b97\u547d + \u6cd5\u4e8b\">\u7b97\u547d See Luck + \u6cd5\u4e8b Ritual<\/option>\n                  <option value=\"\u5176\u4ed6\">\u5176\u4ed6 Others<\/option>\n                <\/select>\n              <\/div>\n            <\/div>\n\n            <div class=\"row\">\n              <div>\n                <label>Main Topic<\/label>\n                <input id=\"topic\" placeholder=\"\" \/>\n              <\/div>\n              <div>\n                <label>Risk Level<\/label>\n                <select id=\"risk\">\n                  <option value=\"\u4f4e\">\u4f4e Low<\/option>\n                  <option value=\"\u4e2d\">\u4e2d Medium<\/option>\n                  <option value=\"\u9ad8\">\u9ad8 High<\/option>\n                  <option value=\"\u7d27\u6025\">\u7d27\u6025 Urgent<\/option>\n                <\/select>\n              <\/div>\n            <\/div>\n\n            <div>\n              <label> Remarks & Notes<\/label>\n              <textarea id=\"notes\" placeholder=\"\"><\/textarea>\n            <\/div>\n          <\/div>\n        <\/div>\n\n        <!-- RIGHT -->\n        <div class=\"panel\">\n          <div class=\"ph\">\n            <h3>Follow-Up Plan<\/h3>\n            <small>Next Adventure<\/small>\n          <\/div>\n          <div class=\"pc\">\n            <div class=\"row\">\n              <div>\n                <label>Next Visit Month<\/label>\n                <input id=\"monthsInterval\" type=\"number\" min=\"0\" step=\"1\" placeholder=\"\" \/>\n                <div class=\"hint\">Previous Service Date<\/div>\n              <\/div>\n              <div>\n                <label>Next Visit Age<\/label>\n                <input id=\"ageTargets\" placeholder=\"\" \/>\n                <div class=\"hint\"><\/div>\n              <\/div>\n            <\/div>\n\n            <div class=\"row\">\n              <div>\n                <label>Next Visit Date<\/label>\n                <input id=\"nextDate\" type=\"date\" \/>\n                <div class=\"hint\"><\/div>\n              <\/div>\n              <div>\n                <label>Next Visit Type<\/label>\n                <select id=\"nextType\">\n                  <option value=\"\u54a8\u8be2\u7b97\u547d\">\u67e5\u8be2\u8fd0\u7a0b Check Luck<\/option>\n                  <option value=\"\u6cd5\u4e8b\u590d\u76d8\">\u6cd5\u4e8b Ritual<\/option>\n                  <option value=\"\u7b97\u547d + \u6cd5\u4e8b\">\u67e5\u8be2\u8fd0\u7a0b Check Luck + \u6cd5\u4e8b Ritual<\/option>\n                  <option value=\"\u770b\u60c5\u51b5\u518d\u5b9a\">\u770b\u60c5\u51b5\u518d\u5b9a Let's Observation<\/option>\n                <\/select>\n              <\/div>\n            <\/div>\n\n            <div class=\"result\" aria-live=\"polite\">\n              <div class=\"big\" id=\"autoSummary\">\u2014<\/div>\n              <div class=\"sub\" id=\"autoDetail\">-<\/div>\n            <\/div>\n\n            <div style=\"height:12px\"><\/div>\n\n            <div class=\"checklist\">\n              <div class=\"item\">\n                <input id=\"chk1\" type=\"checkbox\" \/>\n                <div class=\"t\">\n                  <b>\u54a8\u8be2\u7b97\u547d\uff1a\u786e\u8ba4\u4e3b\u7ebf\u662f\u5426\u8f6c\u5411<\/b>\n                  <span>\u68c0\u67e5\uff1a\u5de5\u4f5c\/\u611f\u60c5\/\u5065\u5eb7\u662f\u5426\u51fa\u73b0\u65b0\u53d8\u91cf\uff1b\u662f\u5426\u9700\u8981\u8c03\u6574\u7b56\u7565\u4e0e\u7981\u5fcc<\/span>\n                <\/div>\n              <\/div>\n\n              <div class=\"item\">\n                <input id=\"chk2\" type=\"checkbox\" \/>\n                <div class=\"t\">\n                  <b>\u6cd5\u4e8b\u590d\u67e5 \u89c2\u5bdf\u300c\u6548\u5e94\u6307\u6807\u300d<\/b>\n                  <span>\u8bb0\u5f55\u6cd5\u4e8b\u540e\u7684\u75c7\u72b6\u3001\u6548\u679c<\/span>\n                <\/div>\n              <\/div>\n\n              <div class=\"item\">\n                <input id=\"chk3\" type=\"checkbox\" \/>\n                <div class=\"t\">\n                  <b>\u91cd\u5927\u5e74\u4efd\u63d0\u9192\uff1a\u751f\u65e5\u6708 \u8f6c\u8fd0\u70b9<\/b>\n                  <span>-<\/span>\n                <\/div>\n              <\/div>\n\n              <div class=\"item\">\n                <input id=\"chk4\" type=\"checkbox\" \/>\n                <div class=\"t\">\n                  <b>\u4e0b\u4e00\u6b65\u884c\u52a8\uff1a\u9884\u7ea6\u3001\u51c6\u5907\u7269\u54c1\u3001\u6ce8\u610f\u4e8b\u9879<\/b>\n                  <span>-<\/span>\n                <\/div>\n              <\/div>\n            <\/div>\n\n            <div style=\"height:12px\"><\/div>\n\n            <div class=\"row\">\n              <div>\n                <label>Reminder Message<\/label>\n                <textarea id=\"reminder\" placeholder=\"\"><\/textarea>\n              <\/div>\n              <div>\n                <label>Appointment Status<\/label>\n                <select id=\"status\">\n                  <option value=\"\u672a\u9884\u7ea6\">Without Booking<\/option>\n                  <option value=\"\u5df2\u63d0\u9192\">Reminded<\/option>\n                  <option value=\"\u5df2\u9884\u7ea6\">Appoinment Booked<\/option>\n                  <option value=\"\u5df2\u5b8c\u6210\">Completed<\/option>\n                <\/select>\n                <div class=\"hint\">THE White Card is for you to awareness on your daily life statues<\/div>\n              <\/div>\n            <\/div>\n\n          <\/div>\n        <\/div>\n      <\/div>\n\n\n      <!-- TIMELINE -->\n      <div class=\"grid\" style=\"padding-top:0;\">\n        <div class=\"panel\" style=\"grid-column: 1 \/ -1;\">\n          <div class=\"ph\">\n            <h3>35-Year Timeline<\/h3>\n            <small>as a reference only<\/small>\n          <\/div>\n          <div class=\"pc\">\n            <div class=\"timelineControls\">\n              <div class=\"field\">\n                <label>Timeline Start Month<\/label>\n                <input id=\"timelineStart\" type=\"month\" \/>\n                <div class=\"hint\">-<\/div>\n              <\/div>\n\n              <div class=\"field\">\n                <label>Quick Present<\/label>\n                <select id=\"timelinePreset\">\n                  <option value=\"\">Selections<\/option>\n                  <option value=\"ritual_7_21_49_100\">\u6cd5\u4e8b\u540e\u68c0\u9a8c\u65e5\u671f<\/option>\n                  <option value=\"annual_birthday\">\u6bcf\u5e74\u751f\u65e5\u6708\uff1a\u54a8\u8be2\u7b97\u547d<\/option>\n                  <option value=\"age_18_21_24_27_30_33_36\">\u5173\u952e\u5e74\u9f84 Key Age<\/option>\n                <\/select>\n                <div class=\"hint\">-<\/div>\n              <\/div>\n\n              <div class=\"field\" style=\"flex:0; min-width: 260px;\">\n                <label>Actions<\/label>\n                <div style=\"display:flex; gap:10px; flex-wrap:wrap;\">\n                  <button type=\"button\" onclick=\"buildTimeline()\">Refresh<\/button>\n                  <button type=\"button\" class=\"danger\" onclick=\"clearTimelineMarks()\"> \u26a0\ufe0f Delete<\/button>\n                  <button type=\"button\" class=\"primary\" onclick=\"exportTimelineToNotes()\">Export to Remarks<\/button>\n                <\/div>\n                <div class=\"hint\"><\/div>\n              <\/div>\n            <\/div>\n\n            <div class=\"timelineLegend\">\n              <span><span class=\"legendDot ritual\"><\/span> \u6cd5\u4e8b Ritual<\/span>\n              <span><span class=\"legendDot reading\"><\/span> \u54a8\u8be2\u7b97\u547d Reading<\/span>\n              <span><span class=\"legendDot both\"><\/span> \u4e24\u8005 Both<\/span>\n              <span class=\"pill\" style=\"padding:4px 10px;\">IMPORTANT<\/span>\n            <\/div>\n\n            <div class=\"timelineTableWrap\" style=\"margin-top:12px;\">\n              <table class=\"timeline\" aria-label=\"35 year timeline table\">\n                <thead>\n                  <tr>\n                    <th>Year<\/th>\n                    <th>Jan<\/th>\n                    <th>Feb<\/th>\n                    <th>Mar<\/th>\n                    <th>Apr<\/th>\n                    <th>May<\/th>\n                    <th>Jun<\/th>\n                    <th>Jul<\/th>\n                    <th>Aug<\/th>\n                    <th>Sep<\/th>\n                    <th>Oct<\/th>\n                    <th>Nov<\/th>\n                    <th>Dec<\/th>\n                  <\/tr>\n                <\/thead>\n                <tbody id=\"timelineBody\"><\/tbody>\n              <\/table>\n            <\/div>\n\n            <div class=\"timelineTip\">\n              Please write down your own note if you have any note want to note down<b> REQUIRE to let's consultant\n                double check of your note before signed.<\/b>\n            <\/div>\n          <\/div>\n        <\/div>\n      <\/div>\n\n      <div class=\"footer\">\n        <div class=\"sign\">\n          <div class=\"sigbox\">\n            <label>Client Signature<\/label>\n            <input id=\"clientSign\" placeholder=\"Signature\" \/>\n            <div class=\"smallnote\"><\/div>\n          <\/div>\n\n          <div class=\"sigbox\">\n            <label>Practitioner Signature<\/label>\n            <input id=\"pracSign\" placeholder=\"Signature\" \/>\n            <div class=\"smallnote\">by WONG SHEE YEE, LOUIS<\/div>\n          <\/div>\n\n          <div class=\"sigbox\" style=\"grid-column: 1 \/ -1;\">\n            <div class=\"smallnote\">\n              Disclaimer | This Follow-Up Card and Timeline are provided solely as a service record and advisory\n              reference for consultation and ritual planning purposes. They do not constitute, and should not be\n              interpreted as, legal advice, medical advice, medical diagnosis, treatment, or any form of professional\n              guarantee. All consultation insights, timelines, and suggested follow-up intervals are non-binding\n              recommendations based on contextual analysis at the time of service. Actual outcomes may vary due to\n              personal decisions, external circumstances, and factors beyond reasonable control. Follow-up schedules,\n              consultation intervals, and ritual arrangements may be adjusted at any time according to the Client\u2019s\n              actual situation, condition, readiness, or mutual agreement. By using this card, the Client acknowledges\n              and accepts that all services are provided on an advisory and discretionary basis only. All Right Reserved\n              \u00a9 copyright\n            <\/div>\n          <\/div>\n        <\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n\n  <script>\n    \/\/ ===== Helpers =====\n    const $ = (id) => document.getElementById(id);\n\n    function todayISO() {\n      const d = new Date();\n      d.setMinutes(d.getMinutes() - d.getTimezoneOffset());\n      return d.toISOString().slice(0, 10);\n    }\n\n    function addMonths(date, months) {\n      const d = new Date(date);\n      const day = d.getDate();\n      d.setMonth(d.getMonth() + Number(months || 0));\n      \/\/ handle month rollover\n      if (d.getDate() < day) d.setDate(0);\n      return d;\n    }\n\n    function ageOnDate(dob, onDate) {\n      const b = new Date(dob);\n      const d = new Date(onDate);\n      let age = d.getFullYear() - b.getFullYear();\n      const m = d.getMonth() - b.getMonth();\n      if (m < 0 || (m === 0 && d.getDate() < b.getDate())) age--;\n      return age;\n    }\n\n    function nextBirthdayYearAge(dob, targetAge) {\n      \/\/ returns the date at which user turns targetAge (approx: on birthday)\n      const b = new Date(dob);\n      const y = b.getFullYear() + Number(targetAge);\n      return new Date(y, b.getMonth(), b.getDate());\n    }\n\n    \/\/ ===== Core Auto-calc =====\n    function recalc() {\n      const dob = $(\"dob\").value;\n      const lastDate = $(\"lastDate\").value;\n      const monthsInterval = Number($(\"monthsInterval\").value || 0);\n      const ageTargetsRaw = $(\"ageTargets\").value.trim();\n\n      \/\/ default created date\n      if (!$(\"createdDate\").value) $(\"createdDate\").value = todayISO();\n\n      let next = null;\n      let ruleUsed = [];\n\n      \/\/ Rule A: months interval based on lastDate\n      if (lastDate && monthsInterval > 0) {\n        next = addMonths(lastDate, monthsInterval);\n        ruleUsed.push(`\u6bcf ${monthsInterval} \u4e2a\u6708\u56de\u8bbf`);\n      }\n\n      \/\/ Rule B: age targets\n      if (dob && ageTargetsRaw) {\n        const ages = ageTargetsRaw\n          .split(\",\")\n          .map(s => s.trim())\n          .filter(Boolean)\n          .map(Number)\n          .filter(n => Number.isFinite(n) && n > 0)\n          .sort((a, b) => a - b);\n\n        if (ages.length) {\n          const now = new Date();\n          const nowISO = new Date(now.getFullYear(), now.getMonth(), now.getDate());\n          const currentAge = ageOnDate(dob, nowISO);\n\n          const nextAge = ages.find(a => a > currentAge);\n          if (nextAge) {\n            const d = nextBirthdayYearAge(dob, nextAge);\n            \/\/ choose earlier between rule A and rule B if both exist\n            if (!next || d < next) next = d;\n            ruleUsed.push(`\u6307\u5b9a\u5e74\u9f84\uff1a${nextAge}\u5c81`);\n          }\n        }\n      }\n\n      \/\/ Update nextDate field if computed\n      if (next) {\n        const iso = new Date(next);\n        iso.setMinutes(iso.getMinutes() - iso.getTimezoneOffset());\n        $(\"nextDate\").value = iso.toISOString().slice(0, 10);\n      }\n\n      \/\/ Summary\n      const nextDate = $(\"nextDate\").value;\n      if (dob && nextDate) {\n        const age = ageOnDate(dob, nextDate);\n        const y = new Date(nextDate).getFullYear();\n        $(\"autoSummary\").textContent = `\u5efa\u8bae\u56de\u8bbf\uff1a${nextDate}\uff08${y}\u5e74\uff0c\u5ba2\u6237\u7ea6 ${age} \u5c81\uff09`;\n        const rule = ruleUsed.length ? `\u89c4\u5219\uff1a${ruleUsed.join(\" + \")}` : \"\u89c4\u5219\uff1a\u624b\u52a8\u8bbe\u5b9a\";\n        $(\"autoDetail\").textContent = `${rule}\u3002\u4f60\u4e5f\u53ef\u4ee5\u624b\u52a8\u6539\u201c\u4e0b\u4e00\u6b21\u5efa\u8bae\u56de\u8bbf\u65e5\u671f\u201d\u3002`;\n\n        \/\/ auto reminder message if empty\n        if (!$(\"reminder\").value.trim()) {\n          $(\"reminder\").value =\n            `\u60a8\u597d\uff0c\u7cfb\u7edf\u63d0\u9192\u60a8\u5728 ${nextDate}\uff08\u7ea6${age}\u5c81\uff09\u5efa\u8bae\u56de\u8bbf\u54a8\u8be2\uff0c\u8fdb\u884c\u8fd0\u52bf\u590d\u76d8\u4e0e\u6cd5\u4e8b\u6548\u5e94\u786e\u8ba4\u3002\u5982\u9700\u9884\u7ea6\uff0c\u8bf7\u56de\u590d\u6211\u65b9\u4fbf\u7684\u65e5\u671f\u4e0e\u65f6\u95f4\u3002LOUIS THAI`;\n        }\n      } else {\n        $(\"autoSummary\").textContent = \"\u2014\";\n        $(\"autoDetail\").textContent = \"\";\n      }\n    }\n\n\n    \/\/ ===== Timeline (35 years) =====\n    \/\/ State: none | ritual | reading | both\n    window.__timelineState = window.__timelineState || {\n      startMonth: null,   \/\/ \"YYYY-MM\"\n      marks: {}           \/\/ key \"YYYY-MM\" -> {state:\"none|ritual|reading|both\", note:\"\"}\n    };\n\n    function monthKeyFromDate(d) {\n      const y = d.getFullYear();\n      const m = String(d.getMonth() + 1).padStart(2, '0');\n      return `${y}-${m}`;\n    }\n\n    function setDefaultTimelineStart() {\n      const el = $(\"timelineStart\");\n      if (!el) return;\n      if (el.value) return;\n      const now = new Date();\n      const key = monthKeyFromDate(now);\n      el.value = key;\n      window.__timelineState.startMonth = key;\n    }\n\n    function parseMonthKey(key) {\n      const [y, m] = (key || \"\").split(\"-\").map(Number);\n      if (!y || !m) return null;\n      return new Date(y, m - 1, 1);\n    }\n\n    function stateCycle(s) {\n      \/\/ none -> ritual -> reading -> both -> none\n      if (s === \"none\") return \"ritual\";\n      if (s === \"ritual\") return \"reading\";\n      if (s === \"reading\") return \"both\";\n      return \"none\";\n    }\n\n    function stateLabel(s) {\n      if (s === \"ritual\") return \"\u6cd5\u4e8b\";\n      if (s === \"reading\") return \"\u54a8\u8be2\";\n      if (s === \"both\") return \"\u6cd5\u4e8b+\u54a8\u8be2\";\n      return \"\";\n    }\n\n    function buildTimeline() {\n      const body = $(\"timelineBody\");\n      if (!body) return;\n\n      setDefaultTimelineStart();\n      const startKey = $(\"timelineStart\").value || window.__timelineState.startMonth;\n      if (!startKey) return;\n\n      window.__timelineState.startMonth = startKey;\n\n      const start = parseMonthKey(startKey) || new Date();\n      const startYear = start.getFullYear();\n\n      body.innerHTML = \"\";\n\n      \/\/ Keep only marks inside the visible 10-year range? (no, keep all, but render visible)\n      const dob = $(\"dob\")?.value || null;\n\n      for (let y = startYear; y < startYear + 35; y++) {\n        const tr = document.createElement(\"tr\");\n\n        \/\/ Year label cell\n        const tdYear = document.createElement(\"td\");\n        const yearStart = new Date(y, 0, 1);\n        const ageYear = (dob) ? ageOnDate(dob, yearStart) : null;\n        tdYear.innerHTML = `\n          <div class=\"y\">\n            <div>${y}<\/div>\n            <small>${dob ? `\u7ea6 ${ageYear} \u5c81\uff08\u5e74\u521d\uff09` : \"\"}<\/small>\n          <\/div>\n        `;\n        tr.appendChild(tdYear);\n\n        \/\/ 12 months\n        for (let m = 0; m < 12; m++) {\n          const td = document.createElement(\"td\");\n          const d = new Date(y, m, 1);\n\n          \/\/ Hide months before start month in the first year\n          if (y === startYear) {\n            const startMonthIndex = start.getMonth();\n            if (m < startMonthIndex) {\n              td.innerHTML = \"\";\n              td.style.background = \"#fff\";\n              td.style.borderRight = \"1px solid var(--line)\";\n              tr.appendChild(td);\n              continue;\n            }\n          }\n\n          const key = monthKeyFromDate(d);\n          const mark = window.__timelineState.marks[key] || { state: \"none\", note: \"\" };\n          const age = dob ? ageOnDate(dob, d) : null;\n          const note = (mark.note || \"\").trim();\n\n          const cell = document.createElement(\"div\");\n          cell.className = `monthCell state-${mark.state || \"none\"}`;\n          cell.dataset.key = key;\n          cell.dataset.state = mark.state || \"none\";\n          cell.dataset.note = note;\n\n          const mm = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"][m];\n\n          cell.innerHTML = `            <div class=\"monthTop\" role=\"button\" tabindex=\"0\" aria-label=\"toggle state\">\n              <div class=\"m\">${mm}<\/div>\n              <div class=\"a\">${dob ? `Age ${age}` : \"Age \u2014\"}<\/div>\n              <div class=\"badge\">${note ? \"Note\" : (cell.dataset.state === \"none\" ? \"\" : stateLabel(cell.dataset.state))}<\/div>\n            <\/div>\n            <textarea class=\"monthNote\" placeholder=\"\" rows=\"3\">${note ? note : \"\"}<\/textarea>`;\n\n          \/\/ click to cycle state\n          cell.querySelector(\".monthTop\").addEventListener(\"click\", (e) => {\n            const k = cell.dataset.key;\n            const current = cell.dataset.state || \"none\";\n            const next = stateCycle(current);\n\n            cell.dataset.state = next;\n            cell.className = `monthCell state-${next}`;\n\n            const prev = window.__timelineState.marks[k] || { state: \"none\", note: \"\" };\n            window.__timelineState.marks[k] = { state: next, note: prev.note || \"\" };\n\n            const label = (window.__timelineState.marks[k].note || \"\").trim()\n              ? window.__timelineState.marks[k].note\n              : stateLabel(next);\n\n            const badge = cell.querySelector(\".badge\");\n            badge.textContent = ((window.__timelineState.marks[k].note || \"\").trim()) ? \"Note\" : (next === \"none\" ? \"\" : stateLabel(next));\n            cell.querySelector(\".a\").textContent = dob ? `Age ${ageOnDate(dob, d)}` : \"Age \u2014\";\n            \/\/ keyboard toggle (Enter\/Space)\n            cell.querySelector(\".monthTop\").addEventListener(\"keydown\", (e) => {\n              if (e.key === \"Enter\" || e.key === \" \") {\n                e.preventDefault();\n                cell.querySelector(\".monthTop\").click();\n              }\n            });\n\n            \/\/ note typing (auto-save)\n            const noteEl = cell.querySelector(\".monthNote\");\n            noteEl.addEventListener(\"input\", () => {\n              const k = cell.dataset.key;\n              const prev = window.__timelineState.marks[k] || { state: \"none\", note: \"\" };\n              window.__timelineState.marks[k] = { state: prev.state || \"none\", note: (noteEl.value || \"\").trim() };\n              cell.dataset.note = (noteEl.value || \"\").trim();\n\n              \/\/ update badge\n              const badge = cell.querySelector(\".badge\");\n              const has = (noteEl.value || \"\").trim().length > 0;\n              badge.textContent = has ? \"Note\" : (cell.dataset.state === \"none\" ? \"\" : stateLabel(cell.dataset.state));\n            });\n\n          });\n\n          \/\/ right click \/ long press to edit note\n          cell.addEventListener(\"contextmenu\", (e) => { e.preventDefault(); const noteEl = cell.querySelector(\".monthNote\"); noteEl.focus(); noteEl.select?.(); });\n\n          td.appendChild(cell);\n          tr.appendChild(td);\n        }\n\n        body.appendChild(tr);\n      }\n\n      \/\/ Apply preset if selected\n      applyTimelinePreset();\n    }\n\n    function clearTimelineMarks() {\n      if (!confirm(\"\u786e\u5b9a\u6e05\u7a7a Timeline \u6240\u6709\u6807\u8bb0\u4e0e\u5907\u6ce8\uff1f\")) return;\n      window.__timelineState.marks = {};\n      buildTimeline();\n    }\n\n    function applyTimelinePreset() {\n      const preset = $(\"timelinePreset\")?.value || \"\";\n      if (!preset) return;\n\n      \/\/ do not overwrite existing custom marks; only fill blanks\n      const dob = $(\"dob\")?.value || null;\n      const lastDate = $(\"lastDate\")?.value || null;\n\n      const startKey = $(\"timelineStart\").value || window.__timelineState.startMonth;\n      const start = parseMonthKey(startKey) || new Date();\n      const startYear = start.getFullYear();\n\n      const withinRange = (key) => {\n        const d = parseMonthKey(key);\n        if (!d) return false;\n        const y = d.getFullYear();\n        return (y >= startYear && y < startYear + 35);\n      };\n\n      \/\/ helper to mark a date's month\n      const markMonth = (dateObj, state, note = \"\") => {\n        const k = monthKeyFromDate(dateObj);\n        if (!withinRange(k)) return;\n        const existing = window.__timelineState.marks[k];\n        if (existing && (existing.state !== \"none\" || (existing.note || \"\").trim())) return; \/\/ keep custom\n        window.__timelineState.marks[k] = { state, note };\n      };\n\n      if (preset === \"annual_birthday\" && dob) {\n        const b = new Date(dob);\n        const birthMonth = b.getMonth();\n        for (let y = startYear; y < startYear + 35; y++) {\n          const d = new Date(y, birthMonth, 1);\n          markMonth(d, \"reading\", \"\u751f\u65e5\u6708\u54a8\u8be2\");\n        }\n      }\n\n      if (preset === \"age_18_21_24_27_30_33_36\" && dob) {\n        const targets = [18, 21, 24, 27, 30, 33, 36];\n        for (const a of targets) {\n          const d = nextBirthdayYearAge(dob, a); \/\/ exact birthday date\n          markMonth(new Date(d.getFullYear(), d.getMonth(), 1), \"both\", `\u5173\u952e\u5e74\u9f84 ${a}`);\n        }\n      }\n\n      if (preset === \"ritual_7_21_49_100\" && lastDate) {\n        \/\/ mark the months that contain these days-after dates\n        const base = new Date(lastDate);\n        const days = [7, 21, 49, 100];\n        for (const dd of days) {\n          const d = new Date(base);\n          d.setDate(d.getDate() + dd);\n          markMonth(new Date(d.getFullYear(), d.getMonth(), 1), \"ritual\", `\u6cd5\u4e8b\u590d\u76d8 ${dd}\u5929`);\n        }\n      }\n\n      \/\/ rebuild once to reflect preset changes and reset preset back to blank\n      $(\"timelinePreset\").value = \"\";\n      buildTimeline(); \/\/ rebuild will re-apply preset? preset blank now, so safe\n    }\n\n    function exportTimelineToNotes() {\n      \/\/ Export marked months to Notes field (append)\n      const entries = Object.entries(window.__timelineState.marks || {})\n        .filter(([k, v]) => (v && v.state && v.state !== \"none\") || (v && (v.note || \"\").trim()))\n        .sort(([a], [b]) => a.localeCompare(b))\n        .map(([k, v]) => {\n          const tag = v.state && v.state !== \"none\" ? stateLabel(v.state) : \"\u5907\u6ce8\";\n          const note = (v.note || \"\").trim();\n          return `\u2022 ${k}\uff5c${tag}${note ? `\uff5c${note}` : \"\"}`;\n        });\n\n      if (!entries.length) return alert(\"Timeline \u6ca1\u6709\u4efb\u4f55\u6807\u8bb0\u53ef\u5bfc\u51fa\u3002\");\n\n      const block = `\\n\\n[35-Year Timeline]\\n${entries.join(\"\\n\")}`;\n      const notes = $(\"notes\");\n      notes.value = (notes.value || \"\").trim() + block;\n      alert(\"\u5df2\u5bfc\u51fa\u5230\u5907\u6ce8 Notes\uff08\u8ffd\u52a0\u5728\u672b\u5c3e\uff09\u3002\");\n    }\n\n\n    \/\/ ===== Save\/Load =====\n    const KEY = \"LOUIS_THAI_FOLLOWUP_CARD_V1\";\n\n    function getAllFields() {\n      const ids = [\n        \"cardNo\", \"createdDate\", \"practitioner\",\n        \"clientName\", \"clientPhone\", \"dob\", \"birthTime\",\n        \"lastDate\", \"lastType\", \"topic\", \"risk\", \"notes\",\n        \"monthsInterval\", \"ageTargets\", \"nextDate\", \"nextType\",\n        \"chk1\", \"chk2\", \"chk3\", \"chk4\",\n        \"reminder\", \"status\", \"clientSign\", \"pracSign\"\n      ];\n      const data = {};\n      ids.forEach(id => {\n        const el = $(id);\n        if (!el) return;\n        if (el.type === \"checkbox\") data[id] = el.checked;\n        else data[id] = el.value;\n      });\n\n      \/\/ timeline\n      data[\"timelineState\"] = window.__timelineState || { startMonth: null, marks: {} };\n\n      return data;\n    }\n\n    function setAllFields(data) {\n      Object.keys(data || {}).forEach(id => {\n        if (id === \"timelineState\") return;\n        const el = $(id);\n        if (!el) return;\n        if (el.type === \"checkbox\") el.checked = !!data[id];\n        else el.value = data[id];\n      });\n\n      if (data && data.timelineState) {\n        window.__timelineState = data.timelineState;\n        \/\/ restore start month\n        if ($(\"timelineStart\") && window.__timelineState.startMonth) {\n          $(\"timelineStart\").value = window.__timelineState.startMonth;\n        }\n      }\n\n      recalc();\n      buildTimeline();\n    }\n\n    function saveCard() {\n      const data = getAllFields();\n      localStorage.setItem(KEY, JSON.stringify(data));\n      alert(\"\u5df2\u4fdd\u5b58\u5230\u6d4f\u89c8\u5668\uff08localStorage\uff09\u3002\");\n    }\n\n    function loadCard(silent = false) {\n      const raw = localStorage.getItem(KEY);\n      if (!raw) {\n        if (!silent) alert(\"\u6ca1\u6709\u627e\u5230\u5df2\u4fdd\u5b58\u7684\u6570\u636e\u3002\");\n        return;\n      }\n      try {\n        const data = JSON.parse(raw);\n        setAllFields(data);\n        if (!silent) alert(\"\u5df2\u8f7d\u5165\u3002\");\n      } catch (e) {\n        if (!silent) alert(\"\u8f7d\u5165\u5931\u8d25\uff1a\u6570\u636e\u635f\u574f\u3002\");\n      }\n    }\n\n    function clearCard() {\n      if (!confirm(\"\u786e\u5b9a\u6e05\u7a7a\u6240\u6709\u680f\u4f4d\uff1f\")) return;\n      document.querySelectorAll(\"input, textarea, select\").forEach(el => {\n        if (el.type === \"checkbox\") el.checked = false;\n        else el.value = \"\";\n      });\n      $(\"createdDate\").value = todayISO();\n      recalc();\n    }\n\n    \/\/ ===== Export Function =====\n    async function exportCardAsImage(btn) {\n      const card = $(\"card\");\n      const originalText = btn.textContent;\n\n      try {\n        btn.disabled = true;\n        btn.textContent = \"Processing...\";\n\n        \/\/ Set high resolution scale\n        const scale = 2.5;\n\n        \/\/ Use html2canvas to capture the card element\n        const canvas = await html2canvas(card, {\n          scale: scale,\n          useCORS: true,\n          allowTaint: true,\n          backgroundColor: null, \/\/ Allow transparency for rounded corners\n          logging: false,\n          scrollY: -window.scrollY, \/\/ Correct for scroll position\n          onclone: (clonedDoc) => {\n            const clonedCard = clonedDoc.getElementById(\"card\");\n            \/\/ Preserve border radius as requested\n            clonedCard.style.boxShadow = \"none\";\n            clonedCard.style.border = \"1px solid #e2e8f0\";\n\n            \/\/ Expand timeline to show all years and months without scrollbars\n            const wraps = clonedDoc.querySelectorAll(\".timelineTableWrap\");\n            wraps.forEach(wrap => {\n              wrap.style.overflow = \"visible\";\n              wrap.style.width = \"auto\";\n              wrap.style.maxWidth = \"none\";\n            });\n            const tables = clonedDoc.querySelectorAll(\"table.timeline\");\n            tables.forEach(table => {\n              table.style.width = \"100%\";\n              table.style.minWidth = \"0\";\n              \/\/ Ensure sticky elements don't cause issues in static render\n              const stickies = table.querySelectorAll(\"th, td\");\n              stickies.forEach(s => {\n                if (window.getComputedStyle(s).position === 'sticky') {\n                  s.style.position = 'static';\n                }\n              });\n            });\n\n            \/\/ Ensure all textareas are tall enough to show full content\n            const textareas = clonedDoc.querySelectorAll('textarea');\n            textareas.forEach(t => {\n              t.style.height = t.scrollHeight + 'px';\n            });\n          }\n        });\n\n        const image = canvas.toDataURL(\"image\/png\");\n\n        const link = document.createElement(\"a\");\n        const clientName = ($(\"clientName\").value || \"Customer\").toLowerCase().replace(\/[^a-z0-9]\/gi, '_');\n\n        link.download = `${clientName}_followup_card.png`;\n        link.href = image;\n        link.click();\n\n      } catch (err) {\n        console.error(\"Export failed:\", err);\n        alert(\"Export failed. See console for details.\");\n      } finally {\n        btn.disabled = false;\n        btn.textContent = originalText;\n      }\n    }\n\n    async function exportCardAsPDF(btn) {\n      const card = $(\"card\");\n      const originalText = btn.textContent;\n\n      try {\n        btn.disabled = true;\n        btn.textContent = \"Generating PDF...\";\n\n        const scale = 2.0; \/\/ Slightly lower scale for PDF to keep file size reasonable\n\n        const canvas = await html2canvas(card, {\n          scale: scale,\n          useCORS: true,\n          allowTaint: true,\n          backgroundColor: \"#ffffff\",\n          logging: false,\n          scrollY: -window.scrollY,\n          onclone: (clonedDoc) => {\n            const clonedCard = clonedDoc.getElementById(\"card\");\n            clonedCard.style.boxShadow = \"none\";\n            clonedCard.style.borderRadius = \"0\"; \/\/ PDFs look better without individual element rounded corners if it's the whole page\n\n            const wraps = clonedDoc.querySelectorAll(\".timelineTableWrap\");\n            wraps.forEach(wrap => {\n              wrap.style.overflow = \"visible\";\n              wrap.style.width = \"auto\";\n              wrap.style.maxWidth = \"none\";\n            });\n            const tables = clonedDoc.querySelectorAll(\"table.timeline\");\n            tables.forEach(table => {\n              table.style.width = \"100%\";\n              table.style.minWidth = \"0\";\n              const stickies = table.querySelectorAll(\"th, td\");\n              stickies.forEach(s => {\n                if (window.getComputedStyle(s).position === 'sticky') {\n                  s.style.position = 'static';\n                }\n              });\n            });\n            const textareas = clonedDoc.querySelectorAll('textarea');\n            textareas.forEach(t => {\n              t.style.height = t.scrollHeight + 'px';\n            });\n          }\n        });\n\n        const imgData = canvas.toDataURL(\"image\/jpeg\", 0.95);\n        const { jsPDF } = window.jspdf;\n\n        \/\/ Calculate dimensions\n        const imgWidth = 210; \/\/ Base width in mm (A4 width)\n        const imgHeight = (canvas.height * imgWidth) \/ canvas.width;\n\n        \/\/ Create PDF with custom page size to fit all content on a single page\n        const doc = new jsPDF({\n          orientation: 'p',\n          unit: 'mm',\n          format: [imgWidth, imgHeight]\n        });\n\n        doc.addImage(imgData, 'JPEG', 0, 0, imgWidth, imgHeight);\n\n        const clientName = ($(\"clientName\").value || \"Customer\").toLowerCase().replace(\/[^a-z0-9]\/gi, '_');\n        doc.save(`${clientName}_followup_card.pdf`);\n\n      } catch (err) {\n        console.error(\"PDF Export failed:\", err);\n        alert(\"PDF Export failed. See console for details.\");\n      } finally {\n        btn.disabled = false;\n        btn.textContent = originalText;\n      }\n    }\n\n    \/\/ ===== Listeners =====\n    [\"dob\", \"lastDate\", \"monthsInterval\", \"ageTargets\", \"nextDate\"].forEach(id => {\n      $(id).addEventListener(\"input\", recalc);\n      $(id).addEventListener(\"change\", recalc);\n    });\n\n    window.addEventListener(\"DOMContentLoaded\", () => {\n      $(\"createdDate\").value = todayISO();\n\n      \/\/ Load saved data first\n      loadCard(true);\n\n      recalc();\n\n      \/\/ timeline init\n      setDefaultTimelineStart();\n      buildTimeline();\n\n      $(\"timelineStart\")?.addEventListener(\"change\", () => {\n        window.__timelineState.startMonth = $(\"timelineStart\").value;\n        buildTimeline();\n      });\n      $(\"timelinePreset\")?.addEventListener(\"change\", () => {\n        \/\/ buildTimeline will apply preset\n        buildTimeline();\n      });\n      $(\"dob\")?.addEventListener(\"change\", () => buildTimeline());\n      $(\"lastDate\")?.addEventListener(\"change\", () => buildTimeline());\n    });\n  <\/script>\n<\/body>\n\n<\/html>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<\/div>","protected":false},"excerpt":{"rendered":"<p>THE White Card \u2022 LOUIS THAI white-card.png LOUIS THAI THE White Card | Follow-Up &#038; Revisit White Card Print Export PNG (High Res) Export PDF Save to Browser Load Reset Active Follow-Up Stay With U THE White Card Ownership, Confidentiality &#038; Disclaimer | This card (\u201cTHE White Card\u201d) is a document and intellectual property asset [&hellip;]<\/p>","protected":false},"author":3,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"ocean_post_layout":"","ocean_both_sidebars_style":"","ocean_both_sidebars_content_width":0,"ocean_both_sidebars_sidebars_width":0,"ocean_sidebar":"","ocean_second_sidebar":"","ocean_disable_margins":"enable","ocean_add_body_class":"","ocean_shortcode_before_top_bar":"","ocean_shortcode_after_top_bar":"","ocean_shortcode_before_header":"","ocean_shortcode_after_header":"","ocean_has_shortcode":"","ocean_shortcode_after_title":"","ocean_shortcode_before_footer_widgets":"","ocean_shortcode_after_footer_widgets":"","ocean_shortcode_before_footer_bottom":"","ocean_shortcode_after_footer_bottom":"","ocean_display_top_bar":"default","ocean_display_header":"default","ocean_header_style":"","ocean_center_header_left_menu":"","ocean_custom_header_template":"","ocean_custom_logo":0,"ocean_custom_retina_logo":0,"ocean_custom_logo_max_width":0,"ocean_custom_logo_tablet_max_width":0,"ocean_custom_logo_mobile_max_width":0,"ocean_custom_logo_max_height":0,"ocean_custom_logo_tablet_max_height":0,"ocean_custom_logo_mobile_max_height":0,"ocean_header_custom_menu":"","ocean_menu_typo_font_family":"","ocean_menu_typo_font_subset":"","ocean_menu_typo_font_size":0,"ocean_menu_typo_font_size_tablet":0,"ocean_menu_typo_font_size_mobile":0,"ocean_menu_typo_font_size_unit":"px","ocean_menu_typo_font_weight":"","ocean_menu_typo_font_weight_tablet":"","ocean_menu_typo_font_weight_mobile":"","ocean_menu_typo_transform":"","ocean_menu_typo_transform_tablet":"","ocean_menu_typo_transform_mobile":"","ocean_menu_typo_line_height":0,"ocean_menu_typo_line_height_tablet":0,"ocean_menu_typo_line_height_mobile":0,"ocean_menu_typo_line_height_unit":"","ocean_menu_typo_spacing":0,"ocean_menu_typo_spacing_tablet":0,"ocean_menu_typo_spacing_mobile":0,"ocean_menu_typo_spacing_unit":"","ocean_menu_link_color":"","ocean_menu_link_color_hover":"","ocean_menu_link_color_active":"","ocean_menu_link_background":"","ocean_menu_link_hover_background":"","ocean_menu_link_active_background":"","ocean_menu_social_links_bg":"","ocean_menu_social_hover_links_bg":"","ocean_menu_social_links_color":"","ocean_menu_social_hover_links_color":"","ocean_disable_title":"default","ocean_disable_heading":"default","ocean_post_title":"","ocean_post_subheading":"","ocean_post_title_style":"","ocean_post_title_background_color":"","ocean_post_title_background":0,"ocean_post_title_bg_image_position":"","ocean_post_title_bg_image_attachment":"","ocean_post_title_bg_image_repeat":"","ocean_post_title_bg_image_size":"","ocean_post_title_height":0,"ocean_post_title_bg_overlay":0.5,"ocean_post_title_bg_overlay_color":"","ocean_disable_breadcrumbs":"default","ocean_breadcrumbs_color":"","ocean_breadcrumbs_separator_color":"","ocean_breadcrumbs_links_color":"","ocean_breadcrumbs_links_hover_color":"","ocean_display_footer_widgets":"default","ocean_display_footer_bottom":"default","ocean_custom_footer_template":"","_joinchat":[],"footnotes":""},"class_list":["post-7946","page","type-page","status-publish","hentry","entry"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/louisthai.com\/hk\/wp-json\/wp\/v2\/pages\/7946","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/louisthai.com\/hk\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/louisthai.com\/hk\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/louisthai.com\/hk\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/louisthai.com\/hk\/wp-json\/wp\/v2\/comments?post=7946"}],"version-history":[{"count":13,"href":"https:\/\/louisthai.com\/hk\/wp-json\/wp\/v2\/pages\/7946\/revisions"}],"predecessor-version":[{"id":7959,"href":"https:\/\/louisthai.com\/hk\/wp-json\/wp\/v2\/pages\/7946\/revisions\/7959"}],"wp:attachment":[{"href":"https:\/\/louisthai.com\/hk\/wp-json\/wp\/v2\/media?parent=7946"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}