chore: stale Cardecky-Refs entfernt + apps/landing/ gelöscht
Some checks are pending
CI / validate (push) Waiting to run

- apps/landing/ entfernt (Cardecky-Marketing-Astro-Site, war nie
  deployed — der nginx-Block in landings.conf zeigte auf einen
  Pfad ohne Inhalt)
- Stale Doc-Kommentare in api+web: Cardecky → Wordeck wo passend
- fsrs.ts subIndexCount: image-occlusion + audio-front Cases raus
  (CardType-Enum hat sie nicht mehr — der throw wäre toter Code)
- fork.ts: image-occlusion-Sonderfall in subIndexCountFor weg
- cards-domain tests: schemas-Test prüft jetzt Wordeck-Typen + dass
  image-occlusion/audio-front ABGELEHNT werden
- cards-domain tests: image-occlusion-throw-Test entfernt
- api tests/cards.test.ts: 3 image-occlusion-Cases zu 1 negative
  Tested (422 expected, weil Schema sie verbietet)

51 Tests grün (cards-domain).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-05-17 21:58:52 +02:00
parent 9b37243b1d
commit c77100e85a
33 changed files with 13 additions and 2348 deletions

View file

@ -15,7 +15,7 @@
*
* Fail-open im Original: bei mana-llm-Ausfall wurde `flag` gesetzt,
* damit ein menschlicher Reviewer es trotzdem sieht. Solange wir nur
* Cardecky-Decks publishen, ist der Stub `pass` ausreichend Cardecky
* Wordeck-Decks publishen, ist der Stub `pass` ausreichend Wordeck
* ist eine kuratierte Identität.
*/

View file

