Files
mini-pages/aromatherapy-cards.jsx
Timmy 7615da5b4f Add aromatherapy card reading app
- aromatherapy-cards.jsx (React component / card data)
- 精油卡牌解讀.html (standalone single-page app)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 14:37:14 +08:00

277 lines
36 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState, useRef, useEffect } from "react";
const CARDS = [
{ id: "AR501", zh: "雪松", en: "CEDARWOOD", tagline: "化解心結,打破僵局", physical: "呼吸系統、神經系統、脂漏、落髮、控油", chakra: "氣管/喉輪", reading: { past: "你是一個內心有很多承載的人,習慣把情緒和委屈往心裡壓,有時候外表看起來很穩,但其實早就在某個地方打了結。這樣的性格讓你很能撐,但也讓你容易把自己困住。", present: "眼前有一個解不開的局,可能是一段關係、一件事、或是一個還沒說出口的心結。你知道問題在哪,但就是很難開口,也很難踏出那一步。那種僵在原地的感覺,有點悶,有點耗。", future: "雪松帶來的訊息是:打破僵局的鑰匙其實就在你手上。試著把那些壓著的話說出來,或者先讓自己好好呼吸一下,不用急著解決,先讓心鬆開來,事情就會開始流動。" } },
{ id: "AR502", zh: "羅馬洋甘菊", en: "ROMAN CHAMOMILE", tagline: "告別黑暗,迎接黎明的曙光", physical: "止痛、失眠、鎮靜安撫、皮膚消炎", chakra: "心/眉心輪", reading: { past: "你走過了一段很不容易的時期,那段日子可能讓你覺得喘不過氣、甚至看不到出口。但你還是撐過來了——哪怕撐得很辛苦,你都沒有放棄自己。", present: "最近你的情緒有點沉,可能失眠、可能焦慮、可能只是說不清楚的鬱悶。有些事讓你感到不安,睡前腦子停不下來,身體也跟著緊繃著。", future: "洋甘菊告訴你:最黑的地方快過去了。現在你需要的不是努力,而是讓自己真正休息。找一個讓你安心的人或空間,好好放鬆一次,黎明比你想的還要近。" } },
{ id: "AR503", zh: "快樂鼠尾草", en: "CLARY SAGE", tagline: "聆聽自我、召喚幸福未來", physical: "放鬆、平衡荷爾蒙、頭痛、泌尿道及陰道感染", chakra: "生殖區/性輪", reading: { past: "你是一個感受力很強的人,內心世界很豐富,但因為太在乎別人的眼光或感受,常常把自己真正想要的壓在最底下,久而久之有點忘了自己的聲音是什麼。", present: "你現在正處於一種想聽清楚自己內心卻又很吵的狀態,外界有太多聲音、太多期待,讓你很難靜下來。荷爾蒙或情緒的起伏也可能讓你覺得自己不太穩。", future: "快樂鼠尾草邀請你往內走,先把外面的雜音關掉一下,問問自己:我真正想要的是什麼?那個答案早就在你心裡,只是需要一點安靜才能聽見。" } },
{ id: "AR504", zh: "絲柏", en: "CYPRESS", tagline: "生命的轉折,無非是最好的安排", physical: "水腫、橘皮、淋巴循環、痔瘡、呼吸道", chakra: "下肢、泌尿道/基底輪", reading: { past: "你是一個經歷過不少轉折的人,每次以為準備好了,生活又丟來一個意外。但你都撐過來了,那些曲折其實都在悄悄塑造你現在的樣子。", present: "你正在經歷一個改變,也許是你不想要的那種,讓你感到不安或失控。身體可能有些循環不順的訊號,提醒你需要讓能量重新流動起來。", future: "絲柏說的是:放手讓它發生吧。這個轉折不是懲罰,是引導。你不需要抓住舊的那個,試著信任這個過程,前方有更適合你的安排在等著。" } },
{ id: "AR505", zh: "尤加利", en: "EUCALYPTUS", tagline: "走出陰霾,心靈大掃除", physical: "止咳去痰、肌肉關節鎮痛", chakra: "肺/喉輪", reading: { past: "你曾經悶在一種沉重的氛圍裡很久,可能是環境、可能是某段關係,讓你喘不過氣。那種陰霾雖然過去了,但有時候還是會留下一些影子。", present: "現在有些積累的情緒或想法需要被清理,就像空氣不流通的房間,你感覺有點沉、有點悶。呼吸道或許有些小狀況,是身體在提醒你:該讓空氣進來了。", future: "尤加利帶來的是清新的風,你需要的是一次大掃除——不只是空間,是心裡那些已經不需要的東西。清掉它,你會發現自己比以為的輕很多。" } },
{ id: "AR506", zh: "乳香", en: "FRANKINCENSE", tagline: "安慰無形的傷害,解開心底的烙印", physical: "撫平紋路、提升免疫力、經期、更年期", chakra: "頭/頂輪", reading: { past: "你的內心深處有一些很早就留下來的傷,可能是來自家庭、來自某段重要的關係,或是某個讓你覺得自己不夠好的經歷。那些傷不一定看得見,但它一直都在。", present: "你現在可能感覺到某種莫名的沉重,或是在某些時刻會被舊日的傷痛觸動。身體上也許有一些與免疫或女性荷爾蒙相關的訊號,在提醒你內在需要被好好撫慰。", future: "乳香是最古老的療癒香氣,它說:那些烙印是可以被解開的。你不需要假裝沒受過傷,允許自己去感受、去哀悼、再放下。你值得被完整地愛,包括那些曾經破碎的部分。" } },
{ id: "AR507", zh: "天竺葵", en: "GERANIUM", tagline: "身心平衡的高手", physical: "傷口修復、甲狀腺失調、經前及更年期症候群", chakra: "生殖區/性輪", reading: { past: "你天生有一種能夠照顧別人、讓環境變和諧的能力,在人群中常常是那個穩住場面的人。只是照顧別人久了,有時候會忘了也要照顧自己。", present: "現在你的身心有些失衡,也許是體力跟不上、情緒起伏比較大,或是感覺自己被掏空了一點。這不是你的問題,只是你的能量需要被補充和整理。", future: "天竺葵的方向是:重新找回自己的節奏。不要再把自己放在最後一個,先讓自己的身心穩下來,你才有能力繼續去平衡周圍的一切。" } },
{ id: "AR508", zh: "薑", en: "GINGER", tagline: "重回人生舞台", physical: "傷風感冒、活絡循環、排毒、腸胃不適、反胃", chakra: "循環/心輪", reading: { past: "你曾經是舞台的主角,有熱情、有行動力,但某個時刻可能因為挫折或疲憊,讓你慢慢退到了側邊。那股火還在,只是需要被重新點燃。", present: "你最近有點提不起勁,可能對一些事情失去了熱情,或是感覺自己被邊緣化了。身體也許有些循環不好或腸胃的小問題,反映出你內在的能量有點停滯。", future: "薑的能量是溫熱而有力的,它說:是時候重新走回舞台中央了。你不需要等到萬事俱備,帶著你現在有的,先踏出去,那股熱情會在行動中慢慢回來。" } },
{ id: "AR509", zh: "葡萄柚", en: "GRAPEFRUIT", tagline: "激發快樂荷爾蒙,啟動自身免疫力", physical: "消水腫、時差問題、平衡內耳、舒緩緊張焦慮", chakra: "頭/頂輪", reading: { past: "你是一個其實很有活力的人,生命裡有很多讓你開心的小事。但過去某些壓力或環境,讓你慢慢把那種輕盈的快樂壓下去,變得比較沉重、比較謹慎。", present: "你現在可能有些緊繃和焦慮,身體的循環或能量感覺有點卡住,甚至有些暈眩或不在狀態的感覺。你需要的是一劑讓自己鬆開來的東西。", future: "葡萄柚說:讓快樂回來吧!試著做一件讓你純粹開心的事,不需要有意義,不需要有產出。當你的快樂荷爾蒙流動起來,你的免疫力和活力都會跟著回升。" } },
{ id: "AR510", zh: "佛手柑", en: "BERGAMOTTE", tagline: "撥開雲霧見青天", physical: "抗菌、抗沮喪、更年期症狀、失眠", chakra: "頭/頂輪", reading: { past: "你是一個內心深處其實很樂觀的人,但過去某些經歷在你心裡留下了一層霧,讓你有時候看事情會不自覺地往悲觀的方向想,怕受傷、怕失望。", present: "最近你的思緒有點被烏雲籠罩,可能情緒低落、可能睡不好,或是有種說不清楚的抑鬱感。那些雲不是你的本質,只是暫時擋住了光。", future: "佛手柑說:雲是會散的。你只需要給自己一點時間和空間,不要急著硬撐,讓情緒自然流動,青天就在雲的後面等著你,比你想的還要近。" } },
{ id: "AR511", zh: "檸檬", en: "LEMON", tagline: "過動不是病,更不是壞孩子", physical: "專注力、增強免疫力、腸胃炎、細菌及病毒感染", chakra: "肝/臍輪", reading: { past: "你是一個思維跳躍、充滿創意的人,腦子裡有很多想法同時在跑。也許小時候或過去曾被說「坐不住」、「太衝動」,但那其實是你獨特能量的一部分。", present: "你現在可能有點分心,很多事情同時在進行,讓你難以集中。免疫力也可能有點下降,身體在告訴你:能量需要被整理和聚焦,不是更用力,而是更有方向。", future: "檸檬帶來清晰和活力,它告訴你:你的那股衝勁是禮物,不是問題。學著把它引導到你真正在意的事情上,你的爆發力會讓你做到別人做不到的事。" } },
{ id: "AR512", zh: "有機甜橙", en: "ORGANIC ORANGE", tagline: "點燃心中的小太陽", physical: "消化不良、噁心、嘔吐、焦慮壓力、失眠、憂鬱", chakra: "腸胃/臍輪", reading: { past: "你天生有一種溫暖和陽光的特質,很容易讓人感到舒服。但過去某些讓你受傷或失望的經歷,讓那道光有時候會被遮住,你變得比較不敢輕易去相信或期待。", present: "你現在內心有點暗,可能情緒低沉,腸胃也跟著不太好,睡眠可能也受影響。那個你裡面的小太陽還沒有熄滅,只是需要一點撥弄才能重新燃起。", future: "有機甜橙帶來的是最純粹的快樂能量。從今天開始,做一件讓你微笑的小事,讓溫暖從內而外慢慢散開。你心裡的那道光,很快就會重新照亮你和身邊的人。" } },
{ id: "AR513", zh: "廣藿香", en: "PATCHOULI", tagline: "不要用別人的錯誤懲罰自己", physical: "香港腳、乾癬、泌尿道、攝護腺、催情、緩解焦慮", chakra: "生殖泌尿/基底輪", reading: { past: "你過去有過被人傷害或讓你失望的經歷,那些事在心裡留下了痕跡。有時候你會不自覺地把那些傷拿來懲罰自己,覺得是自己不夠好才會這樣。", present: "你現在可能還帶著某種委屈或憤怒,或是一種低沉的焦慮感,說不清楚從哪裡來。身體也許有些底層的緊繃,提醒你有些情緒需要被好好釋放。", future: "廣藿香說的很清楚:別人的錯不是你的責任。你值得被溫柔對待,包括被自己溫柔對待。從今天開始,練習放下那些不屬於你的重量,讓腳踩在地上,踏實地往前走。" } },
{ id: "AR514", zh: "歐薄荷", en: "PEPPERMINT", tagline: "預先看見的洞察力", physical: "止痛、脹氣、腹瀉、嘔吐", chakra: "腸胃/臍輪", reading: { past: "你是一個很有洞察力的人,常常能感覺到別人還沒注意到的事。這種敏銳讓你在很多時候都能提前準備,但也讓你有時候想太多、太早擔心。", present: "你現在可能正在思考一件還沒發生的事,或是對未來有些不確定和焦慮。腸胃的不舒服是你內在緊張的具體表現,身體比你更早說出你的擔憂。", future: "歐薄荷說:你的洞察力是真實的,但不要讓它變成過度的焦慮。先看清楚眼前的這一步,用你的直覺和清醒的頭腦,你早就有能力看見正確的方向。" } },
{ id: "AR515", zh: "檀香", en: "SANDALWOOD", tagline: "按下人生的暫停鍵", physical: "提升專注及記憶、深度放鬆、尿道發炎、性冷感", chakra: "頭/頂輪", reading: { past: "你是一個一直在努力、一直在給的人,很少讓自己真正停下來。過去你可能習慣用忙碌來填滿時間,因為一停下來就會有很多感覺湧上來,不知道怎麼面對。", present: "現在你的身體和心都在發出訊號:你需要暫停。可能是注意力無法集中、可能是對很多事提不起興趣,甚至感覺有點麻木。這是身體在保護你,要求你休息。", future: "檀香說:按下暫停鍵不是放棄,是智慧。給自己一段安靜的時間,不需要做任何事,只是讓自己存在。在那個靜止裡,你會找到真正屬於你的清明和方向。" } },
{ id: "AR516", zh: "有機薰衣草", en: "ORGANIC LAVENDER", tagline: "無條件的愛、包容與肯定", physical: "鎮靜止痛、抗痙攣、失眠、急性皮膚外傷", chakra: "心/眉心輪", reading: { past: "你是一個很渴望愛和被接納的人,過去也許有過一些讓你覺得自己不夠被愛、不夠被看見的經歷。那些經歷讓你學會了很多,但也讓你在愛裡面變得有點小心翼翼。", present: "現在你的內心需要一個很溫柔的擁抱,也許是身體的緊繃、也許是情緒的敏感,告訴你現在特別需要被好好照顧和肯定。", future: "有機薰衣草帶來的是最純粹的愛的能量。你值得被無條件地愛——不需要表現得夠好,不需要讓所有人滿意。先從對自己說一句「你已經很好了」開始,讓那份愛從心裡長出來。" } },
{ id: "AR517", zh: "有機杜松", en: "ORGANIC JUNIPER", tagline: "淨化思緒,斷捨離", physical: "尿道炎、靜脈曲張、橘皮、淨化呼吸道、消化不良", chakra: "肝膽/臍輪", reading: { past: "你是一個累積了很多的人,不管是情感、記憶、還是一些已經過去但還沒放下的事。這種累積讓你有厚度,但有時候也讓你背得很重。", present: "你現在需要一次清理。可能是生活裡有太多雜事堆著、可能是心裡有些關係或情緒一直放不下。身體也在提醒你:是時候讓一些東西流走了。", future: "有機杜松說的是斷捨離。不是狠心,而是清醒。試著問自己:這個還需要嗎?如果不需要,就讓它走。清空之後,你會發現空間裡有更好的東西正在等著進來。" } },
{ id: "AR518", zh: "有機迷迭香", en: "ORGANIC ROSEMARY", tagline: "創新、集中、應變力", physical: "神經系統止痛、促進循環、支氣管炎", chakra: "循環/心輪", reading: { past: "你是一個腦子很靈活、喜歡創新的人,過去在很多地方都能快速找到解決方法。但有時候因為太快進入行動,反而少了一點深思熟慮的空間。", present: "現在你需要同時應對很多事,可能感覺有點分散,需要重新把注意力集中起來。循環或呼吸道的小狀況,是身體在提醒你稍微慢下來,先把思緒整理好。", future: "有機迷迭香說:你的創新力和應變力是你最大的武器。現在把焦點收回來,選定一個最重要的方向,然後用你的靈活和行動力全力投入,你一定能找到突破口。" } },
{ id: "AR519", zh: "有機茶樹", en: "ORGANIC TEA TREE", tagline: "好奇心-創造力的先決條件", physical: "傷口、痘痘抗菌、泌尿道及呼吸道抗感染", chakra: "呼吸道/喉輪", reading: { past: "你天生有一顆好奇的心,對很多事都想探索、想理解。但過去某些讓你碰壁或被否定的經歷,讓你學會了在好奇之前先考慮後果,那股衝勁慢慢變得謹慎了。", present: "你現在可能對某些事失去了興趣,或是對自己的想法缺乏信心,覺得「試了也沒用」。身體上的一些小問題,反映出你的創造力和表達能量有些被壓住了。", future: "有機茶樹帶來的是乾淨、清爽的能量,它說:好奇心是你最珍貴的東西,不要讓它死掉。試著對一件事重新感到好奇,讓那個探索的心帶著你往前走,創造力自然就會跟著回來。" } },
{ id: "AR520", zh: "有機香水樹", en: "ORGANIC YLANG YLANG", tagline: "真正的幸福歸屬感", physical: "止痛、失眠、鎮靜安撫、催情、更年期及經前症候群", chakra: "腎/性輪", reading: { past: "你是一個很渴望歸屬感的人,想要有一個真正屬於自己的地方,有人真正懂你、接納你。過去可能有過找不到這種感覺的時期,讓你有點漂浮、有點孤單。", present: "你現在可能感覺到一種內心的空洞,或是對某段關係、某個狀態感到不滿足。身體上的緊張或失眠,是你內心那個渴望被看見、被愛的部分在敲門。", future: "有機香水樹說:真正的幸福和歸屬感,從你跟自己的關係開始。先讓自己感到被自己珍惜,你就會吸引到真正能給你這種感覺的人和環境。那個家,你可以為自己建造。" } },
{ id: "AR521", zh: "有機綠花白千層", en: "ORGANIC NIAOULI", tagline: "心靈守護者、溫暖、支持力", physical: "生殖泌尿道抗感染、退燒、化痰、扁桃腺炎", chakra: "腎/基底輪", reading: { past: "你是一個很會照顧別人的人,身邊的人遇到困難常常會來找你。你給了很多溫暖和支持,但有時候你自己需要被支持的時候,卻不太知道怎麼開口。", present: "現在你可能感覺有點孤立無援,或是在默默撐著一些很重的東西。身體上的一些症狀,像是抵抗力下降或喉嚨的不舒服,是你長期付出後需要被補充的訊號。", future: "有機綠花白千層說:你也值得被守護。試著讓自己接受別人的幫助,或是找到一個能讓你真正卸下重量的地方和人。給自己的那份溫暖,一點都不比給別人的少重要。" } },
{ id: "AR522", zh: "有機桉油醇樟", en: "ORGANIC RAVINTSARA", tagline: "釐清思緒、適應力", physical: "強化免疫力、呼吸道問題、失眠、肌肉疲勞", chakra: "肺/喉輪", reading: { past: "你是一個適應力強、腦子轉得快的人,面對變化通常不會慌,會先想辦法。但因為想得太多、太快,有時候反而讓自己很累,腦子很少真正停下來。", present: "現在你的思緒有點亂,事情可能一下子來太多,或是有一個決定讓你想了很久還想不清楚。身體也可能出現疲憊或呼吸道的小狀況,在提醒你需要放慢。", future: "有機桉油醇樟說的是:先深呼吸,讓空氣進來,思緒自然就會清晰。你不需要同時解決所有事,一件一件來,你的適應力會帶你找到出路。" } },
{ id: "AR523", zh: "苦橙葉", en: "PETITGRAIN", tagline: "擺脫框架、看到更廣闊的世界", physical: "睡眠困擾、沮喪、止痛、抗感染", chakra: "心/心輪", reading: { past: "你過去很習慣在某種框架裡生活,可能是別人給的規則、期待,或是你自己設下的限制。這讓你很安全,但也讓你有時候感到受困,覺得呼吸不太順。", present: "現在你感覺到有什麼東西在擠壓你,可能是情緒、可能是某段關係、或是一種說不清楚的壓抑感。你知道現在的狀態不對,但還不確定要怎麼突破。", future: "苦橙葉告訴你:那個框架是可以打開的。試著用不同的角度看同一件事,你會發現世界比你以為的還要大。先給心一點空間,其他的事情就會跟著鬆開。" } },
{ id: "AR524", zh: "羅勒", en: "BASIL", tagline: "理性與感性的平衡點", physical: "抗病毒、抗細菌、強化免疫力、腸胃問題", chakra: "肝/臍輪", reading: { past: "你是一個理性和感性都很強的人,有時候腦子和心會往不同方向跑,讓你在做決定的時候拉扯得很辛苦。你不是優柔寡斷,只是你感受得很深、也想得很細。", present: "最近你正在一個需要平衡的時刻——可能是理智說一件事,但情感又是另一個答案。這種內在的拉鋸讓你有點疲憊,腸胃或許也跟著有點不舒服。", future: "羅勒提醒你:理性和感性不是對立的,它們都是你的一部分。試著讓兩者都說話,然後找到中間那個讓你最安心的位置,那就是屬於你的平衡點。" } },
{ id: "AR525", zh: "沒藥", en: "MYRRH", tagline: "撫平隱藏的傷口", physical: "皮膚傷口、黏膜潰瘍癒合、消炎止痛、平衡荷爾蒙", chakra: "甲狀腺/喉輪", reading: { past: "你是一個把很多傷藏得很深的人,外表可能看起來沒事,但其實有些東西一直沒有被好好處理。那些隱藏的傷口不是因為你堅強,而是因為你還沒找到安全的地方去碰它。", present: "你現在可能有些說不清楚的身體不適,或是情緒上的低落,像是有什麼東西悶在裡面出不來。喉嚨或甲狀腺的部位也許有些訊號,提醒你有話沒說、有傷沒癒。", future: "沒藥是最深層的療癒香氣,它說:那些隱藏的傷口是可以被好好照料的。你不需要一個人撐著,試著讓自己被看見,讓那些傷有機會透透氣,它們才能真正癒合。" } },
{ id: "AR526", zh: "有機沉香醇百里香", en: "ORGANIC THYME", tagline: "調整心態,成為真正可以做決定的人", physical: "提升免疫力、噁心反胃、嘔吐、生殖泌尿道抗感染", chakra: "泌尿道/基底輪", reading: { past: "你過去常常在做決定的時候猶豫,或者把決定權交給別人,不是因為你沒有想法,而是你太顧慮結果、太怕做錯。這種謹慎讓你少犯錯,但也讓你少了一些主導自己人生的感覺。", present: "你現在站在一個需要做出選擇的地方,有些事已經拖了一段時間,你知道不能再等,但還沒有完全準備好。身體底部的緊繃感,或是對某些事的抗拒,都是你內心還在掙扎的訊號。", future: "有機百里香帶來的力量是:相信自己有能力做決定。你不需要等到百分百確定才行動,先踏出一步,你會在走的過程中找到方向。真正的自主,是從練習做選擇開始的。" } },
{ id: "AR527", zh: "甜馬鬱蘭", en: "MARJORAM", tagline: "認清自我,懷抱夢想", physical: "消除壓力、提升睡眠品質、肌肉扭拉傷、淡血", chakra: "循環/心輪", reading: { past: "你是一個心裡其實有很多夢想的人,但過去可能因為現實、因為別人的眼光,把那些夢輕輕放下了。你不是沒有熱情,只是有時候忘了自己最初想要的是什麼。", present: "最近你感覺有點壓力,可能睡不太好,肩頸或肌肉有些緊繃。心裡好像也在問自己:我現在做的這些,真的是我想要的嗎?那個問題讓你有點不安。", future: "甜馬鬱蘭說的是:回去找那個你放掉的夢。不一定要馬上實現,但先讓自己記得它還在。認清你是誰,你想要什麼,然後往那個方向走,哪怕是一小步也好。" } },
{ id: "AR528", zh: "有機岩玫瑰", en: "ORGANIC LABDANUM CISTUS", tagline: "化解內心的矛質與糾結", physical: "肌肉酸痛、頭痛、生理痛、抗皺、止血、腸病毒、水痘", chakra: "心/眉心輪", reading: { past: "你是一個內心很複雜的人,常常同時有好幾種感受在拉扯,讓你很難跟別人解釋清楚自己的狀態。這種矛盾不是壞事,是你深度的一部分。", present: "現在你的內心有個糾結解不開,可能是一個兩難的選擇,可能是對某個人或某件事又愛又恨。那種卡在中間的感覺很耗力氣,頭或身體可能也跟著不舒服。", future: "有機岩玫瑰的訊息是:你不需要在矛盾裡找到唯一的答案。試著先接受那個衝突的存在,允許自己同時有不同的感受,慢慢地,那個糾結會自己鬆開。" } },
{ id: "AR529", zh: "松紅梅", en: "MANUKA", tagline: "找回內在的小孩", physical: "香港腳、唇皰疹、尿布疹、帶狀皰疹", chakra: "生殖泌尿/基底輪", reading: { past: "你小時候一定有過那種很純粹的快樂,對什麼都好奇、什麼都想試。但長大的過程裡,那個無憂無慮的自己慢慢被藏起來了,換上了更多「應該」和「不可以」。", present: "你現在有點累,不只是身體,是那種靈魂層面的疲憊。可能是太久沒有為自己做一件單純開心的事了,或是周遭有讓你感到不安全的狀況。", future: "松紅梅邀請你去找那個內在的小孩。不用做什麼大事,只是讓自己玩一下、笑一下,做一件只是因為喜歡而做的事。那個純粹的你,還在,等著被找回來。" } },
];
const C = { gold:"#c9a84c", goldLight:"#e8d5a3", dark:"#1a1410", cream:"#f5f0e8", muted:"#7a6e62", cardBg:"#fdfaf5", textDark:"#2c2520", textMid:"#6a5d4e" };
const ROLE_LABELS = [
{ key:"past", label:"過去", sub:"你的性格底色" },
{ key:"present", label:"現在", sub:"當下面對的狀況" },
{ key:"future", label:"未來", sub:"建議的方向" },
];
function SearchBox({ selected, onAdd }) {
const [q, setQ] = useState("");
const [open, setOpen] = useState(false);
const ref = useRef();
const filtered = q.trim().length === 0 ? [] : CARDS.filter(c =>
!selected.find(s => s.id === c.id) && (
c.zh.includes(q) ||
c.en.toLowerCase().includes(q.toLowerCase()) ||
c.id.toLowerCase().includes(q.toLowerCase())
)
).slice(0, 8);
useEffect(() => {
function click(e) { if (ref.current && !ref.current.contains(e.target)) setOpen(false); }
document.addEventListener("mousedown", click);
return () => document.removeEventListener("mousedown", click);
}, []);
function pick(card) { onAdd(card); setQ(""); setOpen(false); }
return (
<div ref={ref} style={{ position:"relative", marginBottom:"0.8rem" }}>
<input
value={q}
onChange={e => { setQ(e.target.value); setOpen(true); }}
onFocus={() => setOpen(true)}
placeholder="🔍 搜尋卡片名稱或編號…"
style={{ width:"100%", background:"rgba(255,255,255,0.06)",
border:`1px solid rgba(201,168,76,0.4)`, borderRadius:10,
padding:"0.82rem 1rem", color:C.cream,
fontFamily:"inherit", fontSize:"0.92rem", outline:"none", boxSizing:"border-box" }}
/>
{open && q.trim().length > 0 && (
<div style={{ position:"absolute", top:"calc(100% + 4px)", left:0, right:0, zIndex:99,
background:"#241e18", border:`1px solid rgba(201,168,76,0.3)`,
borderRadius:10, overflow:"hidden", boxShadow:"0 8px 24px rgba(0,0,0,0.5)" }}>
{filtered.length > 0 ? filtered.map(card => (
<div key={card.id} onClick={() => pick(card)}
style={{ padding:"0.7rem 1rem", cursor:"pointer", display:"flex", alignItems:"center", gap:"0.8rem",
borderBottom:"1px solid rgba(255,255,255,0.05)" }}
onMouseEnter={e => e.currentTarget.style.background="rgba(201,168,76,0.12)"}
onMouseLeave={e => e.currentTarget.style.background="transparent"}>
<span style={{ fontSize:"0.6rem", color:C.gold, minWidth:36 }}>{card.id}</span>
<span style={{ fontSize:"0.9rem", color:C.cream }}>{card.zh}</span>
<span style={{ fontSize:"0.65rem", color:C.muted, marginLeft:"auto" }}>{card.en}</span>
</div>
)) : (
<div style={{ padding:"0.8rem 1rem", color:C.muted, fontSize:"0.82rem" }}>找不到符合的卡片</div>
)}
</div>
)}
</div>
);
}
export default function App() {
const [selected, setSelected] = useState([]);
const [reading, setReading] = useState(null);
function addCard(card) {
setSelected(prev => {
if (prev.find(c => c.id === card.id)) return prev;
if (prev.length >= 3) return [...prev.slice(1), card];
return [...prev, card];
});
setReading(null);
}
function removeCard(id) { setSelected(prev => prev.filter(c => c.id !== id)); setReading(null); }
function analyze() {
if (selected.length !== 3) return;
const [c1,c2,c3] = selected;
setReading({
past: { card:c1, text:c1.reading.past },
present: { card:c2, text:c2.reading.present },
future: { card:c3, text:c3.reading.future },
summary: `${c1.zh}看見你過去的底色,${c2.zh}映照出你此刻正面對的狀態,而${c3.zh}指引你接下來可以走的方向。三張牌放在一起,像是你內在風景的一幅全圖——你不是沒有力量,只是現在需要一點停頓,讓自己重新對齊。`
});
}
function reset() { setSelected([]); setReading(null); }
return (
<div style={{ background:C.dark, minHeight:"100vh", color:C.cream,
fontFamily:"Georgia,'Noto Serif TC',serif",
display:"flex", flexDirection:"column", alignItems:"center",
padding:"2rem 1rem 4rem" }}>
{/* Header */}
<div style={{ textAlign:"center", marginBottom:"1.8rem" }}>
<div style={{ fontSize:"1.5rem", color:C.gold, letterSpacing:"0.15em" }}> 精油卡牌解讀 </div>
<div style={{ width:120, height:1, background:`linear-gradient(90deg,transparent,${C.gold},transparent)`, margin:"0.6rem auto" }} />
<div style={{ fontSize:"0.75rem", color:C.muted }}>第1張 過去 · 第2張 現在 · 第3張 未來</div>
</div>
<div style={{ width:"100%", maxWidth:480 }}>
{/* 搜尋框 */}
<SearchBox selected={selected} onAdd={addCard} />
{/* 已選卡槽 */}
<div style={{ display:"grid", gridTemplateColumns:"repeat(3,1fr)", gap:"0.5rem", marginBottom:"1rem" }}>
{[0,1,2].map(i => {
const card = selected[i];
const role = ROLE_LABELS[i];
return (
<div key={i} style={{ border:`1px dashed ${card ? C.gold : "rgba(201,168,76,0.25)"}`,
borderRadius:10, padding:"0.7rem 0.4rem", textAlign:"center", minHeight:78,
background: card ? "rgba(201,168,76,0.1)" : "rgba(255,255,255,0.02)",
position:"relative", transition:"all 0.2s" }}>
<div style={{ fontSize:"0.6rem", color:C.gold, marginBottom:"0.2rem" }}>{role.label}</div>
{card ? (
<>
<div style={{ fontSize:"0.56rem", color:C.muted }}>{card.id}</div>
<div style={{ fontSize:"0.82rem", color:C.cream, margin:"0.1rem 0", lineHeight:1.3 }}>{card.zh}</div>
<div style={{ fontSize:"0.5rem", color:C.muted }}>{card.en}</div>
<button onClick={() => removeCard(card.id)} style={{ position:"absolute", top:4, right:6,
background:"none", border:"none", color:C.muted, fontSize:"0.7rem", cursor:"pointer", padding:0 }}></button>
</>
) : (
<div style={{ fontSize:"0.7rem", color:"rgba(201,168,76,0.25)", marginTop:"0.3rem" }}>待選</div>
)}
</div>
);
})}
</div>
{/* 分隔線 + 格子標題 */}
<div style={{ display:"flex", alignItems:"center", gap:"0.6rem", marginBottom:"0.6rem" }}>
<div style={{ flex:1, height:1, background:"rgba(201,168,76,0.15)" }} />
<span style={{ fontSize:"0.68rem", color:C.muted, letterSpacing:"0.08em" }}>或直接點選</span>
<div style={{ flex:1, height:1, background:"rgba(201,168,76,0.15)" }} />
</div>
{/* 卡片格子 */}
<div style={{ display:"grid", gridTemplateColumns:"repeat(3,1fr)", gap:"0.4rem", marginBottom:"1.1rem" }}>
{CARDS.map(card => {
const idx = selected.findIndex(c => c.id === card.id);
const isSel = idx !== -1;
return (
<div key={card.id} onClick={() => isSel ? removeCard(card.id) : addCard(card)} style={{
background: isSel ? "rgba(201,168,76,0.18)" : "rgba(255,255,255,0.03)",
border:`1px solid ${isSel ? C.gold : "rgba(201,168,76,0.15)"}`,
boxShadow: isSel ? `0 0 0 1px ${C.gold}` : "none",
borderRadius:8, padding:"0.55rem 0.25rem",
textAlign:"center", cursor:"pointer", transition:"all 0.15s" }}>
<div style={{ fontSize:"0.52rem", color:C.gold, marginBottom:"0.1rem" }}>{card.id}</div>
<div style={{ fontSize:"0.76rem", color:C.cream, lineHeight:1.25 }}>{card.zh}</div>
<div style={{ fontSize:"0.46rem", color:C.muted, marginTop:"0.05rem" }}>{card.en}</div>
{isSel && <div style={{ fontSize:"0.55rem", color:C.gold, marginTop:"0.15rem" }}> {ROLE_LABELS[idx].label}</div>}
</div>
);
})}
</div>
{/* 解讀按鈕 */}
<div style={{ textAlign:"center", marginBottom:"1.5rem" }}>
<button onClick={analyze} disabled={selected.length !== 3} style={{
background: selected.length===3 ? C.gold : "rgba(201,168,76,0.2)",
color:C.dark, border:"none", borderRadius:10,
padding:"0.9rem 2.5rem", fontFamily:"inherit",
fontSize:"1rem", fontWeight:700, letterSpacing:"0.08em",
cursor: selected.length===3 ? "pointer" : "not-allowed" }}>
{selected.length===3 ? "✦ 開始解讀" : `還需選 ${3-selected.length}`}
</button>
</div>
{/* 結果 */}
{reading && (
<div style={{ background:C.cardBg, color:C.dark, borderRadius:16,
padding:"1.8rem 1.4rem", position:"relative", overflow:"hidden" }}>
<div style={{ position:"absolute", top:0, left:0, right:0, height:4,
background:`linear-gradient(90deg,${C.gold},${C.goldLight},${C.gold})` }} />
<div style={{ display:"flex", gap:"0.5rem", marginBottom:"1.3rem" }}>
{ROLE_LABELS.map(({ key, label, sub }) => {
const c = reading[key].card;
return (
<div key={key} style={{ flex:1, textAlign:"center",
background:"rgba(201,168,76,0.07)", border:"1px solid rgba(201,168,76,0.2)",
borderRadius:8, padding:"0.55rem 0.3rem" }}>
<div style={{ fontSize:"0.58rem", color:C.gold, fontWeight:700 }}>{label}</div>
<div style={{ fontSize:"0.78rem", fontWeight:700, color:C.dark, margin:"0.1rem 0", lineHeight:1.3 }}>{c.zh}</div>
<div style={{ fontSize:"0.52rem", color:C.textMid, fontStyle:"italic" }}>{sub}</div>
</div>
);
})}
</div>
{ROLE_LABELS.map(({ key, label }) => {
const { card, text } = reading[key];
return (
<div key={key} style={{ marginBottom:"1.2rem" }}>
<div style={{ fontSize:"0.66rem", letterSpacing:"0.1em", color:C.muted,
display:"flex", alignItems:"center", gap:"0.4rem", marginBottom:"0.4rem" }}>
<span style={{ color:C.gold }}></span>{label}{card.zh}
<div style={{ flex:1, height:1, background:"rgba(0,0,0,0.09)" }} />
</div>
<div style={{ fontSize:"0.88rem", lineHeight:1.85, color:C.textDark, marginBottom:"0.6rem" }}>{text}</div>
<div style={{ background:"rgba(201,168,76,0.06)", border:"1px solid rgba(201,168,76,0.18)",
borderRadius:8, padding:"0.55rem 0.75rem", display:"flex", flexDirection:"column", gap:"0.3rem" }}>
<div style={{ display:"flex", gap:"0.4rem", alignItems:"flex-start" }}>
<span style={{ color:C.gold, fontSize:"0.7rem", flexShrink:0, marginTop:"0.05rem" }}></span>
<span style={{ fontSize:"0.75rem", color:C.textMid, lineHeight:1.6 }}>{card.physical}</span>
</div>
<div style={{ display:"flex", gap:"0.4rem", alignItems:"center" }}>
<span style={{ color:C.gold, fontSize:"0.7rem", flexShrink:0 }}></span>
<span style={{ fontSize:"0.75rem", color:C.textMid }}>{card.chakra}</span>
</div>
</div>
</div>
);
})}
<div style={{ background:"rgba(201,168,76,0.1)", borderLeft:`3px solid ${C.gold}`,
borderRadius:"0 8px 8px 0", padding:"0.7rem 0.9rem", marginTop:"0.2rem" }}>
<div style={{ fontSize:"0.66rem", color:C.muted, letterSpacing:"0.1em", marginBottom:"0.3rem" }}> 整合訊息</div>
<div style={{ fontSize:"0.86rem", lineHeight:1.85, color:C.textDark, fontStyle:"italic" }}>{reading.summary}</div>
</div>
<div style={{ textAlign:"center", marginTop:"1.2rem" }}>
<button onClick={reset} style={{ background:"transparent",
border:`1px solid rgba(201,168,76,0.4)`, color:C.muted, borderRadius:8,
padding:"0.55rem 1.5rem", fontFamily:"inherit", fontSize:"0.82rem", cursor:"pointer" }}>
重新抽牌
</button>
</div>
</div>
)}
</div>
</div>
);
}