diff --git a/src/apps/epic/management/__init__.py b/src/apps/epic/management/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/apps/epic/management/commands/__init__.py b/src/apps/epic/management/commands/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/apps/epic/management/commands/reset_staging_db.py b/src/apps/epic/management/commands/reset_staging_db.py
new file mode 100644
index 0000000..c8702c5
--- /dev/null
+++ b/src/apps/epic/management/commands/reset_staging_db.py
@@ -0,0 +1,110 @@
+"""Wipe the configured database and re-run all migrations from scratch.
+
+Intended for ephemeral environments (staging) where losing every user, room,
+billpost, token, etc. is acceptable. Refuses to run when DEBUG=False unless
+the operator explicitly confirms with --i-mean-it, and always prints the
+DB host before doing anything destructive.
+
+Typical staging usage from the deploy host:
+
+ docker exec -it gamearray python manage.py reset_staging_db --i-mean-it
+
+Locally (sqlite, DEBUG=True) the safety prompt is skipped:
+
+ python src/manage.py reset_staging_db
+"""
+from django.conf import settings
+from django.core.management import call_command
+from django.core.management.base import BaseCommand, CommandError
+from django.db import connection
+
+
+PROD_HOST_FRAGMENTS = ("earthmanrpg.me",)
+
+
+class Command(BaseCommand):
+ help = "Drop every table in the default DB and re-run migrations. Destructive."
+
+ def add_arguments(self, parser):
+ parser.add_argument(
+ "--i-mean-it",
+ action="store_true",
+ help="Required when DEBUG=False. Bypasses the interactive confirmation.",
+ )
+ parser.add_argument(
+ "--no-superuser",
+ action="store_true",
+ help="Skip the post-migrate ensure_superuser call.",
+ )
+
+ def handle(self, *args, **opts):
+ db_settings = settings.DATABASES["default"]
+ engine = db_settings.get("ENGINE", "")
+ host = db_settings.get("HOST") or db_settings.get("NAME") or "(unknown)"
+
+ # Refuse outright if the host name suggests production
+ if any(frag in str(host) for frag in PROD_HOST_FRAGMENTS) and not host.startswith("staging"):
+ if "staging" not in str(host):
+ raise CommandError(
+ f"Refusing to reset DB at host={host!r} — looks like production. "
+ "Edit PROD_HOST_FRAGMENTS in this command if you really mean it."
+ )
+
+ self.stdout.write(self.style.WARNING(
+ f"\nAbout to wipe DB:\n ENGINE: {engine}\n HOST/NAME: {host}\n"
+ ))
+
+ if not settings.DEBUG and not opts["i_mean_it"]:
+ raise CommandError(
+ "DEBUG=False — pass --i-mean-it to confirm. "
+ "(This is the staging-safety check; it does not bypass the prod-host refusal above.)"
+ )
+
+ if settings.DEBUG and not opts["i_mean_it"]:
+ answer = input("Type the DB host/name to confirm: ").strip()
+ if answer != str(host):
+ raise CommandError(f"Got {answer!r}, expected {str(host)!r}. Aborting.")
+
+ # Drop schema. Postgres + sqlite both honor `flush --no-input`'s
+ # truncate-tables-in-place model, but for a *fresh* migration run we
+ # need the migration history wiped too. For Postgres the cleanest
+ # route is `DROP SCHEMA public CASCADE; CREATE SCHEMA public;` —
+ # for sqlite, deleting the file is simpler but Django's connection
+ # has it open. So: introspect the connection and drop all tables.
+ self.stdout.write("Dropping all tables…")
+ self._drop_all_tables()
+
+ self.stdout.write("Running migrate from scratch…")
+ call_command("migrate", verbosity=1, interactive=False)
+
+ if not opts["no_superuser"]:
+ try:
+ call_command("ensure_superuser", verbosity=1)
+ except Exception as exc: # pragma: no cover - depends on env vars
+ self.stdout.write(self.style.WARNING(
+ f"ensure_superuser skipped/failed: {exc}"
+ ))
+
+ self.stdout.write(self.style.SUCCESS("\nDB reset complete."))
+
+ def _drop_all_tables(self):
+ vendor = connection.vendor
+ with connection.cursor() as cursor:
+ if vendor == "postgresql":
+ cursor.execute("DROP SCHEMA public CASCADE;")
+ cursor.execute("CREATE SCHEMA public;")
+ cursor.execute("GRANT ALL ON SCHEMA public TO public;")
+ elif vendor == "sqlite":
+ cursor.execute("PRAGMA foreign_keys = OFF;")
+ cursor.execute(
+ "SELECT name FROM sqlite_master "
+ "WHERE type='table' AND name NOT LIKE 'sqlite_%'"
+ )
+ tables = [row[0] for row in cursor.fetchall()]
+ for table in tables:
+ cursor.execute(f'DROP TABLE IF EXISTS "{table}"')
+ cursor.execute("PRAGMA foreign_keys = ON;")
+ else:
+ raise CommandError(
+ f"reset_staging_db only knows postgresql + sqlite, got {vendor!r}"
+ )
diff --git a/src/apps/epic/migrations/0007_finalize_earthman_deck.py b/src/apps/epic/migrations/0007_finalize_earthman_deck.py
new file mode 100644
index 0000000..0615876
--- /dev/null
+++ b/src/apps/epic/migrations/0007_finalize_earthman_deck.py
@@ -0,0 +1,200 @@
+"""Collapse of old migrations 0007–0022 into a single end-state finalize.
+
+Schema:
+ - mechanisms → energies (was 0008)
+ - articulations → operations (was 0008)
+ - reversal → reversal_qualifier (was 0014)
+ - +italic_word (was 0018)
+
+Data (operates on the Earthman deck seeded in 0004; idempotent against the
+current schema, which is the post-rename state thanks to the operations
+above running first):
+ - Middle court reversal_qualifier per suit (was 0007)
+ - Schizo energies + operations w/ .card-ref (was 0008 + 0009)
+ - fa-hand-dots fallback for empty MAJOR icons (was 0010; only card 41
+ in fresh-seed state)
+ - Card 49 polarity-split reversal titles (was 0015 + 0016)
+ - Castanedan Virtues: trumps 6-9 + 19-21 (was 0017)
+ - italic_word for trumps 19-21 (was 0019)
+ - Trump 8 rename + non-breaking hyphen (was 0020 + 0021)
+ - Trump 9 non-breaking space (was 0021)
+ - Pip cards (number 1-10) MIDDLE → MINOR arcana (was 0022)
+
+Skipped (all no-ops against fresh 0004 seed):
+ - 0011 (nomad/schizo icons already correct in 0004)
+ - 0012 (no PENTACLES seeded for Earthman in 0004)
+ - 0013 (nomad icon already fa-hat-cowboy-side in 0004)
+"""
+from django.db import migrations, models
+
+
+# ── Schizo energies + operations ─────────────────────────────────────────────
+CR = '{}'
+
+SCHIZO_ENERGIES = [
+ {"type": "LIBIDO", "effect": f'When encountering territorial Libido, may convert Emanation into {CR.format("1. The Priest")}.'},
+ {"type": "NUMEN", "effect": f'When encountering despotic Numen, may convert Emanation into {CR.format("1. The Powerful")}.'},
+ {"type": "VOLUPTAS", "effect": f'When encountering axiomatic Voluptas, may convert Emanation into {CR.format("1. The Normal")}.'},
+ {"type": "VOLUPTAS", "effect": f'When encountering annihilating Voluptas, may convert Emanation into {CR.format("1. The Surrendered")}.'},
+]
+
+SCHIZO_OPERATIONS = [
+ {"type": "COVER", "effect": f'When covering {CR.format("2. The Occultist")} she may choose, by converting her own Reversal into {CR.format("2. Pestilence")}, to convert this Reversal into {CR.format("1. The Pervert")}.'},
+ {"type": "CROWN", "effect": f'When crowning {CR.format("3. The Despot")} she may choose, by converting her own Reversal into {CR.format("3. War")}, to convert this Reversal into {CR.format("1. The Paranoiac")}.'},
+ {"type": "BEHIND", "effect": f'When behind {CR.format("4. The Capitalist")} he may choose, by converting his own Reversal into {CR.format("4. Famine")}, to convert this Reversal into {CR.format("1. The Neurotic")}.'},
+ {"type": "BEFORE", "effect": f'When before {CR.format("5. The Fascist")} he may choose, by converting his own Reversal into {CR.format("5. Death")}, to convert this Reversal into {CR.format("1. The Suicidal")}.'},
+]
+
+# ── Middle court suit reversals ──────────────────────────────────────────────
+SUIT_REVERSAL_QUALIFIER = {
+ "BRANDS": "Seething",
+ "GRAILS": "Gloomy",
+ "BLADES": "Nervous",
+ "CROWNS": "Vacant",
+}
+COURT_NUMBERS = [11, 12, 13, 14]
+
+# ── Castanedan Virtues ───────────────────────────────────────────────────────
+IMPLICIT_VIRTUES = [
+ # (number, levity_qualifier, gravity_qualifier, reversal_title)
+ (6, "Sublimating", "Sedimentary", "Indulged Folly"),
+ (7, "Sublimating", "Sedimentary", "Indulgent Doing"),
+ (8, "Sublimating", "Sedimentary", "Self-Indulgence"),
+ (9, "Sublimating", "Sedimentary", "Indulging Personal History"),
+]
+
+EXPLICIT_VIRTUES = [
+ # (number, levity_emanation, gravity_emanation, levity_reversal, gravity_reversal, italic_word)
+ (19, "The Hunter's Stalking", "The Hunter's Stalking", "The Sleeper's Stalking", "The Quarry's Stalking", "Stalking"),
+ (20, "The Dreamer's Dreaming", "The Dreamer's Dreaming", "The Sleeper's Dreaming", "The Dreamed's Dreaming", "Dreaming"),
+ (21, "The Warrior's Intent", "The Warrior's Intent", "The Sleeper's Intent", "The Predator's Intent", "Intent"),
+]
+
+
+def finalize(apps, schema_editor):
+ TarotCard = apps.get_model("epic", "TarotCard")
+ DeckVariant = apps.get_model("epic", "DeckVariant")
+ try:
+ earthman = DeckVariant.objects.get(slug="earthman")
+ except DeckVariant.DoesNotExist:
+ return
+
+ # Middle court suit reversals
+ for suit, qualifier in SUIT_REVERSAL_QUALIFIER.items():
+ TarotCard.objects.filter(
+ deck_variant=earthman, arcana="MIDDLE", suit=suit,
+ number__in=COURT_NUMBERS,
+ ).update(reversal_qualifier=qualifier)
+
+ # Schizo: clear stray reversal_qualifier (0004 seeds 'Territoriality') +
+ # populate energies/operations
+ TarotCard.objects.filter(
+ deck_variant=earthman, arcana="MAJOR", number=1,
+ ).update(
+ reversal_qualifier="",
+ energies=SCHIZO_ENERGIES,
+ operations=SCHIZO_OPERATIONS,
+ )
+
+ # fa-hand-dots fallback for empty MAJOR icons (number ≥ 2)
+ TarotCard.objects.filter(
+ deck_variant=earthman, arcana="MAJOR", number__gte=2, icon="",
+ ).update(icon="fa-hand-dots")
+
+ # Card 49 polarity reversal titles
+ TarotCard.objects.filter(
+ deck_variant=earthman, arcana="MAJOR", number=49,
+ ).update(
+ levity_reversal="The Vibrational Mould of Man",
+ gravity_reversal="The Bestowing Eagle",
+ )
+
+ # Castanedan Virtues — implicit (trumps 6-9): trump 7 name canonicalize +
+ # qualifiers + reversal titles
+ TarotCard.objects.filter(
+ deck_variant=earthman, arcana="MAJOR", number=7,
+ ).update(name="Not-Doing")
+ for number, lvty, grav, rev in IMPLICIT_VIRTUES:
+ TarotCard.objects.filter(
+ deck_variant=earthman, arcana="MAJOR", number=number,
+ ).update(
+ levity_qualifier=lvty,
+ gravity_qualifier=grav,
+ levity_reversal=rev,
+ gravity_reversal=rev,
+ )
+
+ # Castanedan Virtues — explicit (trumps 19-21): polarity-split titles +
+ # italic_word for the agency stem
+ for number, le, ge, lr, gr, word in EXPLICIT_VIRTUES:
+ TarotCard.objects.filter(
+ deck_variant=earthman, arcana="MAJOR", number=number,
+ ).update(
+ levity_emanation=le,
+ gravity_emanation=ge,
+ levity_reversal=lr,
+ gravity_reversal=gr,
+ italic_word=word,
+ )
+
+ # Trump 8: "Losing Self-Importance" → "Self‑Unimportance" w/ U+2011
+ # non-breaking hyphen (keeps title on one line above qualifier)
+ TarotCard.objects.filter(
+ deck_variant=earthman, arcana="MAJOR", number=8,
+ ).update(name="Self‑Unimportance", slug="self-unimportance")
+
+ # Trump 9: insert U+00A0 between "Personal" and "History" so they wrap as
+ # a single unit ("Erasing / Personal History, / Sublimating")
+ TarotCard.objects.filter(
+ deck_variant=earthman, arcana="MAJOR", number=9,
+ ).update(name="Erasing Personal History")
+
+ # Pip cards (number 1-10) → MINOR arcana; courts (11-14) stay MIDDLE
+ TarotCard.objects.filter(
+ deck_variant=earthman, arcana="MIDDLE", number__lte=10,
+ ).update(arcana="MINOR")
+
+
+def revert(apps, schema_editor):
+ """Reverse just enough to restore 0006 schema state. Data reverts to the
+ raw 0004 seed shape (without any of the post-seed tweaks)."""
+ TarotCard = apps.get_model("epic", "TarotCard")
+ DeckVariant = apps.get_model("epic", "DeckVariant")
+ try:
+ earthman = DeckVariant.objects.get(slug="earthman")
+ except DeckVariant.DoesNotExist:
+ return
+ # Pip arcana
+ TarotCard.objects.filter(
+ deck_variant=earthman, arcana="MINOR", number__lte=10,
+ ).update(arcana="MIDDLE")
+ # Trump 8 + 9 names back
+ TarotCard.objects.filter(
+ deck_variant=earthman, arcana="MAJOR", number=8,
+ ).update(name="Losing Self-Importance", slug="losing-self-importance")
+ TarotCard.objects.filter(
+ deck_variant=earthman, arcana="MAJOR", number=9,
+ ).update(name="Erasing Personal History")
+ # Trump 7 name back
+ TarotCard.objects.filter(
+ deck_variant=earthman, arcana="MAJOR", number=7,
+ ).update(name="Not Doing")
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("epic", "0006_add_deck_variant_to_tableseat"),
+ ]
+
+ operations = [
+ migrations.RenameField("TarotCard", "mechanisms", "energies"),
+ migrations.RenameField("TarotCard", "articulations", "operations"),
+ migrations.RenameField("TarotCard", "reversal", "reversal_qualifier"),
+ migrations.AddField(
+ model_name="tarotcard",
+ name="italic_word",
+ field=models.CharField(blank=True, default="", max_length=50),
+ ),
+ migrations.RunPython(finalize, reverse_code=revert),
+ ]
diff --git a/src/apps/epic/migrations/0007_populate_middle_arcana_reversals.py b/src/apps/epic/migrations/0007_populate_middle_arcana_reversals.py
deleted file mode 100644
index 26a3d26..0000000
--- a/src/apps/epic/migrations/0007_populate_middle_arcana_reversals.py
+++ /dev/null
@@ -1,73 +0,0 @@
-"""Populate TarotCard.reversal for Earthman Middle Arcana court cards.
-
-Each suit has a fixed reversal qualifier that replaces the polarity qualifier
-(Elevated/Graven) when the card is spun to its reversed face:
- Brands → Seething Grails → Gloomy Blades → Nervous Crowns → Vacant
-
-Also clears the incorrectly inherited reversal on The Schizo (card 1), which
-mistakenly carried 'Territoriality' from The Occultist (card 2).
-"""
-from django.db import migrations
-
-SUIT_REVERSAL_QUALIFIER = {
- "BRANDS": "Seething",
- "GRAILS": "Gloomy",
- "BLADES": "Nervous",
- "CROWNS": "Vacant",
-}
-
-RANK_NAMES = {11: "Maid", 12: "Jack", 13: "Queen", 14: "King"}
-
-
-def populate_reversals(apps, schema_editor):
- TarotCard = apps.get_model("epic", "TarotCard")
- DeckVariant = apps.get_model("epic", "DeckVariant")
-
- try:
- earthman = DeckVariant.objects.get(slug="earthman")
- except DeckVariant.DoesNotExist:
- return
-
- # Middle Arcana court cards
- for suit, qualifier in SUIT_REVERSAL_QUALIFIER.items():
- TarotCard.objects.filter(
- deck_variant=earthman,
- arcana="MIDDLE",
- suit=suit,
- number__in=list(RANK_NAMES.keys()),
- ).update(reversal=qualifier)
-
- # Clear The Schizo's incorrectly inherited reversal (belongs to The Occultist)
- TarotCard.objects.filter(
- deck_variant=earthman,
- arcana="MAJOR",
- number=1,
- ).update(reversal="")
-
-
-def clear_reversals(apps, schema_editor):
- TarotCard = apps.get_model("epic", "TarotCard")
- DeckVariant = apps.get_model("epic", "DeckVariant")
- try:
- earthman = DeckVariant.objects.get(slug="earthman")
- except DeckVariant.DoesNotExist:
- return
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MIDDLE",
- suit__in=list(SUIT_REVERSAL_QUALIFIER.keys()),
- number__in=list(RANK_NAMES.keys()),
- ).update(reversal="")
- TarotCard.objects.filter(deck_variant=earthman, arcana="MAJOR", number=1).update(
- reversal="Territoriality"
- )
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("epic", "0006_add_deck_variant_to_tableseat"),
- ]
-
- operations = [
- migrations.RunPython(populate_reversals, reverse_code=clear_reversals),
- ]
diff --git a/src/apps/epic/migrations/0008_rename_energies_operations_seed_schizo.py b/src/apps/epic/migrations/0008_rename_energies_operations_seed_schizo.py
deleted file mode 100644
index db8ff8c..0000000
--- a/src/apps/epic/migrations/0008_rename_energies_operations_seed_schizo.py
+++ /dev/null
@@ -1,57 +0,0 @@
-"""Rename mechanisms→energies and articulations→operations on TarotCard;
-seed The Schizo (Earthman major arcana card 1) with Energy and Operation entries.
-"""
-from django.db import migrations
-
-CR = '{}'
-
-SCHIZO_ENERGIES = [
- {"type": "LIBIDO", "effect": f'When encountering territorial Libido, may convert Emanation into {CR.format("1. The Priest")}.'},
- {"type": "NUMEN", "effect": f'When encountering despotic Numen, may convert Emanation into {CR.format("1. The Powerful")}.'},
- {"type": "VOLUPTAS", "effect": f'When encountering axiomatic Voluptas, may convert Emanation into {CR.format("1. The Normal")}.'},
- {"type": "VOLUPTAS", "effect": f'When encountering annihilating Voluptas, may convert Emanation into {CR.format("1. The Surrendered")}.'},
-]
-
-SCHIZO_OPERATIONS = [
- {"type": "COVER", "effect": f'When covering {CR.format("2. The Occultist")} she may choose, by converting her own Reversal into {CR.format("2. Pestilence")}, to convert this Reversal into {CR.format("1. The Pervert")}.'},
- {"type": "CROWN", "effect": f'When crowning {CR.format("3. The Despot")} she may choose, by converting her own Reversal into {CR.format("3. War")}, to convert this Reversal into {CR.format("1. The Paranoiac")}.'},
- {"type": "BEHIND", "effect": f'When behind {CR.format("4. The Capitalist")} he may choose, by converting his own Reversal into {CR.format("4. Famine")}, to convert this Reversal into {CR.format("1. The Neurotic")}.'},
- {"type": "BEFORE", "effect": f'When before {CR.format("5. The Fascist")} he may choose, by converting his own Reversal into {CR.format("5. Death")}, to convert this Reversal into {CR.format("1. The Suicidal")}.'},
-]
-
-
-def seed_schizo(apps, schema_editor):
- TarotCard = apps.get_model("epic", "TarotCard")
- DeckVariant = apps.get_model("epic", "DeckVariant")
- try:
- earthman = DeckVariant.objects.get(slug="earthman")
- except DeckVariant.DoesNotExist:
- return
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MAJOR", number=1,
- ).update(energies=SCHIZO_ENERGIES, operations=SCHIZO_OPERATIONS)
-
-
-def clear_schizo(apps, schema_editor):
- TarotCard = apps.get_model("epic", "TarotCard")
- DeckVariant = apps.get_model("epic", "DeckVariant")
- try:
- earthman = DeckVariant.objects.get(slug="earthman")
- except DeckVariant.DoesNotExist:
- return
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MAJOR", number=1,
- ).update(energies=[], operations=[])
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("epic", "0007_populate_middle_arcana_reversals"),
- ]
-
- operations = [
- migrations.RenameField("TarotCard", "mechanisms", "energies"),
- migrations.RenameField("TarotCard", "articulations", "operations"),
- migrations.RunPython(seed_schizo, reverse_code=clear_schizo),
- ]
diff --git a/src/apps/epic/migrations/0009_schizo_card_ref_spans.py b/src/apps/epic/migrations/0009_schizo_card_ref_spans.py
deleted file mode 100644
index 5889562..0000000
--- a/src/apps/epic/migrations/0009_schizo_card_ref_spans.py
+++ /dev/null
@@ -1,53 +0,0 @@
-"""Re-seed The Schizo's energies and operations with .card-ref HTML spans."""
-from django.db import migrations
-
-CR = '{}'
-
-SCHIZO_ENERGIES = [
- {"type": "LIBIDO", "effect": f'When encountering territorial Libido, may convert Emanation into {CR.format("1. The Priest")}.'},
- {"type": "NUMEN", "effect": f'When encountering despotic Numen, may convert Emanation into {CR.format("1. The Powerful")}.'},
- {"type": "VOLUPTAS", "effect": f'When encountering axiomatic Voluptas, may convert Emanation into {CR.format("1. The Normal")}.'},
- {"type": "VOLUPTAS", "effect": f'When encountering annihilating Voluptas, may convert Emanation into {CR.format("1. The Surrendered")}.'},
-]
-
-SCHIZO_OPERATIONS = [
- {"type": "COVER", "effect": f'When covering {CR.format("2. The Occultist")} she may choose, by converting her own Reversal into {CR.format("2. Pestilence")}, to convert this Reversal into {CR.format("1. The Pervert")}.'},
- {"type": "CROWN", "effect": f'When crowning {CR.format("3. The Despot")} she may choose, by converting her own Reversal into {CR.format("3. War")}, to convert this Reversal into {CR.format("1. The Paranoiac")}.'},
- {"type": "BEHIND", "effect": f'When behind {CR.format("4. The Capitalist")} he may choose, by converting his own Reversal into {CR.format("4. Famine")}, to convert this Reversal into {CR.format("1. The Neurotic")}.'},
- {"type": "BEFORE", "effect": f'When before {CR.format("5. The Fascist")} he may choose, by converting his own Reversal into {CR.format("5. Death")}, to convert this Reversal into {CR.format("1. The Suicidal")}.'},
-]
-
-
-def seed_schizo(apps, schema_editor):
- TarotCard = apps.get_model("epic", "TarotCard")
- DeckVariant = apps.get_model("epic", "DeckVariant")
- try:
- earthman = DeckVariant.objects.get(slug="earthman")
- except DeckVariant.DoesNotExist:
- return
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MAJOR", number=1,
- ).update(energies=SCHIZO_ENERGIES, operations=SCHIZO_OPERATIONS)
-
-
-def clear_schizo(apps, schema_editor):
- TarotCard = apps.get_model("epic", "TarotCard")
- DeckVariant = apps.get_model("epic", "DeckVariant")
- try:
- earthman = DeckVariant.objects.get(slug="earthman")
- except DeckVariant.DoesNotExist:
- return
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MAJOR", number=1,
- ).update(energies=[], operations=[])
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("epic", "0008_rename_energies_operations_seed_schizo"),
- ]
-
- operations = [
- migrations.RunPython(seed_schizo, reverse_code=clear_schizo),
- ]
diff --git a/src/apps/epic/migrations/0010_major_arcana_hand_dots_icon.py b/src/apps/epic/migrations/0010_major_arcana_hand_dots_icon.py
deleted file mode 100644
index d7ee1dd..0000000
--- a/src/apps/epic/migrations/0010_major_arcana_hand_dots_icon.py
+++ /dev/null
@@ -1,49 +0,0 @@
-"""Assign fa-hand-dots icon to all Earthman Major Arcana cards with number >= 2.
-
-Cards 0 (The Nomad) and 1 (The Schizo) keep their existing icon value so they
-can receive distinct icons later. All other Major Arcana groups (Popes, Implicit
-Virtues, Elements, Realms, Explicit Virtues, Zodiac, Lunars, Planets, Inner Rings,
-polarity-split finals) default to fa-hand-dots until per-group icons are assigned.
-"""
-from django.db import migrations
-
-
-def assign_hand_dots(apps, schema_editor):
- TarotCard = apps.get_model("epic", "TarotCard")
- DeckVariant = apps.get_model("epic", "DeckVariant")
- try:
- earthman = DeckVariant.objects.get(slug="earthman")
- except DeckVariant.DoesNotExist:
- return
- TarotCard.objects.filter(
- deck_variant=earthman,
- arcana="MAJOR",
- number__gte=2,
- icon="",
- ).update(icon="fa-hand-dots")
-
-
-def clear_hand_dots(apps, schema_editor):
- TarotCard = apps.get_model("epic", "TarotCard")
- DeckVariant = apps.get_model("epic", "DeckVariant")
- try:
- earthman = DeckVariant.objects.get(slug="earthman")
- except DeckVariant.DoesNotExist:
- return
- TarotCard.objects.filter(
- deck_variant=earthman,
- arcana="MAJOR",
- number__gte=2,
- icon="fa-hand-dots",
- ).update(icon="")
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("epic", "0009_schizo_card_ref_spans"),
- ]
-
- operations = [
- migrations.RunPython(assign_hand_dots, reverse_code=clear_hand_dots),
- ]
diff --git a/src/apps/epic/migrations/0011_nomad_schizo_icons.py b/src/apps/epic/migrations/0011_nomad_schizo_icons.py
deleted file mode 100644
index ef968cf..0000000
--- a/src/apps/epic/migrations/0011_nomad_schizo_icons.py
+++ /dev/null
@@ -1,43 +0,0 @@
-"""Assign individual icons to The Nomad (0) and The Schizo (1).
-
-All other Major Arcana already have fa-hand-dots from migration 0010.
-"""
-from django.db import migrations
-
-ICONS = {0: 'fa-hat-cowboy-side', 1: 'fa-hat-wizard'}
-
-
-def assign_icons(apps, schema_editor):
- TarotCard = apps.get_model("epic", "TarotCard")
- DeckVariant = apps.get_model("epic", "DeckVariant")
- try:
- earthman = DeckVariant.objects.get(slug="earthman")
- except DeckVariant.DoesNotExist:
- return
- for number, icon in ICONS.items():
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MAJOR", number=number
- ).update(icon=icon)
-
-
-def clear_icons(apps, schema_editor):
- TarotCard = apps.get_model("epic", "TarotCard")
- DeckVariant = apps.get_model("epic", "DeckVariant")
- try:
- earthman = DeckVariant.objects.get(slug="earthman")
- except DeckVariant.DoesNotExist:
- return
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MAJOR", number__in=list(ICONS.keys())
- ).update(icon="")
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("epic", "0010_major_arcana_hand_dots_icon"),
- ]
-
- operations = [
- migrations.RunPython(assign_icons, reverse_code=clear_icons),
- ]
diff --git a/src/apps/epic/migrations/0012_delete_stray_pentacles.py b/src/apps/epic/migrations/0012_delete_stray_pentacles.py
deleted file mode 100644
index 8e281c3..0000000
--- a/src/apps/epic/migrations/0012_delete_stray_pentacles.py
+++ /dev/null
@@ -1,27 +0,0 @@
-"""Delete 4 stray PENTACLES court cards from the Earthman deck.
-
-These survived the migration collapse; the Earthman deck uses
-BRANDS/GRAILS/BLADES/CROWNS only.
-"""
-from django.db import migrations
-
-
-def delete_pentacles(apps, schema_editor):
- TarotCard = apps.get_model("epic", "TarotCard")
- DeckVariant = apps.get_model("epic", "DeckVariant")
- try:
- earthman = DeckVariant.objects.get(slug="earthman")
- except DeckVariant.DoesNotExist:
- return
- TarotCard.objects.filter(deck_variant=earthman, suit="PENTACLES").delete()
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("epic", "0011_nomad_schizo_icons"),
- ]
-
- operations = [
- migrations.RunPython(delete_pentacles, reverse_code=migrations.RunPython.noop),
- ]
diff --git a/src/apps/epic/migrations/0013_fix_nomad_icon.py b/src/apps/epic/migrations/0013_fix_nomad_icon.py
deleted file mode 100644
index a327cd1..0000000
--- a/src/apps/epic/migrations/0013_fix_nomad_icon.py
+++ /dev/null
@@ -1,25 +0,0 @@
-"""Fix The Nomad icon: fa-hat-cowboy → fa-hat-cowboy-side."""
-from django.db import migrations
-
-
-def fix_nomad_icon(apps, schema_editor):
- TarotCard = apps.get_model("epic", "TarotCard")
- DeckVariant = apps.get_model("epic", "DeckVariant")
- try:
- earthman = DeckVariant.objects.get(slug="earthman")
- except DeckVariant.DoesNotExist:
- return
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MAJOR", number=0, icon="fa-hat-cowboy"
- ).update(icon="fa-hat-cowboy-side")
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("epic", "0012_delete_stray_pentacles"),
- ]
-
- operations = [
- migrations.RunPython(fix_nomad_icon, reverse_code=migrations.RunPython.noop),
- ]
diff --git a/src/apps/epic/migrations/0014_rename_reversal_to_reversal_qualifier.py b/src/apps/epic/migrations/0014_rename_reversal_to_reversal_qualifier.py
deleted file mode 100644
index 2def261..0000000
--- a/src/apps/epic/migrations/0014_rename_reversal_to_reversal_qualifier.py
+++ /dev/null
@@ -1,22 +0,0 @@
-"""Rename TarotCard.reversal → TarotCard.reversal_qualifier.
-
-Symmetric naming with levity_qualifier / gravity_qualifier; disambiguates the
-qualifier-text field from the reversal *axis* state and the keywords_reversed
-list. Pure column rename — no data movement.
-"""
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("epic", "0013_fix_nomad_icon"),
- ]
-
- operations = [
- migrations.RenameField(
- model_name="tarotcard",
- old_name="reversal",
- new_name="reversal_qualifier",
- ),
- ]
diff --git a/src/apps/epic/migrations/0015_card49_polarity_reversal_titles.py b/src/apps/epic/migrations/0015_card49_polarity_reversal_titles.py
deleted file mode 100644
index 0aff797..0000000
--- a/src/apps/epic/migrations/0015_card49_polarity_reversal_titles.py
+++ /dev/null
@@ -1,60 +0,0 @@
-"""Populate card 49's polarity-split reversal titles.
-
-The Earthman deck's last two cards (48–49) carry distinct titles per polarity
-(stored in `levity_emanation` / `gravity_emanation` / `levity_reversal` /
-`gravity_reversal`) rather than a shared title + qualifier.
-
-Card 48 had its full set seeded in migration 0004:
- levity: Father Sky → reversal: The Storm
- gravity: Mother Sea → reversal: The Flood
-
-Card 49 had only emanations seeded; this migration fills the reversals:
- levity: The Effulgent Mould of Man → reversal: The Vibrational Mould of Man
- gravity: The Devouring Eagle → reversal: The All-Bestowing Eagle
-
-The "qualifier" (Effulgent / Vibrational / Devouring / All-Bestowing) is baked
-into the title between "The" and the title-proper rather than rendered as a
-separate qualifier slot — the per-polarity title strings are stored verbatim.
-"""
-from django.db import migrations
-
-
-def populate_card49_reversals(apps, schema_editor):
- TarotCard = apps.get_model("epic", "TarotCard")
- DeckVariant = apps.get_model("epic", "DeckVariant")
- try:
- earthman = DeckVariant.objects.get(slug="earthman")
- except DeckVariant.DoesNotExist:
- return
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MAJOR", number=49,
- ).update(
- levity_reversal="The Vibrational Mould of Man",
- gravity_reversal="The All-Bestowing Eagle",
- )
-
-
-def clear_card49_reversals(apps, schema_editor):
- TarotCard = apps.get_model("epic", "TarotCard")
- DeckVariant = apps.get_model("epic", "DeckVariant")
- try:
- earthman = DeckVariant.objects.get(slug="earthman")
- except DeckVariant.DoesNotExist:
- return
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MAJOR", number=49,
- ).update(
- levity_reversal="",
- gravity_reversal="",
- )
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("epic", "0014_rename_reversal_to_reversal_qualifier"),
- ]
-
- operations = [
- migrations.RunPython(populate_card49_reversals, reverse_code=clear_card49_reversals),
- ]
diff --git a/src/apps/epic/migrations/0016_card49_bestowing_eagle.py b/src/apps/epic/migrations/0016_card49_bestowing_eagle.py
deleted file mode 100644
index 551618f..0000000
--- a/src/apps/epic/migrations/0016_card49_bestowing_eagle.py
+++ /dev/null
@@ -1,37 +0,0 @@
-"""Tweak card 49 gravity_reversal: 'All-Bestowing Eagle' → 'Bestowing Eagle'."""
-from django.db import migrations
-
-
-def forward(apps, schema_editor):
- TarotCard = apps.get_model("epic", "TarotCard")
- DeckVariant = apps.get_model("epic", "DeckVariant")
- try:
- earthman = DeckVariant.objects.get(slug="earthman")
- except DeckVariant.DoesNotExist:
- return
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MAJOR", number=49,
- ).update(gravity_reversal="The Bestowing Eagle")
-
-
-def reverse(apps, schema_editor):
- TarotCard = apps.get_model("epic", "TarotCard")
- DeckVariant = apps.get_model("epic", "DeckVariant")
- try:
- earthman = DeckVariant.objects.get(slug="earthman")
- except DeckVariant.DoesNotExist:
- return
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MAJOR", number=49,
- ).update(gravity_reversal="The All-Bestowing Eagle")
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("epic", "0015_card49_polarity_reversal_titles"),
- ]
-
- operations = [
- migrations.RunPython(forward, reverse_code=reverse),
- ]
diff --git a/src/apps/epic/migrations/0017_castanedan_virtues.py b/src/apps/epic/migrations/0017_castanedan_virtues.py
deleted file mode 100644
index d0df690..0000000
--- a/src/apps/epic/migrations/0017_castanedan_virtues.py
+++ /dev/null
@@ -1,114 +0,0 @@
-"""Populate the seven Castanedan Virtues — trumps 6–9 (Implicit) + 19–21 (Explicit).
-
-Implicit Virtues (6–9): emanation qualifier differs by polarity (Sublimating /
-Sedimentary), name is shared. Reversal is a single full string shared across
-both polarities (the agency word — Controlled / Not / Losing / Erasing —
-flips to Indulged / Indulgent / Self-Indulgence / Indulging). We fill the
-standard `levity_qualifier` / `gravity_qualifier` slots so the major-arcana
-upright renders "Controlled Folly,\nSublimating" via the existing template
-branch; we fill BOTH `levity_reversal` + `gravity_reversal` with the same
-string so a FLIP'd reversal still picks up the override (an empty side falls
-through to the default major-arcana rendering).
-
-Explicit Virtues (19–21): emanation is shared across polarities (e.g. "The
-Hunter's Stalking" — no qualifier + stem decomposition), reversal differs by
-polarity. All four polarity-split title fields filled.
-
-Also canonicalizes trump 7's name from "Not Doing" to "Not-Doing" per the spec
-doc (slug "not-doing" already correct).
-"""
-from django.db import migrations
-
-
-IMPLICIT = [
- # (number, levity_qualifier, gravity_qualifier, reversal_title)
- (6, "Sublimating", "Sedimentary", "Indulged Folly"),
- (7, "Sublimating", "Sedimentary", "Indulgent Doing"),
- (8, "Sublimating", "Sedimentary", "Self-Indulgence"),
- (9, "Sublimating", "Sedimentary", "Indulging Personal History"),
-]
-
-EXPLICIT = [
- # (number, levity_emanation, gravity_emanation, levity_reversal, gravity_reversal)
- (19, "The Hunter's Stalking", "The Hunter's Stalking", "The Sleeper's Stalking", "The Quarry's Stalking"),
- (20, "The Dreamer's Dreaming", "The Dreamer's Dreaming", "The Sleeper's Dreaming", "The Dreamed's Dreaming"),
- (21, "The Warrior's Intent", "The Warrior's Intent", "The Sleeper's Intent", "The Predator's Intent"),
-]
-
-
-def forward(apps, schema_editor):
- TarotCard = apps.get_model("epic", "TarotCard")
- DeckVariant = apps.get_model("epic", "DeckVariant")
- try:
- earthman = DeckVariant.objects.get(slug="earthman")
- except DeckVariant.DoesNotExist:
- return
-
- # Trump 7 name canonicalization
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MAJOR", number=7,
- ).update(name="Not-Doing")
-
- for number, lvty, grav, rev in IMPLICIT:
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MAJOR", number=number,
- ).update(
- levity_qualifier=lvty,
- gravity_qualifier=grav,
- levity_reversal=rev,
- gravity_reversal=rev,
- )
-
- for number, le, ge, lr, gr in EXPLICIT:
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MAJOR", number=number,
- ).update(
- levity_emanation=le,
- gravity_emanation=ge,
- levity_reversal=lr,
- gravity_reversal=gr,
- )
-
-
-def reverse(apps, schema_editor):
- TarotCard = apps.get_model("epic", "TarotCard")
- DeckVariant = apps.get_model("epic", "DeckVariant")
- try:
- earthman = DeckVariant.objects.get(slug="earthman")
- except DeckVariant.DoesNotExist:
- return
-
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MAJOR", number=7,
- ).update(name="Not Doing")
-
- for number, _lvty, _grav, _rev in IMPLICIT:
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MAJOR", number=number,
- ).update(
- levity_qualifier="",
- gravity_qualifier="",
- levity_reversal="",
- gravity_reversal="",
- )
-
- for number, _le, _ge, _lr, _gr in EXPLICIT:
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MAJOR", number=number,
- ).update(
- levity_emanation="",
- gravity_emanation="",
- levity_reversal="",
- gravity_reversal="",
- )
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("epic", "0016_card49_bestowing_eagle"),
- ]
-
- operations = [
- migrations.RunPython(forward, reverse_code=reverse),
- ]
diff --git a/src/apps/epic/migrations/0018_add_italic_word.py b/src/apps/epic/migrations/0018_add_italic_word.py
deleted file mode 100644
index ebb301c..0000000
--- a/src/apps/epic/migrations/0018_add_italic_word.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 6.0 on 2026-05-01 03:28
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('epic', '0017_castanedan_virtues'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='tarotcard',
- name='italic_word',
- field=models.CharField(blank=True, default='', max_length=50),
- ),
- ]
diff --git a/src/apps/epic/migrations/0019_explicit_virtues_italic_word.py b/src/apps/epic/migrations/0019_explicit_virtues_italic_word.py
deleted file mode 100644
index 5727e90..0000000
--- a/src/apps/epic/migrations/0019_explicit_virtues_italic_word.py
+++ /dev/null
@@ -1,51 +0,0 @@
-"""Set TarotCard.italic_word for trumps 19-21 (Stalking / Dreaming / Intent).
-
-Each of these three Castanedan virtues has its title key-word italicized
-across every emanation/reversal slot ("The Hunter's *Stalking*", "The
-Sleeper's *Stalking*", etc.). Storing the word in a single field lets the
-renderer wrap it in at display time without HTML in the data.
-"""
-from django.db import migrations
-
-
-WORDS = {
- 19: "Stalking",
- 20: "Dreaming",
- 21: "Intent",
-}
-
-
-def forward(apps, schema_editor):
- TarotCard = apps.get_model("epic", "TarotCard")
- DeckVariant = apps.get_model("epic", "DeckVariant")
- try:
- earthman = DeckVariant.objects.get(slug="earthman")
- except DeckVariant.DoesNotExist:
- return
- for number, word in WORDS.items():
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MAJOR", number=number,
- ).update(italic_word=word)
-
-
-def reverse(apps, schema_editor):
- TarotCard = apps.get_model("epic", "TarotCard")
- DeckVariant = apps.get_model("epic", "DeckVariant")
- try:
- earthman = DeckVariant.objects.get(slug="earthman")
- except DeckVariant.DoesNotExist:
- return
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MAJOR", number__in=list(WORDS),
- ).update(italic_word="")
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("epic", "0018_add_italic_word"),
- ]
-
- operations = [
- migrations.RunPython(forward, reverse_code=reverse),
- ]
diff --git a/src/apps/epic/migrations/0020_self_unimportance.py b/src/apps/epic/migrations/0020_self_unimportance.py
deleted file mode 100644
index 1e7b7fc..0000000
--- a/src/apps/epic/migrations/0020_self_unimportance.py
+++ /dev/null
@@ -1,41 +0,0 @@
-"""Trump 8 rename: Losing Self-Importance → Self-Unimportance.
-
-The renamed form fits on one fan-card line above the Sublimating/Sedimentary
-qualifier without a scaleX squeeze.
-"""
-from django.db import migrations
-
-
-def forward(apps, schema_editor):
- TarotCard = apps.get_model("epic", "TarotCard")
- DeckVariant = apps.get_model("epic", "DeckVariant")
- try:
- earthman = DeckVariant.objects.get(slug="earthman")
- except DeckVariant.DoesNotExist:
- return
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MAJOR", number=8,
- ).update(name="Self-Unimportance", slug="self-unimportance")
-
-
-def reverse(apps, schema_editor):
- TarotCard = apps.get_model("epic", "TarotCard")
- DeckVariant = apps.get_model("epic", "DeckVariant")
- try:
- earthman = DeckVariant.objects.get(slug="earthman")
- except DeckVariant.DoesNotExist:
- return
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MAJOR", number=8,
- ).update(name="Losing Self-Importance", slug="losing-self-importance")
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("epic", "0019_explicit_virtues_italic_word"),
- ]
-
- operations = [
- migrations.RunPython(forward, reverse_code=reverse),
- ]
diff --git a/src/apps/epic/migrations/0021_trump9_nbsp.py b/src/apps/epic/migrations/0021_trump9_nbsp.py
deleted file mode 100644
index 14cec28..0000000
--- a/src/apps/epic/migrations/0021_trump9_nbsp.py
+++ /dev/null
@@ -1,61 +0,0 @@
-"""Long-title wrap fixes for trumps 8 and 9.
-
- Trump 8 "Self-Unimportance" → swap the hyphen for U+2011 (non-breaking
- hyphen) so it stays glued and the title sits on one line above
- Sublimating / Sedimentary.
-
- Trump 9 "Erasing Personal History" → insert U+00A0 (non-breaking space)
- between "Personal" and "History" so the browser keeps them together,
- forcing "Erasing" alone on line 1 and "Personal History," on line 2.
-"""
-from django.db import migrations
-
-
-# Trump 8
-OLD_8 = "Self-Unimportance"
-NEW_8 = "Self‑Unimportance"
-
-# Trump 9
-OLD_9 = "Erasing Personal History"
-NEW_9 = "Erasing Personal History"
-
-
-def forward(apps, schema_editor):
- TarotCard = apps.get_model("epic", "TarotCard")
- DeckVariant = apps.get_model("epic", "DeckVariant")
- try:
- earthman = DeckVariant.objects.get(slug="earthman")
- except DeckVariant.DoesNotExist:
- return
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MAJOR", number=8,
- ).update(name=NEW_8)
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MAJOR", number=9,
- ).update(name=NEW_9)
-
-
-def reverse(apps, schema_editor):
- TarotCard = apps.get_model("epic", "TarotCard")
- DeckVariant = apps.get_model("epic", "DeckVariant")
- try:
- earthman = DeckVariant.objects.get(slug="earthman")
- except DeckVariant.DoesNotExist:
- return
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MAJOR", number=8,
- ).update(name=OLD_8)
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MAJOR", number=9,
- ).update(name=OLD_9)
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("epic", "0020_self_unimportance"),
- ]
-
- operations = [
- migrations.RunPython(forward, reverse_code=reverse),
- ]
diff --git a/src/apps/epic/migrations/0022_pips_to_minor_arcana.py b/src/apps/epic/migrations/0022_pips_to_minor_arcana.py
deleted file mode 100644
index fc2fe61..0000000
--- a/src/apps/epic/migrations/0022_pips_to_minor_arcana.py
+++ /dev/null
@@ -1,42 +0,0 @@
-"""Reclassify Earthman pip cards (number 1-10) from MIDDLE to MINOR arcana.
-
-The 0004 reseed initially lumped pips + court cards under MIDDLE; pips
-should be MINOR arcana, with MIDDLE reserved for the Earthman court
-cards (Maid/Jack/Queen/King at numbers 11-14).
-"""
-from django.db import migrations
-
-
-def forward(apps, schema_editor):
- TarotCard = apps.get_model("epic", "TarotCard")
- DeckVariant = apps.get_model("epic", "DeckVariant")
- try:
- earthman = DeckVariant.objects.get(slug="earthman")
- except DeckVariant.DoesNotExist:
- return
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MIDDLE", number__lte=10,
- ).update(arcana="MINOR")
-
-
-def reverse(apps, schema_editor):
- TarotCard = apps.get_model("epic", "TarotCard")
- DeckVariant = apps.get_model("epic", "DeckVariant")
- try:
- earthman = DeckVariant.objects.get(slug="earthman")
- except DeckVariant.DoesNotExist:
- return
- TarotCard.objects.filter(
- deck_variant=earthman, arcana="MINOR", number__lte=10,
- ).update(arcana="MIDDLE")
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("epic", "0021_trump9_nbsp"),
- ]
-
- operations = [
- migrations.RunPython(forward, reverse_code=reverse),
- ]