Files
palette/internal/web/static/app.css
T
fen 2149fcc2e1
CI / test (pull_request) Successful in 49s
CI / docker (pull_request) Skipped
Fix #167: gutter numbers align with wrapped-line starts
- splitLines(): join .codeline blocks with "" not "\n" — newline text
  nodes under pre-wrap rendered an extra line box per logical line, so
  every gutter number drifted one row per line
- renumber(): geometry-driven placement — one gutline per visual row of
  the code body, each number assigned to the row matching its line's
  measured offsetTop; verified second pass re-runs if a partially-filled
  last wrapped row reflows after the gutter rebuild
- app.css: mobile media query gives .code .gutter the same 13px font as
  .code so gutter rows and code rows share one line box at 375px

Verified in local build with headless chromium (long unbroken token on
line 5, wrap ON): all numbers align with line starts, no misalignment,
no horizontal scroll at 1400x900 and 375x812.
2026-09-10 12:09:16 -05:00

818 lines
39 KiB
CSS

/* Palette UI tokens (mirrors sketches/themes/tokens.css, midnight approved preset) */
:root {
--bg: #241B30; --surface: #2D2340; --surface-2: #3A2D52;
--muted: #7A6A9E; --muted-fg: #C0B2DE; --fg: #F2EDF8;
--accent: #C4A8F0; --border: #42355C;
--radius-lg: 20px; --radius: 10px; --radius-sm: 999px;
--chevron: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%23C8C8D4' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); /* #136: select chevron stroke (dark presets) */
--chevron-fg: #C8C8D4;
--font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
--font-mono: ui-monospace, "JetBrains Mono", "Fira Code", monospace;
}
/* semantic status colors (dark preset values; light presets override below) */
:root {
--ok: #9CD49C; --warn: #E8C77B; --err: #F2A3B3;
--on-accent: #241B30; /* text placed on accent-colored backgrounds */
}
[data-preset="smooth"] {
--bg: #F6F5FA; --surface: #FFFFFF; --surface-2: #DAD7E6;
--muted: #B5B1C9; --muted-fg: #7A7796; --fg: #2A2A36;
--accent: #7A7796; --border: #DAD7E6;
--ok: #456F45; --warn: #7A5E1B; --err: #9E4054;
--on-accent: #F6F5FA;
--chevron: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%23555560' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); /* #136: light presets use a dark stroke */
--chevron-fg: #555560;
}
[data-preset="pastel-lavender"] {
--bg: #e6e0f5; --surface: #f1edfa; --surface-2: #cbb8e7;
--muted: #a99cc9; --muted-fg: #5f5390; --fg: #3E3059;
--accent: #806bb8; --border: #c4b6e0;
--ok: #3E6B3E; --warn: #7A5E1B; --err: #9E4054;
--on-accent: #f1edfa;
--chevron: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%23555560' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); /* #136: light presets use a dark stroke */
--chevron-fg: #555560;
}
[data-preset="pastel-peach"] {
--bg: #ffe0d6; --surface: #fff0ea; --surface-2: #ffc4a8;
--muted: #d9a08c; --muted-fg: #7a4632; --fg: #4F2318;
--accent: #f9826c; --border: #ffc9b5;
--ok: #3E6B3E; --warn: #7A5E1B; --err: #9E4054;
--on-accent: #4F2318;
--chevron: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%23555560' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); /* #136: light presets use a dark stroke */
--chevron-fg: #555560;
}
[data-preset="pastel-cloud"] {
--bg: #fff0f6; --surface: #fff7fb; --surface-2: #ffc8dd;
--muted: #d9b9c9; --muted-fg: #7d5670; --fg: #4A3355;
--accent: #a2d2ff; --border: #ffccd9;
--ok: #3E6B3E; --warn: #7A5E1B; --err: #9E4054;
--on-accent: #274a6b;
--chevron: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%23555560' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); /* #136: light presets use a dark stroke */
--chevron-fg: #555560;
}
/* matching light/dark counterparts: same hue family, flipped lightness roles */
/* Midnight Light: light counterpart of midnight (violet family) */
[data-preset="midnight-light"] {
--bg: #F3EFF9; --surface: #FAF8FD; --surface-2: #E1D9EF;
--muted: #A99CC9; --muted-fg: #5F5390; --fg: #2B2140;
--accent: #7B5FC0; --border: #D8CEEC;
--ok: #3E6B3E; --warn: #7A5E1B; --err: #9E4054;
--on-accent: #FAF8FD;
--chevron: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%23555560' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); /* #136: light presets use a dark stroke */
--chevron-fg: #555560;
}
/* Smooth Dark: dark counterpart of smooth (muted slate-violet family) */
[data-preset="smooth-dark"] {
--bg: #23222E; --surface: #2C2B3A; --surface-2: #3B3A4D;
--muted: #7A7796; --muted-fg: #B5B1C9; --fg: #E8E7F0;
--accent: #A9A6C4; --border: #45435A;
--ok: #9CD49C; --warn: #E8C77B; --err: #F2A3B3;
--on-accent: #23222E;
}
/* Pastel Lavender Dark: dark counterpart (lavender family) */
[data-preset="pastel-lavender-dark"] {
--bg: #2A2340; --surface: #352C50; --surface-2: #4A3E6B;
--muted: #8F7FB8; --muted-fg: #CBBEE8; --fg: #EFEAF8;
--accent: #B39DE4; --border: #4E4272;
--ok: #9CD49C; --warn: #E8C77B; --err: #F2A3B3;
--on-accent: #2A2340;
}
/* Pastel Peach Dark: dark counterpart (peach family) */
[data-preset="pastel-peach-dark"] {
--bg: #3A241C; --surface: #4A2F24; --surface-2: #654234;
--muted: #B07A64; --muted-fg: #F0C4B0; --fg: #FBEBE2;
--accent: #F9826C; --border: #75503F;
--ok: #9CD49C; --warn: #E8C77B; --err: #F2A3B3;
--on-accent: #3A241C;
}
/* Pastel Cloud Dark: dark counterpart (pink cloud family, blue accent) */
[data-preset="pastel-cloud-dark"] {
--bg: #33222E; --surface: #402C3A; --surface-2: #583D50;
--muted: #A87E97; --muted-fg: #E5C2D6; --fg: #FAEEF5;
--accent: #A2D2FF; --border: #644759;
--ok: #9CD49C; --warn: #E8C77B; --err: #F2A3B3;
--on-accent: #33222E;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: var(--font-body);
background: var(--bg);
color: var(--fg);
line-height: 1.45;
-webkit-font-smoothing: antialiased;
min-height: 100vh;
font-size: 24.2px;
}
.topbar {
display: flex; align-items: center; gap: 20px;
padding: 0 22px; height: 76px;
background: var(--surface); border-bottom: 1px solid var(--border);
}
.logo { font-weight: 700; font-size: 27.6px; letter-spacing: -0.02em; text-decoration: none; color: var(--fg); }
.logo em { font-style: normal; color: var(--muted-fg); font-weight: 400; }
.topbar nav { display: flex; gap: 4px; }
.topbar nav a { color: var(--muted-fg); text-decoration: none; padding: 6px 12px; border-radius: var(--radius); font-size: 23.2px; }
.topbar nav a:hover { background: var(--surface-2); color: var(--fg); }
.topbar nav a.on { background: var(--accent); color: var(--bg); }
.topbar .spacer { flex: 1; }
.kbd { font-family: var(--font-mono); font-size: 18.9px; border: 1px solid var(--border); border-radius: var(--radius); padding: 2px 6px; color: var(--muted-fg); }
.float {
background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
box-shadow:
0 1px 2px rgba(0, 0, 0, .10),
0 2px 6px rgba(0, 0, 0, .08),
0 8px 24px rgba(0, 0, 0, .07);
overflow: hidden;
}
/* new paste page */
.deck {
display: grid; grid-template-columns: 1fr 300px; gap: 16px;
padding: 16px 20px 20px; height: calc(100vh - 76px);
max-width: 1400px; margin: 0 auto;
}
.pane-r { display: flex; flex-direction: column; gap: 16px; overflow-y: auto; padding-bottom: 4px; }
.side-section { padding: 14px 16px; flex-shrink: 0; }
.side-section h3 { font-size: 18.9px; text-transform: uppercase; letter-spacing: .08em; color: var(--muted-fg); margin-bottom: 10px; }
.pane-l-col { display: flex; flex-direction: column; gap: 14px; min-height: 0; }
.pane-l-head { flex-shrink: 0; }
.editor-head {
display: flex; align-items: stretch; gap: 12px; padding: 12px 16px;
}
/* #147: title lives in its own pill; select+refresh stay paired in a second pill */
.editor-head-title,
.editor-head-settings {
display: flex; align-items: center; gap: 8px;
background: var(--surface-2); border: 1px solid var(--border);
border-radius: var(--radius); padding: 6px 10px;
}
.editor-head-title { flex: 1; min-width: 0; }
.editor-head-settings { flex: 0 0 fit-content; }
.editor-head-title:focus-within { border-color: var(--accent); }
.editor-head input {
border: none; outline: none; background: transparent; color: var(--fg); font: inherit; font-size: 23.2px; flex: 1;
}
.editor-head select {
border: 1px solid var(--border); background-color: var(--surface-2); color: var(--muted-fg);
border-radius: var(--radius); padding: 4px 10px; font: inherit; font-size: 21.6px; cursor: pointer;
}
/* shared code line metrics (#50): gutter + code must share one line box */
:root { --code-lh: 1.7; --code-fs: 21.6px; }
.editor-wrap { flex: 1; display: flex; min-height: 0; }
.gutter {
padding: 14px 10px; text-align: right; color: var(--muted); font-family: var(--font-mono);
font-size: var(--code-fs); line-height: var(--code-lh); user-select: none; white-space: pre; overflow: hidden;
border-right: 1px solid var(--border);
}
.editor {
flex: 1; padding: 14px 16px; font-family: var(--font-mono); font-size: 21.6px; line-height: 1.7;
white-space: pre; outline: none; overflow: auto; border: none; background: transparent; color: var(--fg);
resize: none; width: 100%;
}
.editor::placeholder { color: var(--muted); }
.actionbar {
display: flex; align-items: center; gap: 14px;
padding: 10px 4px;
}
.actionbar .btn { padding: 10px 26px; }
.btn {
background: var(--accent); color: var(--bg); border: none; cursor: pointer;
padding: 8px 18px; border-radius: var(--radius); font: inherit; font-size: 22.4px; font-weight: 600;
}
.btn:hover { filter: brightness(1.08); }
.hint { font-size: 20.7px; color: var(--muted-fg); }
.hint b { color: var(--fg); font-weight: 550; }
.seg { display: flex; flex-direction: column; gap: 2px; }
.seg label { display: flex; align-items: center; gap: 8px; padding: 5px 8px; border-radius: var(--radius); cursor: pointer; font-size: 22.4px; }
.seg label:hover { background: var(--surface-2); }
.seg input { accent-color: var(--accent); }
.toggle { display: flex; align-items: center; gap: 8px; font-size: 22.4px; cursor: pointer; padding: 5px 8px; border-radius: var(--radius); }
.toggle:hover { background: var(--surface-2); }
.toggle input { accent-color: var(--accent); }
.deck .row { display: flex; justify-content: space-between; align-items: center; font-size: 22.4px; padding: 4px 0; }
.row input[type="text"] {
border: 1px solid var(--border); border-radius: var(--radius); padding: 5px 8px; background: var(--bg);
color: var(--fg); font: inherit; font-size: 21.6px; width: 130px;
}
/* #168: compact side notification pill (paste-created feedback) */
.created-pill {
position: fixed; right: 12px; bottom: 12px; z-index: 200;
background: var(--surface-2); color: var(--ok); border: 1px solid var(--ok);
border-radius: var(--radius-sm); padding: 2px 8px; font-size: 11.5px;
opacity: 0; pointer-events: none; transform: translateY(6px);
transition: opacity .25s ease, transform .25s ease;
box-shadow: 0 4px 16px rgba(0,0,0,.25);
}
.created-pill.show { opacity: 1; transform: translateY(0); }
@media (max-width: 640px) { .created-pill { right: 12px; bottom: 12px; } }
/* paste view */
.meta-bar { display: flex; align-items: center; gap: 12px; padding: 12px 18px; flex-wrap: wrap; }
.meta-bar h1 { font-size: 29.2px; font-weight: 600; }
.slug { font-family: var(--font-mono); font-size: 21.6px; color: var(--muted-fg); background: var(--surface-2); padding: 3px 9px; border-radius: var(--radius-sm); }
.tag { font-size: 19.8px; color: var(--muted-fg); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 2px 9px; }
.paste-title-bar { display: flex; align-items: center; gap: 12px; padding: 12px 18px; flex-wrap: wrap; }
.paste-title-bar h1 { font-size: 29.2px; font-weight: 600; margin: 0; word-break: normal; overflow-wrap: anywhere; }
.stats-pill { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.stats-head { display: flex; align-items: center; gap: 16px; width: 100%; background: none; border: 0; border-bottom: 1px solid var(--border); color: var(--muted-fg); font: inherit; font-size: 21.6px; padding: 14px 18px; cursor: pointer; text-align: left; }
.stats-head:hover { color: var(--fg); background: var(--surface-2); }
.stats-chev { width: 18px; height: 18px; flex: none; transition: transform 0.15s ease; }
.stats-pill.open .stats-chev { transform: rotate(180deg); }
.stats-summary { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; letter-spacing: .01em; }
@media (max-width: 640px) { .stats-summary { white-space: normal; word-break: break-word; } }
.stats-body { /* #169: divider under the dropdown title is the head's own persistent border-bottom */ }
.stats-grid { display: grid; grid-template-columns: max-content 1fr; gap: 6px 18px; padding: 12px 16px; font-size: 20.7px; }
.stats-k { color: var(--muted-fg); }
.stats-v { color: var(--fg); word-break: break-all; }
.meta-bar .spacer { flex: 1; }
.iconbtn { border: 1px solid var(--border); background: var(--surface-2); color: var(--muted-fg); border-radius: var(--radius); padding: 5px 12px; font: inherit; font-size: 21.6px; cursor: pointer; text-decoration: none; }
.iconbtn.gear { display: inline-flex; align-items: center; padding: 5px 9px; }
.iconbtn.gear svg { width: 22px; height: 22px; }
/* #127: gear highlights like selected nav tabs; dark toggle button */
.iconbtn.gear.on { background: var(--accent); color: var(--bg); border-color: var(--accent); }
.iconbtn.dark-toggle { display: inline-flex; align-items: center; gap: 6px; padding: 5px 9px; }
.iconbtn.dark-toggle svg { width: 22px; height: 22px; }
.iconbtn.dark-toggle .icon-moon { display: none; }
.iconbtn.dark-toggle .icon-sun { display: inline-block; }
.dark .iconbtn.dark-toggle .icon-sun { display: none; }
.dark .iconbtn.dark-toggle .icon-moon { display: inline-block; }
/* #127: gear highlights like selected nav tabs; dark toggle button */
.iconbtn.gear.on { background: var(--accent); color: var(--bg); border-color: var(--accent); }
.iconbtn.dark-toggle { display: inline-flex; align-items: center; gap: 6px; padding: 5px 9px; }
.iconbtn.dark-toggle svg { width: 22px; height: 22px; }
.iconbtn.dark-toggle .icon-moon { display: none; }
.iconbtn.dark-toggle .icon-sun { display: inline-block; }
.dark .iconbtn.dark-toggle .icon-sun { display: none; }
.dark .iconbtn.dark-toggle .icon-moon { display: inline-block; }
.settings-head { padding: 12px 18px; border-bottom: 1px solid var(--border); }
.settings-head h1 { font-size: 29.2px; font-weight: 600; margin: 0; }
.settings-body { padding: 16px 18px; color: var(--muted-fg); font-size: 21.6px; }
.settings-section-title { color: var(--fg); font-size: 24px; margin-bottom: 6px; }
/* theme switcher cards (#100) */
.theme-grid { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 14px; }
/* #161: more breathing room between settings sections */
#settings-dark-toggle { margin-bottom: 20px; }
.settings-body h3 { margin-top: 28px; }
.settings-body .theme-grid { margin-bottom: 8px; }
.theme-card {
display: flex; flex-direction: column; gap: 10px; align-items: flex-start;
padding: 14px 16px; min-width: 180px;
background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
color: var(--fg); font: inherit; font-size: 20px; cursor: pointer;
}
.theme-card:hover { border-color: var(--muted); }
.theme-card[aria-pressed="true"] { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.theme-card .swatches { display: flex; gap: 4px; }
.theme-card .swatch { width: 26px; height: 26px; border-radius: 6px; border: 1px solid rgba(255,255,255,.15); }
.theme-name { font-weight: 600; }
.iconbtn:hover { color: var(--fg); border-color: var(--muted); }
/* #130: line wrap toggle, accent when active like other toggles */
/* #166: wrap toggle must show its state: neutral when off, accent when on.
The .btn base is accent filled, which made on and off look identical. */
.btn-icon.wrap-toggle { background: var(--surface-2); color: var(--muted-fg); font-size: 21.6px; font-weight: inherit; border: 1px solid var(--border); }
.btn-icon.wrap-toggle:hover { color: var(--fg); border-color: var(--muted-fg); filter: none; }
.iconbtn.wrap-toggle[aria-pressed="true"],
.btn-icon.wrap-toggle[aria-pressed="true"] { background: var(--accent); color: var(--bg); border-color: var(--accent); }
/* #152: wrap ON must break long unbroken tokens mid-word and allow no horizontal scrolling */
html[data-wrap] .codebody { white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-all; overflow-x: hidden; }
html[data-wrap] .editor { white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-all; overflow-x: hidden; }
/* #152: with wrap on nothing may scroll horizontally, including the code container and mobile floats */
html[data-wrap] .code { overflow-x: hidden; }
html[data-wrap] .float { overflow-x: hidden; }
.iconbtn.danger:hover { color: #ff8fa3; border-color: #ff8fa3; }
.code-head {
display: flex; align-items: center; gap: 10px; padding: 8px 16px;
border-bottom: 1px solid var(--border); font-size: 21.6px; color: var(--muted-fg);
}
.code-head .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); }
.code {
font-family: var(--font-mono); font-size: var(--code-fs); line-height: var(--code-lh);
padding: 14px 0; display: flex; overflow-x: auto;
}
.code .gutter { flex-shrink: 0; }
/* gutter/code share line metrics; the editor gutter keeps its own padding (#50) */
.code .gutter { padding-top: 0; padding-bottom: 0; }
.codebody { padding: 0 18px; white-space: pre; }
/* #167: each logical line is its own block so offsetTop identifies its first visual row */
.codeline { display: block; }
/* #167 rev: gutter number spans must stack one per visual row (wrap on) */
.code .gutter .gutline { display: block; }
/* syntax highlight tokens (#1) */
.tok-kw { color: #c792ea; }
.tok-str { color: #a5e075; }
.tok-num { color: #f78c6c; }
.tok-com { color: #6a737d; font-style: italic; }
.footnote { display: flex; gap: 20px; padding: 10px 18px; font-size: 20.7px; color: var(--muted-fg); border-top: 1px solid var(--border); flex-wrap: wrap; }
/* history */
.page { max-width: 1200px; margin: 0 auto; padding: 20px 20px 28px; display: flex; flex-direction: column; gap: 16px; min-height: calc(100vh - 76px); }
.pager { position: sticky; bottom: 0; margin-top: auto; z-index: 5; }
.head-row { display: flex; align-items: baseline; gap: 14px; }
.head-row h1 { font-size: 34.5px; font-weight: 600; }
.search {
display: flex; align-items: center; gap: 8px; background: var(--surface);
border: 1px solid var(--border); border-radius: var(--radius); padding: 8px 14px; width: 260px;
}
.search input { border: none; outline: none; background: transparent; color: var(--fg); font: inherit; font-size: 22.4px; width: 100%; }
table { width: 100%; border-collapse: collapse; font-size: 22.4px; table-layout: fixed; }
th {
text-align: left; font-size: 18.9px; text-transform: uppercase; letter-spacing: .08em;
color: var(--muted-fg); padding: 10px 16px; border-bottom: 1px solid var(--border); font-weight: 600;
}
td { padding: 10px 16px; border-bottom: 1px solid var(--border); overflow: clip; text-overflow: ellipsis; white-space: nowrap; }
td:has(> a.slug), td:has(> .slug) { overflow: visible; }
tr:last-child td { border-bottom: none; }
tr.row { cursor: pointer; }
tr.row:hover td { background: var(--surface-2); }
tr.row:hover td a.slug { color: var(--accent); }
td a.slug { font-family: var(--font-mono); font-size: 21.6px; color: var(--fg); text-decoration: none; }
td a.slug:hover { color: var(--accent); }
/* PASTE column fallback for untitled pastes: plain text, identical to a titled paste. URL/ID chips keep .slug styling. */
td a.slug.paste-name { background: none; padding: 0; border-radius: 0; font-family: inherit; font-size: inherit; color: var(--fg); }
.badge { font-size: 18.9px; border: 1px solid var(--border); color: var(--muted-fg); border-radius: var(--radius-sm); padding: 1px 8px; }
.badge.lock { color: var(--accent); border-color: var(--accent); }
.dim { color: var(--muted-fg); white-space: nowrap; }
.pager { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; font-size: 21.6px; color: var(--muted-fg); }
.pager .pg { display: flex; gap: 6px; }
.pager button { border: 1px solid var(--border); background: var(--surface-2); color: var(--muted-fg); border-radius: var(--radius); padding: 4px 11px; font: inherit; font-size: 21.6px; cursor: pointer; }
.pager button:hover:not(:disabled) { color: var(--fg); border-color: var(--muted); }
.pager button.on { background: var(--accent); color: var(--bg); border-color: var(--accent); }
.pager button:disabled { opacity: .4; cursor: default; }
.empty { text-align: center; padding: 40px 16px; color: var(--muted-fg); font-size: 22.4px; }
/* unlock */
.center { display: flex; align-items: center; justify-content: center; padding: 20px; height: calc(100vh - 52px); }
.center .float { width: 400px; max-width: 100%; }
.inner { padding: 28px; text-align: center; }
.lockring {
width: 56px; height: 56px; margin: 0 auto 16px; border-radius: 50%;
background: var(--surface-2); display: flex; align-items: center; justify-content: center; font-size: 41.4px;
}
.inner h1 { font-size: 29.2px; font-weight: 600; margin-bottom: 6px; }
.inner .sub { font-size: 22.4px; color: var(--muted-fg); margin-bottom: 20px; }
/* #173: missing paste message box */
.notfound-err { color: var(--err); }
.notfound-card .btn { display: block; margin-top: 10px; }
.pwinput {
width: 100%; padding: 10px 14px; border: 1px solid var(--border); border-radius: var(--radius);
background: var(--bg); color: var(--fg); font: inherit; font-size: 23.2px; outline: none; text-align: center;
letter-spacing: .12em;
}
.pwinput:focus { border-color: var(--accent); }
.center .btn { width: 100%; margin-top: 12px; }
.err { display: none; margin-top: 12px; font-size: 21.6px; color: #ff8fa3; }
.center .foot { font-size: 20.7px; color: var(--muted-fg); padding: 14px; border-top: 1px solid var(--border); }
.btn-icon {
padding: 4px 8px; font-size: 24.2px; line-height: 1; overflow: visible;
display: inline-flex; align-items: center; justify-content: center;
min-width: 40px; height: 36px;
}
/* selection controls: pill-style selected states (#14) */
.seg label, .toggle {
border: 1px solid transparent;
transition: background .12s ease, border-color .12s ease, color .12s ease;
}
.seg label:hover, .toggle:hover {
background: var(--surface-2);
color: var(--fg);
}
.seg label:has(input:checked),
.toggle:has(input:checked) {
background: var(--surface-2);
border-color: var(--accent);
color: var(--fg);
border-radius: var(--radius-sm);
}
.seg label:has(input:focus-visible),
.toggle:has(input:focus-visible) {
outline: 2px solid var(--accent);
outline-offset: 1px;
}
.seg input, .toggle input { accent-color: var(--accent); width: 16px; height: 16px; margin: 0; }
/* toast (#19, #168): small pill anchored to the corner, out of content flow */
.toast {
position: fixed; right: 16px; bottom: 16px;
background: var(--surface-2); color: var(--fg); border: 1px solid var(--border);
border-radius: var(--radius-sm); padding: 6px 12px; font-size: 13px;
opacity: 0; pointer-events: none; transition: opacity .25s ease, transform .25s ease; z-index: 200;
box-shadow: 0 4px 16px rgba(0,0,0,.25);
}
.toast.show { opacity: 1; transform: translateY(0); }
/* status variants (#16) */
.toast.success { border-color: var(--ok); color: var(--ok); }
.toast.error { border-color: var(--err); color: var(--err); }
/* result box color-coding (#105): colored left border + text tint per status */
.result-ok, .result-err, .result-warn { border-left: 4px solid transparent; padding-left: 10px; }
.result-ok { border-left-color: var(--ok); color: var(--ok); }
.result-err { border-left-color: var(--err); color: var(--err); }
.result-warn { border-left-color: var(--warn); color: var(--warn); }
.result-ok a { color: var(--ok); }
/* protection section rhythm (#20) */
.protect { display: flex; flex-direction: column; gap: 2px; }
.protect .pw-row { padding: 2px 8px 4px; }
/* submenu number boxes (#157): styled number inputs + unit selects,
padded + indented to line up with parent option labels */
.submenu {
margin: 6px 0 4px 8px;
padding: 8px 10px;
background: var(--bg);
border: 1px solid var(--border);
border-radius: var(--radius);
}
.exp-group { display: flex; align-items: center; gap: 8px; }
.num-input {
width: 72px;
border: 1px solid var(--border); border-radius: var(--radius);
background: var(--surface-2); color: var(--fg);
font: inherit; font-size: 21.6px; padding: 6px 10px; outline: none;
-moz-appearance: textfield; appearance: textfield;
}
.num-input::-webkit-outer-spin-button,
.num-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.num-input:focus { border-color: var(--accent); }
.unit-select {
border: 1px solid var(--border); border-radius: var(--radius);
background-color: var(--surface-2); color: var(--muted-fg);
font: inherit; font-size: 21.6px; padding: 6px 28px 6px 10px;
cursor: pointer; outline: none;
appearance: none; -webkit-appearance: none;
background-image: var(--chevron);
background-repeat: no-repeat; background-position: right 10px center;
}
.unit-select:focus { border-color: var(--accent); }
.readable-group { gap: 8px; }
.readable-label { font-size: 19px; color: var(--muted-fg); white-space: nowrap; }
.pw-field {
display: flex; align-items: center; gap: 2px; width: 100%;
border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg);
}
.pw-field:focus-within { border-color: var(--accent); }
.pw-field input {
flex: 1; min-width: 0; border: none; outline: none; background: transparent; color: var(--fg);
font: inherit; font-size: 21.6px; padding: 7px 12px; letter-spacing: .08em;
}
.pw-field input::placeholder { color: var(--muted); letter-spacing: normal; }
.pw-field .reveal {
background: none; border: none; color: var(--muted-fg); cursor: pointer;
display: flex; align-items: center; justify-content: center; padding: 0 10px; height: 100%;
flex-shrink: 0;
}
.pw-field .reveal:hover { color: var(--fg); }
.pw-field .reveal .eye-slash { display: none; }
.pw-field .reveal.off .eye-slash { display: block; }
.pw-field svg { width: 20px; height: 20px; display: block; }
/* custom URL input (#22) */
.deck .row input[type="text"] { width: 100%; }
.custom-input {
display: block; width: 100%;
border: 1px solid var(--border); border-radius: var(--radius); padding: 7px 12px;
background: var(--bg); color: var(--fg); font: inherit; font-size: 21.6px; outline: none;
}
.custom-input:focus { border-color: var(--accent); }
.custom-input::placeholder { color: var(--muted); }
/* btn-icon svg (#24) */
.btn-icon svg { width: 20px; height: 20px; display: block; }
/* search spinner (#32) */
.search-spinner {
width: 16px; height: 16px; flex-shrink: 0;
border: 2px solid var(--border); border-top-color: var(--accent); border-radius: 50%;
animation: spin .8s linear infinite; visibility: hidden;
}
@keyframes spin { to { transform: rotate(360deg); } }
/* unlock redesign (#27) */
.unlock-card .pw-field { margin: 18px 0 4px; text-align: left; }
.unlock-card .pw-field input { text-align: left; }
.unlock-err { margin-top: 10px; font-size: 20.7px; color: #ff8fa3; }
/* paste name under slug pill in Paste column (#43) */
.paste-sub { font-size: 19.8px; color: var(--muted-fg); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.paste-sub.dim { color: var(--muted); }
td a.slug.url-link { max-width: 100%; display: inline-block; vertical-align: middle; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; box-sizing: border-box; }
td .id-link { color: var(--muted-fg); text-decoration: none; font-family: var(--font-mono); font-size: 19.8px; }
td .id-link:hover { color: var(--accent); }
/* sortable column headers (#42) */
th.sortable { cursor: pointer; user-select: none; }
th.sortable:hover { color: var(--fg); }
th.sortable { cursor: pointer; user-select: none; white-space: nowrap; }
/* #101: arrow sits LEFT of the label, further spaced, vertically centered,
and absolutely positioned so it never shifts the label text. */
th.sortable { position: relative; padding-left: 24px; }
th.sortable .sort-ind {
position: absolute; left: 8px; top: 50%; transform: translateY(-50%);
display: inline-block; width: 0; height: 0;
border-left: 5px solid transparent; border-right: 5px solid transparent;
}
th.sorted.asc .sort-ind { border-bottom: 6px solid var(--accent); }
th.sorted.desc .sort-ind { border-top: 6px solid var(--accent); }
/* #102: small unobtrusive admin link on the settings page */
a.admin-link { color: var(--muted-fg); font-size: 15px; text-decoration: none; }
a.admin-link:hover { color: var(--fg); text-decoration: underline; }
/* ============================================================
Consistency audit (#18) — shared tokens across inputs, buttons,
headings. Visual-only, no behavior change.
============================================================ */
/* shared text-input treatment: .search input, .pw-field input, #title, #custom */
.search input,
.pw-field input,
.editor-head input#title,
.custom-input,
.row input[type="text"] {
font-size: 21.6px;
color: var(--fg);
}
.search input::placeholder,
.pw-field input::placeholder,
.editor-head input#title::placeholder,
.custom-input::placeholder {
color: var(--muted);
}
.custom-input { border-radius: var(--radius); }
/* buttons (incl. icon-only variants) share one radius + focus ring */
.btn-icon, .iconbtn {
border-radius: var(--radius);
transition: color .12s ease, border-color .12s ease, background .12s ease;
}
.iconbtn:focus-visible, .btn:focus-visible, .btn-icon:focus-visible, .pager button:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 1px;
}
/* in-place copy success feedback (#53) */
.iconbtn.ok, .btn.ok {
color: var(--ok);
border-color: var(--ok);
}
/* headings: unified treatment (mirrors .side-section h3) */
.settings-head h1, .paste-title-bar h1, .head-row h1, .inner h1 {
letter-spacing: -0.01em;
}
/* consistent card padding scale: 12px 18px for wide card heads/bodies */
.settings-head { padding: 12px 18px; }
.settings-body { padding: 16px 18px; }
/* topbar: Git external-link arrow (#56) */
.topbar nav a .ext { width: 14px; height: 14px; margin-left: 4px; opacity: .55; vertical-align: -1px; }
@media (max-width: 640px) {
body { font-size: 16px; }
/* topbar: tighten so logo + nav + gear fit */
.topbar { gap: 6px; padding: 0 10px; height: 56px; }
.logo { font-size: 17px; white-space: nowrap; }
.logo em { display: none; }
.topbar nav { gap: 2px; flex-shrink: 0; }
.topbar nav a { padding: 4px 6px; font-size: 15px; }
.iconbtn.gear { padding: 4px 7px; flex-shrink: 0; }
.iconbtn.gear svg { width: 18px; height: 18px; }
.iconbtn.dark-toggle { padding: 4px 7px; flex-shrink: 0; }
.iconbtn.dark-toggle svg { width: 18px; height: 18px; }
.iconbtn.dark-toggle span { display: none; }
.iconbtn.dark-toggle { padding: 4px 7px; flex-shrink: 0; }
.iconbtn.dark-toggle svg { width: 18px; height: 18px; }
.iconbtn.dark-toggle span { display: none; }
/* new paste: stack editor above sidebar, natural page height */
.deck {
display: flex; flex-direction: column;
height: auto; min-height: calc(100vh - 56px);
padding: 12px; gap: 12px;
}
.pane-l-col { order: 0; }
.pane-r { order: 1; overflow-y: visible; }
.editor-wrap { min-height: 45vh; }
.editor { font-size: 15px; }
.gutter { font-size: 15px; }
.editor-head { flex-wrap: wrap; }
.editor-head-title { flex: 1 1 100%; }
.editor-head input { min-width: 0; font-size: 16px; }
.editor-head select { max-width: 120px; font-size: 14px; }
.editor-head-title, .editor-head-settings { padding: 4px 8px; }
.actionbar { flex-wrap: wrap; }
.actionbar .btn { padding: 12px 22px; }
.hint { font-size: 13px; }
/* history: horizontal-scroll table inside its card */
.page { padding: 12px; }
.head-row { flex-wrap: wrap; }
.head-row h1 { font-size: 22px; }
.search { width: 100%; }
.search input { font-size: 16px; }
.float { overflow-x: auto; -webkit-overflow-scrolling: touch; }
table { min-width: 720px; }
th { padding: 8px 10px; font-size: 12px; white-space: nowrap; }
td { padding: 8px 10px; font-size: 14px; }
.pager { flex-wrap: wrap; gap: 8px; font-size: 13px; }
/* paste view */
.paste-title-bar { padding: 10px 12px; gap: 8px; }
.paste-title-bar h1 { font-size: 18px; }
.slug { font-size: 13px; word-break: break-all; }
.stats-head { font-size: 13px; }
.stats-grid { font-size: 13px; padding: 10px 12px; }
.code { font-size: 13px; }
/* #167: gutter and code must share one line box at mobile width too — a
taller gutter font stacks its rows taller than the code rows and every
number drifts off its line start */
.code .gutter { font-size: 13px; }
.codebody { padding: 0 12px; }
.footnote { font-size: 12px; padding: 8px 12px; gap: 10px; }
.iconbtn { font-size: 13px; padding: 6px 10px; }
/* unlock card */
.center { height: auto; min-height: calc(100vh - 56px); padding: 16px; }
.center .float { width: 100%; }
.inner { padding: 20px 16px; }
.inner h1 { font-size: 20px; }
.inner .sub { font-size: 14px; }
.unlock-err { font-size: 13px; }
.center .foot { font-size: 13px; }
}
/* #4: can view page + can builder */
.can-page h1 { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.can-items { display: flex; flex-direction: column; gap: 12px; margin-top: 14px; }
.can-item { padding: 12px 14px; }
.can-item-head { display: flex; align-items: center; gap: 10px; }
.can-item-head strong { flex: 1; word-break: break-all; }
.can-item-body { margin-top: 8px; }
.can-item-body summary { cursor: pointer; font-size: 19px; color: var(--muted, #888); }
.can-item-body pre.code { margin: 8px 0 0; overflow-x: auto; }
.can-item-row { margin-top: 8px; }
.can-item-row .can-item-title { width: 100%; margin-bottom: 6px; }
.can-item-row .can-item-content { width: 100%; font-family: var(--mono, monospace); resize: vertical; }
/* ============================================================
Styled controls (#123) — buttons, selects, checkboxes, radios
match the custom URL box house pattern: 1px var(--border) on
var(--bg), var(--radius) corners, accent border on focus, no
default browser chrome (appearance: none).
============================================================ */
/* selects: replace native chrome with a house-styled pill */
select {
appearance: none;
-webkit-appearance: none;
border: 1px solid var(--border);
border-radius: var(--radius);
background: var(--bg) var(--chevron) no-repeat right 10px center; /* #136: themed chevron */
color: var(--fg);
font: inherit;
padding: 4px 30px 4px 12px;
cursor: pointer;
outline: none;
transition: border-color .12s ease, background-color .12s ease;
}
select:hover { border-color: var(--muted-fg); background-color: var(--surface-2); }
select:focus-visible { border-color: var(--accent); outline: 2px solid var(--accent); outline-offset: 1px; }
.editor-head select { padding: 4px 30px 4px 10px; }
/* checkboxes and radios: custom drawn, accent when checked */
input[type="checkbox"],
input[type="radio"] {
appearance: none;
-webkit-appearance: none;
width: 16px; height: 16px; margin: 0; flex-shrink: 0;
border: 1px solid var(--border);
background: var(--bg);
cursor: pointer;
position: relative;
transition: border-color .12s ease, background-color .12s ease;
}
input[type="checkbox"] { border-radius: 4px; }
input[type="radio"] { border-radius: 50%; }
input[type="checkbox"]:hover,
input[type="radio"]:hover { border-color: var(--muted-fg); }
input[type="checkbox"]:checked,
input[type="radio"]:checked { background: var(--accent); border-color: var(--accent); }
input[type="checkbox"]:checked::after {
content: "";
position: absolute; inset: 0;
background: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='10' viewBox='0 0 12 10'%3E%3Cpath d='M1 5l4 4 6-8' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 11px no-repeat;
}
input[type="radio"]:checked::after {
content: "";
position: absolute; inset: 3px;
border-radius: 50%;
background: var(--bg);
}
input[type="checkbox"]:focus-visible,
input[type="radio"]:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 1px;
}
/* buttons: every submit-style button gets the .btn treatment,
and .btn itself gains the house surface/border/hover pattern */
button[type="submit"] {
background: var(--accent); color: var(--bg); border: 1px solid var(--accent);
border-radius: var(--radius); font: inherit; font-weight: 600;
padding: 8px 18px; cursor: pointer;
transition: filter .12s ease;
}
button[type="submit"]:hover { filter: brightness(1.08); }
button[type="submit"]:focus-visible,
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.btn {
border: 1px solid var(--accent);
}
.btn:hover { filter: brightness(1.08); }
/* settings: give the Admin link clear separation from the theme grid */
.admin-link-row {
margin-top: 32px;
padding-top: 20px;
border-top: 1px solid var(--border);
}
/* #38: attachment upload on /new + attachment display on paste view */
.side-section .file-input { display: none; }
.dropzone {
border: 1px dashed var(--border);
border-radius: var(--radius-lg);
padding: 14px 12px;
text-align: center;
color: var(--muted-fg);
cursor: pointer;
font-size: 20.7px;
transition: border-color .15s, background .15s;
background: var(--surface-2, transparent);
}
.dropzone:hover, .dropzone.dragover {
border-color: var(--accent);
background: var(--surface-2);
}
.dropzone.dragover { border-width: 2px; }
/* #171: uploaded-file preview replaces the editor area; image fitted inside */
.file-preview {
flex: 1;
min-width: 0;
min-height: 0;
max-width: 100%;
max-height: 100%;
object-fit: contain;
border-radius: 8px;
}
.editor-wrap.previewing .editor,
.editor-wrap.previewing .gutter { display: none; }
.file-chip {
display: flex; align-items: center; gap: 10px;
border: 1px solid var(--border); border-radius: var(--radius);
padding: 8px 14px; margin-top: 8px; background: var(--surface);
font-size: 21.6px;
}
.file-chip .file-chip-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
.file-chip .file-chip-size { color: var(--muted-fg); font-size: 19px; }
.file-chip .file-chip-remove {
border: none; background: none; color: var(--muted-fg); cursor: pointer;
font-size: 22px; padding: 0 4px; border-radius: var(--radius-sm);
}
.file-chip .file-chip-remove:hover { color: var(--danger, #c0392b); }
.attachment-bar { display: flex; flex-direction: column; gap: 10px; }
.attachment-chip {
display: inline-flex; align-items: center; gap: 12px; align-self: flex-start;
border: 1px solid var(--border); border-radius: var(--radius);
padding: 8px 16px; text-decoration: none; color: var(--fg);
background: var(--surface-2); font-size: 21.6px;
}
.attachment-chip:hover { border-color: var(--accent); }
.attachment-chip .attachment-size { color: var(--muted-fg); font-size: 19px; }
.attachment-preview img { max-width: 480px; max-height: 360px; border-radius: var(--radius); border: 1px solid var(--border); }
/* #139: CSP-safe replacements for inline style attributes (style-src 'self') */
.hidden { display: none; }
.can-page { max-width: 900px; width: 100%; }
.col-a { width: 260px; } .col-b { width: 140px; } .col-c { width: 120px; }
.col-d { width: 96px; } .col-d2 { width: 150px; } .col-e { width: 190px; }
.col-f { width: 100px; } .col-g { width: 190px; }
.spacer-flex { flex: 1; }
.input-num { width: 80px; }
.input-num-sm { width: 64px; }
.danger-hint { color: var(--danger, #c0392b); margin-top: 6px; }
.hint-lg { font-size: 19px; }
.mt6 { margin-top: 6px; }
.mt18 { margin-top: 18px; }
.toggle-inline { display: inline-flex; }
.wrap-normal { word-break: normal; overflow-wrap: break-word; }