@ -146,11 +146,6 @@ export async function forkDeckForUser(
function subIndexCountFor(type: string, fields: Record<string, string>): number {
if (type === 'cloze') return subIndexCountForCloze(fields.text ?? '');
if (type === 'image-occlusion') {
// image-occlusion hat dynamische subIndexes via mask_regions —
// im Marketplace-Fork bisher nicht unterstützt. Default 1.
return 1;
}
return subIndexCount(type);
}

View file

@ -9,7 +9,7 @@ import { authorBlocks, authors, deckReports, publicDecks } from '../../db/schema
/**
* Moderation-Endpoints App-Review-Pflicht für User-Generated-Content
* (App-Store-Guideline 5.1.1(v)). Aus Cardecky-Native erreichbar als
* (App-Store-Guideline 5.1.1(v)). Aus Wordeck-Native erreichbar als
* Melden" und „Author blockieren".
*
* - `POST /decks/:slug/report` Meldung zu einem Deck. Idempotent

View file

@ -75,7 +75,7 @@ describe('cardsRouter — Input-Validation', () => {
expect(res.status).toBe(422);
});
it('POST mit image-occlusion ohne mask_regions ist 422', async () => {
it('POST mit image-occlusion ist 422 (CardType nicht mehr akzeptiert)', async () => {
const { app } = buildApp();
const res = await app.request('/api/v1/cards', {
method: 'POST',
@ -83,49 +83,12 @@ describe('cardsRouter — Input-Validation', () => {
body: JSON.stringify({
deck_id: 'd-1',
type: 'image-occlusion',
fields: { image_ref: 'm1' },
fields: { image_ref: 'm1', mask_regions: '[]' },
}),
});
expect(res.status).toBe(422);
});
it('POST mit image-occlusion mit kaputtem mask_regions ist 422', async () => {
const { app } = buildApp();
const res = await app.request('/api/v1/cards', {
method: 'POST',
headers: { 'X-User-Id': 'u-1', 'Content-Type': 'application/json' },
body: JSON.stringify({
deck_id: 'd-1',
type: 'image-occlusion',
fields: { image_ref: 'm1', mask_regions: 'not json' },
}),
});
expect(res.status).toBe(422);
const body = (await res.json()) as { issues: string[] };
expect(body.issues[0]).toMatch(/mask_regions/);
});
it('POST mit gültiger image-occlusion erreicht Deck-Lookup (404 bei stub)', async () => {
const { app } = buildApp();
const res = await app.request('/api/v1/cards', {
method: 'POST',
headers: { 'X-User-Id': 'u-1', 'Content-Type': 'application/json' },
body: JSON.stringify({
deck_id: 'd-1',
type: 'image-occlusion',
fields: {
image_ref: 'm1',
mask_regions: JSON.stringify([
{ id: 'r1', x: 0.1, y: 0.1, w: 0.1, h: 0.1 },
]),
},
}),
});
expect(res.status).toBe(404);
const body = (await res.json()) as { error: string };
expect(body.error).toBe('deck_not_found');
});
it('POST mit cloze-Card ohne text-Feld ist 422', async () => {
const { app } = buildApp();
const res = await app.request('/api/v1/cards', {

View file

@ -1,12 +0,0 @@
import { defineConfig } from 'astro/config';
import tailwind from '@astrojs/tailwind';
import sitemap from '@astrojs/sitemap';
export default defineConfig({
site: 'https://cardecky.mana.how',
integrations: [
tailwind({ applyBaseStyles: false }),
sitemap(),
],
output: 'static',
});

View file

@ -1,21 +0,0 @@
{
"name": "@cards/landing",
"type": "module",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "astro dev --port 4380",
"build": "astro build",
"preview": "astro preview --port 4380",
"astro": "astro"
},
"dependencies": {
"astro": "^5.8.0"
},
"devDependencies": {
"@astrojs/tailwind": "^6.0.2",
"@astrojs/sitemap": "^3.4.1",
"tailwindcss": "^3.4.17",
"typescript": "^5.8.3"
}
}

View file

@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="none">
<rect x="2" y="6" width="18" height="14" rx="3" stroke="#16a34a" stroke-width="2.5"/>
<rect x="12" y="12" width="18" height="14" rx="3" fill="white" stroke="#16a34a" stroke-width="2.5"/>
</svg>

Before

Width:  |  Height:  |  Size: 271 B

View file

@ -1,75 +0,0 @@
---
const posts = [
{
href: '/blog/quizlet-paywall',
tag: 'Migration',
title: 'Quizlet zieht die Paywall hoch — was jetzt?',
summary:
'Fünf Millionen Nutzer:innen haben Quizlet in den letzten zwei Jahren verlassen. Was hinter der Bezahlschranke steckt und wie der Wechsel zu Cardecky in wenigen Minuten klappt.',
},
{
href: '/blog/fsrs-algorithmus',
tag: 'Algorithmus',
title: 'Weniger lernen, mehr behalten: Was FSRS bedeutet',
summary:
'FSRS reduziert die nötigen Wiederholungen um 2030 % bei gleicher Retention. Wie der Algorithmus funktioniert — und warum er bei anderen Apps noch nicht der Standard ist.',
},
{
href: '/blog/deine-daten',
tag: 'Datenschutz',
title: 'Deine Karten, deine Daten',
summary:
'Lerndaten verraten mehr als die meisten ahnen. Was Cardecky damit macht — und was nicht — lässt sich im öffentlichen Quellcode nachprüfen.',
},
{
href: '/blog/anki-zu-kompliziert',
tag: 'Vergleich',
title: 'Anki ist mächtig — und trotzdem schwer empfehlbar',
summary:
'Anki ist technisch überlegen. Trotzdem hören die meisten Nutzer:innen auf — nicht wegen des Algorithmus, sondern wegen Interface und Lernkurve.',
},
{
href: '/blog/gute-lernkarten',
tag: 'Lernen',
title: 'Wie man Lernkarten schreibt, die wirklich funktionieren',
summary:
'Der beste Algorithmus nützt nichts, wenn die Karten schlecht gebaut sind. Fünf Prinzipien — mit Gegenbeispielen.',
},
] as const;
---
<section class="border-b border-rule py-20 sm:py-28">
<div class="mx-auto max-w-content px-6">
<p class="section-label">Artikel</p>
<h2 class="mt-3 font-serif text-display text-ink">Hintergrund &amp; Methodik.</h2>
<p class="mt-4 max-w-prose text-muted">
Warum Spaced Repetition funktioniert, was die Konkurrenz falsch macht — und wie man
Karten baut, die das Gehirn tatsächlich behält.
</p>
<ul class="mt-12 grid gap-6 sm:grid-cols-2 lg:grid-cols-3" role="list">
{posts.map((post) => (
<li>
<a
href={post.href}
class="group flex h-full flex-col rounded-xl border border-rule bg-paper p-6 transition-shadow hover:shadow-md"
>
<span class="section-label mb-3 inline-block">{post.tag}</span>
<p class="font-serif text-lg font-semibold leading-snug text-ink group-hover:text-leaf transition-colors">
{post.title}
</p>
<p class="mt-3 flex-1 text-sm leading-relaxed text-muted">
{post.summary}
</p>
<span class="mt-5 inline-flex items-center gap-1 text-sm font-medium text-leaf">
Lesen
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
</span>
</a>
</li>
))}
</ul>
</div>
</section>

View file

@ -1,35 +0,0 @@
---
const APP_URL = 'https://cardecky.mana.how';
---
<section class="border-b border-rule bg-leaf py-20 sm:py-24">
<div class="mx-auto max-w-content px-6 text-center">
<h2 class="font-serif text-display text-white">
Bereit, das Lernen neu zu denken?
</h2>
<p class="mt-4 text-lg text-white/80">
Cardecky ist kostenlos. Keine Kreditkarte, keine versteckten Kosten.
</p>
<div class="mt-10 flex flex-wrap justify-center gap-4">
<a
href={APP_URL}
class="inline-flex items-center gap-2 rounded-lg bg-white px-6 py-3 font-medium text-leaf transition-colors hover:bg-white/90"
>
Jetzt starten
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
</a>
<a
href="https://git.mana.how/mana/cards"
target="_blank"
rel="noopener"
class="inline-flex items-center gap-2 rounded-lg border border-white/30 px-6 py-3 font-medium text-white transition-colors hover:border-white/60 hover:bg-white/10"
>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/>
</svg>
Quellcode ansehen
</a>
</div>
</div>
</section>

View file

@ -1,74 +0,0 @@
---
const types = [
{
name: 'Klassisch',
tag: 'basic',
icon: `<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="M3 10h18"/></svg>`,
desc: 'Vorderseite und Rückseite. Der Klassiker — simpel, bewährt, effektiv für einfache Fakten.',
},
{
name: 'Multiple Choice',
tag: 'multiple-choice',
icon: `<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><circle cx="12" cy="12" r="9"/><path d="M12 8v4l3 3"/></svg>`,
desc: 'Vier Antwortoptionen zur Auswahl. Fehlende Distractors generiert das System automatisch aus dem Deck.',
highlight: true,
},
{
name: 'Lückentext',
tag: 'cloze',
icon: `<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><path d="M4 6h16M4 12h8M4 18h12"/><rect x="13" y="10" width="7" height="4" rx="1" fill="currentColor" opacity=".15" stroke="currentColor"/></svg>`,
desc: 'Text mit {{c1::Lücken}} — jeder markierte Cluster wird als eigene Karte abgefragt.',
},
{
name: 'Bild-Okklusion',
tag: 'image-occlusion',
icon: `<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5" fill="currentColor"/><path d="m21 15-5-5L5 21"/><rect x="11" y="10" width="7" height="5" rx="1" fill="currentColor" opacity=".2" stroke="currentColor"/></svg>`,
desc: 'Bereiche auf einem Bild ausblenden — ideal für Anatomie, Landkarten, Diagramme.',
},
{
name: 'Tippen',
tag: 'typing',
icon: `<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><rect x="2" y="7" width="20" height="12" rx="2"/><path d="M8 11h.01M12 11h.01M16 11h.01M8 15h8"/></svg>`,
desc: 'Die Antwort eintippen statt nur aufzudecken. Fuzzy-Match verzeiht kleine Tippfehler.',
},
{
name: 'Audio',
tag: 'audio-front',
icon: `<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2M12 19v4M8 23h8"/></svg>`,
desc: 'Audioclip als Prompt — perfekt für Sprachlernen, Vokabeln und Aussprache-Training.',
},
] as const;
---
<section id="kartentypen" class="border-b border-rule py-20 sm:py-28">
<div class="mx-auto max-w-content px-6">
<p class="section-label">Kartentypen</p>
<h2 class="mt-3 font-serif text-display text-ink">Sechs Formate, ein System.</h2>
<p class="mt-4 max-w-prose text-muted">
Nicht jedes Wissen lässt sich gleich lernen. Cardecky hat für jeden Lerninhalt den richtigen
Kartentyp — alle im gleichen Deck, alle mit FSRS-Scheduling.
</p>
<div class="mt-12 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{types.map((type) => (
<div class:list={[
'group rounded-xl border p-6 transition-all',
type.highlight
? 'border-leaf-border bg-leaf-light'
: 'border-rule bg-white hover:border-leaf-border hover:bg-leaf-light/40'
]}>
<div class:list={[
'mb-4 inline-flex rounded-lg p-2',
type.highlight ? 'bg-leaf/10 text-leaf' : 'bg-rule text-muted group-hover:bg-leaf/10 group-hover:text-leaf'
]}>
<Fragment set:html={type.icon} />
</div>
<div class="mb-1 flex items-center gap-2">
<h3 class="font-medium text-ink">{type.name}</h3>
<code class="rounded bg-rule px-1.5 py-0.5 text-xs text-muted">{type.tag}</code>
</div>
<p class="text-sm leading-relaxed text-muted">{type.desc}</p>
</div>
))}
</div>
</div>
</section>

View file

@ -1,57 +0,0 @@
---
const features = [
{
icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>`,
title: 'Open Source',
body: 'Der gesamte Quellcode liegt offen auf Forgejo. Keine Blackbox, kein Vendor-Lock-in.',
},
{
icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>`,
title: 'Datensouveränität',
body: 'Deine Daten liegen auf mana-Infrastruktur in Europa. Kein Google, kein AWS, keine Datenhändler.',
},
{
icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>`,
title: 'FSRS-Algorithmus',
body: 'Free Spaced Repetition Scheduler — nachweislich effektiver als SM-2, das Herzstück von Anki.',
},
{
icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>`,
title: 'Anki-Import',
body: '.apkg-Dateien direkt importieren — Vorlagen werden automatisch auf Cardecky-Kartentypen gemappt.',
},
{
icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/></svg>`,
title: 'Öffentliche Bibliothek',
body: 'Kuratierte Decks zu Periodensystem, Geografie, Sprachen, Philosophie und mehr — ein Klick zum Start.',
},
{
icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>`,
title: 'Föderiert im mana-Ökosystem',
body: 'Cardecky teilt Identität und Credits mit den anderen mana-Apps — ein Login, eine Community.',
},
] as const;
---
<section id="funktionen" class="border-b border-rule py-20 sm:py-28">
<div class="mx-auto max-w-content px-6">
<p class="section-label">Funktionen</p>
<h2 class="mt-3 font-serif text-display text-ink">Gebaut für ernsthaftes Lernen.</h2>
<p class="mt-4 max-w-prose text-muted">
Kein Feature-Bloat. Nur was du brauchst, um Wissen dauerhaft zu verankern.
</p>
<div class="mt-12 grid gap-8 sm:grid-cols-2 lg:grid-cols-3">
{features.map((f) => (
<div class="flex gap-4">
<div class="mt-0.5 flex-shrink-0 text-leaf">
<Fragment set:html={f.icon} />
</div>
<div>
<h3 class="font-semibold text-ink">{f.title}</h3>
<p class="mt-1 text-sm leading-relaxed text-muted">{f.body}</p>
</div>
</div>
))}
</div>
</div>
</section>

View file

@ -1,51 +0,0 @@
---
const year = new Date().getFullYear();
---
<footer class="bg-ink py-12 text-white/60">
<div class="mx-auto max-w-content px-6">
<div class="grid gap-8 sm:grid-cols-2 lg:grid-cols-4">
<div>
<p class="font-serif text-base font-semibold text-white">Cardecky</p>
<p class="mt-2 text-sm leading-relaxed">
Spaced-Repetition-App des mana e.V.<br/>
Freie Software für freie Menschen.
</p>
</div>
<div>
<p class="text-xs font-semibold uppercase tracking-widest text-white/40">App</p>
<ul class="mt-3 space-y-2 text-sm">
<li><a href="https://cardecky.mana.how" class="hover:text-white transition-colors">Zur App</a></li>
<li><a href="https://cardecky.mana.how/decks" class="hover:text-white transition-colors">Bibliothek</a></li>
<li><a href="https://cardecky.mana.how/study" class="hover:text-white transition-colors">Lernen</a></li>
</ul>
</div>
<div>
<p class="text-xs font-semibold uppercase tracking-widest text-white/40">Artikel</p>
<ul class="mt-3 space-y-2 text-sm">
<li><a href="/blog/quizlet-paywall" class="hover:text-white transition-colors">Quizlet-Alternative</a></li>
<li><a href="/blog/fsrs-algorithmus" class="hover:text-white transition-colors">Was ist FSRS?</a></li>
<li><a href="/blog/deine-daten" class="hover:text-white transition-colors">Deine Daten</a></li>
<li><a href="/blog/anki-zu-kompliziert" class="hover:text-white transition-colors">Anki vs. Cardecky</a></li>
<li><a href="/blog/gute-lernkarten" class="hover:text-white transition-colors">Gute Lernkarten</a></li>
</ul>
</div>
<div>
<p class="text-xs font-semibold uppercase tracking-widest text-white/40">Verein</p>
<ul class="mt-3 space-y-2 text-sm">
<li><a href="https://mana-ev.ch" class="hover:text-white transition-colors" target="_blank" rel="noopener">mana e.V.</a></li>
<li><a href="https://mana-ev.ch#mitglied" class="hover:text-white transition-colors" target="_blank" rel="noopener">Mitglied werden</a></li>
<li><a href="https://git.mana.how/mana/cards" class="hover:text-white transition-colors" target="_blank" rel="noopener">Quellcode</a></li>
<li><a href="https://mana-ev.ch/datenschutz" class="hover:text-white transition-colors" target="_blank" rel="noopener">Datenschutz</a></li>
</ul>
</div>
</div>
<div class="mt-10 border-t border-white/10 pt-6 text-xs">
© {year} mana e.V. · Cardecky ist freie Software (AGPL-3.0) ·
<a href="https://mana-ev.ch" class="hover:text-white transition-colors">mana-ev.ch</a>
</div>
</div>
</footer>

View file

@ -1,43 +0,0 @@
---
const APP_URL = 'https://cardecky.mana.how';
---
<section class="relative overflow-hidden border-b border-rule bg-paper py-24 sm:py-32">
<!-- Subtle grid background -->
<div
class="pointer-events-none absolute inset-0 opacity-[0.03]"
style="background-image: linear-gradient(#111812 1px, transparent 1px), linear-gradient(90deg, #111812 1px, transparent 1px); background-size: 48px 48px;"
aria-hidden="true"
></div>
<div class="relative mx-auto max-w-content px-6">
<div class="max-w-prose">
<p class="section-label mb-4">Spaced Repetition · mana e.V.</p>
<h1 class="font-serif text-hero text-ink">
Lernkarten, die<br/>
<span class="text-leaf">tatsächlich wirken.</span>
</h1>
<p class="mt-6 text-lg leading-relaxed text-muted">
Cardecky nutzt den FSRS-Algorithmus — den aktuellen Stand der Wissenschaft im Spaced
Repetition — um dir jede Karte genau dann zu zeigen, wenn du sie kurz vor dem Vergessen bist.
Keine Abos, keine Tracker, keine dark patterns.
Freie Software des <a href="https://mana-ev.ch" class="underline underline-offset-2 hover:text-ink transition-colors">mana e.V.</a>
</p>
<div class="mt-10 flex flex-wrap items-center gap-4">
<a href={APP_URL} class="btn-primary">
Kostenlos starten
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
</a>
<a href="#kartentypen" class="btn-ghost">Alle Kartentypen ansehen</a>
</div>
<p class="mt-6 text-xs text-muted">
Kein Account erforderlich für den ersten Blick · DSGVO-konform · Open Source
</p>
</div>
</div>
</section>

View file

@ -1,35 +0,0 @@
---
const steps = [
{
n: '01',
title: 'Deck erstellen oder importieren',
body: 'Leg ein neues Deck an und füge Karten über das Web-Interface hinzu — oder importiere deine bestehenden Anki-Decks direkt als .apkg-Datei. Decks aus der Bibliothek mit einem Klick übernehmen.',
},
{
n: '02',
title: 'Karten anlegen',
body: 'Wähle den passenden Kartentyp und füll die Felder aus. Für Multiple-Choice-Karten zieht das System automatisch passende Distractors aus dem restlichen Deck — du gibst nur die richtige Antwort vor.',
},
{
n: '03',
title: 'Täglich kurz lernen',
body: 'Der FSRS-Algorithmus berechnet für jede Karte den optimalen Wiederholungszeitpunkt. Du bewertest dich selbst (Nochmal / Schwer / Gut / Leicht) — damit kalibriert sich das System auf deinen persönlichen Vergessenskurve.',
},
] as const;
---
<section class="border-b border-rule bg-white py-20 sm:py-28">
<div class="mx-auto max-w-content px-6">
<p class="section-label">So funktioniert's</p>
<h2 class="mt-3 font-serif text-display text-ink">In drei Schritten zur täglichen Praxis.</h2>
<div class="mt-12 grid gap-10 sm:grid-cols-3">
{steps.map((s) => (
<div>
<span class="font-serif text-4xl font-bold text-rule">{s.n}</span>
<h3 class="mt-4 font-semibold text-ink">{s.title}</h3>
<p class="mt-2 text-sm leading-relaxed text-muted">{s.body}</p>
</div>
))}
</div>
</div>
</section>

View file

@ -1,46 +0,0 @@
---
---
<section class="border-b border-rule bg-white py-20 sm:py-24">
<div class="mx-auto max-w-content px-6">
<div class="grid gap-12 lg:grid-cols-2 lg:items-center">
<div>
<p class="section-label">mana e.V.</p>
<h2 class="mt-3 font-serif text-display text-ink">
Software als Gemeingut.
</h2>
<p class="mt-4 leading-relaxed text-muted">
Cardecky ist ein Projekt des <strong class="text-ink">mana e.V.</strong> — einem Schweizer Verein,
der digitale Infrastruktur als Gemeingut begreift. Keine Investoren, keine Werbung,
kein Daten-Harvesting. Finanziert durch freiwillige Mitgliedsbeiträge.
</p>
<p class="mt-3 leading-relaxed text-muted">
80 % der Einnahmen fließen direkt in den Betrieb der Apps. 10 % in Langlebigkeit
und Nachhaltigkeit. 10 % in den Vereinsbetrieb. Transparenz ist kein Marketing —
wir veröffentlichen jeden Quartal einen Transparenzbericht.
</p>
<div class="mt-8 flex flex-wrap gap-4">
<a href="https://mana-ev.ch" target="_blank" rel="noopener" class="btn-primary">
mana-ev.ch besuchen
</a>
<a href="https://mana-ev.ch#mitglied" target="_blank" rel="noopener" class="btn-ghost">
Mitglied werden
</a>
</div>
</div>
<div class="grid grid-cols-2 gap-4">
{[
{ label: 'Open Source', desc: 'Quellcode liegt offen auf git.mana.how' },
{ label: 'Kein Tracking', desc: 'Keine Werbung, keine externen Dienste' },
{ label: 'Europa', desc: 'Daten auf Infrastruktur in Europa' },
{ label: 'Transparent', desc: 'Quartalsbericht zu Finanzen & Betrieb' },
].map((item) => (
<div class="rounded-xl border border-rule bg-paper p-5">
<p class="font-semibold text-ink">{item.label}</p>
<p class="mt-1 text-sm text-muted">{item.desc}</p>
</div>
))}
</div>
</div>
</div>
</section>

View file

@ -1,24 +0,0 @@
---
const APP_URL = 'https://cardecky.mana.how';
---
<header class="sticky top-0 z-50 border-b border-rule bg-paper/90 backdrop-blur-sm">
<div class="mx-auto flex max-w-content items-center justify-between px-6 py-3">
<a href="/" class="flex items-center gap-2 font-serif text-lg font-semibold text-ink">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" aria-hidden="true">
<rect x="2" y="4" width="14" height="10" rx="2" stroke="currentColor" stroke-width="1.75" class="text-leaf"/>
<rect x="8" y="10" width="14" height="10" rx="2" fill="white" stroke="currentColor" stroke-width="1.75"/>
</svg>
Cardecky
</a>
<nav class="hidden items-center gap-6 text-sm text-muted sm:flex">
<a href="#kartentypen" class="hover:text-ink transition-colors">Kartentypen</a>
<a href="#funktionen" class="hover:text-ink transition-colors">Funktionen</a>
<a href="https://mana-ev.ch" target="_blank" rel="noopener" class="hover:text-ink transition-colors">mana e.V.</a>
</nav>
<a href={APP_URL} class="btn-primary text-xs py-2 px-4">
Zur App →
</a>
</div>
</header>

View file

@ -1,37 +0,0 @@
---
interface Props {
title: string;
description: string;
ogImage?: string;
}
const { title, description, ogImage = '/og.png' } = Astro.props;
const canonical = new URL(Astro.url.pathname, Astro.site);
---
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="canonical" href={canonical} />
<title>{title}</title>
<meta name="description" content={description} />
<!-- Open Graph -->
<meta property="og:type" content="website" />
<meta property="og:url" content={canonical} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={new URL(ogImage, Astro.site)} />
<!-- Twitter -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content={new URL(ogImage, Astro.site)} />
</head>
<body>
<slot />
</body>
</html>

View file

@ -1,321 +0,0 @@
---
import Layout from '../../layouts/Layout.astro';
import Nav from '../../components/Nav.astro';
import Footer from '../../components/Footer.astro';
import '../../styles/base.css';
const APP_URL = 'https://cardecky.mana.how';
const ANKI_IMPORT_URL = 'https://cardecky.mana.how/import';
const title = 'Anki ist mächtig — und trotzdem schwer empfehlbar | Cardecky';
const description =
'Anki ist der Goldstandard für Spaced Repetition. Aber die Lernkurve ist brutal, das' +
' Interface stammt aus 2006, und einen Freund damit anzufangen ist fast unmöglich. Was' +
' Cardecky anders macht.';
---
<Layout {title} {description}>
<Nav />
<main class="mx-auto max-w-prose px-6 py-16 sm:py-24">
<!-- Artikel-Header -->
<header class="mb-12">
<p class="section-label mb-4">Vergleich · Anki</p>
<h1 class="font-serif text-4xl leading-tight text-ink sm:text-5xl">
Anki ist mächtig&nbsp;— und trotzdem schwer empfehlbar
</h1>
<p class="mt-5 text-lg leading-relaxed text-muted">
Anki ist der technisch überlegene Lernkarten-Standard. Die meisten Nutzer:innen scheitern
trotzdem — nicht am Algorithmus, sondern am Interface. Was das konkret bedeutet und wo
Cardecky ansetzt.
</p>
<p class="mt-4 text-xs text-muted">Stand: Mai 2026</p>
</header>
<article class="prose-cardecky">
<!-- 1 -->
<h2>Das Paradox: alle wissen, dass es funktioniert</h2>
<p>
Wenn jemand über Lernkarten redet, fällt Anki. Wenn jemand über Spaced Repetition redet,
fällt Anki. Medizinstudenten schwören seit Jahren darauf, Sprachenlerner haben ganze
Karrieren darauf aufgebaut, und wer tief in der Lernforschung sitzt, kennt die Studie, die
die Studie belegt. Anki funktioniert. Das ist nicht strittig.
</p>
<p>
Und trotzdem kennt fast jeder mindestens eine Person, die Anki angefangen und aufgehört hat.
Nicht weil das Lernen nicht funktioniert hat — sondern weil die App selbst im Weg stand. Das
ist das Paradox: ein Werkzeug, das technisch unbestritten ist und dessen Nutzer:innen
massenweise aussteigen, bevor sie je richtig angefangen haben.
</p>
<!-- 2 -->
<h2>Was Anki wirklich kann</h2>
<p>
Es wäre unfair, Anki nur durch den Blick auf seine Schwächen zu beschreiben. Anki ist seit
2006 im Aufbau und hat in dieser Zeit eine Tiefe entwickelt, die kein neueres Tool auch nur
annähernd erreicht.
</p>
<p>
Das Add-on-Ökosystem allein ist bemerkenswert: hunderte von Community-Erweiterungen, von
automatischer Bildsuche über LaTeX-Rendering bis zu spezialisierten Scheduling-Varianten.
Die Community-Deck-Bibliothek im Medizin-Bereich — Zanki, Lightyear und ihre Abkömmlinge
— ist jahrelange kollektive Arbeit, die sich schlicht nicht replizieren lässt. Wer Medizin
studiert und in dieses Ökosystem einsteigt, bekommt kuratierte, geprüfte, von Tausenden
mitgepflegte Decks. Das ist ein realer Wert.
</p>
<p>
Seit 2024 ist zudem FSRS — Free Spaced Repetition Scheduler — als opt-in in Anki enthalten.
FSRS modelliert zwei Gedächtnisparameter pro Karte (Stabilität und Abrufbarkeit) und passt
Wiederholungsintervalle präziser an als der ältere SM-2-Algorithmus, auf dem Anki lange
fuhr. Wer FSRS in Anki aktiviert, hat Stand 2026 einen der genauesten Scheduling-Algorithmen,
die öffentlich verfügbar sind.
</p>
<p>
Anki ist außerdem Open Source, kostenlos auf Desktop und Android, und wird von einer
kleinen Kern-Entwicklergruppe mit bemerkenswerter Kontinuität gepflegt. Das ist keine
Selbstverständlichkeit.
</p>
<!-- 3 -->
<h2>Warum es trotzdem nicht funktioniert — für die meisten</h2>
<p>
Das Problem ist nicht der Algorithmus. Das Problem ist alles darum herum.
</p>
<p>
Wer Anki zum ersten Mal öffnet, trifft auf ein Interface, das seit 2006 im Wesentlichen
gleich geblieben ist. Das ist kein Vorwurf an die Entwickler:innen — es ist eine
Beschreibung der Realität. Decks, Tags, Note-Types, Card-Types, Fields, Templates,
Scheduling-Optionen, Add-on-Management: all das erwartet eine Einarbeitungszeit, die in
keinem Verhältnis zum eigentlichen Lernziel steht. Wer Vokabeln für die nächste
Prüfung lernen will, muss zuerst verstehen, was der Unterschied zwischen einer Note und
einer Card ist.
</p>
<p>
Das „Notes vs. Cards"-Konzept verdient einen eigenen Absatz: Anki speichert Inhalte als
„Notes" (das Quell-Datenpaar) und generiert daraus „Cards" (die tatsächlich wiederholten
Einheiten). Das ist konzeptionell sauber und erlaubt Dinge wie eine Note, die
vorwärts und rückwärts abgefragt wird. Es ist aber auch ein Konzept, das nirgendwo erklärt
wird — man stolpert irgendwann hinein. Anfänger:innen glauben oft, sie hätten hundert
Karten erstellt, und wundern sich dann, warum die Queue zweihundert Einträge zeigt.
</p>
<p>
Hinzu kommt die Card-Creation-Tax: Anki hat keine Abkürzungen für das Erstellen von Karten.
Alles ist manuell, alles erfordert Entscheidungen über Felder und Vorlagen, bevor
überhaupt der erste Inhalt steht. Für jemanden, der gerade mit Lernen anfangen will, ist
das eine hohe Hürde. Nicht unüberwindbar, aber hoch genug, dass viele vorher aufhören.
</p>
<p>
Und dann sind da noch die Einstellungen. Anki bietet eine Einstellung namens „Maximum
interval" mit einem Standardwert von „36500" — ohne sichtbare Einheit. 36500 was? Tage,
wie sich herausstellt. Das ist charakteristisch für das Interface insgesamt: viel Kontrolle,
wenig Kontext.
</p>
<!-- 4 -->
<h2>Der iOS-Preis als Zugangsbarriere</h2>
<p>
Anki ist die einzige App im Vergleichsfeld, die auf einer der großen Plattformen Geld
kostet: AnkiMobile für iOS ist $24.99. Desktop und Android sind kostenlos. Das ist kein
Fehler, sondern eine bewusste Entscheidung des Entwicklers, der die iOS-App als primäre
Finanzierungsquelle für das Gesamtprojekt nutzt — eine legitime und transparente Wahl.
</p>
<p>
Trotzdem ist es faktisch eine Barriere. Wer Anki auf dem Telefon nutzen will — und das
ist die realistische Nutzungssituation für tägliche Wiederholungen —, zahlt einmalig
$24.99. Für viele ist das in Ordnung. Für andere, gerade für Schüler:innen oder
Studierende mit knappem Budget, ist es der Moment, an dem sie aufhören zu schauen.
</p>
<!-- 5 -->
<h2>Der Backlog-Zyklus: wie man in den Burn-out kommt</h2>
<p>
Spaced Repetition funktioniert nur, wenn man es regelmäßig macht. Was passiert, wenn man
eine Woche aussetzt — Prüfungsphase, Urlaub, einfach keine Zeit —, ist bei Anki besonders
schmerzhaft: die übersprungenen Wiederholungen stapeln sich nicht auf morgen, sie stapeln
sich auf heute. Wer nach zehn Tagen Pause zurückkommt, trifft auf einen Rückstand von
mehreren hundert Karten. Nicht als Richtwert, sondern als reale Queue, die abgearbeitet
werden will.
</p>
<p>
Das ist kein Bug von Anki, sondern eine direkte Konsequenz des Algorithmus: Karten, die
hätten wiederholt werden sollen, werden fällig. Aber die psychologische Wirkung ist
destruktiv. Die meisten Menschen, die nach einer Pause auf 500+ fällige Karten schauen,
schließen die App wieder. Manche löschen das Deck. Der Backlog wird zur Mauer, die das
Weiterlernen blockiert.
</p>
<p>
Es gibt Add-ons und manuelle Workarounds, die den Rückstand kappen oder verteilen. Aber
auch das setzt voraus, dass man weiß, dass es sie gibt — und wie man sie konfiguriert.
</p>
<!-- 6 -->
<h2>Was Cardecky daraus gelernt hat — und was Cardecky nicht hat</h2>
<p>
Cardecky ist aus der Beobachtung entstanden, dass die meisten Menschen Anki nicht wegen
des Algorithmus verlassen, sondern wegen des Aufwands davor, danach und drumherum. Das
ist die Zielgruppe, die Cardecky bedienen will: Nutzer:innen, die Anki wollten und
gescheitert sind — nicht Power-User, die Anki lieben.
</p>
<p>
Was das konkret bedeutet: FSRS ist in Cardecky der Standard, kein opt-in. Card-Erstellung
ohne Vorlage-Entscheidungen, direkt starten. Backlog-Management mit weicheren
Auffang-Mechanismen nach Pausen. Kein Notes-vs-Cards-Konzept, das erklärt werden muss.
Kein Interface aus 2006.
</p>
<p>
Gleichzeitig soll hier nichts beschönigt werden. Cardecky hat nicht, was Anki in zwanzig
Jahren aufgebaut hat:
</p>
<ul>
<li>
<strong>Keine medizinischen Community-Decks.</strong> Zanki, Lightyear und ihre Varianten
sind in Anki, nicht in Cardecky. Wer Medizin studiert und von diesen Decks abhängt,
wechselt nicht zu Cardecky.
</li>
<li>
<strong>Kein Add-on-Ökosystem.</strong> Die Tiefe, die Anki durch hunderte
Community-Erweiterungen hat, gibt es bei Cardecky nicht. Was ausgeliefert wird, ist das,
was da ist.
</li>
<li>
<strong>Keine zwanzigjährige Community.</strong> Foren, Reddit-Threads, YouTube-Tutorials,
Stack-Overflow-Fragen — all das existiert für Anki in einem Umfang, den Cardecky nicht
hat und in absehbarer Zeit nicht haben wird.
</li>
<li>
<strong>Kein Desktop-Client.</strong> Cardecky ist eine Web-App, installierbar als PWA.
Eine native App für iOS und Android ist in Planung, aber noch nicht verfügbar.
</li>
</ul>
<p>
Das sind reale Einschränkungen, keine Randnotizen.
</p>
<!-- 7 -->
<h2>Wer schon Anki-Decks hat: Import mitgebracht</h2>
<p>
Für alle, die bereits in Anki investiert haben und den Wechsel trotzdem ausprobieren
wollen: Cardecky liest .apkg-Dateien direkt ein. Decks und Karten werden übernommen,
Formatierung soweit das Format es trägt. Der FSRS-Lernstand beginnt in Cardecky frisch —
das ist eine technische Einschränkung, keine Absicht: FSRS-Parameter sind algorithmisch
nicht direkt zwischen verschiedenen Implementierungen übertragbar.
</p>
<p>
Wer seine Anki-Decks nach <a href={ANKI_IMPORT_URL} target="_blank" rel="noopener">cardecky.mana.how/import</a>
hochlädt, kann sofort weiterlernen. Die Queue fängt bei null an — was je nach Perspektive
ein Nachteil oder ein Neubeginn ist.
</p>
<!-- CTA -->
<hr class="my-12 border-rule" />
<div class="rounded-xl border border-rule bg-paper p-8">
<p class="font-serif text-2xl text-ink">Anki wollte, aber nicht durchgehalten?</p>
<p class="mt-3 text-muted leading-relaxed">
Cardecky ist für genau diesen Fall gebaut. Bestehende Anki-Decks können direkt
importiert werden. Kein Abo, kein Kreditkartenfeld, keine Konfiguration vor dem ersten
Lernen.
</p>
<div class="mt-6 flex flex-wrap gap-4">
<a href={APP_URL} class="btn-primary">
Cardecky öffnen
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
</a>
<a href={ANKI_IMPORT_URL} class="btn-ghost">Anki-Decks importieren</a>
</div>
</div>
</article>
</main>
<Footer />
</Layout>
<style>
/* Prose-Styles für den Artikel — ohne externes @tailwindcss/typography */
.prose-cardecky h2 {
font-family: var(--font-serif, Georgia, serif);
font-size: 1.375rem;
font-weight: 600;
color: theme('colors.ink');
margin-top: 2.5rem;
margin-bottom: 0.75rem;
line-height: 1.3;
}
.prose-cardecky p {
color: theme('colors.ink');
line-height: 1.75;
margin-bottom: 1.25rem;
}
.prose-cardecky ul,
.prose-cardecky ol {
margin-bottom: 1.25rem;
padding-left: 1.5rem;
}
.prose-cardecky ul {
list-style-type: disc;
}
.prose-cardecky ol {
list-style-type: decimal;
}
.prose-cardecky li {
color: theme('colors.ink');
line-height: 1.75;
margin-bottom: 0.5rem;
}
.prose-cardecky li + li {
margin-top: 0.375rem;
}
.prose-cardecky strong {
font-weight: 600;
color: theme('colors.ink');
}
.prose-cardecky a {
color: theme('colors.leaf');
text-decoration: underline;
text-underline-offset: 2px;
}
.prose-cardecky a:hover {
color: theme('colors.ink');
}
.prose-cardecky hr {
border-color: theme('colors.rule');
}
</style>

View file

@ -1,289 +0,0 @@
---
import Layout from '../../layouts/Layout.astro';
import Nav from '../../components/Nav.astro';
import Footer from '../../components/Footer.astro';
import '../../styles/base.css';
const APP_URL = 'https://cardecky.mana.how';
const GIT_URL = 'https://git.mana.how/mana/cards';
const title = 'Deine Karten, deine Daten — warum Data Ownership beim Lernen wichtig ist | Cardecky';
const description =
'Lerndaten verraten mehr als die meisten Nutzer:innen ahnen. Was Cardecky' +
' stattdessen tut: 0 Tracker, DSGVO-Self-Service, vollständiger Export — und' +
' warum die Vereinsform das strukturell absichert.';
---
<Layout {title} {description}>
<Nav />
<main class="mx-auto max-w-prose px-6 py-16 sm:py-24">
<header class="mb-12">
<p class="section-label mb-4">Datenschutz · Data Ownership</p>
<h1 class="font-serif text-4xl leading-tight text-ink sm:text-5xl">
Deine Karten, deine&nbsp;Daten
</h1>
<p class="mt-5 text-lg leading-relaxed text-muted">
Lerndaten sind eine der intimsten Datenkategorien, die eine App sammeln kann.
Was Cardecky damit macht — und was nicht —, lässt sich im Code nachprüfen.
</p>
<p class="mt-4 text-xs text-muted">Stand: Mai 2026</p>
</header>
<article class="prose-cardecky">
<h2>Was Lerndaten eigentlich verraten</h2>
<p>
Stell dir vor, du lernst seit drei Jahren Japanisch. Jeden Morgen zehn Minuten,
tausende Wiederholungen, ein Lernstand, der dich kennt: welche Zeichen du immer
wieder vergisst, wie schnell du abrufst, wann du einbrichst. Diese Muster sind
nicht banal. Sie sind ein Protokoll deines Gedächtnisses.
</p>
<p>
Ein Lernprofil über 18 Monate ist ein Fingerabdruck deiner Denkweise — präziser
als die meisten Profile, die Werbetreibende kaufen. Was lässt sich daraus
ableiten? Lerngeschwindigkeit und Konzentrationsmuster. Schwächen und Stärken
nach Fachgebiet. Lernzeiten — und damit Lebensrhythmus. Bei Kindern und
Jugendlichen kommen Schul- und Entwicklungsprofile hinzu, die besonders
schutzbedürftig sind.
</p>
<p>
Das ist kein Paranoia-Szenario. Es ist eine nüchterne Beschreibung dessen, was
eine App sammelt, die Spaced Repetition betreibt. Die Frage ist, wer diese Daten
kontrolliert.
</p>
<h2>Was andere Apps damit machen</h2>
<p>
Quizlet hat 20222023 seine Kernfunktionen hinter eine Bezahlschranke gesperrt.
Wer wechseln wollte, stand vor einem weiteren Problem: Quizlet bietet keinen
sauberen Export. Sets lassen sich nicht als standardisiertes Dateiformat
herunterladen. Wer geht, verliert seinen Lernstand oder muss ihn mühsam manuell
übertragen. Das ist kein Zufall — das ist Geschäftsmodell. Lock-in hält Churn
unten, und Churn unten zu halten ist für eine VC-finanzierte Plattform ein
wichtigerer Wert als Portabilität.
</p>
<p>
Das zweite Problem ist weniger sichtbar: Quizlets Datenschutzerklärung erlaubt
die Nutzung von Lerndaten für Produktverbesserungen, Forschung und
„personalisierte Erfahrungen" — Formulierungen, die weit genug sind, um viel
zu erlauben. Das gilt strukturell für viele ad-finanzierte oder
VC-finanzierte Bildungsplattformen: Das Geschäftsmodell enthält einen
Anreiz, Nutzerdaten zu verwerten. Dieser Anreiz verschwindet nicht, weil
die App ein Lern-Interface hat.
</p>
<h2>Was „0 Tracker" konkret bedeutet — und wie man es nachprüft</h2>
<p>
Cardecky enthält keine Tracking-Bibliotheken. Kein PostHog, kein Plausible,
kein Google Analytics, kein Mixpanel, kein Segment. Das ist nicht eine
Marketing-Aussage — es ist im Quellcode nachprüfbar.
</p>
<pre><code>git grep -i "posthog\|plausible\|mixpanel\|segment\|analytics" apps/web/src/</code></pre>
<p>
Das Ergebnis ist leer. Wer das nicht glaubt, kann es selbst ausführen —
der Code liegt öffentlich auf{' '}
<a href={GIT_URL} target="_blank" rel="noopener">git.mana.how/till/cards-web</a>.
Das ist der Unterschied zwischen einem Versprechen und einem Nachweis.
</p>
<p>
Was stattdessen passiert: Serverseitig werden funktionale Logs für Betrieb
und Fehlerdiagnose geschrieben — Request-Zeiten, HTTP-Status-Codes, keine
Nutzerprofile. Es gibt keine Cross-Session-Verknüpfung, keinen
Behaviour-Graph, keine Retargeting-Pixel. Die Server stehen auf dem eigenen
Mac Mini von mana e.V. in der Schweiz, hinter einem Cloudflare Tunnel als
reine TLS-Terminierung — kein US-Cloud-Provider verarbeitet deine Lerndaten.
</p>
<h2>DSGVO-Export und Anti-Lock-In — als aktive Features</h2>
<p>
Daten-Souveränität ist in Cardecky kein Compliance-Text im Footer. Sie ist in
konkreten Features sichtbar.
</p>
<p>
<strong>Self-Service-Export.</strong> Unter Einstellungen kannst du deine Daten
jederzeit exportieren, ohne Support-Ticket, ohne Wartezeit. Das geht in drei
Formaten:
</p>
<ul>
<li>
<strong>.apkg</strong> (Anki-Format) — der de-facto-Standard für Lernkarten,
importierbar in Anki, AnkiDroid, AnkiMobile und jede andere
FSRS-kompatible App
</li>
<li>
<strong>.csv</strong> — lesbar in jeder Tabellenkalkulation, importierbar
in andere Systeme
</li>
<li>
<strong>FSRS-State-JSON</strong> — enthält deinen vollständigen Lernstand:
Wiederholungshistorie, Stabilitätswerte, Fälligkeitsdaten. Damit nimmst du
nicht nur deine Karten mit, sondern auch das Gedächtnis, das die App über
sie aufgebaut hat
</li>
</ul>
<p>
Das ist der Unterschied zwischen Export-als-Rückgabe und Export-als-Portabilität.
Du kannst mit vollem Lernstand zu Anki wechseln. Du verlierst nichts.
</p>
<p>
<strong>DSGVO-Löschung ohne Ticket.</strong> Wer sein Konto löschen will, tut
das in der App — vollständig, ohne eine E-Mail schreiben zu müssen. Das ist
kein regulatorischer Reflex. Es folgt aus Wert 1 von mana e.V.: Daten gehören
den Nutzer:innen, nicht uns. Wer geht, geht mit allem.
</p>
<h2>Warum die Vereinsform hier einen strukturellen Unterschied macht</h2>
<p>
mana e.V. ist ein Schweizer Verein nach ZGB Art. 60 ff. Kein Investor hat
Anteile. Es gibt keine Wachstumsziele, keine Exit-Strategie, keine Runde, die
zurückgezahlt werden muss. Der Verein finanziert sich aus Mitgliedsbeiträgen,
Spenden und fairen Credit-Käufen — nicht aus Werbeeinnahmen oder dem Verkauf
von Nutzerdaten.
</p>
<p>
VC-finanzierte Apps stehen unter dem Druck, einen Return zu liefern. Das formt
Produktentscheidungen, oft unsichtbar. Lock-in ist wertvoll, weil er Churn
verhindert. Daten sind wertvoll, weil sie Targeting ermöglichen.
Engagement-Maximierung ist wertvoll, weil sie Nutzungszeit erhöht. Keiner
dieser Anreize trifft auf mana e.V. zu — nicht weil die Menschen dahinter
besonders tugendhaft sind, sondern weil die Rechtsform diese Anreize
strukturell nicht enthält.
</p>
<p>
Die Schweizer Rechtsform ist dabei kein Steuertrick. Sie gibt dem Verein
politische Unabhängigkeit außerhalb der großen Blöcke EU und USA — und sie
bedeutet: Es gibt keine Unternehmensanteile, die eine andere Firma kaufen
könnte, um die Datenschutzversprechen anschließend wegzuverhandeln.
</p>
<h2>Offene Punkte</h2>
<p>
Ehrlichkeit darüber, was noch fehlt:
</p>
<p>
<strong>Native Mobile-App.</strong> Eine SwiftUI-App für iOS und iPadOS ist in
Entwicklung — die Kern-Lernschleife ist fertig, der App-Store-Release steht noch
aus. Wer heute auf dem iPhone lernen will, nutzt die Web-App über den Browser.
</p>
<p>
<strong>Offline-Support.</strong> Die PWA ist online-first. Vollständiger
Offline-Betrieb — Karten laden und bewerten ohne Verbindung, Sync beim
nächsten Online-Gang — ist geplant, aber noch nicht live.
</p>
<p>
Das sind keine versteckten Einschränkungen. Sie stehen hier, weil eine App,
die Transparenz als Wert nennt, das auch auf sich selbst anwenden muss.
</p>
<hr class="my-12 border-rule" />
<div class="rounded-xl border border-rule bg-paper p-8">
<p class="font-serif text-2xl text-ink">Lernen ohne Tracking.</p>
<p class="mt-3 text-muted leading-relaxed">
Konto erstellen, loslegen. Keine Werbung, kein Analytics-Code,
jederzeit exportierbar.
</p>
<div class="mt-6 flex flex-wrap gap-4">
<a href={APP_URL} class="btn-primary">
Cardecky öffnen
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
</a>
<a href={GIT_URL} target="_blank" rel="noopener" class="btn-ghost">
Code ansehen
</a>
</div>
</div>
</article>
</main>
<Footer />
</Layout>
<style>
.prose-cardecky h2 {
font-family: var(--font-serif, Georgia, serif);
font-size: 1.375rem;
font-weight: 600;
color: theme('colors.ink');
margin-top: 2.5rem;
margin-bottom: 0.75rem;
line-height: 1.3;
}
.prose-cardecky p {
color: theme('colors.ink');
line-height: 1.75;
margin-bottom: 1.25rem;
}
.prose-cardecky ul,
.prose-cardecky ol {
margin-bottom: 1.25rem;
padding-left: 1.5rem;
}
.prose-cardecky ul { list-style-type: disc; }
.prose-cardecky ol { list-style-type: decimal; }
.prose-cardecky li {
color: theme('colors.ink');
line-height: 1.75;
margin-bottom: 0.5rem;
}
.prose-cardecky strong {
font-weight: 600;
color: theme('colors.ink');
}
.prose-cardecky a {
color: theme('colors.leaf');
text-decoration: underline;
text-underline-offset: 2px;
}
.prose-cardecky a:hover { color: theme('colors.ink'); }
.prose-cardecky hr { border-color: theme('colors.rule'); }
.prose-cardecky pre {
background: theme('colors.paper');
border: 1px solid theme('colors.rule');
border-radius: 0.5rem;
padding: 1rem 1.25rem;
overflow-x: auto;
margin-bottom: 1.25rem;
}
.prose-cardecky code {
font-family: ui-monospace, 'Cascadia Code', monospace;
font-size: 0.875rem;
color: theme('colors.ink');
}
</style>

View file

@ -1,246 +0,0 @@
---
import Layout from '../../layouts/Layout.astro';
import Nav from '../../components/Nav.astro';
import Footer from '../../components/Footer.astro';
import '../../styles/base.css';
const APP_URL = 'https://cardecky.mana.how';
const title = 'Weniger lernen, mehr behalten: Was FSRS bedeutet | Cardecky';
const description =
'FSRS ist der modernste Spaced-Repetition-Algorithmus — und reduziert die nötigen' +
' Wiederholungen um 2030 % bei gleicher Lernretention. Wie er funktioniert,' +
' warum er noch nicht überall Standard ist, und wie Cardecky ihn einsetzt.';
---
<Layout {title} {description}>
<Nav />
<main class="mx-auto max-w-prose px-6 py-16 sm:py-24">
<header class="mb-12">
<p class="section-label mb-4">Algorithmus · Spaced Repetition</p>
<h1 class="font-serif text-4xl leading-tight text-ink sm:text-5xl">
Weniger lernen, mehr&nbsp;behalten
</h1>
<p class="mt-5 text-lg leading-relaxed text-muted">
FSRS ist der aktuell präziseste Algorithmus für Lernkarten — und er ist noch
nicht der Standard. Was sich hinter dem Kürzel verbirgt, warum es einen
Unterschied macht, und was er nicht lösen kann.
</p>
<p class="mt-4 text-xs text-muted">Stand: Mai 2026</p>
</header>
<article class="prose-cardecky">
<h2>Das Paradox des Lernens</h2>
<p>
Wer viel lernt, behält oft wenig. Das liegt nicht an mangelnder Disziplin,
sondern an einer einfachen physiologischen Tatsache: Das Gehirn vergisst nach
einer vorhersagbaren Kurve. Hermann Ebbinghaus hat das im 19. Jahrhundert
empirisch beschrieben. Seitdem hat sich an der Grundkurve nichts geändert —
aber daran, wie gut wir darauf reagieren können, hat sich einiges getan.
</p>
<p>
Die meisten Lernmethoden ignorieren die Vergessenskurve. Sie lernen in Blöcken,
mit Wiederholungen, die zu früh kommen (egal, was man schon weiß) oder zu spät
(wenn es bereits vergessen ist). Das Ergebnis: mehr Zeitaufwand, weniger Retention.
Spaced Repetition ist der direkte Angriff auf dieses Problem.
</p>
<h2>Was Spaced Repetition löst</h2>
<p>
Das Prinzip ist einfach: Eine Karte wird genau dann wieder gezeigt, wenn man sie
vergessen hat — oder kurz davor. Nicht jeden Tag. Nicht nach festem Zeitplan.
Sondern genau dann, wenn das Wiederholen etwas bringt.
</p>
<p>
Eine Karte, die man immer sofort richtig beantwortet, wird seltener gezeigt.
Eine, die man regelmäßig vergisst, kommt häufiger. Das klingt selbstverständlich.
Die entscheidende Frage ist: Wie genau berechnet ein Algorithmus diesen Zeitpunkt?
</p>
<h2>SM-2 und FSRS: Was sich geändert hat</h2>
<p>
Der Algorithmus, der die letzten Jahrzehnte dominiert hat, heißt <strong>SM-2</strong>
— entwickelt von Piotr Woźniak in den späten 1980ern und bis heute das Fundament
der meisten Lern-Apps, einschließlich des größten Teils der Anki-Nutzerbasis.
SM-2 funktioniert. Aber es hat strukturelle Schwächen.
</p>
<p>
SM-2 schätzt, wie lange eine Erinnerung hält, auf Basis eines festen
Intervall-Multiplikators und einer einfachen Bewertungsskala. Es kennt keine
separaten Konzepte für <strong>Stability</strong> (wie lange eine Erinnerung nach
einer Wiederholung hält) und <strong>Difficulty</strong> (wie schwer eine Karte
grundsätzlich ist). Es behandelt beide als einen kombinierten Faktor — was
bedeutet, dass eine Karte, die man einmal vergessen hat, dauerhaft mit schlechterer
Scheduling-Qualität bestraft wird, auch wenn man sie danach zehnmal in Folge
richtig beantwortet.
</p>
<p>
<strong>FSRS</strong> (Free Spaced Repetition Scheduler) arbeitet anders. Der
Algorithmus, hauptsächlich entwickelt von Jarrett Ye und seit 2022 quelloffen
verfügbar, modelliert Stability und Difficulty als getrennte Parameter. Er nutzt
ein Modell, das mit realen Review-Daten trainiert wurde — nicht mit theoretischen
Annahmen über das Gedächtnis.
</p>
<p>
Das Ergebnis: <strong>2030 % weniger Reviews bei gleicher Retention.</strong>
Das ist keine Marketing-Angabe, sondern das Ergebnis von Benchmarks auf realen
Datensätzen, die beim Entwickeln von FSRS veröffentlicht wurden. Wer täglich
200 Karten reviewt, kommt mit FSRS auf 140160 — bei gleichem Lernergebnis.
Über Monate und Jahre summiert sich das auf Dutzende Stunden.
</p>
<h2>Warum FSRS bei anderen nicht der Standard ist</h2>
<p>
Anki hat FSRS erst 2024 in den Kern integriert — und dann nur als opt-in. Wer
Anki installiert und keine Einstellungen ändert, nutzt heute noch SM-2. Die meisten
Nutzer:innen wissen nicht, dass FSRS existiert, geschweige denn, wie sie es
aktivieren.
</p>
<p>
Das ist keine Kritik an Anki. Es ist eine Open-Source-App mit einem langen
Kompatibilitäts-Vertrag gegenüber Millionen Nutzer:innen und deren bestehenden
Decks. Defaults zu ändern ist dort riskant — technisch und in der Community.
</p>
<p>
Quizlet ist der andere relevante Name in diesem Bereich. Dort hat Spaced
Repetition nicht den Weg ins opt-in genommen, sondern vollständig raus.
Algorithmen, die Nutzer:innen davon abhalten, dieselbe Karte immer wieder
zu sehen, erzeugen weniger Session-Zeit. Weniger Session-Zeit ist ein
schlechter Wert für eine werbefinanzierte Plattform.
</p>
<h2>Wie Cardecky das umsetzt</h2>
<p>
Cardecky nutzt FSRS als Standard — kein opt-in, kein Versteck in den
Einstellungen, keine Legacy-Defaults. Jede neue Karte, jede neue Wiederholung
wird von FSRS geplant. Das ist der Ausgangspunkt, nicht ein Premium-Feature.
</p>
<p>
Der nächste Schritt ist die <strong>FSRS-Parameter-Optimierung pro Nutzer:in</strong>.
FSRS enthält 17 Parameter, die beschreiben, wie ein individuelles Gedächtnis auf
Wiederholungen reagiert. Die Standard-Parameter wurden auf einem großen,
heterogenen Datensatz trainiert — sie sind gut, aber sie passen nicht perfekt
auf jede Person und jedes Lerngebiet. Wer Vokabeln einer neuen Sprache lernt,
vergisst anders als jemand, der medizinische Fakten büffelt. Aus der persönlichen
Review-History lassen sich diese Parameter re-optimieren. Das ist in Cardecky
in Arbeit.
</p>
<p>
Ebenfalls geplant: ein <strong>Transparenz-Tooltip pro Karte</strong> — „Warum
werde ich heute gefragt?" Nicht als technische Dokumentation, sondern als
konkreter Einblick: Stability, Difficulty, letztes Rating, berechnetes nächstes
Intervall. Wer verstehen will, warum eine Karte jetzt auftaucht und nicht in
drei Wochen, bekommt die Antwort direkt.
</p>
<h2>Was FSRS nicht löst</h2>
<p>
Ein guter Algorithmus ersetzt keine guten Karten.
</p>
<p>
FSRS kann eine Karte optimal planen — aber wenn sie schlecht formuliert ist,
zu viel Information auf einmal trägt, oder eine Verbindung zum eigenen Kontext
fehlt, hilft das beste Scheduling wenig. Das Gehirn verankert Informationen,
wenn sie bedeutungsvoll sind. Dafür ist kein Algorithmus zuständig.
</p>
<p>
Das andere offene Problem: Wer eine längere Pause macht — Urlaub, Prüfungsstress,
Krankheit — baut sich eine Backlog-Queue auf. Anki-Nutzer:innen kennen das Gefühl,
nach zehn Tagen Pause auf 500+ fällige Karten zu schauen. Mit einer besseren
Scheduling-Basis ist das Problem kleiner, weil FSRS Intervalle realistischer setzt
und weniger Überplanungen produziert — aber wer lange pausiert, kommt um den
Backlog nicht ganz herum. Strategien dafür (selektive Queue-Verkleinerung,
Deck-Priorisierung nach Prüfungsdatum) sind ein separates Problem vom Algorithmus.
</p>
<hr class="my-12 border-rule" />
<div class="rounded-xl border border-rule bg-paper p-8">
<p class="font-serif text-2xl text-ink">Mit FSRS starten — kostenlos.</p>
<p class="mt-3 text-muted leading-relaxed">
Cardecky nutzt FSRS by default. Kein Opt-in, kein Abo, kein Konfigurieren.
Einfach loslegen.
</p>
<div class="mt-6 flex flex-wrap gap-4">
<a href={APP_URL} class="btn-primary">
Cardecky öffnen
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
</a>
</div>
</div>
</article>
</main>
<Footer />
</Layout>
<style>
.prose-cardecky h2 {
font-family: var(--font-serif, Georgia, serif);
font-size: 1.375rem;
font-weight: 600;
color: theme('colors.ink');
margin-top: 2.5rem;
margin-bottom: 0.75rem;
line-height: 1.3;
}
.prose-cardecky p {
color: theme('colors.ink');
line-height: 1.75;
margin-bottom: 1.25rem;
}
.prose-cardecky ul,
.prose-cardecky ol {
margin-bottom: 1.25rem;
padding-left: 1.5rem;
}
.prose-cardecky ul { list-style-type: disc; }
.prose-cardecky ol { list-style-type: decimal; }
.prose-cardecky li {
color: theme('colors.ink');
line-height: 1.75;
margin-bottom: 0.5rem;
}
.prose-cardecky strong {
font-weight: 600;
color: theme('colors.ink');
}
.prose-cardecky a {
color: theme('colors.leaf');
text-decoration: underline;
text-underline-offset: 2px;
}
.prose-cardecky a:hover { color: theme('colors.ink'); }
.prose-cardecky hr { border-color: theme('colors.rule'); }
</style>

View file

@ -1,408 +0,0 @@
---
import Layout from '../../layouts/Layout.astro';
import Nav from '../../components/Nav.astro';
import Footer from '../../components/Footer.astro';
import '../../styles/base.css';
const APP_URL = 'https://cardecky.mana.how';
const title = 'Wie man Lernkarten schreibt, die wirklich funktionieren | Cardecky';
const description =
'Die meisten Lernkarten sind zu lang, zu abstrakt oder zu kontextfrei. Fünf konkrete' +
' Prinzipien für Karten, die das Gehirn tatsächlich verankert — mit Gegenbeispielen.';
---
<Layout {title} {description}>
<Nav />
<main class="mx-auto max-w-prose px-6 py-16 sm:py-24">
<!-- Artikel-Header -->
<header class="mb-12">
<p class="section-label mb-4">Lernen · Methodik</p>
<h1 class="font-serif text-4xl leading-tight text-ink sm:text-5xl">
Wie man Lernkarten schreibt, die wirklich funktionieren
</h1>
<p class="mt-5 text-lg leading-relaxed text-muted">
Der beste Algorithmus nützt nichts, wenn die Karten schlecht gebaut sind. Fünf
Prinzipien für Karten, die das Gehirn tatsächlich behält.
</p>
<p class="mt-4 text-xs text-muted">Stand: Mai 2026</p>
</header>
<article class="prose-cardecky">
<!-- Einstieg -->
<p>
Wer mit Spaced Repetition anfängt und nach drei Wochen merkt, dass es nicht
funktioniert, sucht den Fehler meistens an der falschen Stelle: beim Algorithmus, bei
der App, bei der Wiederholungsfrequenz. Die eigentliche Ursache sitzt fast immer
früher — in den Karten selbst.
</p>
<p>
Schlechte Karten machen gutes Scheduling wertlos. Eine Karte, die drei Fakten
gleichzeitig abfragt, hat kein definiertes Lernergebnis. Eine Karte ohne Kontext
wird isoliert abgespeichert — und isoliert Gespeichertes verblasst, egal wie oft
es wiederholt wird. FSRS kann berechnen, wann du eine Karte wieder sehen sollst;
es kann nicht reparieren, was die Karte von vornherein falsch fragt.
</p>
<p>
Die folgenden fünf Prinzipien kommen aus der Lernforschung, aus Piotr Woźniaks
„Twenty Rules of Formulating Knowledge" und aus der Praxis der Anki- und
FSRS-Community. Sie sind nicht kompliziert — aber sie erfordern beim Schreiben
Disziplin.
</p>
<!-- Prinzip 1 -->
<h2>1. Eine Frage, eine Antwort</h2>
<p>
Das ist der häufigste Anfängerfehler, und er fühlt sich beim Schreiben nicht wie
ein Fehler an. Man kennt ein Thema, will es „effizient" auf Karten bringen, und
packt fünf zusammengehörige Fakten auf eine Karte. Das Ergebnis ist eine Karte, die
du dir nie wirklich merken kannst — weil du nie weißt, ob du sie gewusst hast.
</p>
<p>Beispiel einer schlechten Karte:</p>
<pre>F: Was sind die Eigenschaften von Mitochondrien?
A: Doppelmembran, eigene DNA, ATP-Produktion durch
oxidative Phosphorylierung, Apoptose-Steuerung,
Ursprung aus endosymbiotischer Bakterien.</pre>
<p>Besser: fünf Karten.</p>
<pre>F: Welche Membran-Struktur haben Mitochondrien?
A: Doppelmembran (Außen- und Innenmembran).
F: Wodurch unterscheidet sich die Mitochondrien-DNA
von der Zellkern-DNA?
A: Sie ist zirkulär und erinnert an Bakterien-DNA
— Hinweis auf den endosymbiontischen Ursprung.</pre>
<p>
Jede Karte hat jetzt ein klares Richtig oder Falsch. Wenn du sie beim Scheduling
als „schwierig" markierst, weiß der Algorithmus, was du nicht weißt — und kann
das gezielt wiederholen. Mit der Sammelkarte kann er das nicht.
</p>
<!-- Prinzip 2 -->
<h2>2. Kontext ist kein Luxus</h2>
<p>
Isolierte Fakten vergisst das Gehirn schneller als eingebettete. Das ist keine
Faustregel, sondern ein gut belegter Effekt: Bedeutungsträgern gegenüber
bedeutungsfreien Stimuli fällt das Behalten leichter, weil das Gehirn neue
Information an bestehende Netzwerke knüpfen kann.
</p>
<p>Eine typische kontextfreie Karte:</p>
<pre>F: Was ist Apoptose?
A: Programmierter Zelltod.</pre>
<p>Eine Karte mit Kontext:</p>
<pre>F: Warum ist Apoptose für die Embryonalentwicklung
wichtig?
A: Finger entstehen, weil die Zellen zwischen ihnen
gezielt in Apoptose gehen — nicht, weil Finger
wachsen, sondern weil das Material dazwischen
verschwindet.</pre>
<p>
Die zweite Karte fragt dasselbe Konzept ab — aber in einem Zusammenhang, der sich
bildlich einprägt. „Programmierter Zelltod" bleibt ein Begriff. Finger, die sich
aus einem Gewebeklumpen herausschälen, bleibt ein Bild.
</p>
<p>
Das bedeutet nicht, dass jede Karte einen langen Erklärungstext braucht. Ein
einziger konkreter Anwendungsfall, ein Beispiel, ein Gegenbeispiel — das reicht
meistens. Die Frage „in welchem Zusammenhang?" vor dem Schreiben stellen.
</p>
<!-- Prinzip 3 -->
<h2>3. Minimales, aber vollständiges Wissen</h2>
<p>
Piotr Woźniak nennt das „minimum information principle": eine Karte so kurz wie
möglich, aber nicht kürzer. Die Spannung liegt im „aber nicht kürzer" — eine Karte,
die so gekürzt wurde, dass sie nur noch mit Vorwissen richtig beantwortet werden
kann, das du beim Wiederholen vielleicht nicht mehr parat hast, testet nichts
Sinnvolles.
</p>
<p>Zu lang:</p>
<pre>F: Erkläre den Unterschied zwischen prokaryotischen
und eukaryotischen Zellen, inklusive Beispielen,
evolutionärem Ursprung und Zellorganellen.
A: [langer Absatz]</pre>
<p>Zu kurz (und zu vage):</p>
<pre>F: Prokaryoten?
A: Keine Zellkerne.</pre>
<p>Genau richtig:</p>
<pre>F: Was fehlt Prokaryoten, das Eukaryoten haben?
A: Einen membranumhüllten Zellkern.</pre>
<p>
Ein gutes Testkriterium: Wenn du eine Karte nach einem Monat Pause siehst und die
Frage vollständig verstehst, ist sie gut formuliert. Wenn du erst rekonstruieren
musst, was du damals gemeint hast, ist sie zu kurz oder zu vage.
</p>
<!-- Prinzip 4 -->
<h2>4. Cloze richtig einsetzen</h2>
<p>
Lückentexte — in Anki-Notation <code>&#123;&#123;c1::Begriff&#125;&#125;</code> — sind nicht
einfach eine andere Formatierung derselben Karte. Sie eignen sich besonders gut für
Prozesse, Sequenzen und Zusammenhänge, weil der umgebende Satz als Kontext direkt
sichtbar bleibt.
</p>
<p>Klassische Frage-Antwort-Karte für einen Begriff im Kontext:</p>
<pre>F: Was ist das Prinzip hinter dem Krebs-Zyklus?
A: Acetyl-CoA wird oxidiert, ATP gewonnen,
CO₂ abgegeben.</pre>
<p>Als Cloze — stärker, weil der Satz als Ganzes gelernt wird:</p>
<pre>Der Krebs-Zyklus oxidiert {{c1::Acetyl-CoA}},
gewinnt dabei {{c2::ATP}} und gibt
{{c3::CO₂}} ab.</pre>
<p>
Cloze ist schwächer, wenn der Lückentext rein zufällig wirkt — also wenn aus dem
Satz nicht klar wird, was gesucht ist, und jedes beliebige Wort passen könnte.
Dann ist eine direkte Frage ehrlicher.
</p>
<p>
Cloze ist auch kein Allheilmittel gegen das Ein-Fakt-Problem: ein Satz mit fünf
Lücken ist immer noch eine Karte mit fünf Fakten. Besser: pro Satz eine Lücke,
mehrere Karten.
</p>
<!-- Prinzip 5 -->
<h2>5. Eigene Worte, eigene Bilder</h2>
<p>
Auswendig gelernte Definitionen verblassen schneller als selbst formulierte
Erklärungen. Das liegt daran, dass bei der Eigenformulierung das Verstehen
vorausgeht — wer etwas in eigene Worte übersetzt, hat es bereits verarbeitet,
nicht nur kopiert.
</p>
<p>
Lehrbuchdefinition direkt übernommen — typische Anfänger-Karte:
</p>
<pre>F: Was ist osmotischer Druck?
A: Der Druck, der aufgewendet werden muss, um den
osmotischen Fluss einer Lösung durch eine
semipermeable Membran zu verhindern.</pre>
<p>Eigenformulierung:</p>
<pre>F: Was ist osmotischer Druck — in einem Satz,
ohne Fachsprache?
A: Das Wasser will auf die Seite mit mehr
gelöstem Zeug; der osmotische Druck ist der
Gegendruck, der das verhindert.</pre>
<p>
Die zweite Version verliert Präzision — das ist ein echter Trade-off. Für
klausurrelevante exakte Definitionen braucht man beide: die präzise Formulierung
und die eigene Erklärung. Dann zwei Karten. Nicht eine Karte, die beides
gleichzeitig will.
</p>
<p>
Bilder funktionieren nach demselben Prinzip: visuell verankerte Information hat
mehr Anknüpfungspunkte im Gedächtnis als rein verbale. Ein Diagramm, ein
selbst gezeichneter Sketch, ein Screenshot aus einem Lehrvideo — das schlägt
eine nochmals kopierte Textdefinition fast immer.
</p>
<!-- AI-Generierung -->
<h2>Was AI-Generierung kann — und was nicht</h2>
<p>
AI-Tools können Karten aus einem Text generieren, und das kann die Arbeit
reduzieren: statt zwanzig Karten von Hand zu schreiben, bekommt man einen
Entwurf, der überarbeitet werden kann. Das ist ein nützliches Werkzeug.
</p>
<p>
Es ersetzt aber nicht das Nachdenken über die eigene Formulierung. AI-generierte
Karten folgen oft dem Muster der Lehrbuchdefinition — sie übernehmen Sprache
aus dem Quelltext, statt sie zu übersetzen. Sie splitten zu selten von alleine
auf ein Fakt pro Karte. Und sie können nicht wissen, welcher Kontext für dich
der einprägsame ist.
</p>
<p>
Der sinnvolle Workflow: AI generiert einen Rohsatz von Karten, du überarbeitest
ihn nach den fünf Prinzipien. Das ist weniger Arbeit als von null, aber es ist
immer noch Arbeit.
</p>
<!-- Cardecky-Einbindung -->
<h2>Welche Kartentypen welches Prinzip unterstützen</h2>
<p>
Cardecky hat vier native Kartentypen, die direkt auf diese Prinzipien einzahlen:
</p>
<ul>
<li>
<strong>Vorderseite/Rückseite</strong> — der Standard für Prinzip 1 und 3. Eine
Frage, eine Antwort. Keine Extras.
</li>
<li>
<strong>Lückentext (Cloze)</strong> — für Prinzip 4: Prozesse und Zusammenhänge,
bei denen der umgebende Satz als Kontext stehen bleibt.
</li>
<li>
<strong>Bild-Karte</strong> — für Prinzip 5: Diagramme, Sketchnotes, annotierte
Screenshots als eigenständiger Karteninhalt.
</li>
<li>
<strong>Typing-Karte</strong> — erzwingt freies Erinnern statt
Wiedererkennen; sinnvoll für Formeln, Fachbegriffe und exakte
Definitionen, bei denen das Erkennen zu wenig ist.
</li>
</ul>
<p>
Multiple Choice ist ebenfalls verfügbar — nützlich für erste Annäherungen an neues
Material, wo freies Erinnern noch nicht möglich ist. Für konsolidiertes Wissen ist
Wiedererkennen aus Optionen aber schwächer als freies Abrufen.
</p>
<p>
In Arbeit: ein AI-Cloze-Generator, der aus einem eigenen Text direkt
Lückentextkarten erstellt, und ein Card-Split-Vorschlag, der Karten mit
mehreren Fakten automatisch zur Teilung vorschlägt. Beide Features sind noch nicht
live — sie werden ergänzt, wenn sie auf einem Qualitätsniveau sind, das die
oben beschriebenen Prinzipien nicht untergräbt.
</p>
<!-- CTA -->
<hr class="my-12 border-rule" />
<div class="rounded-xl border border-rule bg-paper p-8">
<p class="font-serif text-2xl text-ink">Karten schreiben — in Cardecky</p>
<p class="mt-3 text-muted leading-relaxed">
Alle fünf Kartentypen verfügbar, FSRS-Scheduling ab der ersten Karte,
kein Abo für das eigentliche Lernen.
</p>
<div class="mt-6 flex flex-wrap gap-4">
<a href={APP_URL} class="btn-primary">
Cardecky öffnen
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
</a>
</div>
</div>
</article>
</main>
<Footer />
</Layout>
<style>
/* Prose-Styles für den Artikel — ohne externes @tailwindcss/typography */
.prose-cardecky h2 {
font-family: var(--font-serif, Georgia, serif);
font-size: 1.375rem;
font-weight: 600;
color: theme('colors.ink');
margin-top: 2.5rem;
margin-bottom: 0.75rem;
line-height: 1.3;
}
.prose-cardecky p {
color: theme('colors.ink');
line-height: 1.75;
margin-bottom: 1.25rem;
}
.prose-cardecky ul,
.prose-cardecky ol {
margin-bottom: 1.25rem;
padding-left: 1.5rem;
}
.prose-cardecky ul {
list-style-type: disc;
}
.prose-cardecky ol {
list-style-type: decimal;
}
.prose-cardecky li {
color: theme('colors.ink');
line-height: 1.75;
margin-bottom: 0.5rem;
}
.prose-cardecky li + li {
margin-top: 0.375rem;
}
.prose-cardecky strong {
font-weight: 600;
color: theme('colors.ink');
}
.prose-cardecky a {
color: theme('colors.leaf');
text-decoration: underline;
text-underline-offset: 2px;
}
.prose-cardecky a:hover {
color: theme('colors.ink');
}
.prose-cardecky hr {
border-color: theme('colors.rule');
}
.prose-cardecky pre {
font-family: var(--font-mono, ui-monospace, monospace);
font-size: 0.875rem;
line-height: 1.6;
background-color: theme('colors.paper');
border: 1px solid theme('colors.rule');
border-radius: 0.5rem;
padding: 1rem 1.25rem;
margin-bottom: 1.25rem;
overflow-x: auto;
color: theme('colors.ink');
white-space: pre-wrap;
}
.prose-cardecky code {
font-family: var(--font-mono, ui-monospace, monospace);
font-size: 0.875em;
background-color: theme('colors.paper');
border: 1px solid theme('colors.rule');
border-radius: 0.25rem;
padding: 0.1em 0.35em;
}
</style>

View file

@ -1,97 +0,0 @@
---
import Layout from '../../layouts/Layout.astro';
import Nav from '../../components/Nav.astro';
import Footer from '../../components/Footer.astro';
import '../../styles/base.css';
const posts = [
{
href: '/blog/quizlet-paywall',
tag: 'Migration',
title: 'Quizlet zieht die Paywall hoch — was jetzt?',
summary:
'Fünf Millionen Nutzer:innen haben Quizlet in den letzten zwei Jahren verlassen. Was hinter der Bezahlschranke steckt und wie der Wechsel in wenigen Minuten klappt.',
date: 'Mai 2026',
},
{
href: '/blog/fsrs-algorithmus',
tag: 'Algorithmus',
title: 'Weniger lernen, mehr behalten: Was FSRS bedeutet',
summary:
'FSRS reduziert die nötigen Wiederholungen um 2030 % bei gleicher Retention. Wie der Algorithmus funktioniert — und warum er bei anderen Apps noch nicht der Standard ist.',
date: 'Mai 2026',
},
{
href: '/blog/deine-daten',
tag: 'Datenschutz',
title: 'Deine Karten, deine Daten',
summary:
'Lerndaten verraten mehr als die meisten ahnen. Was Cardecky damit macht — und was nicht — lässt sich im öffentlichen Quellcode nachprüfen.',
date: 'Mai 2026',
},
{
href: '/blog/anki-zu-kompliziert',
tag: 'Vergleich',
title: 'Anki ist mächtig — und trotzdem schwer empfehlbar',
summary:
'Anki ist technisch überlegen. Trotzdem hören die meisten auf — nicht wegen des Algorithmus, sondern wegen Interface und Lernkurve.',
date: 'Mai 2026',
},
{
href: '/blog/gute-lernkarten',
tag: 'Lernen',
title: 'Wie man Lernkarten schreibt, die wirklich funktionieren',
summary:
'Der beste Algorithmus nützt nichts, wenn die Karten schlecht gebaut sind. Fünf Prinzipien — mit Gegenbeispielen.',
date: 'Mai 2026',
},
] as const;
---
<Layout
title="Artikel — Cardecky"
description="Hintergrund und Methodik rund um Spaced Repetition, Lernkarten und die Cardecky-App."
>
<Nav />
<main class="mx-auto max-w-content px-6 py-16 sm:py-24">
<p class="section-label">Blog</p>
<h1 class="mt-3 font-serif text-display text-ink">Artikel.</h1>
<p class="mt-4 max-w-prose text-muted">
Hintergrund zu Spaced Repetition, Vergleiche mit anderen Apps und Methodik
für besseres Lernen.
</p>
<ul class="mt-12 space-y-4" role="list">
{posts.map((post) => (
<li>
<a
href={post.href}
class="group flex flex-col gap-2 rounded-xl border border-rule bg-paper p-6 transition-shadow hover:shadow-md sm:flex-row sm:items-start sm:gap-8"
>
<div class="flex shrink-0 items-center gap-3 sm:w-40 sm:flex-col sm:items-start sm:gap-1">
<span class="section-label">{post.tag}</span>
<span class="text-xs text-muted">{post.date}</span>
</div>
<div class="flex-1">
<p class="font-serif text-xl font-semibold leading-snug text-ink group-hover:text-leaf transition-colors">
{post.title}
</p>
<p class="mt-2 text-sm leading-relaxed text-muted">
{post.summary}
</p>
</div>
<span class="hidden shrink-0 items-center gap-1 text-sm font-medium text-leaf sm:flex">
Lesen
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
</span>
</a>
</li>
))}
</ul>
</main>
<Footer />
</Layout>

View file

@ -1,309 +0,0 @@
---
import Layout from '../../layouts/Layout.astro';
import Nav from '../../components/Nav.astro';
import Footer from '../../components/Footer.astro';
import '../../styles/base.css';
const APP_URL = 'https://cardecky.mana.how';
const IMPORT_URL = 'https://cardecky.mana.how/import';
const title = 'Quizlet zieht die Paywall hoch — was jetzt? | Cardecky';
const description =
'Quizlet hat 20222023 Learn Mode, Test Mode und echtes Spaced Repetition hinter eine' +
' Bezahlschranke gesperrt. Was das konkret bedeutet — und wie der Wechsel zu Cardecky in' +
' wenigen Minuten klappt.';
---
<Layout {title} {description}>
<Nav />
<main class="mx-auto max-w-prose px-6 py-16 sm:py-24">
<!-- Artikel-Header -->
<header class="mb-12">
<p class="section-label mb-4">Lernkarten · Migration</p>
<h1 class="font-serif text-4xl leading-tight text-ink sm:text-5xl">
Quizlet zieht die Paywall hoch&nbsp;— was jetzt?
</h1>
<p class="mt-5 text-lg leading-relaxed text-muted">
Millionen von Nutzer:innen haben jahrelang auf Quizlet gelernt. Dann kam die
Bezahlschranke. Was dahintersteckt, was du verloren hast — und wie du in wenigen Minuten
auf Cardecky umziehst.
</p>
<p class="mt-4 text-xs text-muted">Stand: Mai 2026</p>
</header>
<article class="prose-cardecky">
<!-- 1 -->
<h2>Was Quizlet getan hat</h2>
<p>
Quizlet startete 2005 als Karteikarten-Tool ohne Geschäftsmodell. Das hat sich geändert.
2022 und 2023 hat das Unternehmen schrittweise die Kern-Lernmodi — Learn Mode, Test Mode
und den zugehörigen Scheduling-Algorithmus — hinter ein Abo (Quizlet Plus,
$35.99/Jahr) gesperrt. Wer nicht zahlt, kann Karten ansehen und in simplen
Karteikarten-Ansichten blättern. Der eigentliche Lernmechanismus, der aus dem
Blättern ein gezieltes Üben macht, ist nur noch für Zahler zugänglich.
</p>
<p>
Quizlet hat das nicht als Einschnitt kommuniziert, sondern als
„Premium-Erweiterung". Die Realität: wer sich über Monate eine Karten-Sammlung
aufgebaut hat, kann diese ohne Abo kaum noch sinnvoll nutzen.
</p>
<!-- 2 -->
<h2>Was konkret hinter der Paywall verschwunden ist</h2>
<p>
Das sind keine Randfeatures. Learn Mode ist der Modus, der Quizlet überhaupt vom
einfachen Karteikarten-Blättern unterscheidet: er verfolgt deinen Fortschritt,
wiederholt Karten, die du falsch beantwortest hast, und passt die Reihenfolge
deiner Lernkarten dynamisch an. Test Mode erstellt automatisierte Prüfungs-
Simulationen aus deinen Sets. Ohne diese Modi ist Quizlet ein Karten-Viewer —
nützlich zum Nachschlagen, nicht zum Lernen.
</p>
<p>
Hinzu kommt: Quizlet sammelt Nutzungsdaten und schaltet Werbung für Nicht-Zahler.
Der Code ist proprietär — du kannst nicht nachprüfen, welche Daten wohin gehen.
Und Daten-Export gibt es nur in begrenztem Umfang; wer zu einem anderen Dienst
wechseln will, hat keine einfache Brücke.
</p>
<p>
Rund fünf Millionen Nutzer:innen haben in den vergangenen zwei Jahren aktiv nach
Alternativen gesucht. Das ist kein Protest, sondern eine vernünftige Reaktion
darauf, dass ein Werkzeug, auf das man sich verlassen hatte, unter den Händen
wegbricht.
</p>
<!-- 3 -->
<h2>Was „echtes Spaced Repetition" bedeutet</h2>
<p>
Spaced Repetition ist ein lernpsychologisches Prinzip: Karten, die du sicher
weißt, siehst du seltener; Karten, bei denen du schwankst oder scheitern,
kommen früher wieder. Der Effekt ist gut belegt — das Vergessen-Intervall passt
sich an dein tatsächliches Erinnerungsvermögen an, statt nach fester Reihenfolge
zu laufen.
</p>
<p>
Die Qualität hängt am Algorithmus. Quizlets hauseigene Implementierung war eine
vereinfachte Variante. Der heute wissenschaftlich bevorzugte Standard ist
<strong>FSRS</strong> (Free Spaced Repetition Scheduler) — entwickelt 2022 von
Jarrett Ye, inzwischen in mehreren Studien gegen ältere Algorithmen getestet,
standardmäßig in Anki 23.10+ enthalten. FSRS modelliert zwei Gedächtnisparameter
pro Karte (Stabilität und Abrufbarkeit) und passt die Wiederholungsabstände
präziser an als frühere Verfahren.
</p>
<p>
Cardecky implementiert FSRS direkt — nicht als nachträgliches Feature, sondern
als Kern des Scheduling-Systems ab Tag 1.
</p>
<!-- 4 -->
<h2>Cardecky als Alternative — was wir bieten und was noch fehlt</h2>
<p>
Cardecky ist die Spaced-Repetition-App des mana e.V., eines Schweizer Vereins.
Das ist keine Marketing-Formel, sondern eine Aussage über das Geschäftsmodell:
kein Investor, kein Renditedruck, keine Exit-Strategie. Die App läuft auf
selbst-betriebener Infrastruktur in Deutschland.
</p>
<p>
Was heute verfügbar ist:
</p>
<ul>
<li>
<strong>FSRS-Algorithmus</strong> — aktueller Stand der Forschung, serverside
berechnet, keine Näherungslösung.
</li>
<li>
<strong>Sechs Kartentypen</strong> — Vorderseite/Rückseite, Lückentext, Multiple
Choice, Bild, Tipp-Karte, Rich Text.
</li>
<li>
<strong>Quizlet-Import</strong> — direkt eingebaut. Deine Sets landen in Minuten
in Cardecky, mit Formatierung.
</li>
<li>
<strong>Anki-Import</strong> — .apkg-Dateien werden geparst, Decks und Karten
übernommen.
</li>
<li>
<strong>Kein Tracking, keine Werbung</strong> — kein Analytics-Code, nachweisbar
im öffentlichen Quellcode unter
<a href="https://git.mana.how/mana/cards" target="_blank" rel="noopener">
git.mana.how/mana/cards</a>.
</li>
<li>
<strong>Anti-Lock-in per Design</strong> — Export jederzeit als .apkg, CSV
oder DSGVO-Vollexport. Wir bauen keine Karte, die du nicht mitnehmen kannst.
</li>
</ul>
<p>
Was noch fehlt, und das soll hier nicht versteckt werden: Cardecky ist jung.
Eine native App (iOS/Android) ist in Planung, aber noch nicht verfügbar — heute
ist Cardecky eine Web-App, installierbar als PWA. Die kuratierte Deck-Bibliothek
wächst; wer spezifische Fachbereiche sucht, findet dort heute noch Lücken.
Kollaborative Decks im Team sind auf der Roadmap, noch nicht live. Wer eine
ausgereifte Desktop-App mit jahrelanger Community erwartet, sollte auch Anki
in Betracht ziehen — das ist keine Schwäche, sondern ein Hinweis auf den
unterschiedlichen Stand.
</p>
<p>
Die Kernfunktionen — Karten anlegen, lernen, importieren, exportieren — sind
vollständig und ohne Bezahlschranke zugänglich. Tiers gibt es nur für
KI-gestützte Features (automatische Kartengenerierung, Erklärungen via mana LLM)
und erweiterten Storage. Das eigentliche Lernen ist und bleibt kostenlos.
</p>
<!-- 5 -->
<h2>Migration in drei Schritten</h2>
<p>
Der Wechsel von Quizlet zu Cardecky dauert je nach Set-Größe fünf bis fünfzehn
Minuten.
</p>
<ol>
<li>
<strong>Quizlet-Export vorbereiten.</strong> Öffne in Quizlet das Set, das du
mitnehmen willst. Unter „Mehr" → „Exportieren" kannst du das Set als
Tab-getrennte Textdatei herunterladen. Quizlet bietet diesen Export auch ohne
Plus-Abo an.
</li>
<li>
<strong>In Cardecky importieren.</strong> Gehe auf
<a href={IMPORT_URL} target="_blank" rel="noopener">cardecky.mana.how/import</a>
und wähle „Quizlet-Import". Lade die Textdatei hoch oder füge den Text direkt
ein — Cardecky erkennt das Format automatisch und zeigt dir vor dem Import eine
Vorschau der Karten.
</li>
<li>
<strong>Deck einrichten und starten.</strong> Nach dem Import landet das Deck in
deiner Bibliothek. FSRS-Scheduling startet automatisch beim ersten Lernen. Du
musst nichts konfigurieren — das System passt sich anhand deiner Antworten an.
</li>
</ol>
<p>
Wer bereits Anki-Decks hat, kann alternativ .apkg-Dateien direkt hochladen — der
Import-Flow ist derselbe.
</p>
<!-- 6 -->
<h2>Datenschutz und Unabhängigkeit</h2>
<p>
Cardecky wird von mana e.V. betrieben — einem Schweizer Verein ohne Investoren,
ohne Werbepartner, ohne Renditeziel. Server stehen in Deutschland, Datenverarbeitung
läuft ausschließlich auf Vereinsinfrastruktur. Es gibt keinen Analytics-Code,
keinen Tracking-Pixel, keine Drittanbieter-SDK für Nutzungsauswertung. Das lässt
sich im öffentlichen Quellcode nachprüfen.
</p>
<p>
Daten-Souveränität ist für mana kein Marketingversprechen, sondern Wert 1 in der
Werte-Hierarchie des Vereins: Daten gehören den Nutzer:innen, nicht uns. Jederzeit
Export, jederzeit Löschung, DSGVO-Auskunft auf Knopfdruck. Diese Versprechen
stehen in den Vereinsstatuten und können nicht durch eine Unternehmens-Übernahme
wegverhandelt werden — weil es keine Unternehmensanteile gibt, die man kaufen
könnte.
</p>
<!-- CTA -->
<hr class="my-12 border-rule" />
<div class="rounded-xl border border-rule bg-paper p-8">
<p class="font-serif text-2xl text-ink">Bereit für den Wechsel?</p>
<p class="mt-3 text-muted leading-relaxed">
Import deiner Quizlet-Sets dauert wenige Minuten. Kein Abo, kein
Kreditkartenfeld, keine Fallstricke.
</p>
<div class="mt-6 flex flex-wrap gap-4">
<a href={APP_URL} class="btn-primary">
Cardecky öffnen
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
</a>
<a href={IMPORT_URL} class="btn-ghost">Direkt zum Import</a>
</div>
</div>
</article>
</main>
<Footer />
</Layout>
<style>
/* Prose-Styles für den Artikel — ohne externes @tailwindcss/typography */
.prose-cardecky h2 {
font-family: var(--font-serif, Georgia, serif);
font-size: 1.375rem;
font-weight: 600;
color: theme('colors.ink');
margin-top: 2.5rem;
margin-bottom: 0.75rem;
line-height: 1.3;
}
.prose-cardecky p {
color: theme('colors.ink');
line-height: 1.75;
margin-bottom: 1.25rem;
}
.prose-cardecky ul,
.prose-cardecky ol {
margin-bottom: 1.25rem;
padding-left: 1.5rem;
}
.prose-cardecky ul {
list-style-type: disc;
}
.prose-cardecky ol {
list-style-type: decimal;
}
.prose-cardecky li {
color: theme('colors.ink');
line-height: 1.75;
margin-bottom: 0.5rem;
}
.prose-cardecky li + li {
margin-top: 0.375rem;
}
.prose-cardecky strong {
font-weight: 600;
color: theme('colors.ink');
}
.prose-cardecky a {
color: theme('colors.leaf');
text-decoration: underline;
text-underline-offset: 2px;
}
.prose-cardecky a:hover {
color: theme('colors.ink');
}
.prose-cardecky hr {
border-color: theme('colors.rule');
}
</style>

View file

@ -1,29 +0,0 @@
---
import Layout from '../layouts/Layout.astro';
import Nav from '../components/Nav.astro';
import Hero from '../components/Hero.astro';
import CardTypes from '../components/CardTypes.astro';
import HowItWorks from '../components/HowItWorks.astro';
import Features from '../components/Features.astro';
import ManaSection from '../components/ManaSection.astro';
import BlogTeaser from '../components/BlogTeaser.astro';
import CTASection from '../components/CTASection.astro';
import Footer from '../components/Footer.astro';
import '../styles/base.css';
---
<Layout
title="Cardecky — Lernkarten mit FSRS-Spaced-Repetition · mana e.V."
description="Cardecky ist eine freie Spaced-Repetition-App des mana e.V. Sechs Kartentypen, FSRS-Algorithmus, Anki-Import und eine kuratierte Bibliothek — ohne Tracking, ohne Abo."
>
<Nav />
<main>
<Hero />
<CardTypes />
<HowItWorks />
<Features />
<ManaSection />
<BlogTeaser />
<CTASection />
</main>
<Footer />
</Layout>

View file

@ -1,26 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
html {
scroll-behavior: smooth;
-webkit-font-smoothing: antialiased;
}
body {
overflow-x: hidden;
background-color: theme('colors.paper');
color: theme('colors.ink');
}
@layer components {
.btn-primary {
@apply inline-flex items-center gap-2 rounded-lg bg-leaf px-5 py-2.5 text-sm font-medium text-white transition-colors hover:bg-leaf-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-leaf focus-visible:ring-offset-2;
}
.btn-ghost {
@apply inline-flex items-center gap-2 rounded-lg border border-rule px-5 py-2.5 text-sm font-medium text-ink transition-colors hover:border-muted hover:bg-rule focus-visible:outline-none;
}
.section-label {
@apply text-xs font-semibold uppercase tracking-widest text-leaf;
}
}

View file

@ -1,31 +0,0 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./src/**/*.{astro,html,js,jsx,ts,tsx}'],
theme: {
extend: {
colors: {
ink: '#111812',
paper: '#f9faf7',
muted: '#6b7a6e',
rule: '#e2e8e0',
leaf: '#16a34a',
'leaf-hover': '#15803d',
'leaf-light': '#f0fdf4',
'leaf-border': '#bbf7d0',
},
fontFamily: {
serif: ['Georgia', 'Cambria', '"Times New Roman"', 'serif'],
sans: ['system-ui', '-apple-system', 'sans-serif'],
},
fontSize: {
hero: ['clamp(2.25rem,5vw,3.5rem)', { lineHeight: '1.1', letterSpacing: '-0.02em' }],
display: ['clamp(1.75rem,3vw,2.5rem)', { lineHeight: '1.2', letterSpacing: '-0.015em' }],
},
maxWidth: {
content: '68rem',
prose: '44rem',
},
},
},
plugins: [],
};

