:root { --bg: #f1ede4; --bg-deep: #e9e3d6; --panel: #fbf9f3; --panel-2: #f6f2e9; --code-bg: #f8f4eb; --ink: #211e18; --ink-2: #4d473c; --muted: #8d8474; --line: #ded7c8; --line-strong: #cec4b2; --accent: #e4663b; --accent-deep: #c94f29; --accent-ink: #fff8f2; --accent-soft: #f8ddd1; --sage: #3f6d5e; --sage-soft: #e2ece6; --danger: #b5442e; --radius: 14px; --radius-sm: 9px; --mono: ui-monospace, "SF Mono", "SFMono-Regular", Menlo, Consolas, "Liberation Mono", monospace; --sans: "Avenir Next", "Segoe UI", "Noto Sans TC", "PingFang TC", "Microsoft JhengHei", system-ui, sans-serif; } * { box-sizing: border-box; } html, body { height: 100%; } body { margin: 0; color: var(--ink); background: var(--bg); font-family: var(--sans); font-size: 14px; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; overflow: hidden; } body::before { content: ""; position: fixed; inset: 0; z-index: 0; pointer-events: none; background-image: radial-gradient(rgba(60, 48, 30, 0.035) 0.6px, transparent 0.7px); background-size: 5px 5px; opacity: 0.55; } button, input, textarea { font: inherit; color: inherit; } button { -webkit-tap-highlight-color: transparent; } svg { display: block; } :focus-visible { outline: 2px solid rgba(228, 102, 59, 0.58); outline-offset: 2px; } * { scrollbar-width: thin; scrollbar-color: #d0c7b7 transparent; } ::-webkit-scrollbar { width: 9px; height: 9px; } ::-webkit-scrollbar-thumb { background: #d0c7b7; border: 2px solid transparent; border-radius: 999px; background-clip: content-box; } ::-webkit-scrollbar-track { background: transparent; } /* ------------------------------------------------------------------ */ /* top bar */ /* ------------------------------------------------------------------ */ .topbar { position: relative; z-index: 2; height: 66px; padding: 0 22px; display: flex; align-items: center; justify-content: space-between; gap: 16px; border-bottom: 1px solid rgba(206, 196, 178, 0.55); background: linear-gradient(180deg, rgba(251, 249, 243, 0.72), rgba(241, 237, 228, 0.42)); } .brand { display: flex; align-items: center; gap: 10px; min-width: 0; color: var(--ink); text-decoration: none; } .brand-mark { width: 32px; height: 32px; flex: none; display: grid; place-items: center; border-radius: 9px; color: #fff7ef; background: var(--accent); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.28), 0 5px 14px -8px rgba(180, 70, 35, 0.7); } .brand-mark svg { width: 20px; height: 20px; } .brand-name { font-size: 19px; font-weight: 720; letter-spacing: -0.045em; line-height: 1; white-space: nowrap; } .brand-sub { margin-left: 2px; padding-left: 11px; border-left: 1px solid var(--line-strong); font-size: 9px; font-weight: 650; letter-spacing: 0.19em; text-transform: uppercase; color: var(--muted); white-space: nowrap; } .topbar-actions { display: flex; align-items: center; gap: 9px; min-width: 0; } .runtime { display: inline-flex; align-items: center; gap: 7px; height: 30px; padding: 0 10px; border: 1px solid var(--line); border-radius: 999px; background: rgba(255, 255, 255, 0.38); color: var(--muted); font-size: 10.5px; font-weight: 600; letter-spacing: 0.01em; white-space: nowrap; transition: color 0.2s, border-color 0.2s, background 0.2s; } .runtime-dot { width: 7px; height: 7px; flex: none; border-radius: 50%; background: #b6ad9c; box-shadow: 0 0 0 3px rgba(182, 173, 156, 0.14); transition: background 0.2s, box-shadow 0.2s; } .runtime[data-state="loading"] { color: #9b6b1f; border-color: #e8d6ab; background: #fbf2dd; } .runtime[data-state="loading"] .runtime-dot { background: #d99a32; box-shadow: 0 0 0 3px rgba(217, 154, 50, 0.16); animation: pulse 1.1s ease-in-out infinite; } .runtime[data-state="ready"] { color: var(--sage); border-color: #c9ddd2; background: #eef6f1; } .runtime[data-state="ready"] .runtime-dot { background: #4f8a72; box-shadow: 0 0 0 3px rgba(79, 138, 114, 0.15); } .runtime[data-state="busy"] { color: var(--accent-deep); border-color: #ecc7b6; background: #fdf0e8; } .runtime[data-state="busy"] .runtime-dot { background: var(--accent); box-shadow: 0 0 0 3px rgba(228, 102, 59, 0.14); animation: pulse 1s ease-in-out infinite; } .runtime[data-state="error"] { color: var(--danger); border-color: #e8c2b7; background: #fbebe6; } .runtime[data-state="error"] .runtime-dot { background: #c8553d; box-shadow: 0 0 0 3px rgba(200, 85, 61, 0.14); } /* ------------------------------------------------------------------ */ /* generic controls */ /* ------------------------------------------------------------------ */ .btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; height: 36px; padding: 0 14px; border: 1px solid transparent; border-radius: var(--radius-sm); background: transparent; font-size: 12px; font-weight: 650; letter-spacing: 0.005em; white-space: nowrap; cursor: pointer; transition: background 0.16s, border-color 0.16s, color 0.16s, transform 0.16s; } .btn svg { width: 15px; height: 15px; flex: none; } .btn:active { transform: translateY(1px); } .btn:disabled { cursor: default; opacity: 0.55; transform: none; } .btn.ghost { border-color: var(--line-strong); background: rgba(255, 255, 255, 0.42); color: var(--ink-2); } .btn.ghost:hover { background: #fffdf8; border-color: #c3b8a4; color: var(--ink); } .btn.run { height: 34px; padding: 0 11px 0 13px; background: var(--accent); border-color: var(--accent); color: var(--accent-ink); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.24), 0 6px 16px -10px rgba(180, 70, 35, 0.9); } .btn.run:hover { background: var(--accent-deep); border-color: var(--accent-deep); } .btn.run svg { width: 13px; height: 13px; } .btn.run.is-busy svg { display: none; } .btn.run.is-busy::before { content: ""; width: 11px; height: 11px; border: 1.5px solid rgba(255, 255, 255, 0.34); border-top-color: #fff; border-radius: 50%; animation: spin 0.8s linear infinite; } .btn.run kbd { margin-left: 1px; padding: 2px 5px; border-radius: 4px; background: rgba(255, 255, 255, 0.18); font-family: var(--sans); font-size: 9px; font-weight: 600; letter-spacing: 0.02em; opacity: 0.82; } .icon-btn { width: 32px; height: 32px; flex: none; display: grid; place-items: center; padding: 0; border: 1px solid transparent; border-radius: 8px; background: transparent; color: var(--muted); cursor: pointer; transition: background 0.16s, color 0.16s, border-color 0.16s; } .icon-btn svg { width: 17px; height: 17px; } .icon-btn:hover { background: rgba(59, 48, 32, 0.05); color: var(--ink-2); } .icon-btn.is-on { color: var(--sage); background: var(--sage-soft); } .icon-btn.tiny { width: 26px; height: 26px; border-radius: 7px; } .icon-btn.tiny svg { width: 14px; height: 14px; } .icon-btn:disabled { opacity: 0.32; cursor: default; } .icon-btn:disabled:hover { background: transparent; color: var(--muted); } /* ------------------------------------------------------------------ */ /* workspace panels */ /* ------------------------------------------------------------------ */ .workspace { position: relative; z-index: 1; width: 100%; max-width: 1720px; height: calc(100vh - 66px); margin: 0 auto; padding: 14px 18px 18px; display: grid; grid-template-columns: 248px minmax(330px, 0.92fr) minmax(380px, 1.08fr); gap: 14px; min-height: 0; } .panel { min-width: 0; min-height: 0; display: flex; flex-direction: column; overflow: hidden; border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.65), 0 16px 34px -32px rgba(52, 42, 27, 0.4); } .panel-head { height: 50px; flex: none; display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 0 13px 0 16px; border-bottom: 1px solid var(--line); } .panel-head h2 { margin: 0; font-size: 12px; font-weight: 680; letter-spacing: 0.03em; color: var(--ink-2); } .panel-count { min-width: 27px; padding: 2px 7px; border: 1px solid var(--line); border-radius: 999px; color: var(--muted); font-size: 10px; font-weight: 600; font-variant-numeric: tabular-nums; text-align: center; } /* ------------------------------------------------------------------ */ /* examples */ /* ------------------------------------------------------------------ */ .examples { background: var(--panel-2); } .example-list { flex: 1; min-height: 0; display: flex; flex-direction: column; gap: 6px; padding: 10px; overflow-y: auto; overscroll-behavior: contain; } .example-card { position: relative; display: flex; align-items: center; gap: 10px; width: 100%; padding: 7px; border: 1px solid transparent; border-radius: 11px; background: transparent; color: var(--ink); text-align: left; cursor: pointer; transition: background 0.16s, border-color 0.16s, box-shadow 0.16s; } .example-card:hover { background: rgba(255, 255, 255, 0.55); border-color: var(--line); } .example-card.is-active { background: #fffdf8; border-color: var(--line-strong); box-shadow: 0 8px 18px -18px rgba(52, 42, 27, 0.6); } .example-card.is-active::before { content: ""; position: absolute; left: -1px; top: 17px; bottom: 17px; width: 2px; border-radius: 0 2px 2px 0; background: var(--accent); } .example-thumb { width: 58px; height: 58px; flex: none; overflow: hidden; border: 1px solid var(--line); border-radius: 9px; background: #f3eee4; } .example-thumb img { width: 100%; height: 100%; display: block; object-fit: cover; } .example-meta { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 2px; } .example-title { font-size: 13px; font-weight: 680; letter-spacing: -0.01em; line-height: 1.25; } .example-sub { overflow: hidden; color: var(--muted); font-size: 9.5px; font-weight: 500; letter-spacing: 0.01em; text-overflow: ellipsis; white-space: nowrap; } .example-index { align-self: flex-start; margin-top: 1px; color: #b8ae9d; font-family: var(--mono); font-size: 8.5px; letter-spacing: 0.06em; } .example-chev { width: 14px; height: 14px; flex: none; margin-right: 2px; color: var(--line-strong); opacity: 0; transform: translateX(-3px); transition: opacity 0.16s, transform 0.16s, color 0.16s; } .example-chev svg { width: 14px; height: 14px; } .example-card:hover .example-chev, .example-card.is-active .example-chev { opacity: 1; transform: translateX(0); } .example-card.is-active .example-chev { color: var(--accent); } .example-skeleton { height: 74px; flex: none; border-radius: 11px; background: linear-gradient(100deg, rgba(255, 255, 255, 0.2) 15%, rgba(255, 255, 255, 0.65) 40%, rgba(255, 255, 255, 0.2) 65%); background-size: 200% 100%; border: 1px solid rgba(222, 215, 200, 0.55); animation: shimmer 1.5s linear infinite; } .examples-foot { flex: none; display: flex; align-items: center; gap: 7px; padding: 11px 16px; border-top: 1px solid var(--line); color: var(--muted); font-size: 9.5px; letter-spacing: 0.01em; } .drop-dot { width: 5px; height: 5px; flex: none; border-radius: 50%; background: var(--accent); } /* ------------------------------------------------------------------ */ /* editor */ /* ------------------------------------------------------------------ */ .file-meta { display: flex; align-items: center; gap: 8px; min-width: 0; } .file-dot { width: 7px; height: 7px; flex: none; border-radius: 50%; background: var(--accent); } .file-name { overflow: hidden; color: var(--ink); font-family: var(--mono); font-size: 11.5px; font-weight: 600; text-overflow: ellipsis; white-space: nowrap; } .state-pill { flex: none; padding: 2px 7px; border: 1px solid var(--line); border-radius: 999px; background: var(--panel-2); color: var(--muted); font-size: 9px; font-weight: 650; letter-spacing: 0.02em; } .state-pill[data-state="modified"] { border-color: #eac6b7; background: var(--accent-soft); color: var(--accent-deep); } .state-pill[data-state="upload"] { border-color: #c8ddd2; background: var(--sage-soft); color: var(--sage); } .state-pill[data-state="error"] { border-color: #e8c2b7; background: #fbebe6; color: var(--danger); } .editor-actions { display: flex; align-items: center; gap: 5px; } .editor-wrap { position: relative; flex: 1; min-height: 0; overflow: hidden; background: var(--code-bg); } .editor-wrap .CodeMirror { height: 100%; background: transparent; color: var(--ink); font-family: var(--mono); font-size: 12.5px; line-height: 1.68; } .CodeMirror-scroll { padding-bottom: 26px; } .CodeMirror-gutters { border-right: 1px solid rgba(222, 215, 200, 0.72); background: transparent; } .CodeMirror-linenumber { padding: 0 9px 0 12px; color: #b4aa98; font-size: 10px; } .CodeMirror-cursor { border-left: 1.5px solid var(--accent); } .CodeMirror-selected { background: rgba(228, 102, 59, 0.13) !important; } .CodeMirror-focused .CodeMirror-selected { background: rgba(228, 102, 59, 0.17) !important; } .CodeMirror-activeline-background { background: rgba(228, 102, 59, 0.032); } .CodeMirror-matchingbracket { color: var(--accent-deep) !important; text-decoration: underline; } .cm-s-default .cm-keyword { color: #b34e2d; font-weight: 600; } .cm-s-default .cm-atom { color: #8b5e2f; } .cm-s-default .cm-number { color: #a34b2c; } .cm-s-default .cm-def { color: var(--sage); } .cm-s-default .cm-variable { color: var(--ink); } .cm-s-default .cm-variable-2 { color: #47647b; } .cm-s-default .cm-property { color: var(--sage); } .cm-s-default .cm-operator { color: #6c6152; } .cm-s-default .cm-comment { color: #9c9282; font-style: italic; } .cm-s-default .cm-string, .cm-s-default .cm-string-2 { color: #8a6a2a; } .cm-s-default .cm-builtin { color: var(--sage); } .cm-s-default .cm-meta { color: #8b5e2f; } .editor-status { height: 31px; flex: none; display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 0 13px; border-top: 1px solid var(--line); background: rgba(255, 255, 255, 0.34); color: var(--muted); font-size: 9.5px; letter-spacing: 0.02em; } .status-left { display: inline-flex; align-items: center; min-width: 0; } .lang-dot { width: 6px; height: 6px; flex: none; margin-right: 6px; border-radius: 50%; background: var(--sage); } .status-right { font-family: var(--mono); font-size: 9px; white-space: nowrap; } /* ------------------------------------------------------------------ */ /* preview */ /* ------------------------------------------------------------------ */ .preview-meta { display: flex; align-items: center; gap: 8px; min-width: 0; } .live-dot { width: 7px; height: 7px; flex: none; border-radius: 50%; background: #5b947b; box-shadow: 0 0 0 3px rgba(91, 148, 123, 0.14); } .object-count { padding: 2px 7px; border: 1px solid var(--line); border-radius: 999px; color: var(--muted); font-size: 9.5px; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; } .view-actions { display: flex; align-items: center; gap: 3px; } .canvas-wrap { position: relative; flex: 1; min-height: 0; overflow: hidden; border-radius: 0 0 var(--radius) var(--radius); background: radial-gradient(90% 90% at 64% 22%, rgba(255, 255, 255, 0.82) 0%, rgba(255, 255, 255, 0) 58%), radial-gradient(100% 100% at 50% 110%, #e6dfd0 0%, #f3eee3 52%, #fbf8f0 100%); } .canvas-wrap::after { content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none; border-radius: inherit; box-shadow: inset 0 0 90px rgba(79, 63, 39, 0.07); } #view { display: block; width: 100%; height: 100%; touch-action: none; outline: none; } .canvas-hint { position: absolute; left: 50%; bottom: 14px; z-index: 2; display: inline-flex; align-items: center; gap: 7px; padding: 6px 11px; border: 1px solid rgba(206, 196, 178, 0.7); border-radius: 999px; background: rgba(251, 249, 243, 0.64); backdrop-filter: blur(7px); color: #8c8272; font-size: 9.5px; letter-spacing: 0.02em; pointer-events: none; transform: translateX(-50%); transition: opacity 0.5s ease, transform 0.5s ease; white-space: nowrap; } .canvas-hint.is-hidden { opacity: 0; transform: translateX(-50%) translateY(4px); } .mouse-icon { position: relative; width: 9px; height: 13px; border: 1.2px solid currentColor; border-radius: 5px; } .mouse-icon::before { content: ""; position: absolute; top: 2px; left: 50%; width: 1px; height: 3px; border-radius: 1px; background: currentColor; transform: translateX(-50%); } .canvas-loading, .canvas-empty { position: absolute; inset: 0; z-index: 3; display: flex; align-items: center; justify-content: center; gap: 12px; background: rgba(243, 238, 227, 0.68); backdrop-filter: blur(9px); text-align: left; } .canvas-loading[hidden], .canvas-empty[hidden], .canvas-error[hidden] { display: none; } .canvas-loading > div, .canvas-empty { flex-direction: column; } .canvas-loading > div { display: flex; flex-direction: column; gap: 2px; } .canvas-loading strong, .canvas-empty strong { color: var(--ink); font-size: 12px; font-weight: 680; letter-spacing: 0.01em; } .canvas-loading span:not(.spinner), .canvas-empty span:not(.empty-mark) { color: var(--muted); font-size: 10px; } .canvas-empty code { padding: 1px 4px; border-radius: 4px; background: rgba(255, 255, 255, 0.62); font-family: var(--mono); font-size: 9.5px; color: var(--ink-2); } .empty-mark { display: grid; place-items: center; width: 38px; height: 38px; margin-bottom: 2px; border: 1px solid var(--line-strong); border-radius: 50%; color: var(--line-strong); font-family: var(--mono); font-size: 17px; } .spinner { width: 27px; height: 27px; flex: none; border: 2px solid rgba(228, 102, 59, 0.18); border-top-color: var(--accent); border-radius: 50%; animation: spin 0.9s linear infinite; } .canvas-error { position: absolute; inset: 0; z-index: 4; display: flex; align-items: center; justify-content: center; padding: 18px; background: rgba(243, 238, 227, 0.58); backdrop-filter: blur(3px); } .error-card { display: flex; flex-direction: column; width: min(100%, 640px); max-height: 100%; min-height: 0; padding: 13px 15px 12px; overflow: hidden; border: 1px solid #e6c7ba; border-radius: 12px; background: rgba(251, 249, 243, 0.99); box-shadow: 0 26px 60px -36px rgba(74, 45, 26, 0.55); } .error-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; color: #a33f2a; font-size: 11.5px; font-weight: 680; letter-spacing: 0.01em; } .canvas-error pre { flex: 0 1 auto; min-height: 0; margin: 9px 0; padding: 10px; overflow: auto; border: 1px solid #e8dccb; border-radius: 8px; background: #f6efe4; color: #6e3a2c; font-family: var(--mono); font-size: 10.5px; line-height: 1.55; white-space: pre-wrap; word-break: break-word; } .canvas-error p { margin: 0; color: var(--muted); font-size: 9.5px; } .toast { position: absolute; left: 50%; bottom: 16px; z-index: 6; max-width: calc(100% - 32px); padding: 7px 14px; border-radius: 999px; background: #2c2821; color: #fbf8f1; font-size: 10.5px; letter-spacing: 0.01em; text-align: center; box-shadow: 0 12px 30px -18px rgba(24, 19, 12, 0.8); transform: translateX(-50%); animation: toast-in 0.22s ease both; } .toast[data-tone="error"] { background: #8e3a27; } /* ------------------------------------------------------------------ */ /* drop overlay */ /* ------------------------------------------------------------------ */ .drop-overlay { position: fixed; inset: 0; z-index: 50; display: grid; place-items: center; background: rgba(241, 237, 228, 0.76); backdrop-filter: blur(7px); } .drop-overlay[hidden] { display: none; } .drop-card { width: min(420px, 80vw); padding: 36px 30px; display: flex; flex-direction: column; align-items: center; gap: 7px; border: 1.5px dashed var(--accent); border-radius: 18px; background: #fbf8f0; box-shadow: 0 30px 60px -40px rgba(74, 45, 26, 0.5); text-align: center; } .drop-card svg { width: 33px; height: 33px; margin-bottom: 4px; color: var(--accent); } .drop-card strong { font-size: 14px; font-weight: 680; } .drop-card span { color: var(--muted); font-size: 10.5px; } /* ------------------------------------------------------------------ */ /* motion */ /* ------------------------------------------------------------------ */ @keyframes spin { to { transform: rotate(360deg); } } @keyframes pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.55; transform: scale(0.82); } } @keyframes shimmer { from { background-position: 150% 0; } to { background-position: -50% 0; } } @keyframes toast-in { from { opacity: 0; transform: translateX(-50%) translateY(5px); } to { opacity: 1; transform: translateX(-50%) translateY(0); } } @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; } } /* ------------------------------------------------------------------ */ /* responsive */ /* ------------------------------------------------------------------ */ @media (max-width: 1280px) { .workspace { grid-template-columns: 222px minmax(310px, 0.9fr) minmax(350px, 1.1fr); gap: 11px; padding: 12px 14px 14px; } .brand-sub { display: none; } } @media (max-width: 1020px) { .workspace { grid-template-columns: 206px minmax(0, 0.92fr) minmax(0, 1.08fr); gap: 9px; padding: 10px 12px 12px; } .example-thumb { width: 50px; height: 50px; } .example-card { gap: 8px; padding: 6px; } .example-sub { display: none; } .btn.run kbd { display: none; } } @media (max-width: 900px) { body { overflow: auto; } .topbar { height: 58px; padding: 0 14px; } .workspace { height: auto; display: flex; flex-direction: column; gap: 10px; padding: 10px 12px 16px; } .examples { flex: none; order: 1; } .preview-panel { order: 2; flex: none; height: 52vh; min-height: 320px; } .editor-panel { order: 3; flex: none; height: 58vh; min-height: 390px; } .examples .panel-head { height: 42px; } .example-list { flex-direction: row; overflow-x: auto; overflow-y: hidden; padding: 8px; gap: 6px; } .example-card { width: 186px; flex: 0 0 186px; padding: 6px; } .example-card.is-active::before { top: auto; left: 16px; right: 16px; bottom: -1px; width: auto; height: 2px; border-radius: 2px 2px 0 0; } .example-thumb { width: 44px; height: 44px; } .example-chev { display: none; } .examples-foot { display: none; } .panel-head { height: 46px; } .canvas-hint { display: none; } } @media (max-width: 560px) { .topbar { gap: 8px; padding: 0 10px; } .brand { gap: 8px; } .brand-mark { width: 29px; height: 29px; border-radius: 8px; } .brand-name { font-size: 17px; } .topbar-actions { gap: 6px; } .btn.ghost { height: 32px; padding: 0 10px; font-size: 11px; } .runtime { display: none; } .workspace { padding: 8px 8px 14px; gap: 8px; } .preview-panel { height: 46vh; min-height: 280px; } .editor-panel { height: 60vh; min-height: 380px; } .file-name { max-width: 118px; } .state-pill { display: none; } .editor-actions .icon-btn { width: 29px; height: 29px; } .btn.run { padding: 0 10px; } .example-card { width: 166px; flex-basis: 166px; } .example-title { font-size: 12px; } .example-thumb { width: 40px; height: 40px; } .canvas-error { padding: 10px; } .error-card { padding: 11px 12px 10px; } }