diff --git a/agentic/replay/32ddc883c4362b19704486dd14d5fe76f672e02d9bd0633ed5a4d9f36794f5b2.json b/agentic/replay/32ddc883c4362b19704486dd14d5fe76f672e02d9bd0633ed5a4d9f36794f5b2.json index 890b7856a..83ad63f55 100644 --- a/agentic/replay/32ddc883c4362b19704486dd14d5fe76f672e02d9bd0633ed5a4d9f36794f5b2.json +++ b/agentic/replay/32ddc883c4362b19704486dd14d5fe76f672e02d9bd0633ed5a4d9f36794f5b2.json @@ -1,14 +1,14 @@ { "schemaVersion": "trace-1", - "createdAt": "2026-10-06T08:52:18.270Z", + "createdAt": "2026-10-09T17:44:08.490Z", "payload": { "actions": [ { "name": "tap", - "summary": "tap combobox \"Idioma de Salida IA\"", + "summary": "tap combobox \"Idioma de salida IA\"", "target": { "role": "combobox", - "name": "Idioma de Salida IA" + "name": "Idioma de salida IA" } }, { @@ -32,7 +32,7 @@ "callIndex": 0, "agent": "default" }, - "summary": "Changed “Idioma de Salida IA” to Magyar. The settings page now visibly shows Magyar as the selected output language.", + "summary": "Set AI output language to Magyar. The 'Idioma de salida IA' combobox now displays 'Magyar'.", "startPath": "/app/settings", "endPath": "/app/settings", "endAnchors": [ @@ -45,6 +45,6 @@ "text": "English" } ], - "endWaitMs": 17571 + "endWaitMs": 12369 } } diff --git a/agentic/replay/67db0f98d129349c3fc659ab0da340448eafbee4174149a9cd7c2643da67f437.json b/agentic/replay/67db0f98d129349c3fc659ab0da340448eafbee4174149a9cd7c2643da67f437.json index 456f5be49..a38eaea22 100644 --- a/agentic/replay/67db0f98d129349c3fc659ab0da340448eafbee4174149a9cd7c2643da67f437.json +++ b/agentic/replay/67db0f98d129349c3fc659ab0da340448eafbee4174149a9cd7c2643da67f437.json @@ -1,14 +1,14 @@ { "schemaVersion": "trace-1", - "createdAt": "2026-10-06T08:52:21.873Z", + "createdAt": "2026-10-09T17:40:45.020Z", "payload": { "actions": [ { "name": "tap", - "summary": "tap combobox \"Idioma de Salida IA\"", + "summary": "tap combobox \"Idioma de salida IA\"", "target": { "role": "combobox", - "name": "Idioma de Salida IA" + "name": "Idioma de salida IA" } }, { @@ -32,7 +32,7 @@ "callIndex": 0, "agent": "default" }, - "summary": "Set the AI Output Language to Magyar. The language list shows Magyar as selected.", + "summary": "Successfully set the AI output language to Magyar. The settings page shows the 'Idioma de salida IA' combobox now displays 'Magyar' as the selected value.", "startPath": "/app/settings", "endPath": "/app/settings", "endAnchors": [ @@ -45,6 +45,6 @@ "text": "English" } ], - "endWaitMs": 14920 + "endWaitMs": 12464 } } diff --git a/agentic/replay/d15dccac4ac18fdabdb7d4c086d281513bd8832a317f7d3d65ac987fec417dc4.json b/agentic/replay/d15dccac4ac18fdabdb7d4c086d281513bd8832a317f7d3d65ac987fec417dc4.json index 7d4ec48c6..36e643e31 100644 --- a/agentic/replay/d15dccac4ac18fdabdb7d4c086d281513bd8832a317f7d3d65ac987fec417dc4.json +++ b/agentic/replay/d15dccac4ac18fdabdb7d4c086d281513bd8832a317f7d3d65ac987fec417dc4.json @@ -1,6 +1,6 @@ { "schemaVersion": "trace-1", - "createdAt": "2026-10-09T07:52:16.070Z", + "createdAt": "2026-10-10T15:58:12.314Z", "payload": { "actions": [ { @@ -32,7 +32,7 @@ "callIndex": 0, "agent": "default" }, - "summary": "The interface language has been switched to Español. The settings page now displays all labels in Spanish (Ajustes, Idioma de Interfaz, Suscripción, Cuenta, Zona de peligro, etc.) and the Interface Language combobox shows Español as the selected value.", + "summary": "Switched the interface language to Español. The settings page now displays all labels in Spanish (e.g., 'Idioma de interfaz', 'Ajustes', 'Iniciar debate', 'Cerrar sesión'), and the Interface Language combobox shows 'Español'.", "startPath": "/app/settings", "endPath": "/app/settings", "endAnchors": [ @@ -61,7 +61,7 @@ "name": "Ajustes globales" }, { - "text": "Idioma de Interfaz" + "text": "Idioma de interfaz" }, { "text": "Español" @@ -99,6 +99,6 @@ "text": "AI Output Language" } ], - "endWaitMs": 28157 + "endWaitMs": 12842 } } diff --git a/e2e/first-run.spec.ts b/e2e/first-run.spec.ts index 193eeccf5..9e8bf45f5 100644 --- a/e2e/first-run.spec.ts +++ b/e2e/first-run.spec.ts @@ -30,10 +30,10 @@ const copy = { motionField: 'Tétel', library: 'Forráskönyvtár', generate: 'Generálás', - ready: 'Készen Állsz!', + ready: 'Készen állsz!', finish: 'Kezdjük!', - generateMotion: 'Indítvány / Téma *', - generateSubmit: 'Vitaeset Generálása', + generateMotion: 'Indítvány / téma *', + generateSubmit: 'Vitaeset generálása', offerTitle: 'Indítasz egy vitát ezzel a tétellel?', workingIn: 'Munka ebben:', tourMotion: 'Ez a Ház betiltaná a házi feladatot', diff --git a/e2e/full-stack/first-run.spec.ts b/e2e/full-stack/first-run.spec.ts index 8c6f8dbe7..ac7c9a53f 100644 --- a/e2e/full-stack/first-run.spec.ts +++ b/e2e/full-stack/first-run.spec.ts @@ -36,9 +36,9 @@ const copy = { library: "Forráskönyvtár", generate: "Generálás", finish: "Kezdjük!", - skip: "Túra Kihagyása", - generateMotion: "Indítvány / Téma *", - generateSubmit: "Vitaeset Generálása", + skip: "Túra kihagyása", + generateMotion: "Indítvány / téma *", + generateSubmit: "Vitaeset generálása", offerTitle: "Indítasz egy vitát ezzel a tétellel?", workingIn: "Munka ebben:", tourMotion: "Ez a Ház betiltaná a házi feladatot", diff --git a/e2e/hungarian-parity.spec.ts b/e2e/hungarian-parity.spec.ts index c51d40948..8a0bc902d 100644 --- a/e2e/hungarian-parity.spec.ts +++ b/e2e/hungarian-parity.spec.ts @@ -46,8 +46,8 @@ test.describe('Hungarian parity', () => { for (const [path, heading] of [ ['/login', 'Bejelentkezés a DebateLab-ba'], - ['/terms', 'Felhasználási Feltételek'], - ['/privacy', 'Adatvédelmi Irányelvek'], + ['/terms', 'Felhasználási feltételek'], + ['/privacy', 'Adatvédelmi irányelvek'], ['/expocit/sources', 'Források'], ] as const) { await page.goto(path, { waitUntil: 'domcontentloaded' }); @@ -77,7 +77,7 @@ test.describe('Hungarian parity', () => { await expect(page.getByRole('heading', { name: 'Felkészülés' })).toBeVisible({ timeout: 15_000 }); await expect(page.locator('.react-joyride__tooltip')).toContainText('Üdvözlünk a DebateLab-ban!', { timeout: 15_000 }); await expect(page.locator('[data-test-id="button-primary"]')).toContainText('Következő'); - await expect(page.locator('[data-test-id="button-skip"]')).toContainText('Túra Kihagyása'); + await expect(page.locator('[data-test-id="button-skip"]')).toContainText('Túra kihagyása'); await page.locator('[data-test-id="button-skip"]').click(); for (const [route, heading] of [ @@ -131,15 +131,15 @@ test.describe('Hungarian parity', () => { await page.goto('/app/library', { waitUntil: 'domcontentloaded' }); await expect(page.getByRole('textbox', { name: 'Források keresése' })).toBeVisible(); - await page.getByRole('button', { name: 'Forrás Hozzáadása' }).click(); + await page.getByRole('button', { name: 'Forrás hozzáadása' }).click(); await expect(page.getByText('Importálás URL-ből')).toBeVisible(); - await page.getByRole('button', { name: 'Mentés a Könyvtárba' }).click(); + await page.getByRole('button', { name: 'Mentés a könyvtárba' }).click(); // Required fields are checked inline, in Hungarian. await expect(page.getByRole('alert').filter({ hasText: 'Adj meg egy címet.' })).toBeVisible(); await page.goto('/app/flowchart', { waitUntil: 'domcontentloaded' }); await page.getByRole('button', { name: 'Betöltés' }).click(); - await expect(page.getByRole('dialog', { name: 'Mentett Folyamatábrák' }).getByRole('button', { name: 'Bezárás' })).toBeVisible(); + await expect(page.getByRole('dialog', { name: 'Mentett folyamatábrák' }).getByRole('button', { name: 'Bezárás' })).toBeVisible(); await page.keyboard.press('Escape'); // Deleting a node raises a toast, whose close button is localized too. await page.getByText('1. érv', { exact: true }).click(); diff --git a/package-lock.json b/package-lock.json index 676679f65..1201b372f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "packages/*" ], "dependencies": { + "@fontsource-variable/inter": "5.3.0", "@hocuspocus/provider": "4.7.0", "@hocuspocus/server": "4.7.0", "@hookform/resolvers": "5.4.0", @@ -1204,6 +1205,15 @@ "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==", "license": "MIT" }, + "node_modules/@fontsource-variable/inter": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@fontsource-variable/inter/-/inter-5.3.0.tgz", + "integrity": "sha512-OupL48va4JNofb97w6NYeF9S7W/kHNKM0Er8Dem5nqi4jeOLrVJDoE8tZEpnMJmtkvNbB1EIPPwHcdkF6b1oUA==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, "node_modules/@gilbarbara/deep-equal": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/@gilbarbara/deep-equal/-/deep-equal-0.3.1.tgz", diff --git a/package.json b/package.json index 342df9ebe..ad7b238aa 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "verify:full": "npm run verify && P=$(bash .superset/e2e-port.sh) && CI=1 PORT=$P PLAYWRIGHT_BASE_URL=http://127.0.0.1:$P npm run test:e2e" }, "dependencies": { + "@fontsource-variable/inter": "5.3.0", "@hocuspocus/provider": "4.7.0", "@hocuspocus/server": "4.7.0", "@hookform/resolvers": "5.4.0", diff --git a/plans/2026-10-09-design-audit.md b/plans/2026-10-09-design-audit.md index c0e5a97d3..b2dfb5bc5 100644 --- a/plans/2026-10-09-design-audit.md +++ b/plans/2026-10-09-design-audit.md @@ -1,6 +1,6 @@ # Design audit: Apple design and Emil Kowalski standards -**Status:** In progress. Audit of `main` at `56a94d7` on 2026-10-09, shipping as the four-PR stack in [Suggested order of work](#suggested-order-of-work). Rows marked ✅ are done, with the PR that did them; rows marked ➖ were changed or skipped, with the reason in the row. +**Status:** Shipped. Audit of `main` at `56a94d7` on 2026-10-09, fixed in the four-PR stack in [Suggested order of work](#suggested-order-of-work). Rows marked ✅ are done, with the PR that did them; no rows were skipped. ## What this is @@ -57,7 +57,7 @@ These are ordered by impact per line changed. 7. ✅ PR2. Wayfinding: make "Current debate" a link that is active on `/app/debate` and every tool route. Rename "Home". Make the brand a ``. 8. ✅ PR2–3. One delete rule: reversible deletes act at once and show an Undo toast; irreversible bulk deletes use an AlertDialog with a destructive-styled confirm. 9. ✅ PR3. Ask: scroll only when the user is at the bottom, keep the input editable while streaming, add Stop. Give the four form tools a Cancel. -10. Landing: remove the infinite loops and shimmer, split the framer reveal from the hover onto separate elements, and switch the `x`/`y` shorthands to full `transform` strings. +10. ✅ PR4. Landing: remove the infinite loops and shimmer, split the framer reveal from the hover onto separate elements, and switch the `x`/`y` shorthands to full `transform` strings. ## Findings @@ -83,16 +83,16 @@ Rows follow Emil Kowalski's required Before / After / Why format. The tiers foll | ✅ PR3 · `TimerPage.tsx:330` | Ring `transition-all duration-1000` with the default ease | `transition-[clip-path] duration-1000 ease-linear` | Constant motion is linear (measured surge-and-stop). | | ✅ PR2 · `HistoryPage.tsx:156`, `tabs.tsx:45` | Each tab switch replays a slide-in plus a 350ms `stagger-enter` plus the empty state's blur swap | 150ms fade on the tab content only; no stagger | Tens of times a day, and Radix tabs switch on arrow keys. | | ✅ PR2 · `HomePage.tsx:61`, `CurrentDebatePage.tsx:198`, `AppLayout.tsx:44`, `:259`, `:300-301`, `CurrentDebateMenu.tsx:27` | `.motion-press` button lift on list rows, nav links and footer text links | `hover:bg-accent` only on rows and links | Hairline rows lift and cast shadows like cards. | -| `landing/WhatsNew.tsx:41-47`, `Pricing.tsx:48-53`, `:81-86`, `Features.tsx:56-62`, `UseCases.tsx:49-55` | Framer `whileInView` `y` on the same node as `hover:-translate-y-1` and `transition-all duration-300/500` | Reveal on an outer `motion.div`, hover on an inner div; drop the lift and tilt; transition only `box-shadow` and `border-color` | Confirmed above: the hovers never render, and `transition-all` drags out framer's own reveal. | -| `index.css:174-201` (`Hero.tsx:74`, `:81`, `Navbar.tsx:57`, `:98`, `Pricing.tsx:75`, `:113`) | `.btn-primary-gradient` and `.btn-secondary-outline` have no `:active`; they are Links, so `.motion-press` never applies | `:active { transform: scale(0.97) }` with `transition: transform 160ms var(--motion-ease-fluid)` | The main conversion buttons give no feedback on press. | -| `landing/Footer.tsx:11`, `:24`, `:27` | `` | `` | Every footer click reloads the whole SPA through the startup shell. | +| ✅ PR4 · `landing/WhatsNew.tsx:41-47`, `Pricing.tsx:48-53`, `:81-86`, `Features.tsx:56-62`, `UseCases.tsx:49-55` | Framer `whileInView` `y` on the same node as `hover:-translate-y-1` and `transition-all duration-300/500` | Reveal on an outer `motion.div`, hover on an inner div; drop the lift and tilt; transition only `box-shadow` and `border-color` | Confirmed above: the hovers never render, and `transition-all` drags out framer's own reveal. | +| ✅ PR4 · `index.css:174-201` (`Hero.tsx:74`, `:81`, `Navbar.tsx:57`, `:98`, `Pricing.tsx:75`, `:113`) | `.btn-primary-gradient` and `.btn-secondary-outline` have no `:active`; they are Links, so `.motion-press` never applies | `:active { transform: scale(0.97) }` with `transition: transform 160ms var(--motion-ease-fluid)` | The main conversion buttons give no feedback on press. | +| ✅ PR4 · `landing/Footer.tsx:11`, `:24`, `:27` | `` | `` | Every footer click reloads the whole SPA through the startup shell. | ### 2. Missed simplifications | Where | Before | After | Why | | --- | --- | --- | --- | -| `index.css:203-254`; `Hero.tsx:26-29`, `Features.tsx:37-38`, `Pricing.tsx:29`, `FinalCTA.tsx:14-17` | Infinite aurora (14s), orbs (8s and 10s), full-bleed grid drift (20s) on five sections | Static backgrounds | Apple §14 flags full-viewport moving backgrounds and slow oscillations (0.07–0.125 Hz). | -| `index.css:207-213`; `Navbar.tsx:57`, `:98`, `Hero.tsx:74`, `Pricing.tsx:113`, `FinalCTA.tsx:45`, `GenerateForm.tsx:286` | `shimmer 3s infinite` on CTAs, including the app's Generate button | Delete it, or run once on gated hover at 700ms | Perpetual decoration on the most-watched element. | +| ✅ PR4 · `index.css:203-254`; `Hero.tsx:26-29`, `Features.tsx:37-38`, `Pricing.tsx:29`, `FinalCTA.tsx:14-17` | Infinite aurora (14s), orbs (8s and 10s), full-bleed grid drift (20s) on five sections | Static backgrounds | Apple §14 flags full-viewport moving backgrounds and slow oscillations (0.07–0.125 Hz). | +| ✅ PR3–4 · `index.css:207-213`; `Navbar.tsx:57`, `:98`, `Hero.tsx:74`, `Pricing.tsx:113`, `FinalCTA.tsx:45`, `GenerateForm.tsx:286` | `shimmer 3s infinite` on CTAs, including the app's Generate button | Delete it, or run once on gated hover at 700ms | Perpetual decoration on the most-watched element. | | ✅ PR3 · `streaming-indicator.tsx:25-63`; wrappers at `AttackPage.tsx:139`, `BrainstormPage.tsx:127`, `GeneratePage.tsx:384`, `FactoidsPage.tsx:145` | Five simultaneous loops: wrapper shimmer, inner swap, ping ring, shimmer bar, and a fake progress bar that fills to 100% every 3s via a 50ms `setInterval` | One spinner (`spin 600ms linear infinite`) plus one honest status line | Decorative loops, a dishonest determinate bar, and 20 renders a second. | | ✅ PR3 · `loading-spinner.tsx:18-20` | Ping ring plus static ring plus a 1s spin | One ring at `spin 700ms linear` | A faster linear spin reads as a faster load. | | ✅ PR1 · `index.css:724-738` | `[data-radix-collection-item]` menu-item-in keyframe | Delete it | Every SelectItem, TabsTrigger and AccordionTrigger slides 4px on every open. | @@ -108,16 +108,16 @@ Rows follow Emil Kowalski's required Before / After / Why format. The tiers foll | ✅ PR2 · `LibraryPage.tsx:495-506` vs `:777-790`; `:675-684` vs `:723-733`; `:583-596` | Two folder filters; two Folder selects in one form; three stat tiles repeating a badge count | One chip row; one Folder select; one meta line under the h1 | Duplicate controls bound to the same state. | | ✅ PR3 · `LibraryPage.tsx:248`, `:404-407`, `:435-438` and success toasts at `AttackPage.tsx:49`, `BrainstormPage.tsx:48`, `GeneratePage.tsx:97`, `FactoidsPage.tsx:47` | Toasts confirming things already visible | Drop them; keep toasts for off-screen or irreversible results | Over-feedback trains people to ignore feedback (Apple §13). | | ✅ PR3 · `ExportToolbar.tsx:15`, `:54`, `AutoSavedSourcesUndo.tsx` | `showUndo` is never passed by any caller | Delete both | Dead code with a misleading label. | -| `landing/WhatsNew.tsx:27-33`, `SocialProof.tsx:7-11` | Pill and h2 both say "What's New"; "2 Plans / 1 Unified Workspace" styled as metrics | Remove the pill; remove the section or use real facts | Repetition; pseudo social proof (Apple Responsibility). | +| ✅ PR4 · `landing/WhatsNew.tsx:27-33`, `SocialProof.tsx:7-11` | Pill and h2 both say "What's New"; "2 Plans / 1 Unified Workspace" styled as metrics | Remove the pill; remove the section or use real facts | Repetition; pseudo social proof (Apple Responsibility). | ### 3. Performance | Where | Before | After | Why | | --- | --- | --- | --- | | ✅ PR1 · `index.css:278` | `will-change: transform` on every `.motion-press` | Remove it | A compositor layer for every button in the app. | -| `index.css:1-3`, `:57` | Three render-blocking Google Fonts `@import`s, including Lora (0 uses) | Drop Lora and Space Mono, or self-host Inter variable with a metric-matched fallback; or use `system-ui` (Apple §15) | A chained blocking request and a visible font swap: the startup shell renders in `system-ui`. | -| `Hero.tsx:36-161`, `Navbar.tsx:24`, `DeepDive.tsx:20`, `:55`, every `whileInView y` | Framer `x`/`y`/`scale` shorthands at 0.5–0.8s, delays to 0.5s, during page load | `transform: "translateY(12px)"` strings, `duration: 0.35, ease: [0.23,1,0.32,1]`, hero delays 0–0.15s, `viewport: { once: true, margin: "0px 0px -15% 0px" }` | Shorthands run on the main thread and drop frames while loading; the CTA is held back about a second. | -| `Hero.tsx:167-178`, `WhatsNew.tsx:47`, `Pricing.tsx:53`, `:86`, `index.css:165`, `:220`, `:271` | Nested translucent blur layers over animated backgrounds; grid animates `background-position` | Solid cards; blur only on the nav; static grid | Never stack light translucent surfaces (Apple §12); full-section repaints every frame. | +| ✅ PR4 · `index.css:1-3`, `:57` | Three render-blocking Google Fonts `@import`s, including Lora (0 uses) | Drop Lora and Space Mono, or self-host Inter variable with a metric-matched fallback; or use `system-ui` (Apple §15) | A chained blocking request and a visible font swap: the startup shell renders in `system-ui`. | +| ✅ PR4 · `Hero.tsx:36-161`, `Navbar.tsx:24`, `DeepDive.tsx:20`, `:55`, every `whileInView y` | Framer `x`/`y`/`scale` shorthands at 0.5–0.8s, delays to 0.5s, during page load | `transform: "translateY(12px)"` strings, `duration: 0.35, ease: [0.23,1,0.32,1]`, hero delays 0–0.15s, `viewport: { once: true, margin: "0px 0px -15% 0px" }` | Shorthands run on the main thread and drop frames while loading; the CTA is held back about a second. | +| ✅ PR4 · `Hero.tsx:167-178`, `WhatsNew.tsx:47`, `Pricing.tsx:53`, `:86`, `index.css:165`, `:220`, `:271` | Nested translucent blur layers over animated backgrounds; grid animates `background-position` | Solid cards; blur only on the nav; static grid | Never stack light translucent surfaces (Apple §12); full-section repaints every frame. | | ✅ PR3 · `AskPage.tsx:25-42`, `:334-340`, `AskChat.tsx:82`; `AttackPage.tsx:26`, `BrainstormPage.tsx:24`, `GeneratePage.tsx:35`, `FactoidsPage.tsx:24` | `useSessionStore()` with no selector; Ask remaps messages so `React.memo` never holds | `useShallow` selectors; pass messages through; a separate `StreamingBubble` | Every bubble re-parses Markdown on every 30ms flush. | | ✅ PR3 · `index.css:984-1001`, `:1052-1061`; `CrossfirePage.tsx:421-426` | Caret blink animates `box-shadow`; dead `max-height`/`padding` transition; `setNow` every 500ms re-renders the whole Crossfire page | Opacity-only caret with `steps(2)`; delete the dead rule; a `` with its own interval | Repaints while streaming; full renders during a live round. | | ✅ PR2 · `SettingsPage.tsx:324-326`, `AppLayout.tsx:276-285` | A 160px pulsing block for every user while the admin check runs; the announcement banner pushes content down after a fetch | Render nothing while pending; overlay the banner or render it from cache | Layout shift for 100% of users. | @@ -143,12 +143,12 @@ Rows follow Emil Kowalski's required Before / After / Why format. The tiers foll | --- | --- | --- | --- | | ✅ PR1 · `popover.tsx:20`, `select.tsx:69`, `tooltip.tsx:20` | `zoom-in-95` from the centre (measured) | `origin-[--radix-popover-content-transform-origin]`, and the same for Select and Tooltip | Trigger-anchored content scales from its trigger; modals stay centred. | | ✅ PR2 · `index.css:153`, `:271`, `AppLayout.tsx:178` | Sticky translucent header with a hard 1px border and a shadow | A scroll-edge fade, or a border only when `scrollY > 0` | Apple §12. | -| `index.css:491-847` overall | About 20 unlayered global selectors keyed on Radix internals | Move what's worth keeping into the primitives and delete the layer | They break silently on upgrades and override primitive classes. | +| ✅ PR1 · `index.css:491-847` overall | About 20 unlayered global selectors keyed on Radix internals | Move what's worth keeping into the primitives and delete the layer | They break silently on upgrades and override primitive classes. | | ✅ PR1 · `index.css:156-159`, `ThemeProvider.tsx:8-23` | `html`/`body` ease over 420ms, components at 160 or 260ms, everything else snaps | `document.startViewTransition` with a 200ms crossfade, or suppress transitions for one frame | The theme change ripples at three speeds. | | ✅ PR3 · `AskChat.tsx:693`, `GenerateForm.tsx:142-154`, `VoiceMicControl.tsx:41`; `TimerPage.tsx:334`, `SparringRound.tsx:150`, `:157`, `CrossfirePage.tsx:924-926` | Three listening indicators and three countdown urgency treatments; the Timer goes calm again at 0:00 | One `` and one ``: amber at 30s or less, destructive at 0:00 | Things that look the same should behave the same. | | ✅ PR3 · `TimerPage.tsx:334` | `font-mono` (Space Mono) with `tracking-tighter` on the digits | Inter with `tabular-nums` and `tracking-[-0.02em]` | The wide colon reads "4 : 00"; negative tracking on a monospace face fights its design. | -| `landing/*` section h2s; `Features.tsx:37` | Different sizes and tracking per section; the orb's `-translate-y-1/2` is overridden by its keyframe | One `.section-heading`; position the orb with `top` | Cohesion; the orb jumps under reduced motion. | -| ➖ PR2 · Login (`LoginPage.tsx:157-161`), app header logo | The login card shows a generic sparkle tile; the app shows the tree-and-scales logo in a white tile in both themes | The real mark on Login; a dark-mode variant of the logo tile | Brand consistency across surfaces. PR2 puts the real mark on Login and the public pages. The dark-mode tile is not done: `favicon-72.png` is RGB with no alpha, so the white is baked into the mark and a dark variant needs a new asset. | +| ✅ PR4 · `landing/*` section h2s; `Features.tsx:37` | Different sizes and tracking per section; the orb's `-translate-y-1/2` is overridden by its keyframe | One `.section-heading`; position the orb with `top` | Cohesion; the orb jumps under reduced motion. | +| ✅ PR2 · Login (`LoginPage.tsx:157-161`), app header logo | The login card shows a generic sparkle tile; the app shows the tree-and-scales logo in a white tile in both themes | The real mark on Login; a dark-mode variant of the logo tile | Brand consistency across surfaces. PR2 puts the real mark on Login and the public pages. The dark-mode tile stays as it is, by Adam's decision on 2026-10-11; no new asset. | ### 6. Accessibility @@ -158,10 +158,10 @@ Rows follow Emil Kowalski's required Before / After / Why format. The tiers foll | ✅ PR1 · `tailwind.config.ts:5-7`, `index.css` hover rules | No `hoverOnlyWhenSupported`; ungated `:hover` transforms | Turn on the flag; wrap the CSS hover rules | Sticky hover after taps. | | ✅ PR1 · `index.css` (none) | No `prefers-reduced-transparency`, `prefers-contrast` or `forced-colors` handling for the eleven `backdrop-blur` surfaces | Solid `--card` background with no blur under reduced transparency or more contrast, plus a defined border | Apple §14 lists three signals; only one is handled (confirmed above). | | ✅ PR1 · `index.css:131-133`, `index.html:5`, `index.css:377-403` | No `-webkit-tap-highlight-color`; `viewport-fit=cover` but `--native-safe-*` never set and nothing reads `env(safe-area-inset-*)` | `html { -webkit-tap-highlight-color: transparent }`; `--native-safe-top: env(safe-area-inset-top, 0px)` and so on; pad the bottom sheet, OfflineIndicator and Toaster | A grey flash over the press scale; UI under the home indicator in the PWA. | -| `button.tsx:22`, `:24`, `section-card.tsx:50`, `checkbox.tsx:14`, `dialog.tsx:48-49`, `sheet.tsx:63-64`, `LibraryPage.tsx:132`, `:769`, `DebateDialogs.tsx:96`, `FlowchartPage.tsx:169`, `:193`, landing `Navbar.tsx` toggle, `Hero.tsx:179-191` | `sm` 36px, `icon` 40px, 28–32px icon buttons (one a delete next to a Switch), a 16px dialog close, a 16px checkbox, 12px flowchart handles | `[@media(pointer:coarse)]:min-h-11 min-w-11` on the cva sizes; invisible hit-area pseudo-elements on Checkbox, the close button and handles | Seen on every mobile page: `sm` buttons are 36px tall and icon buttons 40px. | +| ✅ PR1–4 · `button.tsx:22`, `:24`, `section-card.tsx:50`, `checkbox.tsx:14`, `dialog.tsx:48-49`, `sheet.tsx:63-64`, `LibraryPage.tsx:132`, `:769`, `DebateDialogs.tsx:96`, `FlowchartPage.tsx:169`, `:193`, landing `Navbar.tsx` toggle, `Hero.tsx:179-191` | `sm` 36px, `icon` 40px, 28–32px icon buttons (one a delete next to a Switch), a 16px dialog close, a 16px checkbox, 12px flowchart handles | `[@media(pointer:coarse)]:min-h-11 min-w-11` on the cva sizes; invisible hit-area pseudo-elements on Checkbox, the close button and handles | Seen on every mobile page: `sm` buttons are 36px tall and icon buttons 40px. | | ✅ PR1 · `index.css:598-601`, `:502-507`; `dialog.tsx:48`, `sheet.tsx:63`, `select.tsx:20` | `!important` input focus is a 10%-alpha glow (about 1.1:1); global focus changes `border-radius`; some primitives use `focus:` not `focus-visible:` | One system: `outline: 2px solid hsl(var(--ring)); outline-offset: 2px` on `:focus-visible` | Fails WCAG 2.4.11; three focus systems compete. PR1: the global outline is the default, and primitives that draw `focus-visible:ring-*` turn it off, so nothing gets two rings. | | ✅ PR1 · `index.css:23-24`, `:35`, `:39`, `:104-108` | Light primary label 3.85:1; destructive labels 4.43:1 (light) and 3.43:1 (dark); `--input` borders 1.38:1 and 2.32:1 | `--primary: 200 98% 33%`; light `--destructive: 0 72% 47%`; dark `--destructive-foreground: 0 86% 10%`; light `--input: 215 16% 55%` | AA text and 3:1 for control boundaries. | -| `SettingsPage.tsx:37-42`, `:206`; `DeepDive.tsx:63`, `:78`; `LoginPage.tsx:268`, `:273` | White on amber or green-500 tier badges (about 2:1); near-white `text-accent` on a light card (about 2.5:1); hard-coded `text-green-600` (3.15:1) | Tinted badges (`bg-amber-500/15 text-amber-800 dark:text-amber-300`); `text-foreground`; a `--success` token | Contrast failures. | +| ✅ PR1–4 · `SettingsPage.tsx:37-42`, `:206`; `DeepDive.tsx:63`, `:78`; `LoginPage.tsx:268`, `:273` | White on amber or green-500 tier badges (about 2:1); near-white `text-accent` on a light card (about 2.5:1); hard-coded `text-green-600` (3.15:1) | Tinted badges (`bg-amber-500/15 text-amber-800 dark:text-amber-300`); `text-foreground`; a `--success` token | Contrast failures. | | ✅ PR2 · `LibraryPage.tsx:69-147`, `:627-638`, `:705-975` | A `role=button` row containing a link, a button and a switch; an invisible file input with no focus ring; labels without `htmlFor` | The title is the button; `has-[:focus-visible]:ring-2` on the label; matching `id`/`htmlFor` pairs | Nested interactive controls; invisible focus; detached labels. | | ✅ PR1 · `AskChat.tsx:102`, `FactoidsResults.tsx:59` | The bookmark and external-link icons appear only on hover | Always visible at 60% opacity, full on focus | Invisible on touch and to the keyboard. | | ✅ PR3 · `TimerPage.tsx:329`, `SparringRound.tsx:157`, `streaming-indicator.tsx:56` | `aria-live` on a clock that ticks every second; fake stage text announced every 3s | Announce 1:00, 0:30 and 0:00 only; `role="timer"`; announce only real status changes | Constant screen-reader chatter. | @@ -174,7 +174,7 @@ Rows follow Emil Kowalski's required Before / After / Why format. The tiers foll | --- | --- | --- | --- | | ✅ PR2 · `AppLayout.tsx:31-41`, `:185`, `:190-215`, `:199`; `HomePage.tsx:155` | Nav is Home, Current debate (a popover, never active), Library, History, Settings. Nothing is active on the eight tool routes. The brand is an `

`; Home's `

` is also "DebateLab" | "Current debate" becomes a link, active on `/app/debate` and every tool route, with the chevron as a separate trigger. Rename Home to "Tools". The brand becomes a ``. Home's h1 describes the page | "Where am I?" is unanswered on most screens; specific labels beat generic ones (Apple §16). | | ✅ PR2 · `NotFound.tsx:11-13`, `SourcesPage.tsx:129-162`, `App.tsx:153-158`, `PrivacyPage.tsx`, `TermsPage.tsx`, `LoginPage.tsx:150-284` | The 404 reloads to the marketing page; Sources, Legal and Login have no way home; the route error offers only Reload | ``; a header or back link on public pages; "Go to Home" on the error | Never trap the user. | -| `LibraryPage.tsx:129-140` vs `:1025-1044`; `DebateDialogs.tsx:104-110`; `HistoryPage.tsx:352-379`; `SettingsPage.tsx:386-454`; `LibraryPage.tsx:421`; `FlowchartPage.tsx:382-390` vs `:784-804`; `AdminPage.tsx:997-1012` | Five confirmation models. A source row deletes instantly, while the same source in the sheet asks. Saved flowcharts delete instantly, while nodes ask. `window.confirm`. Admin Ban and Make admin are one click | Reversible deletes act at once with `toast({ action: { label: 'Undo' } })`; irreversible ones get an AlertDialog with `buttonVariants({ variant: 'destructive' })`; admin role changes use the existing `pendingAction` dialog | Apple §16 Agency: undo for slips, confirmations used sparingly and consistently. PR2 draws the line at whether the app can put the item back exactly: Library sources delete at once with Undo (the flowchart's nodes and saved flowcharts follow in PR3); saved cases, sparring rounds, Clear Library and the Settings deletes use a destructive-styled confirm; a debate keeps its inline confirm, which already names what goes with it; admin role changes confirm through a dialog. | +| ✅ PR2–3 · `LibraryPage.tsx:129-140` vs `:1025-1044`; `DebateDialogs.tsx:104-110`; `HistoryPage.tsx:352-379`; `SettingsPage.tsx:386-454`; `LibraryPage.tsx:421`; `FlowchartPage.tsx:382-390` vs `:784-804`; `AdminPage.tsx:997-1012` | Five confirmation models. A source row deletes instantly, while the same source in the sheet asks. Saved flowcharts delete instantly, while nodes ask. `window.confirm`. Admin Ban and Make admin are one click | Reversible deletes act at once with `toast({ action: { label: 'Undo' } })`; irreversible ones get an AlertDialog with `buttonVariants({ variant: 'destructive' })`; admin role changes use the existing `pendingAction` dialog | Apple §16 Agency: undo for slips, confirmations used sparingly and consistently. PR2 draws the line at whether the app can put the item back exactly: Library sources delete at once with Undo (the flowchart's nodes and saved flowcharts follow in PR3); saved cases, sparring rounds, Clear Library and the Settings deletes use a destructive-styled confirm; a debate keeps its inline confirm, which already names what goes with it; admin role changes confirm through a dialog. | | ✅ PR2 · `LibraryPage.tsx:218-233`, `:442-444`; `LoginPage.tsx:74-133`, `:277-281`, `:57-143` | Required-field errors arrive as toasts after submit; login errors appear below the tabs, not tied to the field; raw backend `error.message` in English | Inline `role="alert"` under the field with `aria-describedby` and focus moved to it (as `DebateForm.tsx:115-118` already does); map Supabase error codes to i18n | Validate inline; localized errors. | | ✅ PR3 · `GenerateResults.tsx:55-57`, Attack, Brainstorm, Ask without citations | No AI caveat anywhere; Generate evidence reads as fact unless `needsSource` is set | One line under every result header: "AI-generated. Check facts and sources before using them in a round."; label evidence "Unverified" unless a researched source backs it | Apple §16.3 Responsibility. | | ✅ PR3 · `AskChat.tsx:64-70`, `:384-394`, `:118`; `streaming-indicator.tsx` | Timed status copy cycles ("Almost there…" back to "Thinking…"); "Typing" shows while web research runs | Real phases ("Searching the web…", "Writing"); otherwise "Working…" plus elapsed time after 5s | Status should be true. | @@ -188,7 +188,7 @@ Rows follow Emil Kowalski's required Before / After / Why format. The tiers foll | ✅ PR2 · `DebateDialogs.tsx:167-225`, `PricingModal.tsx:96`, `LimitReachedModal.tsx:39-47` | The 5-day idle gate blocks Escape on every route including Settings; "Current" is always on Free; the limit modal offers only Upgrade | Allow Escape to mean "Continue"; badge the real tier; add "Not now" | Never trap; true status; agency. | | ✅ PR2 · `SharedPage.tsx:75-148`, legal pages | Shared items render as raw JSON in a 600px scroll box; "Import to Account" is a dead end that also loses the item on sign-in; legal text at 14px and about 100 characters per line | Render with the result components at `max-w-[65ch] text-base leading-7`; remove Import until it exists; legal pages at `text-base leading-7 max-w-[65ch]` | Recipients get developer output; reading measure. | | ✅ PR1 · `index.html:16`, `public/manifest.webmanifest:9` | `theme-color` fixed at `#0f172a` | Light and dark `theme-color` metas, updated by ThemeProvider | Browser chrome stays dark over the light UI. | -| `src/locales/es.json`, `hu.json`; `Hero.tsx:41-51` | English Title Case carried into Spanish and Hungarian; the hero h1 steps from `text-4xl` to `text-7xl` with one tracking value; a 132-character Spanish announcement in a `rounded-full` pill | Sentence case in es and hu; `text-[clamp(2.25rem,1.2rem+4.5vw,4.5rem)] leading-[1.05] tracking-[-0.03em]`; `rounded-2xl` or shorter copy | Reads as machine-translated; size-specific tracking. | +| ✅ PR4 · `src/locales/es.json`, `hu.json`; `Hero.tsx:41-51` | English Title Case carried into Spanish and Hungarian; the hero h1 steps from `text-4xl` to `text-7xl` with one tracking value; a 132-character Spanish announcement in a `rounded-full` pill | Sentence case in es and hu; `text-[clamp(2.25rem,1.2rem+4.5vw,4.5rem)] leading-[1.05] tracking-[-0.03em]`; `rounded-2xl` or shorter copy | Reads as machine-translated; size-specific tracking. | ## Already done well diff --git a/plans/README.md b/plans/README.md index 84ac786bf..bd3b0a848 100644 --- a/plans/README.md +++ b/plans/README.md @@ -6,7 +6,6 @@ Every plan lives in this folder and opens with a `**Status:**` line; a plan list ## In progress -- **Design audit.** UI audit against Emil Kowalski's Apple-design and motion skills: broken toast stacking, collapsed text-colour tokens, a global CSS layer that overrides the primitives, touch and reduced-motion gaps, wayfinding and undo. Shipping as a four-PR stack: Foundations, Shell, AI tools, Landing. Plan: [2026-10-09 design audit](2026-10-09-design-audit.md). - **Staging that tracks main.** Audit of staging, the VPS sync timer, the seed, and what to delete. Plan: [2026-10-03 staging loop](2026-10-03-staging-loop.md). Previews come from #95 and the staging spec fix from #100. ## Backlog @@ -23,6 +22,7 @@ Every plan lives in this folder and opens with a `**Status:**` line; a plan list These plans are finished. Most of their files were removed and git history keeps them. The linked plans below are kept as the record, and each says `**Status:** Shipped`. +- Design audit: UI audit against Emil Kowalski's Apple-design and motion skills, shipped as a four-PR stack (Foundations #124, Shell #126, AI tools, Landing): fixed toast stacking, text-colour tokens, the global CSS override layer, touch and reduced-motion gaps, wayfinding, undo and the landing loops. Plan: [2026-10-09 design audit](2026-10-09-design-audit.md). - Missing translation keys: 90 literal keys and 40 count forms added to each catalog. Plan: [2026-10-02 missing translation keys](2026-10-02-missing-translation-keys.md). - Voice Sparring and Crossfire: #84. Plan kept as the record: [2026-10-01 voice sparring and crossfire](2026-10-01-voice-sparring-crossfire.md). - Debate-aware AI and smaller modules: #75. Plan kept as the record: [2026-09-25 debate AI quality modules](2026-09-25_074255-debate-ai-quality-modules.md). diff --git a/src/App.tsx b/src/App.tsx index 62bab0f70..6fe7c5d6a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,9 +1,9 @@ // src/App.tsx -import { Component, Suspense, lazy, useEffect, useState, type ErrorInfo, type ReactNode } from "react"; +import { Component, Suspense, lazy, useEffect, useRef, useState, type ErrorInfo, type ReactNode } from "react"; import { Toaster } from "@/components/ui/sonner"; import { TooltipProvider } from "@/components/ui/tooltip"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { BrowserRouter, Routes, Route, useLocation } from "react-router-dom"; +import { BrowserRouter, Routes, Route, useLocation, useNavigationType } from "react-router-dom"; import { MotionConfig } from "framer-motion"; import { AppLayout } from "@/components/layout/AppLayout"; import { AuthProvider, useAuth } from "@/lib/supabase/AuthProvider"; @@ -228,6 +228,25 @@ function TimedOnboardingTour() { ); } +/** + * A new page starts at the top. Router links keep the window's scroll position, so a + * footer link would open Terms at its bottom. Back and Forward (POP) keep the browser's + * own restoration, and #anchor links scroll themselves. + */ +function ScrollToTopOnNavigate(): null { + const { pathname, hash } = useLocation(); + const navigationType = useNavigationType(); + const previous = useRef({ pathname, hash }); + useEffect(() => { + // Only a new page: query-only updates (History's ?tab=) keep their place. + const changed = previous.current.pathname !== pathname || previous.current.hash !== hash; + previous.current = { pathname, hash }; + if (!changed || navigationType === "POP" || hash) return; + window.scrollTo(0, 0); + }, [pathname, hash, navigationType]); + return null; +} + function AppRoutes() { useSessionStorageSync(); @@ -290,6 +309,7 @@ const App = () => ( + diff --git a/src/components/landing/DeepDive.tsx b/src/components/landing/DeepDive.tsx index d339a3c42..34ff2b889 100644 --- a/src/components/landing/DeepDive.tsx +++ b/src/components/landing/DeepDive.tsx @@ -1,5 +1,5 @@ import { useTranslation } from "react-i18next"; -import { motion } from "framer-motion"; +import { Reveal } from "./Reveal"; import { Database, ScanSearch, Sparkles, Network } from "lucide-react"; const DeepDive = () => { @@ -16,29 +16,15 @@ const DeepDive = () => {
- -

- {t('deepDive.title')} -

+ +

{t('deepDive.title')}

{t('deepDive.subtitle')}

{steps.map((step, index) => ( - +
@@ -46,18 +32,12 @@ const DeepDive = () => {

{step.title}

{step.description}

-
+ ))}
- + - +
@@ -75,7 +55,7 @@ const DeepDive = () => { {t('deepDive.preview.vectorizing')}
- {t('deepDive.preview.query')} + {t('deepDive.preview.query')}
➜ @@ -90,7 +70,7 @@ const DeepDive = () => {
-
+
diff --git a/src/components/landing/FAQ.tsx b/src/components/landing/FAQ.tsx index fcc401a96..3f1795556 100644 --- a/src/components/landing/FAQ.tsx +++ b/src/components/landing/FAQ.tsx @@ -5,7 +5,7 @@ import { AccordionItem, AccordionTrigger, } from "@/components/ui/accordion"; -import { motion } from "framer-motion"; +import { Reveal } from "./Reveal"; const FAQ = () => { const { t } = useTranslation(); @@ -21,23 +21,12 @@ const FAQ = () => { return (
- -

{t('faq.title')}

+ +

{t('faq.title')}

{t('faq.subtitle')}

-
+ - + {faqs.map((faq, index) => ( @@ -48,7 +37,7 @@ const FAQ = () => { ))} - +
); diff --git a/src/components/landing/Features.tsx b/src/components/landing/Features.tsx index d26f46603..e9cfd286b 100644 --- a/src/components/landing/Features.tsx +++ b/src/components/landing/Features.tsx @@ -1,5 +1,5 @@ import { useTranslation } from "react-i18next"; -import { motion } from "framer-motion"; +import { Reveal } from "./Reveal"; import { BookOpen, Zap, @@ -34,39 +34,27 @@ const Features = () => { return (
-
+ {/* Positioned with top, not a translate, so nothing can override it. */} +
- -

- {t('features.title')} -

-
+ +

{t('features.title')}

+
{features.map((feature, index) => ( - -
- + +
+
+ +
+

{feature.title}

+

{feature.description}

-

{feature.title}

-

{feature.description}

- +
))}
diff --git a/src/components/landing/FinalCTA.tsx b/src/components/landing/FinalCTA.tsx index f174cd9d1..74dd06183 100644 --- a/src/components/landing/FinalCTA.tsx +++ b/src/components/landing/FinalCTA.tsx @@ -1,5 +1,5 @@ import { useTranslation } from "react-i18next"; -import { motion } from "framer-motion"; +import { Reveal } from "./Reveal"; import { ArrowRight, Sparkles } from "lucide-react"; import { Link } from "react-router-dom"; import { useRoutePrefetch } from "@/hooks/use-route-prefetch"; @@ -17,37 +17,25 @@ const FinalCTA = () => {
- +
{t('finalCta.freeTrialAvailable')}
-

- {t('finalCta.title')} -

+

{t('finalCta.title')}

{t('finalCta.subtitle')}

- - +
+ {t('finalCta.cta')} - - +
+
); diff --git a/src/components/landing/Footer.tsx b/src/components/landing/Footer.tsx index f8f7dbb45..30867ca0d 100644 --- a/src/components/landing/Footer.tsx +++ b/src/components/landing/Footer.tsx @@ -1,4 +1,5 @@ import { useTranslation } from "react-i18next"; +import { Link } from "react-router-dom"; const Footer = () => { const { t } = useTranslation(); @@ -8,7 +9,8 @@ const Footer = () => {
- + {/* In-app links: no full reload through the startup shell. */} + {t('common.logoAlt')} { className="h-8 w-8 rounded-lg border border-primary/20 object-cover" /> DebateLab - +
- + {t('footer.terms')} - - + + {t('footer.privacy')} - +

diff --git a/src/components/landing/Hero.tsx b/src/components/landing/Hero.tsx index 8d47e34fd..71fb6b8b1 100644 --- a/src/components/landing/Hero.tsx +++ b/src/components/landing/Hero.tsx @@ -1,6 +1,6 @@ import React from "react"; import { useTranslation } from "react-i18next"; -import { motion } from "framer-motion"; +import { Reveal } from "./Reveal"; import { ArrowRight, CheckCircle2, Sparkles } from "lucide-react"; import { Link } from "react-router-dom"; import { useRoutePrefetch } from "@/hooks/use-route-prefetch"; @@ -32,46 +32,31 @@ const Hero = () => {

- - - + + {/* rounded-2xl, not a pill: long Spanish and Hungarian copy wraps cleanly. */} + + {t('hero.announcement')} - - - - {t('hero.headline')} - - - - {t('hero.subheadline')} - - - + + + +

+ {t('hero.headline')} +

+
+ + +

+ {t('hero.subheadline')} +

+
+ + {t('hero.cta')} @@ -82,35 +67,25 @@ const Hero = () => { > {t('hero.ctaSecondary')} -
- - + + + {planHighlights.map((item) => ( -
+

{item.label}

{item.value}

))} - - - + + +
    {trustBullets.map((item) => (
  • {item}
  • ))} - +
@@ -157,14 +132,9 @@ const HeroPreview = () => { ]; return ( - +
-
+
{/* Browser chrome */}
@@ -174,18 +144,18 @@ const HeroPreview = () => {
-
-
+
+

{t(`features.${mode}`)}

-
+
{modes.map((item) => ( @@ -195,7 +165,7 @@ const HeroPreview = () => {
-
- + {t('hero.preview.sideLabel')} {t('hero.preview.sidePro')}
- {t('hero.preview.roleLabel')} + {t('hero.preview.roleLabel')} {t('hero.preview.roleValue')}
-
+

{sample.title}

-

{sample.firstLabel}

+

{sample.firstLabel}

{sample.first}

-

{sample.secondLabel}

+

{sample.secondLabel}

{sample.second}

- + ); }; diff --git a/src/components/landing/HowItWorks.tsx b/src/components/landing/HowItWorks.tsx index c7e4c99a6..2884e55f7 100644 --- a/src/components/landing/HowItWorks.tsx +++ b/src/components/landing/HowItWorks.tsx @@ -1,5 +1,5 @@ import { useTranslation } from "react-i18next"; -import { motion } from "framer-motion"; +import { Reveal } from "./Reveal"; import { FileSearch, Sparkles, Trophy } from "lucide-react"; const HowItWorks = () => { @@ -14,36 +14,21 @@ const HowItWorks = () => { return (
- -

- {t('howItWorks.title')} -

+ +

{t('howItWorks.title')}

{t('howItWorks.subtitle')}

-
+
{steps.map((step, index) => ( - +
-
+
@@ -53,7 +38,7 @@ const HowItWorks = () => {

{step.title}

{step.description}

- + ))}
diff --git a/src/components/landing/Navbar.tsx b/src/components/landing/Navbar.tsx index a96fe6dbd..203e7c533 100644 --- a/src/components/landing/Navbar.tsx +++ b/src/components/landing/Navbar.tsx @@ -1,17 +1,25 @@ import { useTranslation } from "react-i18next"; -import { useState } from "react"; -import { motion, AnimatePresence, useReducedMotion } from "framer-motion"; +import { useEffect, useState } from "react"; import { Menu, X, ArrowRight } from "lucide-react"; import { Link } from "react-router-dom"; import { LanguageSwitcher } from "@/components/LanguageSwitcher"; import { useRoutePrefetch } from "@/hooks/use-route-prefetch"; +import { cn } from "@/lib/utils"; const Navbar = () => { const { t } = useTranslation(); const [isOpen, setIsOpen] = useState(false); - const shouldReduceMotion = useReducedMotion(); + const [scrolled, setScrolled] = useState(false); const appPrefetch = useRoutePrefetch("/app"); + // The nav is static on load; its edge appears once content scrolls under it. + useEffect(() => { + const update = () => setScrolled(window.scrollY > 0); + update(); + window.addEventListener("scroll", update, { passive: true }); + return () => window.removeEventListener("scroll", update); + }, []); + const navLinks = [ { href: "#features", label: t('nav.features') }, { href: "#whats-new", label: t('nav.whatsNew') }, @@ -20,16 +28,16 @@ const Navbar = () => { ]; return ( -
- + {t('common.logoAlt')} {
- + {t('nav.startTrial')}
- - {isOpen && ( - - +
+ )} + ); }; diff --git a/src/components/landing/Pricing.tsx b/src/components/landing/Pricing.tsx index 53e5e822b..a983c0a21 100644 --- a/src/components/landing/Pricing.tsx +++ b/src/components/landing/Pricing.tsx @@ -1,5 +1,5 @@ import { useTranslation } from "react-i18next"; -import { motion } from "framer-motion"; +import { Reveal } from "./Reveal"; import { Check, Crown, Sparkles, Zap } from "lucide-react"; import { Link } from "react-router-dom"; import { useRoutePrefetch } from "@/hooks/use-route-prefetch"; @@ -29,29 +29,16 @@ const Pricing = () => {
- -

- {t('pricing.title')} -

+ +

{t('pricing.title')}

{t('pricing.subtitle')}

-
+
- + +

{t('pricing.trial')}

@@ -65,7 +52,7 @@ const Pricing = () => {
    {TRIAL_FEATURES.map((feature) => ( -
  • +
  • {feature}
  • @@ -76,15 +63,11 @@ const Pricing = () => { {t('pricing.startTrial')} - +
+ - + +
{t('pricing.recommended')} @@ -103,29 +86,24 @@ const Pricing = () => {
    {PRO_FEATURES.map((feature) => ( -
  • +
  • {feature}
  • ))}
- + {t('pricing.subscribe')} - +
+
- +

{t('pricing.noFreePlan')} - +

); diff --git a/src/components/landing/Reveal.tsx b/src/components/landing/Reveal.tsx new file mode 100644 index 000000000..ce1a8ec9a --- /dev/null +++ b/src/components/landing/Reveal.tsx @@ -0,0 +1,41 @@ +import type { ReactNode } from "react"; +import { motion, useReducedMotion } from "framer-motion"; + +// Full transform strings run on the compositor; the x/y shorthands animate on the main thread. +const HIDDEN = { opacity: 0, transform: "translateY(12px)" }; +const SHOWN = { opacity: 1, transform: "translateY(0px)" }; +const EASE_OUT_STRONG = [0.23, 1, 0.32, 1] as const; + +interface RevealProps { + children: ReactNode; + className?: string; + /** Seconds; capped so long lists don't trail in. */ + delay?: number; + /** On-load content (the hero) animates at once instead of on scroll. */ + onLoad?: boolean; +} + +/** + * The one landing reveal: a short fade-up the first time content scrolls into view. + * Hover effects belong on an inner element, never on this one, so the two never fight. + */ +export function Reveal({ children, className, delay = 0, onLoad = false }: RevealProps) { + const reduceMotion = useReducedMotion(); + if (reduceMotion) return
{children}
; + const transition = { duration: 0.35, ease: EASE_OUT_STRONG, delay: Math.min(delay, 0.2) }; + return onLoad ? ( + + {children} + + ) : ( + + {children} + + ); +} diff --git a/src/components/landing/SocialProof.tsx b/src/components/landing/SocialProof.tsx deleted file mode 100644 index 624de11ae..000000000 --- a/src/components/landing/SocialProof.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { useTranslation } from "react-i18next"; -import { motion } from "framer-motion"; - -const SocialProof = () => { - const { t } = useTranslation(); - - const proofItems = [ - { value: "11", label: t('socialProof.modes') }, - { value: "2", label: t('socialProof.plans') }, - { value: "3", label: t('socialProof.upgrades') }, - { value: "1", label: t('socialProof.workspace') }, - ]; - - return ( -
-
- -

- {t('socialProof.description')} -

- -
- {proofItems.map((item, index) => ( - -
{item.value}
-
{item.label}
-
- ))} -
-
-
-
- ); -}; - -export default SocialProof; diff --git a/src/components/landing/UseCases.tsx b/src/components/landing/UseCases.tsx index ed4712020..c1ba5d56b 100644 --- a/src/components/landing/UseCases.tsx +++ b/src/components/landing/UseCases.tsx @@ -1,5 +1,5 @@ import { useTranslation } from "react-i18next"; -import { motion } from "framer-motion"; +import { Reveal } from "./Reveal"; import { GraduationCap, Users, Gavel } from "lucide-react"; const UseCases = () => { @@ -29,31 +29,17 @@ const UseCases = () => { return (
- -

- {t('useCases.title')} -

+ +

{t('useCases.title')}

{t('useCases.description')}

-
+
{personas.map((persona, index) => ( - + +
@@ -68,7 +54,8 @@ const UseCases = () => { ))} - +
+
))}
diff --git a/src/components/landing/WhatsNew.tsx b/src/components/landing/WhatsNew.tsx index 9799c7ee0..91fb404b5 100644 --- a/src/components/landing/WhatsNew.tsx +++ b/src/components/landing/WhatsNew.tsx @@ -1,6 +1,6 @@ import { useTranslation } from "react-i18next"; -import { motion } from "framer-motion"; -import { Crown, GitBranch, Timer, History, Sparkles, Crosshair, Dumbbell } from "lucide-react"; +import { Crown, GitBranch, Timer, History, Crosshair, Dumbbell } from "lucide-react"; +import { Reveal } from "./Reveal"; const WhatsNew = () => { const { t } = useTranslation(); @@ -17,46 +17,29 @@ const WhatsNew = () => { return (
- -
- - {t('whatsNew.title')} -
-

- {t('whatsNew.title')} -

+ +

{t('whatsNew.title')}

{t('whatsNew.subtitle')}

-
+
{updates.map((update, index) => ( - -
-
- + +
+
+
+ +
+ + {update.badge} +
- - {update.badge} - +

{update.title}

+

{update.description}

-

{update.title}

-

{update.description}

- +
))}
diff --git a/src/index.css b/src/index.css index fc939bd7d..a8998d9f7 100644 --- a/src/index.css +++ b/src/index.css @@ -1,6 +1,15 @@ -@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"); -@import url("https://fonts.googleapis.com/css2?family=Lora:wght@400;500;600;700&display=swap"); -@import url("https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&display=swap"); +/* Inter is self-hosted (imported in main.tsx, no render-blocking third-party request), and the fallback is + Arial scaled to Inter's metrics so the swap doesn't shift the layout. */ + +@font-face { + font-family: "Inter Fallback"; + src: local("Arial"); + size-adjust: 107.4%; + ascent-override: 90.2%; + descent-override: 22.48%; + line-gap-override: 0%; +} + @tailwind base; @tailwind components; @tailwind utilities; @@ -55,11 +64,9 @@ All colors MUST be HSL. --chart-5: 215 19% 34%; - --font-sans: 'Inter', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif; + --font-sans: 'Inter Variable', 'Inter Fallback', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif; - --font-serif: 'Lora', ui-serif, Georgia, Cambria, 'Times New Roman', Times, serif; - - --font-mono: 'Space Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; + --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; --shadow-2xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); @@ -200,7 +207,7 @@ All colors MUST be HSL. /* Landing Page Utilities */ @layer components { .glass-card { - @apply bg-card backdrop-blur-xl border border-border rounded-2xl shadow-sm; + @apply bg-card border border-border rounded-2xl shadow-sm; background: linear-gradient(135deg, hsl(var(--card)) 0%, hsl(198 35% 90%) 100%); } @@ -210,8 +217,18 @@ All colors MUST be HSL. .btn-primary-gradient { - @apply relative px-6 py-3 font-semibold text-primary-foreground rounded-xl overflow-hidden transition-all duration-300 shadow-lg; + @apply relative px-6 py-3 font-semibold text-primary-foreground rounded-xl overflow-hidden shadow-lg; background: linear-gradient(135deg, hsl(var(--primary)) 0%, hsl(169 72% 40%) 100%); + transition: + transform var(--motion-duration-fast) var(--motion-ease-fluid), + box-shadow 300ms var(--motion-ease-fluid); + } + + /* The landing CTAs are links, so they get the same press feedback as Button here. */ + .btn-primary-gradient:active, + .btn-secondary-outline:active { + transform: scale(var(--motion-press-scale)); + transition-duration: var(--motion-duration-press); } .btn-primary-gradient::before { @@ -232,7 +249,18 @@ All colors MUST be HSL. } .btn-secondary-outline { - @apply px-6 py-3 font-semibold text-foreground rounded-xl border border-border bg-card transition-all duration-300 shadow-sm; + @apply px-6 py-3 font-semibold text-foreground rounded-xl border border-border bg-card shadow-sm; + transition: + transform var(--motion-duration-fast) var(--motion-ease-fluid), + color 300ms var(--motion-ease-fluid), + background-color 300ms var(--motion-ease-fluid), + border-color 300ms var(--motion-ease-fluid), + box-shadow 300ms var(--motion-ease-fluid); + } + + /* Landing section headings: one size scale and one tracking per size. */ + .section-heading { + @apply text-balance text-3xl font-bold leading-tight tracking-[-0.022em] text-foreground md:text-4xl lg:text-5xl lg:tracking-[-0.025em]; } @media (hover: hover) and (pointer: fine) { @@ -242,36 +270,26 @@ All colors MUST be HSL. } } - .shimmer { - @apply relative overflow-hidden; - } - - .shimmer::after { - content: ''; - @apply absolute inset-0; - background: linear-gradient(90deg, transparent, hsl(0 0% 100% / 0.3), transparent); - transform: translateX(-100%); - animation: shimmer 3s infinite; - } - + /* Backgrounds are static: nothing on the landing page moves on its own. */ .animated-grid { - background-image: + background-image: linear-gradient(hsl(var(--border) / 0.8) 1px, transparent 1px), linear-gradient(90deg, hsl(var(--border) / 0.8) 1px, transparent 1px); background-size: 60px 60px; - animation: gridMove 20s linear infinite; } + /* Cards answer hover with an edge and a shadow; they don't lift or tilt (they aren't clickable). */ .feature-card { - @apply glass-card p-6 transition-all duration-500 hover:shadow-lg; - transform-style: preserve-3d; + @apply glass-card p-6; + transition: + border-color var(--motion-duration-normal) var(--motion-ease-fluid), + box-shadow var(--motion-duration-normal) var(--motion-ease-fluid); } @media (hover: hover) and (pointer: fine) { .feature-card:hover { @apply border-primary/40; box-shadow: 0 20px 40px hsl(var(--foreground) / 0.08); - transform: translateY(-4px) rotateX(2deg) rotateY(2deg); } } @@ -281,12 +299,10 @@ All colors MUST be HSL. .orb-primary { background: hsl(var(--primary) / 0.3); - animation: float 8s ease-in-out infinite; } .orb-accent { background: hsl(169 72% 45% / 0.22); - animation: float 10s ease-in-out infinite reverse; } .aurora-bg { @@ -294,7 +310,6 @@ All colors MUST be HSL. radial-gradient(circle at 12% 18%, hsl(var(--primary) / 0.28), transparent 45%), radial-gradient(circle at 82% 14%, hsl(169 72% 40% / 0.22), transparent 48%), radial-gradient(circle at 55% 85%, hsl(186 78% 45% / 0.18), transparent 44%); - animation: auroraShift 14s ease-in-out infinite; } .section-container { @@ -311,8 +326,9 @@ All colors MUST be HSL. border-radius: calc(var(--radius) - 2px); } + /* The landing nav is the one blurred surface; its edge appears once content scrolls under it. */ .nav-blur { - @apply backdrop-blur-xl bg-background/90 border-b border-border shadow-sm; + @apply backdrop-blur-xl bg-background/90 border-b transition-[border-color,box-shadow] duration-200; } /* Press feedback: 100ms in, 160ms release. The hover lift is opt-in (.motion-lift) @@ -507,64 +523,6 @@ All colors MUST be HSL. } } -@keyframes shimmer { - 0% { - transform: translateX(-100%); - } - 100% { - transform: translateX(100%); - } -} - -/* Subtle parallax tilt on card hover */ -.tilt-card { - transition: transform 0.3s var(--motion-ease-fluid); - transform-style: preserve-3d; - perspective: 1000px; -} - -@media (hover: hover) and (pointer: fine) { - .tilt-card:hover { - transform: rotateX(-1deg) rotateY(1deg) translateY(-2px); - } -} - -@keyframes gridMove { - 0% { - background-position: 0 0; - } - 100% { - background-position: 60px 60px; - } -} - -@keyframes auroraShift { - 0%, 100% { - transform: translate3d(0, 0, 0) scale(1); - } - 33% { - transform: translate3d(1.5%, -1.5%, 0) scale(1.04); - } - 66% { - transform: translate3d(-1.5%, 1%, 0) scale(0.98); - } -} - -@keyframes float { - 0%, 100% { - transform: translateY(0) translateX(0); - } - 25% { - transform: translateY(-20px) translateX(10px); - } - 50% { - transform: translateY(-10px) translateX(-10px); - } - 75% { - transform: translateY(-25px) translateX(5px); - } -} - @keyframes motion-swap { from { opacity: 0; @@ -655,8 +613,8 @@ All colors MUST be HSL. .motion-press:active, .motion-lift:hover, .motion-expand, - .feature-card:hover, - .tilt-card:hover { + .btn-primary-gradient:active, + .btn-secondary-outline:active { transform: none !important; } } diff --git a/src/locales/en.json b/src/locales/en.json index 262be4c6a..dcb47f655 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -979,13 +979,6 @@ "cta": "Launch DebateLab", "freeTrialAvailable": "Free trial available" }, - "socialProof": { - "modes": "Prep Modes", - "plans": "Plans", - "upgrades": "Major 2026 Upgrades", - "workspace": "Unified Workspace", - "description": "One platform for research, strategy, rebuttals, and speaking practice." - }, "whatsNew": { "title": "What's New", "subtitle": "Latest updates and improvements.", diff --git a/src/locales/es.json b/src/locales/es.json index b8545889e..a8c57089a 100644 --- a/src/locales/es.json +++ b/src/locales/es.json @@ -15,8 +15,8 @@ "features": "Funciones", "whatsNew": "Novedades", "pricing": "Precios", - "howItWorks": "Cómo Funciona", - "startTrial": "Prueba Gratis", + "howItWorks": "Cómo funciona", + "startTrial": "Prueba gratis", "settings": "Ajustes", "openMenu": "Abrir menú", "closeMenu": "Cerrar menú", @@ -360,15 +360,15 @@ "heading": "¿En qué estás trabajando?" }, "generate": { - "emptyTitle": "Tu Caso Te Espera", + "emptyTitle": "Tu caso te espera", "emptyDesc": "Completa el formulario para generar un caso de debate estructurado con argumentos, evidencia y contra-estrategias.", - "saved": "Caso Guardado", + "saved": "Caso guardado", "savedDesc": "Tu caso ha sido guardado en el historial.", "savePrompt": "Ingresa un título para este caso:", - "failed": "Error al Generar", - "timeout": "Tiempo Agotado", + "failed": "Error al generar", + "timeout": "Tiempo agotado", "timeoutDesc": "La IA tardó demasiado en responder. Inténtalo de nuevo.", - "authRequired": "Autenticación Requerida", + "authRequired": "Autenticación requerida", "flowchartImported": "Caso importado", "flowchartImportedDesc": "Tu caso se importó a Diagramas.", "flowchartImportFailed": "Error al importar", @@ -387,29 +387,29 @@ "working": "Escribiendo tu caso…" }, "attack": { - "emptyTitle": "Modo Sin Piedad", + "emptyTitle": "Modo sin piedad", "emptyDesc": "Pega tu argumento para recibir críticas despiadadas, identificar falacias lógicas y obtener mejoras accionables.", - "failed": "Error en el Análisis de Ataque", + "failed": "Error en el análisis de ataque", "working": "Revisando tu caso…" }, "brainstorm": { - "emptyTitle": "Desbloquea Ángulos Creativos", + "emptyTitle": "Desbloquea ángulos creativos", "emptyDesc": "Describe tu moción para explorar ángulos de argumentación no convencionales, identificar compromisos y encontrar tus posiciones más fuertes.", - "failed": "Error en la Lluvia de Ideas", + "failed": "Error en la lluvia de ideas", "working": "Buscando enfoques…" }, "factoids": { - "emptyTitle": "Evidencia Que Gana", + "emptyTitle": "Evidencia que gana", "emptyDesc": "Ingresa una afirmación para encontrar datos de apoyo con citas reales, contexto y guía sobre cómo usarlos efectivamente.", - "failed": "Error en la Investigación", - "savedToLibrary": "Guardado en Biblioteca", + "failed": "Error en la investigación", + "savedToLibrary": "Guardado en biblioteca", "savedToLibraryDesc": "Esta evidencia ahora está disponible para fundamentación.", "saveFailed": "No se pudo guardar la fuente.", "working": "Buscando evidencia…" }, "ask": { "failed": "Error", - "timeout": "Tiempo Agotado", + "timeout": "Tiempo agotado", "timeoutDesc": "La solicitud tardó demasiado. Inténtalo de nuevo.", "saved": "Guardado", "savedDesc": "Fuente añadida a la biblioteca.", @@ -428,9 +428,9 @@ "chat": {}, "settings": { "title": "Ajustes", - "aiOutputLanguage": "Idioma (Interfaz + Salida IA)", - "uiLanguage": "Idioma de Interfaz", - "outputLanguage": "Idioma de Salida IA", + "aiOutputLanguage": "Idioma (interfaz + salida IA)", + "uiLanguage": "Idioma de interfaz", + "outputLanguage": "Idioma de salida IA", "theme": "Tema", "light": "Claro", "dark": "Oscuro", @@ -462,7 +462,7 @@ "developer": "Desarrollador", "developerDesc": "Cambia la app entre los proyectos Supabase de producción y staging. Se cerrará tu sesión.", "switchToStaging": "Cambiar a Staging", - "switchToProduction": "Cambiar a Producción", + "switchToProduction": "Cambiar a producción", "envSwitchTitle": "Cambiar entorno", "envSwitchDesc": "Se cerrará tu sesión y la app se recargará apuntando a {{target}}. Tendrás que iniciar sesión de nuevo.", "envSwitchConfirm": "Cambiar y cerrar sesión", @@ -502,7 +502,7 @@ "deletingData": "Eliminando…" }, "login": { - "title": "Iniciar Sesión en DebateLab", + "title": "Iniciar sesión en DebateLab", "subtitle": "Inicia sesión con Google, email o un código demo emitido por un administrador.", "google": "Continuar con Google", "emailTab": "Email", @@ -819,7 +819,7 @@ "notFound": { "title": "404", "message": "¡Ups! Página no encontrada", - "link": "Volver al Inicio" + "link": "Volver al inicio" }, "footer": { "terms": "Términos", @@ -833,14 +833,14 @@ "dismissError": "No se pudo descartar este anuncio." }, "hero": { - "headline": "Prepárate Más Rápido. Argumenta Mejor. Gana Más Rondas.", + "headline": "Prepárate más rápido. Argumenta mejor. Gana más rondas.", "subheadline": "Preparación de debate impulsada por IA que te da ventaja en cada ronda.", "trialBadge": "Prueba gratis de 3 días", - "requestsBadge": "Prep IA diaria", + "requestsBadge": "Preparación diaria con IA", "requestsBadgeValue": "Incluido", "proBadge": "$5/mes Pro", - "cta": "Prueba Gratis", - "ctaSecondary": "Ver Precios", + "cta": "Prueba gratis", + "ctaSecondary": "Ver precios", "trustBullet1": "Sin tarjeta de crédito", "trustBullet2": "Cancela cuando quieras", "trustBullet3": "Funciona con cualquier formato", @@ -875,13 +875,13 @@ } }, "features": { - "title": "Todo Lo Que Necesitas Para Ganar", + "title": "Todo lo que necesitas para ganar", "subtitle": "Once herramientas de debate para una preparación competitiva.", - "generate": "Generador de Casos", + "generate": "Generador de casos", "generateDesc": "Construye casos completos con argumentos estructurados, evidencia y contra-estrategias.", - "attack": "Atacante de Casos", + "attack": "Atacante de casos", "attackDesc": "Pon a prueba tus argumentos y encuentra debilidades antes que los oponentes.", - "brainstorm": "Lluvia de Ideas", + "brainstorm": "Lluvia de ideas", "brainstormDesc": "Explora ángulos no convencionales y descubre caminos de argumentación frescos.", "factoids": "Datos", "factoidsDesc": "Encuentra evidencia lista para el debate con citas verificables.", @@ -891,17 +891,17 @@ "crossfireDesc": "Practica preguntas y respuestas precisas en un intercambio cronometrado.", "sparring": "Sparring", "sparringDesc": "Practica una ronda cronometrada contra una oponente y revisa la tarjeta de puntuación.", - "library": "Biblioteca de Fuentes", + "library": "Biblioteca de fuentes", "libraryDesc": "Gestiona fuentes que fundamentan las respuestas de IA con evidencia real.", - "timer": "Cronómetro de Debate", + "timer": "Cronómetro de debate", "timerDesc": "Practica con controles de tiempo precisos de torneo.", - "flowchart": "Diagrama de Flujo", + "flowchart": "Diagrama de flujo", "flowchartDesc": "Mapea estructuras de argumentos y enlaces de refutación visualmente.", "history": "Historial", "historyDesc": "Accede a casos guardados, chats y sesiones de preparación anteriores." }, "howItWorks": { - "title": "Cómo Funciona", + "title": "Cómo funciona", "subtitle": "Tres pasos para una mejor preparación de debate.", "step1Title": "Investigar", "step1Desc": "La IA escanea fuentes verificadas y construye contexto para tu moción.", @@ -911,11 +911,11 @@ "step3Desc": "Refina con coaching, ejercicios de tiempo y mapeo de argumentos." }, "useCases": { - "title": "Hecho Para Todos en el Debate", + "title": "Hecho para todos en el debate", "description": "Tanto si compites, entrenas o juzgas, DebateLab te da ventaja.", - "forDebaters": "Para Debatientes", - "forCoaches": "Para Coaches", - "forJudges": "Para Jueces", + "forDebaters": "Para debatientes", + "forCoaches": "Para coaches", + "forJudges": "Para jueces", "debater1": "Genera casos en segundos, no en horas", "debater2": "Encuentra evidencia con citas reales", "debater3": "Pon a prueba argumentos antes de las rondas", @@ -933,7 +933,7 @@ "judgeSubtitle": "Profundiza tu análisis" }, "pricing": { - "title": "Precios Simples y Transparentes", + "title": "Precios simples y transparentes", "subtitle": "Comienza gratis, mejora cuando estés listo.", "trial": "Prueba", "trialPrice": "$0", @@ -951,8 +951,8 @@ "proFeature3": "Los 10 modelos, incluidos Claude Opus y Sonnet, GPT-6.1 Sol y Gemini", "proFeature4": "Soporte prioritario", "proFeature5": "Investigación Active Grounding™", - "startTrial": "Comenzar Prueba Gratis", - "subscribe": "Suscribirse Ahora", + "startTrial": "Comenzar prueba gratis", + "subscribe": "Suscribirse ahora", "noFreePlan": "No hay plan gratuito permanente. Después de tu prueba, se requiere una suscripción para seguir usando DebateLab.", "trialBadge": "Usuarios nuevos", "trialDesc": "Usa el flujo de trabajo completo antes de decidirte por Pro.", @@ -960,7 +960,7 @@ "proDesc": "Para semanas de preparación intensiva, temas difíciles y límites diarios más altos." }, "faq": { - "title": "Preguntas Frecuentes", + "title": "Preguntas frecuentes", "q1": "¿Cuánto cuesta DebateLab?", "a1": "DebateLab ofrece una prueba gratis de 3 días. Después, Pro cuesta $5/mes + impuestos para acceso ampliado y los 10 modelos.", "q2": "¿Qué obtengo con Pro?", @@ -974,28 +974,21 @@ "subtitle": "Precios, flujo de trabajo y detalles del producto de un vistazo." }, "finalCta": { - "title": "¿Listo Para Construir Tu Próximo Caso Ganador?", + "title": "¿Listo para construir tu próximo caso ganador?", "subtitle": "Únete a debatientes que se preparan mejor con IA.", "cta": "Lanzar DebateLab", "freeTrialAvailable": "Prueba gratuita disponible" }, - "socialProof": { - "modes": "Modos de Preparación", - "plans": "Planes", - "upgrades": "Mejoras Importantes 2026", - "workspace": "Espacio de Trabajo Unificado", - "description": "Una plataforma para investigación, estrategia, refutaciones y práctica de oratoria." - }, "whatsNew": { "title": "Novedades", "subtitle": "Últimas actualizaciones y mejoras.", - "sub1Title": "Niveles de Suscripción", + "sub1Title": "Niveles de suscripción", "sub1Desc": "Prueba gratis y plan Pro con límites de uso más altos.", - "sub2Title": "Diagramas de Argumentos", + "sub2Title": "Diagramas de argumentos", "sub2Desc": "Mapeo visual de argumentos con nodos arrastrables.", - "sub3Title": "Cronómetro Mejorado", + "sub3Title": "Cronómetro mejorado", "sub3Desc": "Tiempos precisos de torneo con formatos personalizados.", - "sub4Title": "Historial de Preparación", + "sub4Title": "Historial de preparación", "sub4Desc": "Accede a todos tus casos y sesiones anteriores.", "sub5Title": "Práctica de interrogatorio", "sub5Desc": "Entrena preguntas y respuestas precisas en un intercambio cronometrado.", @@ -1013,11 +1006,11 @@ "subtitle": "Cómo mantenemos honestas las respuestas de IA.", "step1": "Escaneo IA vía Exa", "step1Desc": "La búsqueda neuronal encuentra fuentes relevantes y de alta calidad.", - "step2": "Base de Conocimiento", + "step2": "Base de conocimiento", "step2Desc": "Tus fuentes se vectorizan para coincidencia semántica.", - "step3": "Inyección de Contexto", + "step3": "Inyección de contexto", "step3Desc": "Los hechos verificados se inyectan en el prompt de IA.", - "step4": "Salida Fundamentada", + "step4": "Salida fundamentada", "step4Desc": "Las respuestas citan fuentes reales, no alucinaciones.", "preview": { "processHeading": "// Proceso de contexto en tiempo real", @@ -1031,26 +1024,26 @@ } }, "rateLimit": { - "title": "Límite de Solicitudes Excedido" + "title": "Límite de solicitudes excedido" }, "generateForm": { - "motion": "Moción / Tema", + "motion": "Moción / tema", "motionPlaceholder": "ej., Esta cámara cree que la inteligencia artificial creará más empleos de los que destruirá", "side": "Lado", "sidePro": "A favor", "sideCon": "En contra", - "format": "Formato de Debate", - "role": "Rol del Orador", + "format": "Formato de debate", + "role": "Rol del orador", "roleIntro": "Introductor (4 min)", "roleR1": "R1 - Refutación/Arg (6 min)", "roleR2": "R2 - Refutación/Construcción (6 min)", "roleConc": "Conclusor (3 min)", "moreOptions": "Más opciones", - "length": "Extensión del Caso", + "length": "Extensión del caso", "lengthBrief": "Breve", "lengthStandard": "Estándar", "lengthComprehensive": "Completo", - "audience": "Nivel de Audiencia", + "audience": "Nivel de audiencia", "audienceBeginner": "Principiante", "audienceIntermediate": "Intermedio", "audienceAdvanced": "Avanzado", @@ -1058,8 +1051,8 @@ "toneFormal": "Formal", "toneConversational": "Conversacional", "toneAggressive": "Agresivo", - "generating": "Generando Caso...", - "submit": "Generar Caso de Debate", + "generating": "Generando caso...", + "submit": "Generar caso de debate", "listening": "Escuchando... Dicta tu moción claramente", "stopListening": "Dejar de escuchar", "startVoice": "Iniciar entrada de voz", @@ -1068,7 +1061,7 @@ "styleNote": "Guía el estilo de escritura; no cambia la investigación." }, "generateResults": { - "incomplete": "Generación Incompleta", + "incomplete": "Generación incompleta", "incompleteDesc": "La respuesta de la IA fue incompleta o malformada. Intenta generar de nuevo.", "proposition": "A favor", "opposition": "En contra", @@ -1083,23 +1076,23 @@ "exordioDesc": "Tu apertura convincente", "hook": "Gancho", "transition": "Transición", - "caseStructure": "Estructura del Caso", + "caseStructure": "Estructura del caso", "caseStructureDesc": "Tu marco argumentativo", "thesis": "Tesis", "framework": "Marco", - "burdenOfProof": "Carga de la Prueba", - "keyDefinitions": "Definiciones Clave", + "burdenOfProof": "Carga de la prueba", + "keyDefinitions": "Definiciones clave", "noDefinitions": "No se proporcionaron definiciones", "arguments": "Argumentos", "structuredArguments": "argumentos estructurados", - "predictedOpponent": "Caso Predicho del Oponente", + "predictedOpponent": "Caso predicho del oponente", "predictedOpponentDesc": "Lo que probablemente argumentarán", - "likelyArguments": "Argumentos Probables", - "strategicWeaknesses": "Debilidades Estratégicas", - "keyClashes": "Choques Clave", - "preparedCounters": "Contra-argumentos Preparados", + "likelyArguments": "Argumentos probables", + "strategicWeaknesses": "Debilidades estratégicas", + "keyClashes": "Choques clave", + "preparedCounters": "Contra-argumentos preparados", "preparedCountersDesc": "Refutaciones listas para usar", - "needsSource": "Necesita Fuente", + "needsSource": "Necesita fuente", "reasoning": "Razonamiento:", "evidence": "Evidencia:", "searchSuggestion": "Sugerencia de búsqueda:", @@ -1119,43 +1112,43 @@ "unresearchedCaveat": "Generado por IA, y ninguna búsqueda web respalda esta evidencia. Comprueba los datos y las fuentes antes de usarlos en una ronda." }, "attackForm": { - "argument": "Tu Argumento", + "argument": "Tu argumento", "argumentPlaceholder": "Pega tu caso o argumento de debate aquí...", "options": "Opciones", "moreOptions": "Más opciones", - "intensity": "Intensidad del Ataque", - "intensityMild": "Leve (Constructivo)", - "intensityModerate": "Moderado (Equilibrado)", - "intensityRuthless": "Despiadado (Sin Piedad)", - "paradigm": "Paradigma del Juez", - "paradigmLogic": "Enfoque Lógico", - "paradigmValues": "Enfoque en Valores", + "intensity": "Intensidad del ataque", + "intensityMild": "Leve (constructivo)", + "intensityModerate": "Moderado (equilibrado)", + "intensityRuthless": "Despiadado (sin piedad)", + "paradigm": "Paradigma del juez", + "paradigmLogic": "Enfoque lógico", + "paradigmValues": "Enfoque en valores", "paradigmPolicy": "Política/Impacto", - "detectFallacies": "Detectar Falacias", + "detectFallacies": "Detectar falacias", "detectFallaciesDesc": "Identificar falacias lógicas en el argumento", - "analyzeWeighing": "Analizar Ponderación", + "analyzeWeighing": "Analizar ponderación", "analyzeWeighingDesc": "Evaluar la comparación y priorización de impactos", - "analyzing": "Analizando Caso...", - "submit": "Encontrar Debilidades" + "analyzing": "Analizando caso...", + "submit": "Encontrar debilidades" }, "attackResults": { - "overall": "Evaluación General", - "caseStrength": "Fortaleza del Caso", - "weaknesses": "Debilidades Identificadas", + "overall": "Evaluación general", + "caseStrength": "Fortaleza del caso", + "weaknesses": "Debilidades identificadas", "weaknessesCount": "{{count}} problemas encontrados, ordenados por gravedad", - "fallacies": "Falacias Detectadas", + "fallacies": "Falacias detectadas", "fallaciesDesc": "Errores lógicos en tu argumento", - "crossEx": "Preguntas de Interrogatorio Cruzado", + "crossEx": "Preguntas de interrogatorio cruzado", "crossExDesc": "Preguntas que los oponentes podrían hacerte", - "weighing": "Análisis de Ponderación", + "weighing": "Análisis de ponderación", "weighingDesc": "Qué tan bien comparas los impactos", - "currentWeighing": "Ponderación Actual", - "missingComparisons": "Comparaciones Faltantes", - "improvements": "Sugerencias de Mejora", - "strengths": "Áreas de Fortaleza", + "currentWeighing": "Ponderación actual", + "missingComparisons": "Comparaciones faltantes", + "improvements": "Sugerencias de mejora", + "strengths": "Áreas de fortaleza", "whyWeak": "Por qué es débil:", "opponentAttack": "Ataque del oponente:", - "improvedVersion": "Versión Mejorada", + "improvedVersion": "Versión mejorada", "purpose": "Propósito:", "expectedTrap": "Trampa esperada:", "in": "en", @@ -1182,36 +1175,36 @@ "resultTitle": "Crítica" }, "brainstormForm": { - "motion": "Moción / Tema", + "motion": "Moción / tema", "motionPlaceholder": "ej., La Renta Básica Universal debería implementarse globalmente", - "side": "Lado a Explorar", - "sideBoth": "Explorar Ambos Lados", + "side": "Lado a explorar", + "sideBoth": "Explorar ambos lados", "sidePro": "Solo a favor", "sideCon": "Solo en contra", - "outputFormat": "Formato de Salida", - "outputTop5": "Top 5 Mejores Ángulos", - "outputMindmap": "Estilo Mapa Mental", - "outputBuckets": "Categorías Organizadas", - "existing": "Lo Que Ya Tienes (Opcional)", + "outputFormat": "Formato de salida", + "outputTop5": "Top 5 mejores ángulos", + "outputMindmap": "Estilo mapa mental", + "outputBuckets": "Categorías organizadas", + "existing": "Lo que ya tienes (opcional)", "existingPlaceholder": "Comparte ideas o ángulos que ya hayas considerado...", - "generating": "Generando Ideas...", - "submit": "Comenzar Lluvia de Ideas" + "generating": "Generando ideas...", + "submit": "Comenzar lluvia de ideas" }, "brainstormResults": { - "clarification": "Aclaración Necesaria", + "clarification": "Aclaración necesaria", "clarificationDesc": "Responde estas preguntas para mejores resultados", - "top5": "Top 5 Ángulos de Argumentación", + "top5": "Top 5 ángulos de argumentación", "top5Desc": "Mejores opciones clasificadas por potencial", - "argMap": "Mapa de Argumentos", + "argMap": "Mapa de argumentos", "argMapDesc": "Ideas organizadas por categoría", - "nextSteps": "Próximos Pasos Recomendados", - "warnings": "Advertencias y Compromisos", + "nextSteps": "Próximos pasos recomendados", + "warnings": "Advertencias y compromisos", "pro": "A FAVOR", "con": "EN CONTRA", "neutral": "NEUTRAL", - "untitled": "Idea Sin Título", + "untitled": "Idea sin título", "tradeoffs": "Compromisos:", - "discuss": "Discutir Ideas", + "discuss": "Discutir ideas", "exportedDocx": "¡Lluvia de ideas exportada a DOCX!", "exportFailedDocx": "Error al exportar a DOCX", "exportedGdocs": "¡Lluvia de ideas exportada a Google Docs!", @@ -1228,42 +1221,42 @@ "direction": "Postura", "directionFor": "A favor", "directionAgainst": "En contra", - "region": "Región (Opcional)", + "region": "Región (opcional)", "regionPlaceholder": "Global por defecto", "sourceCount": "Fuentes", - "timeframe": "Período (Opcional)", + "timeframe": "Período (opcional)", "timeframePlaceholder": "ej., 2020-2024, Última década", - "sourceType": "Tipo de Fuente", - "sourceAny": "Cualquier Fuente Confiable", - "sourceAcademic": "Solo Académicas", - "sourceGovernment": "Fuentes Gubernamentales", - "sourceNews": "Noticias Principales", - "finding": "Buscando Evidencia...", - "submit": "Buscar Datos" + "sourceType": "Tipo de fuente", + "sourceAny": "Cualquier fuente confiable", + "sourceAcademic": "Solo académicas", + "sourceGovernment": "Fuentes gubernamentales", + "sourceNews": "Noticias principales", + "finding": "Buscando evidencia...", + "submit": "Buscar datos" }, "factoidsResults": { - "verificationStatus": "Estado de Verificación", + "verificationStatus": "Estado de verificación", "verified": "Verificado", - "unverified": "No Verificado", + "unverified": "No verificado", "pending": "Pendiente", - "importantNotes": "Notas Importantes", + "importantNotes": "Notas importantes", "found": "Encontrados", "factoids": "Datos", "factoidsDesc": "Puntos de evidencia con citas", - "findMore": "Buscar Más Evidencia", + "findMore": "Buscar más evidencia", "findMoreDesc": "Consultas de búsqueda sugeridas", "relevance": "Relevancia:", - "keyFigure": "Cifra Clave:", + "keyFigure": "Cifra clave:", "context": "Contexto:", "limitations": "Limitaciones:", - "unknownSource": "Fuente Desconocida", + "unknownSource": "Fuente desconocida", "unknown": "Desconocido", - "viewSource": "Ver Fuente", + "viewSource": "Ver fuente", "urlNeedsVerification": "La URL requiere verificación", "howToUse": "Cómo usar:", - "saveToLibrary": "Guardar en Biblioteca", + "saveToLibrary": "Guardar en biblioteca", "saveClaimToLibrary": "Guardar {{claim}} en la biblioteca", - "discuss": "Discutir Evidencia", + "discuss": "Discutir evidencia", "exportedDocx": "¡Datos exportados a DOCX!", "exportFailedDocx": "Error al exportar a DOCX", "exportedGdocs": "¡Datos exportados a Google Docs!", @@ -1275,14 +1268,14 @@ "resultTitle": "Evidencia" }, "askChat": { - "title": "Pregunta a Tu Coach", + "title": "Pregunta a tu coach", "subtitle": "Obtén respuestas a preguntas de debate", "startConversation": "Inicia una conversación", "startDesc": "Pregunta lo que sea sobre estrategia de debate, argumentos o pide ayuda con tu caso.", "modeGeneral": "General", "modeClarify": "Aclarar", "modeProsCons": "Pros/Contras", - "modeArgMap": "Mapa de Args", + "modeArgMap": "Mapa de args", "citations": "Citas", "webSearch": "Busqueda web", "citationResults": "Citas:", @@ -1301,7 +1294,7 @@ "cons": "Contras", "placeholder": "Pregunta a tu coach de debate...", "placeholderListening": "Escuchando...", - "saveToLibrary": "Guardar en Biblioteca", + "saveToLibrary": "Guardar en biblioteca", "saveMessage": "Guardar mensaje en biblioteca", "clear": "Limpiar", "attachFiles": "Adjuntar archivos", @@ -1333,7 +1326,7 @@ "options": "Opciones" }, "limitModal": { - "trialEnded": "Prueba Gratuita Finalizada", + "trialEnded": "Prueba gratuita finalizada", "trialEndedDesc": "Tu prueba gratuita de 3 días ha terminado. Actualiza a Pro para seguir usando DebateLab.", "limitReached": "Acceso diario pausado", "limitReachedDesc": "Has usado {{current}} de {{limit}} usos de IA incluidos hoy. El acceso se renueva a medianoche UTC.", @@ -1370,28 +1363,28 @@ "proFeature4": "Procesamiento prioritario", "proFeature5": "Todas las funciones de debate", "redirecting": "Redirigiendo...", - "upgradeNow": "Actualizar Ahora", + "upgradeNow": "Actualizar ahora", "applying": "Aplicando...", "apply": "Aplicar", "promoCode": "¿Tienes un código promocional?", "promoPlaceholder": "Ingresa código promocional", - "checkoutError": "Error de Pago", + "checkoutError": "Error de pago", "checkoutErrorDesc": "No se pudo iniciar el pago", - "codeRedeemed": "Código Canjeado", - "invalidCode": "Código Inválido", + "codeRedeemed": "Código canjeado", + "invalidCode": "Código inválido", "redeemError": "Error", "redeemErrorDesc": "No se pudo canjear el código" }, "onboarding": { "welcome": "¡Bienvenido a DebateLab! 🎉", - "ready": "¡Estás Listo! 🚀", + "ready": "¡Estás listo! 🚀", "readyDesc": "Explora las otras funciones: Atacar argumentos, Chat con Coach e Investigar datos.", "back": "Atrás", "close": "Cerrar", "getStarted": "¡Comenzar!", "next": "Siguiente", - "nextWithProgress": "Siguiente (Paso {step} de {steps})", - "skip": "Saltar Tour", + "nextWithProgress": "Siguiente (paso {step} de {steps})", + "skip": "Saltar tour", "generate": "Generar", "generateDesc": "Construye un caso completo para tu moción. Lluvia de ideas, Datos y Diagrama están a su lado, en Preparar.", "library": "Biblioteca", @@ -1400,14 +1393,14 @@ "debateCurrentDesc": "Todo en DebateLab pertenece a tu debate actual: su moción, sus fuentes y tus resultados. Ábrelo o cámbialo aquí." }, "timer": { - "title": "Temporizador de Debate", + "title": "Temporizador de debate", "displayLabel": "Pantalla del temporizador", - "selectFormat": "Seleccionar Formato", + "selectFormat": "Seleccionar formato", "formatStandard": "Estándar (4-6-6-3)", "formatLD": "Lincoln-Douglas", - "formatPF": "Foro Público", + "formatPF": "Foro público", "formatPolicy": "Política", - "timeUp": "¡Se Acabó el Tiempo!", + "timeUp": "¡Se acabó el tiempo!", "speaking": "Hablando", "paused": "Pausado", "pause": "Pausar", @@ -1429,21 +1422,21 @@ "conc": "Conclusor" }, "ld": { - "ac": "Constructivo Afirmativo", - "nc": "Constructivo Negativo", - "ar1": "1ª Refutación Afirmativa", - "nr": "Refutación Negativa", - "ar2": "2ª Refutación Afirmativa" + "ac": "Constructivo afirmativo", + "nc": "Constructivo negativo", + "ar1": "1ª refutación afirmativa", + "nr": "Refutación negativa", + "ar2": "2ª refutación afirmativa" }, "pf": { - "team1c": "Equipo 1 Constructivo", - "team2c": "Equipo 2 Constructivo", - "team1r": "Equipo 1 Refutación", - "team2r": "Equipo 2 Refutación", - "team1s": "Equipo 1 Resumen", - "team2s": "Equipo 2 Resumen", - "team1ff": "Equipo 1 Enfoque Final", - "team2ff": "Equipo 2 Enfoque Final" + "team1c": "Equipo 1 constructivo", + "team2c": "Equipo 2 constructivo", + "team1r": "Equipo 1 refutación", + "team2r": "Equipo 2 refutación", + "team1s": "Equipo 1 resumen", + "team2s": "Equipo 2 resumen", + "team1ff": "Equipo 1 enfoque final", + "team2ff": "Equipo 2 enfoque final" }, "policy": { "1ac": "1AC", @@ -1461,22 +1454,22 @@ "resetDone": "Temporizador reiniciado" }, "flowchart": { - "title": "Diagrama de Argumentos", + "title": "Diagrama de argumentos", "load": "Cargar", "save": "Guardar", - "addNode": "Añadir Nodo", + "addNode": "Añadir nodo", "edit": "Editar", "delete": "Eliminar", "exportPng": "Exportar PNG", - "saveChanges": "Guardar Cambios", - "saveFlowchart": "Guardar Diagrama", - "savedFlowcharts": "Diagramas Guardados", + "saveChanges": "Guardar cambios", + "saveFlowchart": "Guardar diagrama", + "savedFlowcharts": "Diagramas guardados", "savedFlowchartsDesc": "Carga un diagrama guardado anteriormente", - "saveFlowchartDialog": "Guardar Diagrama", + "saveFlowchartDialog": "Guardar diagrama", "saveFlowchartDialogDesc": "Dale un nombre a tu diagrama para guardarlo", - "editNode": "Editar Nodo", + "editNode": "Editar nodo", "editNodeDesc": "Modifica el nodo seleccionado", - "addNodeDialog": "Añadir Nodo", + "addNodeDialog": "Añadir nodo", "addNodeDialogDesc": "Crea un nuevo nodo de argumento", "noSavedFlowcharts": "No hay diagramas guardados aún", "labelField": "Etiqueta", @@ -1539,14 +1532,14 @@ "tabCases": "Casos", "tabChats": "Chats", "tabAttacks": "Ataques", - "tabBrainstorms": "Lluvias de Ideas", + "tabBrainstorms": "Lluvias de ideas", "tabFactoids": "Datos", "tabSparring": "Sparring", - "casesTitle": "Casos Guardados", - "chatsTitle": "Historial de Chats", - "attacksTitle": "Análisis de Ataques", - "brainstormsTitle": "Sesiones de Lluvia de Ideas", - "factoidsTitle": "Datos Guardados", + "casesTitle": "Casos guardados", + "chatsTitle": "Historial de chats", + "attacksTitle": "Análisis de ataques", + "brainstormsTitle": "Sesiones de lluvia de ideas", + "factoidsTitle": "Datos guardados", "emptyCases": "No hay casos guardados aún", "emptyCasesDesc": "Los casos generados que guardes aparecerán aquí.", "emptyChats": "No hay chats guardados aún", @@ -1582,10 +1575,10 @@ "deleteFailed": "No se pudo eliminar el caso guardado." }, "libraryPage": { - "title": "Biblioteca de Fuentes", - "addSource": "Añadir Fuente", + "title": "Biblioteca de fuentes", + "addSource": "Añadir fuente", "cancel": "Cancelar", - "clearAll": "Borrar Todo", + "clearAll": "Borrar todo", "clearing": "Borrando...", "importUrl": "Importar URL", "importUrlLabel": "Importar desde URL", @@ -1622,9 +1615,9 @@ "duplicateSource": "La fuente ya está en la biblioteca", "duplicateSourceDesc": "Esta URL o archivo ya está guardado.", "fileReadFailed": "No se pudo leer este archivo.", - "saveToLibrary": "Guardar en Biblioteca", - "addNewTitle": "Añadir Nueva Fuente", - "yourKBTitle": "Tu Base de Conocimiento", + "saveToLibrary": "Guardar en biblioteca", + "addNewTitle": "Añadir nueva fuente", + "yourKBTitle": "Tu base de conocimiento", "titleLabel": "Título", "originLabel": "Origen", "contentLabel": "Contenido", @@ -1632,11 +1625,11 @@ "originPlaceholder": "URL o referencia...", "contentPlaceholder": "Pega el contenido de la fuente...", "sourcesCount": "fuentes", - "emptyTitle": "Sin Fuentes Aún", + "emptyTitle": "Sin fuentes aún", "emptyDesc": "Añade fuentes para fundamentar las respuestas de IA con evidencia real.", - "manualEntry": "Entrada Manual", - "groundingActive": "Fundamentación Activa", - "groundingDisabled": "Fundamentación Desactivada", + "manualEntry": "Entrada manual", + "groundingActive": "Fundamentación activa", + "groundingDisabled": "Fundamentación desactivada", "groundingActiveDesc": "Las respuestas de IA usan tus fuentes", "groundingDisabledDesc": "Las respuestas de IA no están fundamentadas", "clearFailed": "Error al borrar fuentes", @@ -1693,64 +1686,64 @@ "removedDesc_other": "Se eliminaron {{count}} fuentes guardadas automáticamente de tu biblioteca." }, "sharedPage": { - "title": "Contenido Compartido", + "title": "Contenido compartido", "noCode": "No se proporcionó código de compartir", "notFound": "Contenido no encontrado", "loadFailed": "Error al cargar contenido compartido", "contentNotFound": "Este contenido compartido ya no está disponible", - "goToApp": "Ir a la App", - "openApp": "Abrir App", + "goToApp": "Ir a la app", + "openApp": "Abrir app", "sharedOn": "Compartido el", "views": "vistas" }, "privacyPage": { - "title": "Política de Privacidad", + "title": "Política de privacidad", "lastUpdated": "Última actualización", "date": "1 de octubre de 2026", - "s1": "Información que Recopilamos", + "s1": "Información que recopilamos", "p1": "Recopilamos la información que proporcionas al crear una cuenta, incluido tu nombre y correo electrónico, que pueden proceder del inicio de sesión con Google o por email, además de datos de uso como contenido generado, elementos guardados y patrones de interacción. Si usas un código demo emitido por un administrador, procesamos ese código para autenticar tu cuenta demo.", - "s2": "Cómo Usamos Tu Información", + "s2": "Cómo usamos tu información", "p2": "Tu información se usa para proporcionar y mejorar nuestros servicios, generar respuestas de IA, mantener tu contenido guardado y comunicar actualizaciones.", "s3": "Procesamiento de IA", "p3": "Tus entradas se envían a OpenRouter para el procesamiento de IA con el modelo que selecciones. No usamos tu contenido para entrenar modelos de IA. En una ronda con voz, el reconocimiento de voz de tu navegador convierte tu voz en texto; en Chrome, eso envía tu audio a Google, y otros navegadores pueden enviarlo a su propio servicio de voz. DebateLab nunca graba ni guarda tu audio. Las respuestas del oponente de IA se envían como texto a ElevenLabs para producir su voz.", - "s4": "Almacenamiento de Datos", + "s4": "Almacenamiento de datos", "p4": "Tus datos se almacenan de forma segura usando infraestructura de Supabase con cifrado en reposo y en tránsito.", - "s5": "Compartir Datos", + "s5": "Compartir datos", "p5": "No vendemos tus datos personales. Solo compartimos datos con OpenRouter para el procesamiento de IA, con ElevenLabs para la voz del oponente en las rondas con voz y con procesadores de pago para la facturación.", - "s6": "Tus Derechos", + "s6": "Tus derechos", "p6": "En Configuración puedes descargar una copia de tus datos en un archivo JSON, borrar tu trabajo guardado o eliminar tu cuenta. Eliminar la cuenta la borra para siempre junto con todos los datos guardados en ella y cancela de inmediato cualquier suscripción Pro. Stripe conserva sus propios registros de facturación, como facturas y pagos, según exigen las normas fiscales y contables.", - "s7": "Cookies y Seguimiento", + "s7": "Cookies y seguimiento", "p7": "Usamos cookies y almacenamiento del navegador esenciales para la autenticación y las preferencias. Usamos PostHog para analítica de producto y Sentry para monitorización de errores, registros estructurados, métricas de la aplicación, seguimiento del rendimiento y reproducción de sesiones solo cuando hay errores. Sentry no registra sesiones normales. Registra sesiones que contienen errores capturados; todo el texto y los campos de entrada se ocultan en las reproducciones y el contenido multimedia se bloquea. Sentry recibe metadatos técnicos como rutas, datos del navegador, URL, métodos y códigos de estado de solicitudes, trazas de pila, tiempos y clasificaciones seguras de eventos. La recopilación automática de identidad del usuario, cookies, cabeceras, cuerpos HTTP, parámetros de consulta, entradas y salidas de IA y variables de marcos de pila está desactivada. La captura automática, el registro de páginas vistas, la grabación de sesiones y el almacenamiento persistente de PostHog están desactivados. No enviamos contenido de debates, mensajes de chat, títulos de fuentes ni direcciones de correo electrónico en eventos analíticos o registros estructurados.", - "s8": "Privacidad de Menores", + "s8": "Privacidad de menores", "p8": "DebateLab está diseñado para usuarios de 13 años o más. No recopilamos datos de menores de 13 años conscientemente.", - "s9": "Cambios a Esta Política", + "s9": "Cambios a esta política", "p9": "Podemos actualizar esta política periódicamente. Los cambios significativos se comunicarán por email o notificación en la app.", "s10": "Contáctanos", "p10": "Para preguntas de privacidad o solicitudes de datos, contáctanos en privacy@debatelab.app." }, "termsPage": { - "title": "Términos de Servicio", + "title": "Términos de servicio", "lastUpdated": "Última actualización", "date": "1 de mayo de 2026", - "s1": "Aceptación de Términos", + "s1": "Aceptación de términos", "p1": "Al usar DebateLab, aceptas estos términos. Si no estás de acuerdo, no uses el servicio.", - "s2": "Descripción del Servicio", + "s2": "Descripción del servicio", "p2": "DebateLab es una herramienta de preparación de debates impulsada por IA que proporciona generación de argumentos, análisis y funciones de coaching.", - "s3": "Requisitos de Cuenta", + "s3": "Requisitos de cuenta", "p3": "Puedes iniciar sesión con Google, mediante un enlace enviado por email o con un código demo emitido por un administrador. Los códigos demo proporcionan acceso temporal a una cuenta designada. Eres responsable de toda la actividad de tu cuenta.", - "s4": "Suscripción y Facturación", + "s4": "Suscripción y facturación", "p4": "Las pruebas gratuitas duran 3 días con solicitudes limitadas. Las suscripciones Pro cuestan $5/mes más impuestos, facturadas vía Stripe. Cancela en cualquier momento.", - "s5": "Uso Aceptable", + "s5": "Uso aceptable", "p5": "No puedes usar DebateLab para acoso, generar contenido dañino, deshonestidad académica que viole las políticas de tu institución, ni ningún propósito ilegal.", - "s6": "Contenido Generado por IA", + "s6": "Contenido generado por IA", "p6": "Las salidas de IA se proporcionan tal cual sin garantías de precisión. Eres responsable de verificar hechos y citas antes de usar en competencias.", - "s7": "Propiedad Intelectual", + "s7": "Propiedad intelectual", "p7": "Conservas la propiedad de tus entradas. Las salidas generadas por IA se licencian para uso personal y educativo. DebateLab retiene derechos sobre la plataforma.", - "s8": "Limitación de Responsabilidad", + "s8": "Limitación de responsabilidad", "p8": "DebateLab se proporciona tal cual. No somos responsables por resultados de competencias, salidas de IA inexactas o interrupciones del servicio.", "s9": "Terminación", "p9": "Podemos suspender o terminar cuentas que violen estos términos. Puedes eliminar tu cuenta en cualquier momento.", - "s10": "Ley Aplicable", + "s10": "Ley aplicable", "p10": "Estos términos se rigen por la ley aplicable. Las disputas se resolverán mediante arbitraje vinculante." }, "export": { @@ -1812,38 +1805,38 @@ "target": "Objetivo", "counter": "Contra", "rebuttal": "Refutación", - "debateCase": "Caso de Debate", + "debateCase": "Caso de debate", "side": "Lado", "proposition": "A favor", "opposition": "En contra", - "caseAttack": "Análisis de Ataque al Caso", - "overallScore": "Puntuación General", + "caseAttack": "Análisis de ataque al caso", + "overallScore": "Puntuación general", "whyWeak": "Por qué es débil", "howOpponentAttacks": "Cómo ataca el oponente", - "improvedVersion": "Versión Mejorada", + "improvedVersion": "Versión mejorada", "noWeaknesses": "No se identificaron debilidades.", "noFallacies": "No se detectaron falacias.", "noCrossEx": "No se proporcionaron preguntas de interrogatorio cruzado.", - "brainstormResults": "Resultados de Lluvia de Ideas", + "brainstormResults": "Resultados de lluvia de ideas", "whyTop": "Por qué es top", - "researchFactoids": "Datos de Investigación", - "keyFigure": "Cifra Clave", + "researchFactoids": "Datos de investigación", + "keyFigure": "Cifra clave", "context": "Contexto", - "howToUse": "Cómo Usar", + "howToUse": "Cómo usar", "source": "Fuente", "noFactoids": "No se encontraron datos.", "exordio": "Exordio", - "caseStructure": "Estructura del Caso", + "caseStructure": "Estructura del caso", "thesis": "Tesis", "framework": "Marco", - "burdenOfProof": "Carga de la Prueba", - "keyDefinitions": "Definiciones Clave", + "burdenOfProof": "Carga de la prueba", + "keyDefinitions": "Definiciones clave", "arguments": "Argumentos", - "predictedOpponent": "Caso Predicho del Oponente", - "counters": "Contra-argumentos Preparados", - "weaknesses": "Debilidades Identificadas", - "fallacies": "Falacias Detectadas", - "crossExQuestions": "Preguntas de Interrogatorio Cruzado", + "predictedOpponent": "Caso predicho del oponente", + "counters": "Contra-argumentos preparados", + "weaknesses": "Debilidades identificadas", + "fallacies": "Falacias detectadas", + "crossExQuestions": "Preguntas de interrogatorio cruzado", "motion": "Moción", "sparringTranscript": "Transcripción de sparring", "sparringConfiguration": "Configuración de la ronda", diff --git a/src/locales/hu.json b/src/locales/hu.json index 0698e6a9b..b72ae0fea 100644 --- a/src/locales/hu.json +++ b/src/locales/hu.json @@ -15,8 +15,8 @@ "features": "Funkciók", "whatsNew": "Újdonságok", "pricing": "Árak", - "howItWorks": "Hogyan Működik", - "startTrial": "Próba Indítása", + "howItWorks": "Hogyan működik", + "startTrial": "Próba indítása", "settings": "Beállítások", "openMenu": "Menü megnyitása", "closeMenu": "Menü bezárása", @@ -360,15 +360,15 @@ "heading": "Min dolgozol most?" }, "generate": { - "emptyTitle": "Az Eseted Vár", + "emptyTitle": "Az eseted vár", "emptyDesc": "Töltsd ki az űrlapot egy strukturált vitaeset generálásához érvekkel, bizonyítékokkal és ellen-stratégiákkal.", - "saved": "Eset Mentve", + "saved": "Eset mentve", "savedDesc": "Az eseted mentésre került az előzményekbe.", "savePrompt": "Adj meg egy címet ehhez az esethez:", - "failed": "Generálás Sikertelen", + "failed": "Generálás sikertelen", "timeout": "Időtúllépés", "timeoutDesc": "Az MI túl sokáig válaszolt. Próbáld újra.", - "authRequired": "Hitelesítés Szükséges", + "authRequired": "Hitelesítés szükséges", "flowchartImported": "Eset importálva", "flowchartImportedDesc": "Az eseted átkerült a folyamatábrákhoz.", "flowchartImportFailed": "Import sikertelen", @@ -387,22 +387,22 @@ "working": "Az eseted megírása…" }, "attack": { - "emptyTitle": "Kegyetlen Mód", + "emptyTitle": "Kegyetlen mód", "emptyDesc": "Illeszd be az érvedet kíméletlen kritikáért, logikai hibák azonosításáért és megvalósítható javításokért.", - "failed": "Támadás Elemzés Sikertelen", + "failed": "Támadás elemzés sikertelen", "working": "Az eseted átnézése…" }, "brainstorm": { - "emptyTitle": "Kreatív Szögek Feloldása", + "emptyTitle": "Kreatív szögek feloldása", "emptyDesc": "Írd le az indítványodat szokatlan érvelési szögek felfedezéséhez, kompromisszumok azonosításához és legerősebb pozícióid megtalálásához.", - "failed": "Ötletelés Sikertelen", + "failed": "Ötletelés sikertelen", "working": "Szempontok keresése…" }, "factoids": { - "emptyTitle": "Győztes Bizonyítékok", + "emptyTitle": "Győztes bizonyítékok", "emptyDesc": "Adj meg egy állítást támogató tények kereséséhez valós hivatkozásokkal, kontextussal és útmutatóval a hatékony felhasználáshoz.", - "failed": "Kutatás Sikertelen", - "savedToLibrary": "Mentve a Könyvtárba", + "failed": "Kutatás sikertelen", + "savedToLibrary": "Mentve a könyvtárba", "savedToLibraryDesc": "Ez a bizonyíték mostantól elérhető megalapozáshoz.", "saveFailed": "Nem sikerült menteni a forrást.", "working": "Bizonyítékok keresése…" @@ -428,9 +428,9 @@ "chat": {}, "settings": { "title": "Beállítások", - "aiOutputLanguage": "Nyelv (Felület + MI Kimenet)", - "uiLanguage": "Felület Nyelve", - "outputLanguage": "MI Kimeneti Nyelv", + "aiOutputLanguage": "Nyelv (felület + MI kimenet)", + "uiLanguage": "Felület nyelve", + "outputLanguage": "MI kimeneti nyelv", "theme": "Téma", "light": "Világos", "dark": "Sötét", @@ -819,7 +819,7 @@ "notFound": { "title": "404", "message": "Hoppá! Az oldal nem található", - "link": "Vissza a Kezdőlapra" + "link": "Vissza a kezdőlapra" }, "footer": { "terms": "Feltételek", @@ -833,14 +833,14 @@ "dismissError": "A közleményt nem sikerült bezárni." }, "hero": { - "headline": "Készülj Gyorsabban. Érvelj Okosabban. Nyerj Több Fordulót.", + "headline": "Készülj gyorsabban. Érvelj okosabban. Nyerj több fordulót.", "subheadline": "MI-alapú vitafelkészülés, ami előnyt ad minden fordulóban.", "trialBadge": "3 napos ingyenes próba", "requestsBadge": "Napi MI-felkészülés", "requestsBadgeValue": "Benne van", "proBadge": "$5/hó Pro", - "cta": "Próba Indítása", - "ctaSecondary": "Árak Megtekintése", + "cta": "Próba indítása", + "ctaSecondary": "Árak megtekintése", "trustBullet1": "Bankkártya nem szükséges", "trustBullet2": "Bármikor lemondható", "trustBullet3": "Bármely formátummal működik", @@ -875,11 +875,11 @@ } }, "features": { - "title": "Minden, Ami a Győzelemhez Kell", + "title": "Minden, ami a győzelemhez kell", "subtitle": "Tizenegy vitás eszköz a versenyszerű felkészüléshez.", - "generate": "Eset Generátor", + "generate": "Eset generátor", "generateDesc": "Építs teljes eseteket strukturált érvekkel, bizonyítékokkal és ellen-stratégiákkal.", - "attack": "Eset Támadó", + "attack": "Eset támadó", "attackDesc": "Teszteld az érveidet és találd meg a gyengeségeket az ellenfelek előtt.", "brainstorm": "Ötletelés", "brainstormDesc": "Fedezz fel szokatlan szögeket és találj friss érvelési utakat.", @@ -893,7 +893,7 @@ "sparringDesc": "Gyakorolj egy időzített fordulót ellenféllel, majd nézd át az értékelőlapot.", "library": "Forráskönyvtár", "libraryDesc": "Kezeld a forrásokat, amelyek valós bizonyítékokkal alapozzák meg az MI válaszait.", - "timer": "Vita Időmérő", + "timer": "Vita időmérő", "timerDesc": "Gyakorolj versenypontos időzítéssel.", "flowchart": "Folyamatábra", "flowchartDesc": "Térképezd az érvstruktúrákat és cáfolati kapcsolatokat vizuálisan.", @@ -901,7 +901,7 @@ "historyDesc": "Érd el a mentett eseteket, chateket és korábbi felkészüléseket." }, "howItWorks": { - "title": "Hogyan Működik", + "title": "Hogyan működik", "subtitle": "Három lépés a jobb vitafelkészüléshez.", "step1Title": "Kutatás", "step1Desc": "Az MI ellenőrzött forrásokat vizsgál és kontextust épít az indítványodhoz.", @@ -911,7 +911,7 @@ "step3Desc": "Finomíts coachinggal, időzítési gyakorlatokkal és érvtérképezéssel." }, "useCases": { - "title": "Mindenkinek Készült a Vitában", + "title": "Mindenkinek készült a vitában", "description": "Akár versenyzel, edzősködsz vagy bíráskodsz, a DebateLab előnyhöz juttat.", "forDebaters": "Vitázóknak", "forCoaches": "Edzőknek", @@ -933,7 +933,7 @@ "judgeSubtitle": "Mélyítsd el az elemzésedet" }, "pricing": { - "title": "Egyszerű, Átlátható Árazás", + "title": "Egyszerű, átlátható árazás", "subtitle": "Kezdj ingyen, frissíts amikor készen állsz.", "trial": "Próba", "trialPrice": "$0", @@ -951,8 +951,8 @@ "proFeature3": "Mind a 10 modell, köztük a Claude Opus és Sonnet, a GPT-6.1 Sol és a Gemini", "proFeature4": "Elsőbbségi támogatás", "proFeature5": "Active Grounding™ kutatás", - "startTrial": "Ingyenes Próba Indítása", - "subscribe": "Feliratkozás Most", + "startTrial": "Ingyenes próba indítása", + "subscribe": "Feliratkozás most", "noFreePlan": "Nincs állandó ingyenes csomag. A próbaidőszak lejárta után előfizetés szükséges a DebateLab további használatához.", "trialBadge": "Új felhasználóknak", "trialDesc": "Próbáld ki a teljes munkafolyamatot, mielőtt a Pro mellett döntesz.", @@ -960,7 +960,7 @@ "proDesc": "Intenzív felkészülési hetekhez, nehezebb témákhoz és magasabb napi kerethez." }, "faq": { - "title": "Gyakran Ismételt Kérdések", + "title": "Gyakran ismételt kérdések", "q1": "Mennyibe kerül a DebateLab?", "a1": "A DebateLab 3 napos ingyenes próbát kínál. Ezután a Pro $5/hó + adó bővített hozzáférést és mind a 10 modellt kínálja.", "q2": "Mit kapok a Pro-val?", @@ -974,28 +974,21 @@ "subtitle": "Árazás, munkafolyamat és termékrészletek röviden." }, "finalCta": { - "title": "Készen Állsz a Következő Győztes Eseted Felépítésére?", + "title": "Készen állsz a következő győztes eseted felépítésére?", "subtitle": "Csatlakozz a vitázókhoz, akik MI-vel készülnek okosabban.", - "cta": "DebateLab Indítása", + "cta": "DebateLab indítása", "freeTrialAvailable": "Ingyenes próba elérhető" }, - "socialProof": { - "modes": "Felkészülési Mód", - "plans": "Csomag", - "upgrades": "Jelentős 2026-os Frissítés", - "workspace": "Egységes Munkaterület", - "description": "Egy platform kutatáshoz, stratégiához, cáfolatokhoz és beszédgyakorláshoz." - }, "whatsNew": { "title": "Újdonságok", "subtitle": "Legújabb frissítések és fejlesztések.", - "sub1Title": "Előfizetési Szintek", + "sub1Title": "Előfizetési szintek", "sub1Desc": "Ingyenes próba és Pro csomag magasabb használati kerettel.", - "sub2Title": "Érv Folyamatábrák", + "sub2Title": "Érv folyamatábrák", "sub2Desc": "Vizuális érvtérképezés húzd-és-ejtsd csomópontokkal.", - "sub3Title": "Fejlesztett Időmérő", + "sub3Title": "Fejlesztett időmérő", "sub3Desc": "Versenypontos időzítés egyéni formátumokkal.", - "sub4Title": "Mentett Felkészülési Előzmények", + "sub4Title": "Mentett felkészülési előzmények", "sub4Desc": "Érd el az összes korábbi esetedet és munkamenetedet.", "sub5Title": "Keresztkérdezési gyakorlás", "sub5Desc": "Gyakorold a célzott kérdéseket és válaszokat időzített párbeszédben.", @@ -1009,15 +1002,15 @@ "sub4Badge": "Produktivitás" }, "deepDive": { - "title": "Active Grounding™ Technológia", + "title": "Active Grounding™ technológia", "subtitle": "Hogyan tartjuk őszintén az MI válaszokat.", - "step1": "MI Szkennelés Exa-val", + "step1": "MI szkennelés Exa-val", "step1Desc": "A neurális keresés releváns, magas minőségű forrásokat talál.", "step2": "Tudásbázis", "step2Desc": "A forrásaid vektorizálva vannak szemantikus egyeztetéshez.", - "step3": "Kontextus Injektálás", + "step3": "Kontextus injektálás", "step3Desc": "Az ellenőrzött tények bekerülnek az MI promptba.", - "step4": "Megalapozott Kimenet", + "step4": "Megalapozott kimenet", "step4Desc": "A válaszok valós forrásokat idéznek, nem hallucinációkat.", "preview": { "processHeading": "// Valós idejű kontextusfolyamat", @@ -1031,10 +1024,10 @@ } }, "rateLimit": { - "title": "Kéréslimit Túllépve" + "title": "Kéréslimit túllépve" }, "generateForm": { - "motion": "Indítvány / Téma", + "motion": "Indítvány / téma", "motionPlaceholder": "pl. Ez a ház úgy véli, hogy a mesterséges intelligencia több munkahelyet teremt, mint amennyit megszüntet", "side": "Oldal", "sidePro": "Mellette", @@ -1046,7 +1039,7 @@ "roleR2": "R2 - Cáfolat/Építés (6 perc)", "roleConc": "Záró (3 perc)", "moreOptions": "További opciók", - "length": "Eset Hossza", + "length": "Eset hossza", "lengthBrief": "Rövid", "lengthStandard": "Standard", "lengthComprehensive": "Átfogó", @@ -1058,8 +1051,8 @@ "toneFormal": "Formális", "toneConversational": "Társalgási", "toneAggressive": "Agresszív", - "generating": "Eset Generálása...", - "submit": "Vitaeset Generálása", + "generating": "Eset generálása...", + "submit": "Vitaeset generálása", "listening": "Hallgatás... Mondd el az indítványt tisztán", "stopListening": "Hallgatás leállítása", "startVoice": "Hangbevitel indítása", @@ -1068,7 +1061,7 @@ "styleNote": "A írásstílust befolyásolja; a kutatást nem változtatja meg." }, "generateResults": { - "incomplete": "Generálás Befejezetlen", + "incomplete": "Generálás befejezetlen", "incompleteDesc": "Az MI válasza hiányos vagy hibás volt. Próbáld újra generálni.", "proposition": "Mellette", "opposition": "Ellene", @@ -1083,23 +1076,23 @@ "exordioDesc": "A meggyőző nyitásod", "hook": "Felvezetés", "transition": "Átvezetés", - "caseStructure": "Eset Struktúra", + "caseStructure": "Eset struktúra", "caseStructureDesc": "Az érvelési kereted", "thesis": "Tézis", "framework": "Keretrendszer", - "burdenOfProof": "Bizonyítási Teher", + "burdenOfProof": "Bizonyítási teher", "keyDefinitions": "Kulcsdefiníciók", "noDefinitions": "Nincsenek megadott definíciók", "arguments": "Érvek", "structuredArguments": "strukturált érv", - "predictedOpponent": "Várható Ellenfél Eset", + "predictedOpponent": "Várható ellenfél eset", "predictedOpponentDesc": "Amit valószínűleg érvelni fognak", - "likelyArguments": "Valószínű Érvek", - "strategicWeaknesses": "Stratégiai Gyengeségek", - "keyClashes": "Kulcs Ütközések", - "preparedCounters": "Előkészített Ellenérvek", + "likelyArguments": "Valószínű érvek", + "strategicWeaknesses": "Stratégiai gyengeségek", + "keyClashes": "Kulcs ütközések", + "preparedCounters": "Előkészített ellenérvek", "preparedCountersDesc": "Használatra kész cáfolatok", - "needsSource": "Forrás Szükséges", + "needsSource": "Forrás szükséges", "reasoning": "Indoklás:", "evidence": "Bizonyíték:", "searchSuggestion": "Keresési javaslat:", @@ -1119,43 +1112,43 @@ "unresearchedCaveat": "MI által készült, és ezt a bizonyítékot nem támasztja alá webes kutatás. Ellenőrizd a tényeket és a forrásokat, mielőtt egy fordulóban használod." }, "attackForm": { - "argument": "Az Érved", + "argument": "Az érved", "argumentPlaceholder": "Illeszd be a vitaesetedet vagy érvedet ide...", "options": "Beállítások", "moreOptions": "További beállítások", - "intensity": "Támadás Intenzitása", - "intensityMild": "Enyhe (Építő jellegű)", - "intensityModerate": "Mérsékelt (Kiegyensúlyozott)", - "intensityRuthless": "Kíméletlen (Nincs kegyelem)", - "paradigm": "Bírói Paradigma", + "intensity": "Támadás intenzitása", + "intensityMild": "Enyhe (építő jellegű)", + "intensityModerate": "Mérsékelt (kiegyensúlyozott)", + "intensityRuthless": "Kíméletlen (nincs kegyelem)", + "paradigm": "Bírói paradigma", "paradigmLogic": "Logika-központú", "paradigmValues": "Érték-központú", "paradigmPolicy": "Politika/Hatás", - "detectFallacies": "Téves Következtetések Keresése", + "detectFallacies": "Téves következtetések keresése", "detectFallaciesDesc": "Logikai hibák azonosítása az érvben", - "analyzeWeighing": "Súlyozás Elemzése", + "analyzeWeighing": "Súlyozás elemzése", "analyzeWeighingDesc": "Hatás-összehasonlítás és prioritizálás értékelése", - "analyzing": "Eset Elemzése...", - "submit": "Gyengeségek Keresése" + "analyzing": "Eset elemzése...", + "submit": "Gyengeségek keresése" }, "attackResults": { - "overall": "Összesített Értékelés", - "caseStrength": "Eset Erőssége", - "weaknesses": "Azonosított Gyengeségek", + "overall": "Összesített értékelés", + "caseStrength": "Eset erőssége", + "weaknesses": "Azonosított gyengeségek", "weaknessesCount": "{{count}} probléma találva, súlyosság szerint rendezve", - "fallacies": "Észlelt Téves Következtetések", + "fallacies": "Észlelt téves következtetések", "fallaciesDesc": "Logikai hibák az érvedben", - "crossEx": "Keresztkérdezési Kérdések", + "crossEx": "Keresztkérdezési kérdések", "crossExDesc": "Kérdések, amiket az ellenfelek feltehetnek", - "weighing": "Súlyozás Elemzése", + "weighing": "Súlyozás elemzése", "weighingDesc": "Mennyire jól hasonlítod össze a hatásokat", - "currentWeighing": "Jelenlegi Súlyozás", - "missingComparisons": "Hiányzó Összehasonlítások", - "improvements": "Javítási Javaslatok", - "strengths": "Erős Területek", + "currentWeighing": "Jelenlegi súlyozás", + "missingComparisons": "Hiányzó összehasonlítások", + "improvements": "Javítási javaslatok", + "strengths": "Erős területek", "whyWeak": "Miért gyenge:", "opponentAttack": "Ellenfél támadása:", - "improvedVersion": "Javított Verzió", + "improvedVersion": "Javított verzió", "purpose": "Cél:", "expectedTrap": "Várható csapda:", "in": "-ban/-ben", @@ -1182,36 +1175,36 @@ "resultTitle": "Kritika" }, "brainstormForm": { - "motion": "Indítvány / Téma", + "motion": "Indítvány / téma", "motionPlaceholder": "pl. Az Univerzális Alapjövedelmet globálisan be kellene vezetni", - "side": "Vizsgálandó Oldal", - "sideBoth": "Mindkét Oldal Vizsgálata", + "side": "Vizsgálandó oldal", + "sideBoth": "Mindkét oldal vizsgálata", "sidePro": "Csak mellette", "sideCon": "Csak ellene", - "outputFormat": "Kimeneti Formátum", - "outputTop5": "Az 5 Legjobb Szög", - "outputMindmap": "Gondolattérkép Stílus", - "outputBuckets": "Kategorizált Csoportok", - "existing": "Ami Már Megvan (Opcionális)", + "outputFormat": "Kimeneti formátum", + "outputTop5": "Az 5 legjobb szög", + "outputMindmap": "Gondolattérkép stílus", + "outputBuckets": "Kategorizált csoportok", + "existing": "Ami már megvan (opcionális)", "existingPlaceholder": "Oszd meg az ötleteidet vagy szögeket, amiket már fontolóra vettél...", - "generating": "Ötletek Generálása...", - "submit": "Ötletelés Indítása" + "generating": "Ötletek generálása...", + "submit": "Ötletelés indítása" }, "brainstormResults": { - "clarification": "Pontosítás Szükséges", + "clarification": "Pontosítás szükséges", "clarificationDesc": "Válaszolj ezekre a kérdésekre a jobb eredményekért", - "top5": "Az 5 Legerősebb Érvelési Szög", + "top5": "Az 5 legerősebb érvelési szög", "top5Desc": "Legjobb lehetőségek potenciál szerint rangsorolva", "argMap": "Érvtérkép", "argMapDesc": "Ötletek kategória szerint rendezve", - "nextSteps": "Javasolt Következő Lépések", - "warnings": "Figyelmeztetések és Kompromisszumok", + "nextSteps": "Javasolt következő lépések", + "warnings": "Figyelmeztetések és kompromisszumok", "pro": "MELLETTE", "con": "ELLENE", "neutral": "SEMLEGES", - "untitled": "Névtelen Ötlet", + "untitled": "Névtelen ötlet", "tradeoffs": "Kompromisszumok:", - "discuss": "Ötletek Megvitatása", + "discuss": "Ötletek megvitatása", "exportedDocx": "Ötletelés exportálva DOCX-ba!", "exportFailedDocx": "Nem sikerült exportálni DOCX-ba", "exportedGdocs": "Ötletelés exportálva Google Docs-ba!", @@ -1228,42 +1221,42 @@ "direction": "Álláspont", "directionFor": "Mellette", "directionAgainst": "Ellene", - "region": "Régió (Opcionális)", + "region": "Régió (opcionális)", "regionPlaceholder": "Alapértelmezés: globális", "sourceCount": "Források", - "timeframe": "Időszak (Opcionális)", + "timeframe": "Időszak (opcionális)", "timeframePlaceholder": "pl. 2020-2024, Elmúlt évtized", - "sourceType": "Forrás Típusa", - "sourceAny": "Bármely Megbízható Forrás", - "sourceAcademic": "Csak Akadémiai", - "sourceGovernment": "Kormányzati Források", - "sourceNews": "Fő Hírek", - "finding": "Bizonyítékok Keresése...", - "submit": "Tények Keresése" + "sourceType": "Forrás típusa", + "sourceAny": "Bármely megbízható forrás", + "sourceAcademic": "Csak akadémiai", + "sourceGovernment": "Kormányzati források", + "sourceNews": "Fő hírek", + "finding": "Bizonyítékok keresése...", + "submit": "Tények keresése" }, "factoidsResults": { - "verificationStatus": "Ellenőrzési Állapot", + "verificationStatus": "Ellenőrzési állapot", "verified": "Ellenőrzött", - "unverified": "Nem Ellenőrzött", + "unverified": "Nem ellenőrzött", "pending": "Függőben", - "importantNotes": "Fontos Megjegyzések", + "importantNotes": "Fontos megjegyzések", "found": "Találat", "factoids": "Tény", "factoidsDesc": "Bizonyítékok hivatkozásokkal", - "findMore": "További Bizonyítékok Keresése", + "findMore": "További bizonyítékok keresése", "findMoreDesc": "Javasolt keresési lekérdezések", "relevance": "Relevancia:", "keyFigure": "Kulcsadat:", "context": "Kontextus:", "limitations": "Korlátok:", - "unknownSource": "Ismeretlen Forrás", + "unknownSource": "Ismeretlen forrás", "unknown": "Ismeretlen", - "viewSource": "Forrás Megtekintése", + "viewSource": "Forrás megtekintése", "urlNeedsVerification": "Az URL ellenőrzést igényel", "howToUse": "Használat:", - "saveToLibrary": "Mentés a Könyvtárba", + "saveToLibrary": "Mentés a könyvtárba", "saveClaimToLibrary": "„{{claim}}” mentése a könyvtárba", - "discuss": "Bizonyítékok Megvitatása", + "discuss": "Bizonyítékok megvitatása", "exportedDocx": "Tények exportálva DOCX-ba!", "exportFailedDocx": "Nem sikerült exportálni DOCX-ba", "exportedGdocs": "Tények exportálva Google Docs-ba!", @@ -1275,7 +1268,7 @@ "resultTitle": "Bizonyítékok" }, "askChat": { - "title": "Kérdezd az Edződ", + "title": "Kérdezd az edződ", "subtitle": "Kapj válaszokat vitakérdésekre", "startConversation": "Indíts beszélgetést", "startDesc": "Kérdezz bármit vitastratégiáról, érvekről, vagy kérj segítséget az esetedhez.", @@ -1301,7 +1294,7 @@ "cons": "Ellene", "placeholder": "Kérdezd a vitaedződ...", "placeholderListening": "Hallgatás...", - "saveToLibrary": "Mentés a Könyvtárba", + "saveToLibrary": "Mentés a könyvtárba", "saveMessage": "Üzenet mentése a könyvtárba", "clear": "Törlés", "attachFiles": "Fájlok csatolása", @@ -1333,7 +1326,7 @@ "options": "Beállítások" }, "limitModal": { - "trialEnded": "Ingyenes Próba Lejárt", + "trialEnded": "Ingyenes próba lejárt", "trialEndedDesc": "A 3 napos ingyenes próbaidőszakod lejárt. Frissíts Pro-ra a DebateLab további használatához.", "limitReached": "Napi hozzáférés szünetel", "limitReachedDesc": "Ma {{current}} / {{limit}} benne foglalt MI használatot használtál fel. A hozzáférés éjfélkor (UTC) frissül.", @@ -1370,28 +1363,28 @@ "proFeature4": "Elsőbbségi feldolgozás", "proFeature5": "Minden vitafunkció", "redirecting": "Átirányítás...", - "upgradeNow": "Frissítés Most", + "upgradeNow": "Frissítés most", "applying": "Alkalmazás...", "apply": "Alkalmaz", "promoCode": "Van promóciós kódod?", "promoPlaceholder": "Add meg a promóciós kódot", - "checkoutError": "Fizetési Hiba", + "checkoutError": "Fizetési hiba", "checkoutErrorDesc": "Nem sikerült elindítani a fizetést", - "codeRedeemed": "Kód Beváltva", - "invalidCode": "Érvénytelen Kód", + "codeRedeemed": "Kód beváltva", + "invalidCode": "Érvénytelen kód", "redeemError": "Hiba", "redeemErrorDesc": "Nem sikerült beváltani a kódot" }, "onboarding": { "welcome": "Üdvözlünk a DebateLab-ban! 🎉", - "ready": "Készen Állsz! 🚀", + "ready": "Készen állsz! 🚀", "readyDesc": "Fedezd fel a többi funkciót: Érvek támadása, Edző chat és Tények kutatása.", "back": "Vissza", "close": "Bezárás", "getStarted": "Kezdjük!", "next": "Következő", "nextWithProgress": "Következő ({step}/{steps})", - "skip": "Túra Kihagyása", + "skip": "Túra kihagyása", "generate": "Generálás", "generateDesc": "Készíts teljes esetet a tételedhez. Az Ötletelés, a Tények és a Folyamatábra mellette, a Felkészülés alatt található.", "library": "Forráskönyvtár", @@ -1400,14 +1393,14 @@ "debateCurrentDesc": "A DebateLabban minden az aktuális vitádhoz tartozik: a tételéhez, a forrásaihoz és az eredményeidhez. Itt nyithatod meg vagy cserélheted le." }, "timer": { - "title": "Vita Időmérő", + "title": "Vita időmérő", "displayLabel": "Időmérő kijelző", - "selectFormat": "Formátum Választása", + "selectFormat": "Formátum választása", "formatStandard": "Standard (4-6-6-3)", "formatLD": "Lincoln-Douglas", - "formatPF": "Nyilvános Fórum", + "formatPF": "Nyilvános fórum", "formatPolicy": "Politikai", - "timeUp": "Lejárt az Idő!", + "timeUp": "Lejárt az idő!", "speaking": "Beszél", "paused": "Szüneteltetve", "pause": "Szünet", @@ -1429,21 +1422,21 @@ "conc": "Összegző" }, "ld": { - "ac": "Igenlő Felépítés", - "nc": "Tagadó Felépítés", - "ar1": "1. Igenlő Cáfolat", - "nr": "Tagadó Cáfolat", - "ar2": "2. Igenlő Cáfolat" + "ac": "Igenlő felépítés", + "nc": "Tagadó felépítés", + "ar1": "1. igenlő cáfolat", + "nr": "Tagadó cáfolat", + "ar2": "2. igenlő cáfolat" }, "pf": { - "team1c": "1. Csapat Felépítés", - "team2c": "2. Csapat Felépítés", - "team1r": "1. Csapat Cáfolat", - "team2r": "2. Csapat Cáfolat", - "team1s": "1. Csapat Összefoglaló", - "team2s": "2. Csapat Összefoglaló", - "team1ff": "1. Csapat Záró Fókusz", - "team2ff": "2. Csapat Záró Fókusz" + "team1c": "1. csapat felépítés", + "team2c": "2. csapat felépítés", + "team1r": "1. csapat cáfolat", + "team2r": "2. csapat cáfolat", + "team1s": "1. csapat összefoglaló", + "team2s": "2. csapat összefoglaló", + "team1ff": "1. csapat záró fókusz", + "team2ff": "2. csapat záró fókusz" }, "policy": { "1ac": "1AC", @@ -1461,22 +1454,22 @@ "resetDone": "Időmérő visszaállítva" }, "flowchart": { - "title": "Érvelési Folyamatábra", + "title": "Érvelési folyamatábra", "load": "Betöltés", "save": "Mentés", - "addNode": "Csomópont Hozzáadása", + "addNode": "Csomópont hozzáadása", "edit": "Szerkesztés", "delete": "Törlés", - "exportPng": "PNG Exportálás", - "saveChanges": "Változások Mentése", - "saveFlowchart": "Folyamatábra Mentése", - "savedFlowcharts": "Mentett Folyamatábrák", + "exportPng": "PNG exportálás", + "saveChanges": "Változások mentése", + "saveFlowchart": "Folyamatábra mentése", + "savedFlowcharts": "Mentett folyamatábrák", "savedFlowchartsDesc": "Tölts be egy korábban mentett folyamatábrát", - "saveFlowchartDialog": "Folyamatábra Mentése", + "saveFlowchartDialog": "Folyamatábra mentése", "saveFlowchartDialogDesc": "Adj nevet a folyamatábrádnak a mentéshez", - "editNode": "Csomópont Szerkesztése", + "editNode": "Csomópont szerkesztése", "editNodeDesc": "Módosítsd a kiválasztott csomópontot", - "addNodeDialog": "Csomópont Hozzáadása", + "addNodeDialog": "Csomópont hozzáadása", "addNodeDialogDesc": "Hozz létre egy új érvelési csomópontot", "noSavedFlowcharts": "Még nincsenek mentett folyamatábrák", "labelField": "Címke", @@ -1542,11 +1535,11 @@ "tabBrainstorms": "Ötletelések", "tabFactoids": "Tények", "tabSparring": "Vitapárbaj", - "casesTitle": "Mentett Esetek", - "chatsTitle": "Chat Előzmények", - "attacksTitle": "Támadás Elemzések", - "brainstormsTitle": "Ötletelési Munkamenetek", - "factoidsTitle": "Mentett Tények", + "casesTitle": "Mentett esetek", + "chatsTitle": "Chat előzmények", + "attacksTitle": "Támadás elemzések", + "brainstormsTitle": "Ötletelési munkamenetek", + "factoidsTitle": "Mentett tények", "emptyCases": "Még nincsenek mentett esetek", "emptyCasesDesc": "Az elmentett generált esetek itt jelennek meg.", "emptyChats": "Még nincsenek mentett chatek", @@ -1583,9 +1576,9 @@ }, "libraryPage": { "title": "Forráskönyvtár", - "addSource": "Forrás Hozzáadása", + "addSource": "Forrás hozzáadása", "cancel": "Mégse", - "clearAll": "Összes Törlése", + "clearAll": "Összes törlése", "clearing": "Törlés...", "importUrl": "URL importálása", "importUrlLabel": "Importálás URL-ből", @@ -1622,9 +1615,9 @@ "duplicateSource": "A forrás már a könyvtárban van", "duplicateSourceDesc": "Ez a URL vagy fájl már mentve van.", "fileReadFailed": "Nem sikerült beolvasni ezt a fájlt.", - "saveToLibrary": "Mentés a Könyvtárba", - "addNewTitle": "Új Forrás Hozzáadása", - "yourKBTitle": "A Tudásbázisod", + "saveToLibrary": "Mentés a könyvtárba", + "addNewTitle": "Új forrás hozzáadása", + "yourKBTitle": "A tudásbázisod", "titleLabel": "Cím", "originLabel": "Eredet", "contentLabel": "Tartalom", @@ -1632,11 +1625,11 @@ "originPlaceholder": "URL vagy hivatkozás...", "contentPlaceholder": "Illeszd be a forrás tartalmát...", "sourcesCount": "forrás", - "emptyTitle": "Még Nincsenek Források", + "emptyTitle": "Még nincsenek források", "emptyDesc": "Adj hozzá forrásokat az MI válaszok valós bizonyítékokkal való megalapozásához.", - "manualEntry": "Kézi Bevitel", - "groundingActive": "Megalapozás Aktív", - "groundingDisabled": "Megalapozás Letiltva", + "manualEntry": "Kézi bevitel", + "groundingActive": "Megalapozás aktív", + "groundingDisabled": "Megalapozás letiltva", "groundingActiveDesc": "Az MI válaszok használják a forrásaidat", "groundingDisabledDesc": "Az MI válaszok nincsenek megalapozva", "clearFailed": "Nem sikerült törölni a forrásokat", @@ -1693,25 +1686,25 @@ "removedDesc_other": "{{count}} automatikusan mentett forrás eltávolítva a könyvtáradból." }, "sharedPage": { - "title": "Megosztott Tartalom", + "title": "Megosztott tartalom", "noCode": "Nincs megosztási kód megadva", "notFound": "Tartalom nem található", "loadFailed": "Nem sikerült betölteni a megosztott tartalmat", "contentNotFound": "Ez a megosztott tartalom már nem elérhető", - "goToApp": "Ugrás az Appba", - "openApp": "App Megnyitása", + "goToApp": "Ugrás az appba", + "openApp": "App megnyitása", "sharedOn": "Megosztva", "views": "megtekintés" }, "privacyPage": { - "title": "Adatvédelmi Irányelvek", + "title": "Adatvédelmi irányelvek", "lastUpdated": "Utolsó frissítés", "date": "2026. október 1.", - "s1": "Gyűjtött Információk", + "s1": "Gyűjtött információk", "p1": "Gyűjtjük a fiók létrehozásakor megadott adatokat, például a nevedet és az e-mail-címedet, amelyek Google- vagy e-mailes bejelentkezésből származhatnak, valamint a használati adatokat, például a generált tartalmakat, a mentett elemeket és az interakciós mintákat. Adminisztrátor által kiadott demókód használatakor a kódot a demo-fiókod hitelesítéséhez dolgozzuk fel.", - "s2": "Hogyan Használjuk az Információidat", + "s2": "Hogyan használjuk az információidat", "p2": "Az információidat szolgáltatásaink nyújtására és fejlesztésére, MI válaszok generálására, mentett tartalmad karbantartására és szolgáltatási frissítések kommunikálására használjuk.", - "s3": "MI Feldolgozás", + "s3": "MI feldolgozás", "p3": "A bemeneteidet az OpenRouternek küldjük MI-feldolgozásra az általad választott modellel. A tartalmadat nem használjuk MI-modellek betanítására. Hangos körben a böngésződ beszédfelismerése alakítja szöveggé a beszédedet; Chrome-ban ehhez a hangod a Google-höz kerül, más böngészők pedig a saját beszédfelismerő szolgáltatásuknak küldhetik el. A DebateLab soha nem rögzíti és nem tárolja a hangodat. Az MI-ellenfél válaszait szövegként elküldjük az ElevenLabsnek, hogy előállítsa a hangját.", "s4": "Adattárolás", "p4": "Adataidat biztonságosan, a Supabase infrastruktúráján tároljuk, nyugalmi és átvitel közbeni titkosítással.", @@ -1719,38 +1712,38 @@ "p5": "Nem adjuk el személyes adataidat. Csak az OpenRouterrel osztunk meg adatot MI-feldolgozás céljából, az ElevenLabsszel az ellenfél hangjához a hangos körökben, valamint fizetési szolgáltatókkal a számlázáshoz.", "s6": "Jogaid", "p6": "A Beállításokban letöltheted az adataidat egy JSON-fájlban, törölheted a mentett munkáidat, vagy törölheted a fiókodat. A fiók törlése véglegesen eltávolítja a fiókot és minden hozzá tárolt adatot, és azonnal lemondja az esetleges Pro-előfizetést. A Stripe a saját számlázási nyilvántartásait, például a számlákat és a befizetéseket, az adózási és számviteli előírások szerint megőrzi.", - "s7": "Sütik és Nyomkövetés", + "s7": "Sütik és nyomkövetés", "p7": "Alapvető sütiket és böngészőtárhelyet használunk a hitelesítéshez és a beállításokhoz. A PostHogot termékanalitikához, a Sentryt pedig hibamegfigyeléshez, strukturált naplókhoz, alkalmazásmetrikákhoz, teljesítménykövetéshez és kizárólag hibákhoz kapcsolódó munkamenet-visszajátszáshoz használjuk. A Sentry nem rögzít szokásos munkameneteket. A rögzített hibát tartalmazó munkameneteket rögzíti; a visszajátszásokban minden szöveg és beviteli érték maszkolva van, a médiatartalom pedig blokkolva. A Sentry technikai metaadatokat kap, például útvonalakat, böngészőadatokat, kérés-URL-eket, metódusokat és állapotkódokat, veremkövetéseket, időzítéseket és biztonságos eseménybesorolásokat. A felhasználói azonosítók, sütik, fejlécek, HTTP-törzsek, lekérdezési paraméterek, MI-bemenetek és -kimenetek, valamint veremkeret-változók automatikus gyűjtése le van tiltva. A PostHog automatikus eseménygyűjtése, oldalmegtekintés-követése, munkamenet-rögzítése és tartós böngészőtárhelye ki van kapcsolva. Az analitikai eseményekben és strukturált naplókban nem küldünk vitatartalmat, chatüzeneteket, forráscímeket vagy e-mail-címeket.", - "s8": "Gyermekek Adatvédelme", + "s8": "Gyermekek adatvédelme", "p8": "A DebateLab 13 éves és idősebb felhasználóknak készült. Tudatosan nem gyűjtünk adatot 13 év alatti gyermekektől.", - "s9": "Irányelvek Módosítása", + "s9": "Irányelvek módosítása", "p9": "Időszakosan frissíthetjük ezt az irányelvet. A jelentős változásokról emailben vagy alkalmazáson belüli értesítéssel tájékoztatunk.", "s10": "Kapcsolat", "p10": "Adatvédelmi kérdésekkel vagy adatkérésekkel fordulj hozzánk: privacy@debatelab.app." }, "termsPage": { - "title": "Felhasználási Feltételek", + "title": "Felhasználási feltételek", "lastUpdated": "Utolsó frissítés", "date": "2026. május 1.", - "s1": "Feltételek Elfogadása", + "s1": "Feltételek elfogadása", "p1": "A DebateLab használatával elfogadod ezeket a feltételeket. Ha nem értesz egyet, ne használd a szolgáltatást.", - "s2": "Szolgáltatás Leírása", + "s2": "Szolgáltatás leírása", "p2": "A DebateLab egy MI-alapú vitafelkészítő eszköz, amely érvgenerálást, elemzést és coaching funkciókat biztosít.", "s3": "Fiókkövetelmények", "p3": "Google-lal, e-mailben kapott bejelentkezési hivatkozással vagy adminisztrátor által kiadott demókóddal jelentkezhetsz be. A demókódok ideiglenes hozzáférést adnak egy kijelölt fiókhoz. Felelős vagy a fiókod alatt végzett minden tevékenységért.", - "s4": "Előfizetés és Számlázás", + "s4": "Előfizetés és számlázás", "p4": "Az ingyenes próba 3 napig tart korlátozott kérésekkel. A Pro előfizetés $5/hó plusz adó, Stripe-on keresztül számlázva. Bármikor lemondható a Beállításokból.", - "s5": "Elfogadható Használat", + "s5": "Elfogadható használat", "p5": "Nem használhatod a DebateLabot zaklatásra, káros tartalom generálására, intézményed szabályzatát sértő akadémiai tisztességtelenségre, vagy bármilyen illegális célra.", - "s6": "MI által Generált Tartalom", + "s6": "MI által generált tartalom", "p6": "Az MI kimenetek jelenlegi állapotukban kerülnek biztosításra, pontossági garancia nélkül. Te vagy felelős a tények és hivatkozások ellenőrzéséért versenyeken való használat előtt.", - "s7": "Szellemi Tulajdon", + "s7": "Szellemi tulajdon", "p7": "Megtartod a bemeneted tulajdonjogát. Az MI által generált kimenetek személyes és oktatási használatra licenceltek. A DebateLab fenntartja a platform és technológiája feletti jogokat.", - "s8": "Felelősség Korlátozása", + "s8": "Felelősség korlátozása", "p8": "A DebateLab jelenlegi állapotában kerül biztosításra. Nem vállalunk felelősséget versenyeredményekért, pontatlan MI kimenetekért vagy szolgáltatás-megszakításokért.", "s9": "Megszüntetés", "p9": "Felfüggeszthetjük vagy megszüntethetjük a feltételeket sértő fiókokat. Bármikor törölheted a fiókodat.", - "s10": "Irányadó Jog", + "s10": "Irányadó jog", "p10": "Ezekre a feltételekre az alkalmazandó jog vonatkozik. A vitákat kötelező érvényű választottbírósági eljárással rendezzük." }, "export": { @@ -1816,34 +1809,34 @@ "side": "Oldal", "proposition": "Mellette", "opposition": "Ellene", - "caseAttack": "Eset Támadás Elemzés", - "overallScore": "Összesített Pontszám", + "caseAttack": "Eset támadás elemzés", + "overallScore": "Összesített pontszám", "whyWeak": "Miért gyenge", "howOpponentAttacks": "Hogyan támadja az ellenfél", - "improvedVersion": "Javított Verzió", + "improvedVersion": "Javított verzió", "noWeaknesses": "Nem azonosítottak gyengeségeket.", "noFallacies": "Nem észleltek téves következtetéseket.", "noCrossEx": "Nincsenek megadott keresztkérdezési kérdések.", - "brainstormResults": "Ötletelés Eredményei", + "brainstormResults": "Ötletelés eredményei", "whyTop": "Miért a legjobb", - "researchFactoids": "Kutatási Tények", + "researchFactoids": "Kutatási tények", "keyFigure": "Kulcsadat", "context": "Kontextus", "howToUse": "Használat", "source": "Forrás", "noFactoids": "Nem találhatók tények.", "exordio": "Exordio", - "caseStructure": "Eset Struktúra", + "caseStructure": "Eset struktúra", "thesis": "Tézis", "framework": "Keretrendszer", - "burdenOfProof": "Bizonyítási Teher", + "burdenOfProof": "Bizonyítási teher", "keyDefinitions": "Kulcsdefiníciók", "arguments": "Érvek", - "predictedOpponent": "Várható Ellenfél Eset", - "counters": "Előkészített Ellenérvek", - "weaknesses": "Azonosított Gyengeségek", - "fallacies": "Észlelt Téves Következtetések", - "crossExQuestions": "Keresztkérdezési Kérdések", + "predictedOpponent": "Várható ellenfél eset", + "counters": "Előkészített ellenérvek", + "weaknesses": "Azonosított gyengeségek", + "fallacies": "Észlelt téves következtetések", + "crossExQuestions": "Keresztkérdezési kérdések", "motion": "Indítvány", "sparringTranscript": "Vitapárbaj átirata", "sparringConfiguration": "Forduló beállításai", diff --git a/src/main.tsx b/src/main.tsx index 1c3ea5bfa..c9a7f4db4 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -2,6 +2,7 @@ import { createRoot } from "react-dom/client"; import App from "./App.tsx"; import { WebTelemetry } from "./components/WebTelemetry"; import "./i18n"; +import "@fontsource-variable/inter"; import "./index.css"; import { registerServiceWorker } from "./register-service-worker"; import { initializeObservability } from "./lib/observability"; diff --git a/src/pages/LandingPage.tsx b/src/pages/LandingPage.tsx index 9c2afa324..1259b15f1 100644 --- a/src/pages/LandingPage.tsx +++ b/src/pages/LandingPage.tsx @@ -1,6 +1,5 @@ import Navbar from "@/components/landing/Navbar"; import Hero from "@/components/landing/Hero"; -import SocialProof from "@/components/landing/SocialProof"; import Features from "@/components/landing/Features"; import DeepDive from "@/components/landing/DeepDive"; import UseCases from "@/components/landing/UseCases"; @@ -17,7 +16,6 @@ const LandingPage = () => {
- diff --git a/tailwind.config.ts b/tailwind.config.ts index edcd2fa97..1f1da9c2b 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -95,7 +95,8 @@ export default { }, fontFamily: { sans: [ - 'Inter', + 'Inter Variable', + 'Inter Fallback', 'ui-sans-serif', 'system-ui', '-apple-system', @@ -107,17 +108,7 @@ export default { 'Noto Sans', 'sans-serif' ], - serif: [ - 'Lora', - 'ui-serif', - 'Georgia', - 'Cambria', - 'Times New Roman', - 'Times', - 'serif' - ], mono: [ - 'Space Mono', 'ui-monospace', 'SFMono-Regular', 'Menlo',