View file

@ -1,5 +0,0 @@
{
"extends": "astro/tsconfigs/strict",
"include": [".astro/types.d.ts", "**/*"],
"exclude": ["dist"]
}

View file

@ -18,7 +18,7 @@ export function exportToCsv(cards: Card[]): string {
front = f.front ?? '';
back = f.back ?? '';
} else {
// image-occlusion, audio-front etc. — not representable in CSV
// Wordeck ist text-only — alle Card-Types sind CSV-darstellbar
continue;
}

View file

@ -180,7 +180,7 @@ export const de: TranslationNode = {
what_skipped_title: 'Was nicht übernommen wird',
what_skipped_media: '— (Bilder + Audio werden seit Phase 9k mit übernommen, siehe oben)',
what_skipped_history: 'FSRS-Lernverlauf (Anki-Reviews werden bewusst neu aufgesetzt).',
what_skipped_addons: 'Add-on-spezifische Card-Types (image-occlusion etc.).',
what_skipped_addons: 'Add-on-spezifische Card-Types (nicht Teil von Wordeck text-only).',
anki_label: 'Aus Anki importieren',
dropzone: '📦 .apkg-Datei hier ablegen oder klicken',
dropzone_hint: 'Basic, Basic + Reverse, Cloze · Bilder + Audio werden mit übernommen (Limit 25 MB pro Datei).',

View file

@ -177,7 +177,7 @@ export const en: TranslationNode = {
what_skipped_title: 'What is not imported',
what_skipped_media: '— (Images + audio are imported since Sprint 9k, see above)',
what_skipped_history: 'FSRS learning history (Anki reviews are deliberately reset).',
what_skipped_addons: 'Add-on specific card types (image-occlusion etc.).',
what_skipped_addons: 'Add-on specific card types (non-text etc.).',
anki_label: 'Import from Anki',
dropzone: '📦 Drop .apkg file here or click',
dropzone_hint: 'Basic, Basic + Reverse, Cloze · Images + audio are imported too (limit 25 MB per file).',

View file

@ -143,12 +143,6 @@ export function subIndexCount(type: string): number {
return 2;
case 'type-in':
return 1;
case 'image-occlusion':
throw new Error(
'subIndexCount("image-occlusion") not supported — use maskRegionCount(fields.mask_regions) from @cards/domain'
);
case 'audio-front':
return 1;
case 'typing':
return 1;
case 'multiple-choice':

View file

@ -85,10 +85,6 @@ describe('subIndexCount', () => {
it('cloze wirft — Caller muss subIndexCountForCloze nutzen', () => {
expect(() => subIndexCount('cloze')).toThrow(/subIndexCountForCloze/);
});
it('image-occlusion wirft — Caller muss maskRegionCount nutzen', () => {
expect(() => subIndexCount('image-occlusion')).toThrow(/maskRegionCount/);
});
});
describe('buildScheduler', () => {

View file

@ -11,17 +11,18 @@ import {
} from '../src/schemas/index.ts';
describe('CardTypeSchema', () => {
it('accepts MVP types', () => {
it('accepts Wordeck text-only types', () => {
expect(() => CardTypeSchema.parse('basic')).not.toThrow();
expect(() => CardTypeSchema.parse('basic-reverse')).not.toThrow();
expect(() => CardTypeSchema.parse('cloze')).not.toThrow();
expect(() => CardTypeSchema.parse('image-occlusion')).not.toThrow();
expect(() => CardTypeSchema.parse('typing')).not.toThrow();
expect(() => CardTypeSchema.parse('multiple-choice')).not.toThrow();
});
it('rejects future types not yet in MVP schema', () => {
expect(() => CardTypeSchema.parse('type-in')).toThrow();
it('rejects removed types (image-occlusion, audio-front)', () => {
expect(() => CardTypeSchema.parse('image-occlusion')).toThrow();
expect(() => CardTypeSchema.parse('audio-front')).toThrow();
expect(() => CardTypeSchema.parse('audio')).toThrow();
expect(() => CardTypeSchema.parse('multiple-choice')).toThrow();
});
});
@ -68,15 +69,6 @@ describe('CardCreateSchema', () => {
expect(r.success).toBe(true);
});
it('accepts an image-occlusion card with image_ref + mask_regions', () => {
const r = CardCreateSchema.safeParse({
deck_id: 'd-1',
type: 'image-occlusion',
fields: { image_ref: 'media-id', mask_regions: '[]' },
});
expect(r.success).toBe(true);
});
it('rejects unknown type via CardTypeSchema', () => {
const r = CardCreateSchema.safeParse({
deck_id: 'd-1',