diff --git a/web/src/app.css b/web/src/app.css
index 5bb615e..f78f9cc 100644
--- a/web/src/app.css
+++ b/web/src/app.css
@@ -1,281 +1,283 @@
-:root {
- color-scheme: light;
+@layer app {
+ :root {
+ color-scheme: light;
- --bg: #f6f7f9;
- --surface: #ffffff;
- --text: #1b1e24;
- --text-muted: #5c6470;
- --accent: #2563eb;
- --accent-hover: #1d4ed8;
- --accent-contrast: #ffffff;
- --link: var(--accent);
- --border: #dfe2e8;
- --danger: #e5484d;
- --danger-strong: #b3261e;
- --check-a: #eceff3;
- --check-b: #ffffff;
+ --bg: #f6f7f9;
+ --surface: #ffffff;
+ --text: #1b1e24;
+ --text-muted: #5c6470;
+ --accent: #2563eb;
+ --accent-hover: #1d4ed8;
+ --accent-contrast: #ffffff;
+ --link: var(--accent);
+ --border: #dfe2e8;
+ --danger: #e5484d;
+ --danger-strong: #b3261e;
+ --check-a: #eceff3;
+ --check-b: #ffffff;
- --space-1: 0.25rem;
- --space-2: 0.5rem;
- --space-3: 1rem;
- --space-4: 1.5rem;
- --space-5: 2.5rem;
+ --space-1: 0.25rem;
+ --space-2: 0.5rem;
+ --space-3: 1rem;
+ --space-4: 1.5rem;
+ --space-5: 2.5rem;
- --radius-s: 6px;
- --radius-m: 10px;
+ --radius-s: 6px;
+ --radius-m: 10px;
- --text-xs: 0.8rem;
- --text-s: 0.85rem;
- --text-m: 0.95rem;
- --text-l: 1.1rem;
- --text-xl: 1.25rem;
- --text-xxl: 1.75rem;
- --leading-tight: 1.25;
+ --text-xs: 0.8rem;
+ --text-s: 0.85rem;
+ --text-m: 0.95rem;
+ --text-l: 1.1rem;
+ --text-xl: 1.25rem;
+ --text-xxl: 1.75rem;
+ --leading-tight: 1.25;
- --shadow-card: 0 4px 14px rgb(37 99 235 / 12%);
- --transition-fast: 120ms ease;
+ --shadow-card: 0 4px 14px rgb(37 99 235 / 12%);
+ --transition-fast: 120ms ease;
- --control-height: 2.4rem;
- --control-max-width: 16rem;
+ --control-height: 2.4rem;
+ --control-max-width: 16rem;
- --font-sans:
- system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial,
- sans-serif;
- --font-mono: ui-monospace, "Cascadia Code", Consolas, monospace;
-}
+ --font-sans:
+ system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial,
+ sans-serif;
+ --font-mono: ui-monospace, "Cascadia Code", Consolas, monospace;
+ }
-[data-theme="dark"] {
- color-scheme: dark;
+ [data-theme="dark"] {
+ color-scheme: dark;
- --bg: #12151a;
- --surface: #1b2027;
- --text: #e6e9ee;
- --text-muted: #98a1ad;
- --accent-hover: #4c85f0;
- --link: #6ea0ff;
- --border: #303842;
- --danger: #ff7479;
- --danger-strong: #ff8589;
- --check-a: #232830;
- --check-b: #171b21;
+ --bg: #12151a;
+ --surface: #1b2027;
+ --text: #e6e9ee;
+ --text-muted: #98a1ad;
+ --accent-hover: #4c85f0;
+ --link: #6ea0ff;
+ --border: #303842;
+ --danger: #ff7479;
+ --danger-strong: #ff8589;
+ --check-a: #232830;
+ --check-b: #171b21;
- --shadow-card: 0 4px 14px rgb(0 0 0 / 45%);
-}
+ --shadow-card: 0 4px 14px rgb(0 0 0 / 45%);
+ }
-*,
-*::before,
-*::after {
- box-sizing: border-box;
-}
+ *,
+ *::before,
+ *::after {
+ box-sizing: border-box;
+ }
-* {
- margin: 0;
-}
+ * {
+ margin: 0;
+ }
-html {
- font-family: var(--font-sans);
- color: var(--text);
- background: var(--bg);
- line-height: 1.55;
- -webkit-text-size-adjust: 100%;
-}
+ html {
+ font-family: var(--font-sans);
+ color: var(--text);
+ background: var(--bg);
+ line-height: 1.55;
+ -webkit-text-size-adjust: 100%;
+ }
-body {
- min-height: 100dvh;
-}
+ body {
+ min-height: 100dvh;
+ }
-h1 {
- font-size: var(--text-xxl);
- line-height: var(--leading-tight);
- letter-spacing: -0.01em;
-}
+ h1 {
+ font-size: var(--text-xxl);
+ line-height: var(--leading-tight);
+ letter-spacing: -0.01em;
+ }
-h2 {
- font-size: var(--text-xl);
- line-height: var(--leading-tight);
-}
+ h2 {
+ font-size: var(--text-xl);
+ line-height: var(--leading-tight);
+ }
-p,
-label {
- font-size: var(--text-m);
-}
+ p,
+ label {
+ font-size: var(--text-m);
+ }
-a {
- color: var(--link);
- text-decoration: none;
-}
+ a {
+ color: var(--link);
+ text-decoration: none;
+ }
-a:hover {
- text-decoration: underline;
-}
+ a:hover {
+ text-decoration: underline;
+ }
-button,
-input,
-select {
- font: inherit;
- color: inherit;
-}
+ button,
+ input,
+ select {
+ font: inherit;
+ color: inherit;
+ }
-:focus-visible {
- outline: 2px solid var(--accent);
- outline-offset: 2px;
- border-radius: var(--radius-s);
-}
+ :focus-visible {
+ outline: 2px solid var(--accent);
+ outline-offset: 2px;
+ border-radius: var(--radius-s);
+ }
-.text-muted {
- color: var(--text-muted);
-}
+ .text-muted {
+ color: var(--text-muted);
+ }
-.text-caption {
- font-size: var(--text-s);
-}
+ .text-caption {
+ font-size: var(--text-s);
+ }
-.heading-section {
- font-size: var(--text-l);
- color: var(--text-muted);
- text-transform: uppercase;
- letter-spacing: 0.04em;
-}
+ .heading-section {
+ font-size: var(--text-l);
+ color: var(--text-muted);
+ text-transform: uppercase;
+ letter-spacing: 0.04em;
+ }
-.panel {
- background: var(--surface);
- border: 1px solid var(--border);
- border-radius: var(--radius-m);
-}
+ .panel {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-m);
+ }
-.control {
- padding: var(--space-1) var(--space-2);
- border: 1px solid var(--border);
- border-radius: var(--radius-s);
- background: var(--surface);
- max-width: var(--control-max-width);
-}
+ .control {
+ padding: var(--space-1) var(--space-2);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-s);
+ background: var(--surface);
+ max-width: var(--control-max-width);
+ }
-input.control[type="number"] {
- font-family: var(--font-mono);
- width: 100%;
-}
+ input.control[type="number"] {
+ font-family: var(--font-mono);
+ width: 100%;
+ }
-.error-banner {
- padding: var(--space-2) var(--space-3);
- border: 1px solid var(--danger);
- border-radius: var(--radius-s);
- background: color-mix(in srgb, var(--danger) 8%, var(--surface));
- color: var(--danger-strong);
- font-size: var(--text-s);
-}
+ .error-banner {
+ padding: var(--space-2) var(--space-3);
+ border: 1px solid var(--danger);
+ border-radius: var(--radius-s);
+ background: color-mix(in srgb, var(--danger) 8%, var(--surface));
+ color: var(--danger-strong);
+ font-size: var(--text-s);
+ }
-.visually-hidden {
- position: absolute;
- width: 1px;
- height: 1px;
- margin: -1px;
- padding: 0;
- overflow: hidden;
- clip: rect(0 0 0 0);
- white-space: nowrap;
- border: 0;
-}
+ .visually-hidden {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ margin: -1px;
+ padding: 0;
+ overflow: hidden;
+ clip: rect(0 0 0 0);
+ white-space: nowrap;
+ border: 0;
+ }
-.tool-stage {
- display: grid;
- grid-template-columns: repeat(2, minmax(0, 1fr));
- gap: var(--space-4);
- padding: var(--space-4);
- align-items: start;
- width: 100%;
-}
-
-.tool-stage.single {
- grid-template-columns: 1fr;
-}
-
-.tool-stage .cell {
- min-width: 0;
- display: flex;
- flex-direction: column;
- gap: var(--space-2);
-}
-
-.tool-stage .cell + .cell {
- border-left: 1px solid var(--border);
- padding-left: var(--space-4);
-}
-
-.cell-media {
- flex: 1;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- min-height: 14rem;
- position: relative;
- width: 100%;
-}
-
-.chain-stack {
- display: flex;
- flex-direction: column;
- gap: var(--space-3);
- margin-top: var(--space-4);
-}
-
-.edge-legend {
- position: absolute;
- top: -0.65em;
- padding: 0 var(--space-2);
- background: var(--surface);
- color: var(--text-muted);
- font-size: var(--text-xs);
- text-transform: uppercase;
- letter-spacing: 0.04em;
- line-height: 1.3;
- white-space: nowrap;
- z-index: 1;
-}
-
-.params-sep {
- position: relative;
- border-top: 1px solid var(--border);
- margin: 0 var(--space-4);
- padding-bottom: var(--space-3);
-}
-
-/* Панель этапа для инлайн-раскладки: исходник | параметры | результат */
-.pane {
- position: relative;
- display: flex;
- flex-direction: column;
- min-width: 0;
-}
-
-.pane-legend {
- top: -0.65em;
- left: 50%;
- transform: translateX(-50%);
-}
-
-.actions-row {
- display: flex;
- gap: var(--space-1);
- align-items: stretch;
- width: 100%;
-}
-
-.actions-row > button {
- flex: 1;
- min-width: 0;
-}
-
-@media (max-width: 48rem) {
.tool-stage {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: var(--space-4);
+ padding: var(--space-4);
+ align-items: start;
+ width: 100%;
+ }
+
+ .tool-stage.single {
grid-template-columns: 1fr;
}
+ .tool-stage .cell {
+ min-width: 0;
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-2);
+ }
+
.tool-stage .cell + .cell {
- border-left: none;
- padding-left: 0;
+ border-left: 1px solid var(--border);
+ padding-left: var(--space-4);
+ }
+
+ .cell-media {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ min-height: 14rem;
+ position: relative;
+ width: 100%;
+ }
+
+ .chain-stack {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-3);
+ margin-top: var(--space-4);
+ }
+
+ .edge-legend {
+ position: absolute;
+ top: -0.65em;
+ padding: 0 var(--space-2);
+ background: var(--surface);
+ color: var(--text-muted);
+ font-size: var(--text-xs);
+ text-transform: uppercase;
+ letter-spacing: 0.04em;
+ line-height: 1.3;
+ white-space: nowrap;
+ z-index: 1;
+ }
+
+ .params-sep {
+ position: relative;
border-top: 1px solid var(--border);
- padding-top: var(--space-4);
+ margin: 0 var(--space-4);
+ padding-bottom: var(--space-3);
+ }
+
+ /* Панель этапа для инлайн-раскладки: исходник | параметры | результат */
+ .pane {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ min-width: 0;
+ }
+
+ .pane-legend {
+ top: -0.65em;
+ left: 50%;
+ transform: translateX(-50%);
+ }
+
+ .actions-row {
+ display: flex;
+ gap: var(--space-1);
+ align-items: stretch;
+ width: 100%;
+ }
+
+ .actions-row > button {
+ flex: 1;
+ min-width: 0;
+ }
+
+ @media (max-width: 48rem) {
+ .tool-stage {
+ grid-template-columns: 1fr;
+ }
+
+ .tool-stage .cell + .cell {
+ border-left: none;
+ padding-left: 0;
+ border-top: 1px solid var(--border);
+ padding-top: var(--space-4);
+ }
}
}
diff --git a/web/src/lib/components/kit/ToolCard.svelte b/web/src/lib/components/kit/ToolCard.svelte
index fafa6d1..57c7c69 100644
--- a/web/src/lib/components/kit/ToolCard.svelte
+++ b/web/src/lib/components/kit/ToolCard.svelte
@@ -1,8 +1,8 @@
-
+
{#if icon}{/if}
{title}
diff --git a/web/src/lib/components/kit/TopBar.svelte b/web/src/lib/components/kit/TopBar.svelte
index 6e4b3b9..aa77b10 100644
--- a/web/src/lib/components/kit/TopBar.svelte
+++ b/web/src/lib/components/kit/TopBar.svelte
@@ -77,6 +77,7 @@
gap: 10px;
font: 600 14px var(--font-mono);
color: var(--foreground);
+ text-decoration: none;
}
.brand-mark {
background: var(--blue);
@@ -101,7 +102,7 @@
}
.nav a {
color: var(--muted);
- font: 12px var(--font-mono);
+ font: 10px var(--font-mono);
text-decoration: none;
}
.nav a:hover {
diff --git a/web/src/lib/styles/design2.css b/web/src/lib/styles/design2.css
index a22e745..be74c5f 100644
--- a/web/src/lib/styles/design2.css
+++ b/web/src/lib/styles/design2.css
@@ -5,76 +5,104 @@
@import "@fontsource/ibm-plex-mono/400.css";
@import "@fontsource/ibm-plex-mono/500.css";
-/* Минимальный reset (новый дизайн грузится только на preview-маршрутах). */
-*,
-*::before,
-*::after {
- box-sizing: border-box;
-}
-html,
-body {
- margin: 0;
- padding: 0;
-}
-
-/* Базовая типографика и палитра: без этого весь текст наследует UA-дефолт
- (Times New Roman / чёрный), что даёт сотни расхождений с рефом. */
-html {
- color-scheme: light dark;
- font-family: var(--font-sans);
- -webkit-text-size-adjust: 100%;
-}
-body {
- font-family: var(--font-sans);
- color: var(--foreground);
- background: var(--background);
- -webkit-font-smoothing: antialiased;
- text-rendering: optimizeLegibility;
-}
-
-/* Форм-контролы не наследуют шрифт семьи по умолчанию (браузер ставит Arial). */
-button,
-input,
-select,
-textarea,
-optgroup {
- font: inherit;
-}
-
-/* Новый дизайн: токены (§2 плана redesign).
- Файл грузится только layout'ом новой ветки (preview/+layout), поэтому
- глобально не конфликтует со старым app.css. */
-
-:root {
- --font-sans: "IBM Plex Sans", sans-serif;
- --font-mono: "IBM Plex Mono", monospace;
-
- --background: #eef1f4;
- --panel: #f8fafb;
- --foreground: #17212b;
- --muted: #6d7883;
- --line: #cbd3da;
-
- --blue: #1769d2;
- --cyan: #00a8c7;
- --amber: #bd7411;
- --success: rgb(35, 131, 84);
- --success2: rgb(37, 169, 106);
- --danger: #e5484d;
-
- --radius: 4px;
-}
-
-[data-theme="dark"] {
- --background: #11171d;
- --panel: #182129;
- --foreground: #e8eef2;
- --muted: #91a0ac;
- --line: #33414c;
-
- --blue: #54a2ff;
- --cyan: #00a8c7;
- --amber: #bd7411;
- --success: #25a96a;
- --danger: #ff7479;
+/* Порядок cascade-слоёв (дубль из корневого +layout.svelte — идемпотентен и
+ страхует случай, когда design2.css грузится раньше корневого layout): */
+@layer app, design;
+
+/* Новый дизайн — слой design. В слое design правила выигрывают у того же
+ правила из слоя app (старый app.css) по слою, а не по порядку ,
+ поэтому утечка app.css на /preview при SPA-переходе не перебивает design2. */
+@layer design {
+ /* Минимальный reset (новый дизайн грузится только на preview-маршрутах). */
+ *,
+ *::before,
+ *::after {
+ box-sizing: border-box;
+ }
+ html,
+ body {
+ margin: 0;
+ padding: 0;
+ }
+
+ /* Базовая типографика и палитра: без этого весь текст наследует UA-дефолт
+ (Times New Roman / чёрный), что даёт сотни расхождений с рефом. */
+ html {
+ color-scheme: light dark;
+ font-family: var(--font-sans);
+ -webkit-text-size-adjust: 100%;
+ }
+ body {
+ font-family: var(--font-sans);
+ color: var(--foreground);
+ background: var(--background);
+ -webkit-font-smoothing: antialiased;
+ text-rendering: optimizeLegibility;
+ }
+
+ /* Форм-контролы не наследуют шрифт семьи по умолчанию (браузер ставит Arial). */
+ button,
+ input,
+ select,
+ textarea,
+ optgroup {
+ font: inherit;
+ }
+
+ /* Перебивки глобальных правил старого app.css (слой app ниже), чтобы при
+ утечке app.css на /preview новый дизайн не получал старую типографику.
+ Пустые — заполни при необходимости. */
+ a,
+ a:hover {
+ /* color: inherit; text-decoration: none; */
+ }
+ h1,
+ h2 {
+ /* font-size: revert; line-height: revert; letter-spacing: revert; */
+ }
+ p,
+ label {
+ /* font-size: revert; */
+ }
+ :focus-visible {
+ /* outline: revert; */
+ }
+
+ /* Новый дизайн: токены (§2 плана redesign).
+ Файл грузится только layout'ом новой ветки (preview/+layout), поэтому
+ глобально не конфликтует со старым app.css. */
+
+ :root {
+ --font-sans: "IBM Plex Sans", sans-serif;
+ --font-mono: "IBM Plex Mono", monospace;
+
+ --background: #eef1f4;
+ --panel: #f8fafb;
+ --foreground: #17212b;
+ --muted: #6d7883;
+ --line: #cbd3da;
+
+ --blue: #1769d2;
+ --cyan: #00a8c7;
+ --amber: #bd7411;
+ --success: rgb(35, 131, 84);
+ --success2: rgb(37, 169, 106);
+ --danger: #e5484d;
+
+ --radius: 4px;
+ }
+
+ [data-theme="dark"] {
+ --background: #11171d;
+ --panel: #182129;
+ --foreground: #e8eef2;
+ --muted: #91a0ac;
+ --line: #33414c;
+
+ --blue: #54a2ff;
+ --cyan: #00a8c7;
+ --amber: #bd7411;
+ --success: #25a96a;
+ --danger: #ff7479;
+ }
}
diff --git a/web/src/routes/+layout.svelte b/web/src/routes/+layout.svelte
index 9300351..da1132f 100644
--- a/web/src/routes/+layout.svelte
+++ b/web/src/routes/+layout.svelte
@@ -17,3 +17,12 @@
{@render children()}
+
+
diff --git a/web/src/routes/preview/kit/+page.svelte b/web/src/routes/preview/kit/+page.svelte
index 0459dff..7998a7a 100644
--- a/web/src/routes/preview/kit/+page.svelte
+++ b/web/src/routes/preview/kit/+page.svelte
@@ -1,29 +1,29 @@