Files
shinalokandClaude Sonnet 5 6000de166f keep EasyMDE on desktop/Android, fall back to plain textarea only on iOS
Dropping EasyMDE entirely (previous commit) fixed the Korean IME jamo split
but also removed the live markdown syntax highlighting everywhere, even on
platforms that never had the bug. Only iOS WebKit breaks CJK IME composition
under CodeMirror, so journal-editor.js now detects iOS (including the
desktop-UA-masquerading iPad, via MacIntel + multi-touch) and only skips
EasyMDE there, falling back to a native <textarea>. Everywhere else keeps
the full EasyMDE/CodeMirror editing experience as before.

window.JournalEditor now dispatches to the right engine per textarea
(checking t._easymde) so the toolbar buttons and the category-template
autofill in journal.html work unchanged regardless of which engine backs a
given textarea. Restores the easymde vendor files, base.html includes, and
service worker precache entries removed in the previous commit (cache
bumped to v8).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 08:16:06 +09:00

1346 lines
26 KiB
CSS

:root {
--color-bg: #f5f4ef;
--color-surface: #ffffff;
--color-text: #2b2a27;
--color-text-muted: #6b6a66;
--color-accent: #d97757;
--color-accent-hover: #c15f3c;
--color-accent-rgb: 217, 119, 87;
--color-border: #e8e6df;
--color-success: #4f7a5a;
--color-success-tint: rgba(79, 122, 90, 0.08);
--color-danger: #b3543f;
--color-gold: #a8791a;
--color-gold-tint: rgba(168, 121, 26, 0.12);
--radius-card: 14px;
--radius-control: 10px;
--shadow-card: 0 1px 2px rgba(0, 0, 0, 0.04);
--space-1: 8px;
--space-2: 16px;
--space-3: 24px;
--space-4: 32px;
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "Pretendard", "Malgun Gothic", sans-serif;
}
@media (prefers-color-scheme: dark) {
:root {
--color-bg: #1f1e1b;
--color-surface: #2a2926;
--color-text: #edebe4;
--color-text-muted: #a8a69f;
--color-accent: #e08962;
--color-accent-hover: #eb9c78;
--color-accent-rgb: 224, 137, 98;
--color-border: #3a3833;
--color-success: #6fa47c;
--color-success-tint: rgba(111, 164, 124, 0.14);
--color-danger: #d97c68;
--color-gold: #d9b84f;
--color-gold-tint: rgba(217, 184, 79, 0.16);
--shadow-card: 0 1px 2px rgba(0, 0, 0, 0.2);
}
}
* {
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
}
[x-cloak] {
display: none !important;
}
html,
body {
margin: 0;
padding: 0;
overscroll-behavior-y: none;
}
body {
background: var(--color-bg);
color: var(--color-text);
font-family: var(--font-sans);
font-size: 15px;
line-height: 1.55;
-webkit-font-smoothing: antialiased;
}
a {
color: inherit;
}
.app-shell {
max-width: 640px;
margin: 0 auto;
padding: var(--space-3) var(--space-2) var(--space-4);
}
@media (min-width: 900px) {
.app-shell {
max-width: 760px;
padding-top: var(--space-4);
}
.app-shell.wide {
max-width: 960px;
}
}
h1,
h2,
h3 {
font-weight: 600;
letter-spacing: -0.01em;
line-height: 1.3;
margin: 0 0 var(--space-2);
}
h1 {
font-size: 22px;
}
h2 {
font-size: 17px;
}
p {
margin: 0 0 var(--space-2);
color: var(--color-text-muted);
}
.app-footer {
text-align: center;
padding: var(--space-4) 0 var(--space-2);
font-size: 13px;
}
.app-footer a {
color: var(--color-text-muted);
}
.app-footer a + a::before {
content: "·";
margin: 0 6px;
}
/* iOS 홈 화면 추가 안내 배너 */
.ios-install-banner {
display: none;
align-items: center;
justify-content: space-between;
gap: var(--space-2);
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-control);
box-shadow: var(--shadow-card);
padding: 10px 12px;
margin-bottom: var(--space-2);
font-size: 13px;
color: var(--color-text);
}
.ios-install-banner button {
flex-shrink: 0;
background: none;
border: none;
color: var(--color-text-muted);
font-size: 18px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
/* 상단 네비게이션 */
.top-nav {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
row-gap: var(--space-1);
margin-bottom: var(--space-3);
}
.top-nav .brand {
font-weight: 600;
font-size: 17px;
white-space: nowrap;
}
.top-nav .nav-links {
display: flex;
gap: var(--space-1);
}
.top-nav .nav-links a {
text-decoration: none;
color: var(--color-text-muted);
font-size: 14px;
white-space: nowrap;
padding: 6px 10px;
border-radius: var(--radius-control);
}
.top-nav .nav-links a.active {
color: var(--color-text);
background: var(--color-surface);
box-shadow: var(--shadow-card);
}
.top-nav .nav-user {
display: flex;
align-items: center;
flex-shrink: 0;
gap: var(--space-1);
font-size: 13px;
color: var(--color-text-muted);
}
.top-nav .nav-user span {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
max-width: 96px;
}
.btn-link {
background: none;
border: none;
padding: 0;
font: inherit;
font-size: 13px;
color: var(--color-text-muted);
text-decoration: underline;
white-space: nowrap;
flex-shrink: 0;
cursor: pointer;
}
/* 하단 탭바 (모바일 전용) — 기본은 숨김, 좁은 화면에서만 노출 */
.bottom-tab-bar {
display: none;
}
/* 좁은 화면: 상단은 브랜드+유저정보만 남기고, 이동 네비게이션은 하단 탭바로 옮긴다 */
@media (max-width: 480px) {
.top-nav .nav-links {
display: none;
}
.app-shell {
padding-bottom: calc(64px + env(safe-area-inset-bottom, 0px) + var(--space-2));
}
.bottom-tab-bar {
display: flex;
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 40;
background: var(--color-surface);
border-top: 1px solid var(--color-border);
padding-bottom: env(safe-area-inset-bottom, 0px);
}
.bottom-tab-bar .tab-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 2px;
padding: 8px 4px 6px;
text-decoration: none;
color: var(--color-text-muted);
font-size: 11px;
touch-action: manipulation;
}
.bottom-tab-bar .tab-item svg {
width: 22px;
height: 22px;
}
.bottom-tab-bar .tab-item.active {
color: var(--color-accent);
}
}
/* 카드 */
.card {
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-card);
box-shadow: var(--shadow-card);
padding: var(--space-3);
margin-bottom: var(--space-2);
}
/* 버튼 */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
gap: 6px;
border: none;
border-radius: var(--radius-control);
padding: 10px 16px;
font-size: 14px;
font-weight: 500;
white-space: nowrap;
cursor: pointer;
font-family: inherit;
transition: background-color 0.15s ease, opacity 0.15s ease;
touch-action: manipulation;
}
.btn-primary {
background: var(--color-accent);
color: #fff;
}
.btn-primary:hover {
background: var(--color-accent-hover);
}
.btn-secondary {
background: transparent;
color: var(--color-text);
border: 1px solid var(--color-border);
}
.btn-secondary:hover {
background: var(--color-bg);
}
.btn-danger-ghost {
background: transparent;
color: var(--color-danger);
border: 1px solid var(--color-border);
}
.btn-block {
width: 100%;
}
/* 입력 */
input[type="text"],
input[type="time"],
input[type="date"],
input[type="password"],
select,
textarea {
width: 100%;
font-family: inherit;
font-size: 15px;
padding: 10px 12px;
border: 1px solid var(--color-border);
border-radius: var(--radius-control);
background: var(--color-bg);
color: var(--color-text);
}
textarea {
resize: vertical;
min-height: 90px;
line-height: 1.5;
}
select {
cursor: pointer;
}
input[type="file"] {
width: 100%;
font-family: inherit;
font-size: 14px;
color: var(--color-text-muted);
}
input[type="color"] {
width: 56px;
height: 40px;
padding: 2px;
border: 1px solid var(--color-border);
border-radius: var(--radius-control);
background: var(--color-bg);
cursor: pointer;
}
label {
display: block;
font-size: 13px;
color: var(--color-text-muted);
margin-bottom: 6px;
}
.field {
margin-bottom: var(--space-2);
}
.form-error-text {
color: var(--color-danger);
font-size: 13px;
margin: -8px 0 var(--space-2);
min-height: 1em;
}
/* 탭 — 좁은 화면에서 4개가 다 안 들어가면 줄바꿈 대신 가로 스크롤.
양 끝에 스크롤 가능 방향으로만 그림자가 지도록 하는 순수 CSS 트릭:
앞쪽 두 레이어(배경색→투명 그라데이션, background-attachment: local)는 콘텐츠와 같이 스크롤되면서
탭 텍스트를 가려 페이드아웃시키고, 뒤쪽 두 레이어(radial-gradient 그림자, attachment: scroll)는
스크롤 컨테이너 기준으로 고정되어 있다 — 콘텐츠 레이어가 그 위를 덮을 수 있을 때만(=그 방향으로
더 스크롤할 내용이 있을 때만) 그림자가 드러나 보인다. 끝까지 스크롤하면 콘텐츠 레이어가 그림자를
완전히 덮어 자연스럽게 사라진다. */
.tabs {
display: flex;
flex-wrap: nowrap;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
gap: 6px;
margin-bottom: var(--space-2);
border-bottom: 1px solid var(--color-border);
background:
linear-gradient(to right, var(--color-bg) 50%, transparent) left,
linear-gradient(to left, var(--color-bg) 50%, transparent) right,
radial-gradient(farthest-side at 0 50%, rgba(0, 0, 0, 0.2), transparent) left,
radial-gradient(farthest-side at 100% 50%, rgba(0, 0, 0, 0.2), transparent) right;
background-repeat: no-repeat;
background-color: var(--color-bg);
background-size: 20px 100%, 20px 100%, 10px 100%, 10px 100%;
background-attachment: local, local, scroll, scroll;
}
.tabs a {
text-decoration: none;
color: var(--color-text-muted);
font-size: 14px;
padding: 10px 4px;
margin-right: var(--space-2);
border-bottom: 2px solid transparent;
white-space: nowrap;
flex-shrink: 0;
touch-action: manipulation;
}
.tabs a.active {
color: var(--color-text);
border-bottom-color: var(--color-accent);
font-weight: 600;
}
/* 요일 선택 pill */
.weekday-picker {
display: flex;
gap: 6px;
flex-wrap: wrap;
align-items: center;
}
.weekday-pill {
width: 36px;
height: 36px;
border-radius: 999px;
border: 1px solid var(--color-border);
background: var(--color-bg);
color: var(--color-text);
font-size: 13px;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.weekday-pill.selected {
background: var(--color-accent);
border-color: var(--color-accent);
color: #fff;
}
.everyday-btn {
border-radius: 999px;
border: 1px solid var(--color-border);
background: transparent;
color: var(--color-text-muted);
font-size: 13px;
padding: 8px 14px;
cursor: pointer;
margin-left: 4px;
}
.everyday-btn.selected {
border-color: var(--color-accent);
color: var(--color-accent);
}
/* 목표 기간 난이도 선택 */
.difficulty-picker {
display: flex;
gap: 6px;
flex-wrap: wrap;
}
.difficulty-pill {
border-radius: 999px;
border: 1px solid var(--color-border);
background: transparent;
color: var(--color-text-muted);
font-size: 13px;
padding: 8px 14px;
cursor: pointer;
}
.difficulty-pill.selected {
background: var(--color-accent);
border-color: var(--color-accent);
color: #fff;
}
/* 저널 기분 선택 pill */
.mood-picker {
display: flex;
gap: 6px;
flex-wrap: wrap;
}
.mood-pill {
border-radius: 999px;
border: 1px solid var(--color-border);
background: transparent;
color: var(--color-text-muted);
font-size: 14px;
padding: 8px 12px;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 4px;
}
.mood-pill.selected {
background: var(--color-accent);
border-color: var(--color-accent);
color: #fff;
}
.difficulty-hint {
font-size: 12px;
color: var(--color-text-muted);
margin-top: 6px;
}
.goal-progress-badge {
font-size: 12px;
color: var(--color-text-muted);
}
/* 습관 리스트 아이템 */
.habit-item {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 8px var(--space-2);
padding: 12px 4px;
border-bottom: 1px solid var(--color-border);
touch-action: manipulation;
}
.habit-item:last-child {
border-bottom: none;
}
.habit-item.today-checked {
background: var(--color-success-tint);
border-radius: var(--radius-control);
margin: 0 -4px;
padding-left: 8px;
padding-right: 8px;
}
.habit-item-main {
display: flex;
align-items: center;
gap: 12px;
min-width: 0;
flex: 1 1 auto;
}
.habit-item-name {
font-size: 15px;
overflow-wrap: break-word;
word-break: break-word;
}
.habit-item-condition {
font-size: 13px;
color: var(--color-text-muted);
margin-top: 2px;
}
.habit-item-meta {
font-size: 12px;
color: var(--color-text-muted);
}
.habit-item-stats {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 6px;
}
.drag-handle {
flex-shrink: 0;
cursor: grab;
color: var(--color-text-muted);
font-size: 18px;
line-height: 1;
padding: 4px 2px;
touch-action: none;
user-select: none;
}
.sortable-ghost {
opacity: 0.4;
}
.sortable-chosen .habit-item {
background: var(--color-bg);
border-radius: var(--radius-control);
}
.habit-item-actions {
display: flex;
gap: 6px;
flex-shrink: 0;
}
.check-indicator {
width: 26px;
height: 26px;
padding: 0;
margin: 0;
border-radius: 50%;
border: 2px solid var(--color-border);
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
flex-shrink: 0;
background: transparent;
font-family: inherit;
font-size: 13px;
line-height: 1;
transition: background-color 0.15s ease, border-color 0.15s ease;
}
.check-indicator.checked {
background: var(--color-success);
border-color: var(--color-success);
color: #fff;
}
.empty-state {
text-align: center;
color: var(--color-text-muted);
padding: var(--space-4) var(--space-2);
font-size: 14px;
}
.badge {
display: inline-block;
font-size: 11px;
padding: 2px 8px;
border-radius: 999px;
background: var(--color-bg);
color: var(--color-text-muted);
border: 1px solid var(--color-border);
}
.badge-milestone {
background: var(--color-gold-tint);
color: var(--color-gold);
border-color: var(--color-gold);
font-weight: 600;
}
.celebration-banner {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 16px;
margin-bottom: var(--space-2);
border-radius: var(--radius-card);
background: var(--color-gold-tint);
border: 1px solid var(--color-gold);
color: var(--color-text);
font-size: 14px;
animation: celebration-pop 0.4s ease-out;
}
@keyframes celebration-pop {
from {
opacity: 0;
transform: scale(0.95) translateY(-4px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}
/* 레벨 배지 & XP 진행바 */
.level-badge {
background: var(--color-gold-tint);
color: var(--color-gold);
border-color: var(--color-gold);
font-weight: 600;
}
.xp-bar-wrap {
display: flex;
align-items: center;
gap: 6px;
margin-top: 4px;
}
.xp-bar {
flex: 1 1 auto;
height: 5px;
border-radius: 999px;
background: var(--color-bg);
border: 1px solid var(--color-border);
overflow: hidden;
}
.xp-bar-fill {
height: 100%;
border-radius: 999px;
background: var(--color-accent);
}
.xp-bar-label {
flex-shrink: 0;
font-size: 11px;
color: var(--color-text-muted);
}
.account-level-card {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-2);
}
.account-level-card .level-badge {
font-size: 13px;
padding: 4px 12px;
}
.account-level-card .xp-bar-wrap {
flex: 1 1 auto;
max-width: 220px;
margin-top: 0;
}
.nav-level-pill {
display: inline-block;
font-size: 11px;
font-weight: 600;
padding: 1px 7px;
border-radius: 999px;
background: var(--color-gold-tint);
color: var(--color-gold);
border: 1px solid var(--color-gold);
flex-shrink: 0;
}
/* 월별 캘린더 */
.calendar-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 4px;
margin-bottom: 4px;
}
.calendar-weekday {
text-align: center;
font-size: 12px;
color: var(--color-text-muted);
padding: 4px 0;
}
.calendar-cell {
aspect-ratio: 1;
border-radius: 8px;
border: 1px solid var(--color-border);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 2px;
}
.calendar-cell.muted {
opacity: 0.35;
}
.calendar-date {
font-size: 12px;
font-weight: 500;
}
.calendar-ratio {
font-size: 10px;
color: var(--color-text-muted);
}
.calendar-cell.clickable {
cursor: pointer;
}
.day-detail-card {
margin-top: var(--space-2);
}
.day-detail-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.day-detail-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 0;
border-bottom: 1px solid var(--color-border);
}
.day-detail-item:last-child {
border-bottom: none;
}
.day-detail-status-checked {
color: var(--color-success);
border-color: var(--color-success);
}
.day-detail-status-missed {
color: var(--color-danger);
border-color: var(--color-danger);
}
/* 주별 매트릭스 */
.week-matrix {
width: 100%;
border-collapse: collapse;
font-size: 13px;
white-space: nowrap;
}
.week-matrix th,
.week-matrix td {
padding: 10px 8px;
text-align: center;
border-bottom: 1px solid var(--color-border);
}
.week-matrix th:first-child,
.week-matrix td:first-child {
text-align: left;
}
.week-matrix th {
color: var(--color-text-muted);
font-weight: 500;
font-size: 12px;
}
.wm-check {
color: var(--color-success);
font-weight: 700;
}
.wm-dash {
color: var(--color-border);
}
/* 저널링 */
.calendar-cell-journal {
aspect-ratio: auto;
min-height: 76px;
padding: 6px 5px;
overflow: hidden;
}
.calendar-cell-journal .calendar-date {
font-size: 13px;
}
.journal-day-entries {
display: flex;
flex-direction: column;
align-items: flex-start;
width: 100%;
}
.journal-day-entry {
display: flex;
align-items: center;
gap: 4px;
width: 100%;
min-width: 0;
}
.journal-day-dot {
width: 6px;
height: 6px;
border-radius: 50%;
flex-shrink: 0;
}
.journal-day-title {
min-width: 0;
font-size: 10px;
line-height: 1.3;
color: var(--color-text-muted);
text-align: left;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.journal-prompt-card,
.on-this-day-card {
margin-top: var(--space-2);
padding: var(--space-2);
border-radius: var(--radius-control);
background: var(--color-success-tint);
font-size: 14px;
}
.journal-entry-item {
padding: 10px 0;
border-bottom: 1px solid var(--color-border);
}
.journal-entry-item:last-child {
border-bottom: none;
}
.journal-entry-title {
font-weight: 600;
margin-top: 6px;
}
.journal-entry-content {
margin-top: 4px;
line-height: 1.6;
}
.journal-preview {
margin-top: 6px;
padding: 10px 12px;
border: 1px dashed var(--color-border);
border-radius: var(--radius-control);
background: var(--color-bg);
min-height: 24px;
font-size: 14px;
}
/* 마크다운 에디터 툴바 (EasyMDE는 toolbar:false로 끄고 여기서 자체 버튼으로 대체 —
EasyMDE 기본 툴바는 Font Awesome CDN을 전제로 해서 이 앱의 "CDN 금지" 원칙과 안 맞는다) */
.markdown-toolbar {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 6px;
}
.md-tool-btn {
min-width: 30px;
height: 30px;
padding: 0 6px;
border: 1px solid var(--color-border);
border-radius: 6px;
background: var(--color-surface);
color: var(--color-text);
font-size: 14px;
cursor: pointer;
}
.md-tool-btn:active {
background: var(--color-bg);
}
/* EasyMDE(CodeMirror) 컨테이너를 이 앱의 입력 필드 톤에 맞춘다. iOS에서는
app/static/js/journal-editor.js가 EasyMDE 대신 순수 <textarea>로 폴백하므로(한글 IME
자소분리 회피) 이 규칙은 그 경우 그냥 매칭되지 않는다 — .markdown-editor 자체는 위 공통
textarea 규칙을 그대로 물려받아 별도 스타일 없이도 정상적으로 보인다. */
.markdown-editor + .EasyMDEContainer .CodeMirror {
border: 1px solid var(--color-border);
border-radius: var(--radius-control);
background: var(--color-bg);
color: var(--color-text);
font-family: inherit;
font-size: 15px;
padding: 6px 8px;
overflow: hidden; /* 안에서 뭐가 카드 폭보다 커지려 해도 밖으로 안 새어나가게 */
}
.markdown-editor + .EasyMDEContainer .CodeMirror-cursor {
border-left-color: var(--color-text);
}
.markdown-editor + .EasyMDEContainer .editor-statusbar {
color: var(--color-text-muted);
}
/* 원본 해상도가 큰 이미지를 붙여넣었을 때 에디터/미리보기 폭을 넘어가지 않게 캡핑.
.journal-entry-content img가 미리보기(.journal-preview)는 이미 커버하지만, 에디터 쪽
(CodeMirror가 마크다운 이미지를 인라인 위젯으로 그리는 경우)도 같은 규칙을 강제로 적용. */
.markdown-editor + .EasyMDEContainer .CodeMirror img {
max-width: 100% !important;
height: auto !important;
}
.journal-entry-content > *:first-child {
margin-top: 0;
}
.journal-entry-content > *:last-child {
margin-bottom: 0;
}
.journal-entry-content p,
.journal-entry-content ul,
.journal-entry-content ol,
.journal-entry-content blockquote,
.journal-entry-content pre {
margin: 0 0 8px;
}
.journal-entry-content h1,
.journal-entry-content h2,
.journal-entry-content h3,
.journal-entry-content h4 {
margin: 12px 0 6px;
line-height: 1.3;
}
.journal-entry-content h1 { font-size: 19px; }
.journal-entry-content h2 { font-size: 17px; }
.journal-entry-content h3,
.journal-entry-content h4 { font-size: 15px; }
.journal-entry-content ul,
.journal-entry-content ol {
padding-left: 20px;
}
.journal-entry-content blockquote {
margin-left: 0;
padding-left: 10px;
border-left: 3px solid var(--color-accent);
color: var(--color-text-muted);
}
.journal-entry-content code {
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 13px;
background: var(--color-bg);
border: 1px solid var(--color-border);
border-radius: 4px;
padding: 1px 5px;
}
.journal-entry-content pre {
background: var(--color-bg);
border: 1px solid var(--color-border);
border-radius: var(--radius-control);
padding: 10px;
overflow-x: auto;
}
.journal-entry-content pre code {
border: none;
padding: 0;
}
.journal-entry-content a {
color: var(--color-accent);
}
.journal-entry-content img {
max-width: 100%;
height: auto;
border-radius: var(--radius-control);
display: block;
margin: 4px 0;
}
.journal-entry-tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 8px;
}
.tag-chip {
font-size: 12px;
color: var(--color-text-muted);
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: 999px;
padding: 2px 8px;
}
.mood-icon {
font-size: 18px;
}
.attachment-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
gap: 8px;
margin-top: 8px;
}
.attachment-thumb {
position: relative;
aspect-ratio: 1;
border-radius: var(--radius-control);
overflow: hidden;
border: 1px solid var(--color-border);
}
.attachment-thumb img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.attachment-video-link {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
font-size: 12px;
text-align: center;
color: var(--color-text-muted);
text-decoration: none;
padding: 4px;
}
.attachment-delete-btn {
position: absolute;
top: 2px;
right: 2px;
width: 20px;
height: 20px;
border-radius: 50%;
border: none;
background: rgba(0, 0, 0, 0.6);
color: #fff;
font-size: 14px;
line-height: 1;
cursor: pointer;
}
/* 랜딩 페이지 (/) */
.landing {
max-width: 480px;
margin: 0 auto;
padding: var(--space-4) var(--space-2) var(--space-4);
display: flex;
flex-direction: column;
gap: var(--space-4);
}
.landing-hero {
display: flex;
flex-direction: column;
align-items: center;
gap: var(--space-3);
text-align: center;
}
.landing-logo {
width: 56px;
height: 56px;
border-radius: 14px;
}
/* 앱 이름 h1 — 화면에서 가장 큰 글자여야 한다(위 home.html 주석 참고). */
.landing-brand {
margin: 0;
font-size: 32px;
font-weight: 700;
letter-spacing: -0.02em;
text-wrap: balance;
color: var(--color-accent);
}
/* 태그라인은 앱 이름 아래 부제 */
.landing-hero-title {
font-size: 18px;
font-weight: 600;
letter-spacing: -0.01em;
text-wrap: balance;
margin: 0;
color: var(--color-text);
}
.landing-tagline {
font-size: 16px;
color: var(--color-text-muted);
margin: 0;
}
/* 요일 스트릭 시연: 실제 습관 체크 인터랙션을 그대로 축소해 보여준다 */
.landing-streak {
display: flex;
gap: 8px;
}
.landing-day {
width: 34px;
height: 34px;
border-radius: 999px;
border: 1px solid var(--color-border);
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
color: var(--color-text-muted);
background: var(--color-surface);
}
.landing-day.done {
background: var(--color-accent);
border-color: var(--color-accent);
color: #fff;
opacity: 0;
animation: landing-day-in 0.35s ease-out forwards;
}
.landing-day.done svg {
width: 16px;
height: 16px;
}
.landing-day:nth-of-type(1).done { animation-delay: 0.1s; }
.landing-day:nth-of-type(2).done { animation-delay: 0.25s; }
.landing-day:nth-of-type(3).done { animation-delay: 0.4s; }
.landing-day:nth-of-type(4).done { animation-delay: 0.55s; }
.landing-day:nth-of-type(5).done { animation-delay: 0.7s; }
@keyframes landing-day-in {
from {
opacity: 0;
transform: scale(0.6);
}
to {
opacity: 1;
transform: scale(1);
}
}
@media (prefers-reduced-motion: reduce) {
.landing-day.done {
opacity: 1;
animation: none;
}
}
.landing-copy {
font-size: 15.5px;
line-height: 1.75;
color: var(--color-text);
display: flex;
flex-direction: column;
gap: var(--space-2);
}
.landing-copy strong {
color: var(--color-accent);
}
.landing-features {
display: grid;
gap: var(--space-2);
}
.landing-feature {
display: flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-2);
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-card);
}
.landing-feature svg {
flex-shrink: 0;
width: 20px;
height: 20px;
color: var(--color-accent);
}
.landing-feature p {
margin: 0;
font-size: 14px;
color: var(--color-text);
}
.landing-feature p strong {
display: block;
font-size: 14.5px;
margin-bottom: 2px;
}
/* 기능 설명 / 계정 안내 — 구글 브랜딩 심사가 홈페이지에서 확인하는 "앱의 목적" 설명 영역. */
.landing-section h2 {
font-size: 17px;
font-weight: 700;
margin: 0 0 var(--space-2);
}
.landing-list {
margin: 0;
padding-left: 1.1em;
display: flex;
flex-direction: column;
gap: 10px;
font-size: 14.5px;
line-height: 1.65;
color: var(--color-text);
}
.landing-list strong {
color: var(--color-accent);
}
.landing-note {
margin: 0 0 var(--space-2);
font-size: 13.5px;
line-height: 1.7;
color: var(--color-text-muted);
}
.landing-note:last-child {
margin-bottom: 0;
}
.landing-note strong {
color: var(--color-text);
}