Changelog

Aktuelle Version: v0.5.0 (426bd51)

All notable changes to WoDVTT are documented here.

The format follows Keep a Changelog, and this project uses Semantic Versioning.

[0.5.0] - 2026-09-29

A sign-in that fails on the way back now returns you to the sign-in page with an explanation and a way to try again, instead of the generic error page.

A sign-in now has thirty minutes to complete, enough to read an invitation e-mail, choose a password and confirm an address without the attempt expiring.

The admin theme endpoints now also accept the platform's own governed service credential, alongside an administrator's sign-in.

The latest dice result in a session is now outlined inside the dice panel instead of shown as a second raised card.

The session chat keeps your reading position when new messages arrive, and its character counter now appears at 90 % of the limit.

The session chat's delete confirmation and mobile action menu now use the platform's dialog styling, a message that failed to send is easier to read, and confirmation dialogs across the app now open centred on the screen.

Saving and applying your own custom theme now works end to end. Previously the choice could fail to save at all, and even where it was stored the app silently fell back to a default theme, so your custom theme never appeared however many times you selected it.

Renaming, re-importing or deleting your custom theme now takes effect straight away instead of the previous appearance lingering for several minutes.

Your custom themes remain private to you, and themes saved by other people are still never applied to your view.

The account menu in the top bar is readable again in the light theme. It previously rendered as a solid dark block with an all-but-invisible chevron, and the dropdown it opens had the same problem.

Warning and informational text is also darker in the light theme, so a caution or an explanatory note is now comfortably readable against a light page rather than the least readable thing on it.

On a phone, the data-import alerts now give their message the width of the card instead of wrapping it into a narrow column beside an empty close slot.

The theme picker and the personal-theme editor now open on the app's actual default theme rather than always on Dark. Previously, whenever the app could not read your saved choice — including any moment the theme service was briefly unavailable — both fell back to Dark by name, so the picker could show Dark selected while the page was rendering something else.

Nothing changes about which theme you see when you arrive signed out. That still follows your device's light or dark setting, and your own saved choice still wins over everything.

Themes are now stored under one common shape shared with the platform, in preparation for administering an app's themes from the platform's own admin surface. Themes stay in this application's own database, and every existing theme keeps its name, colours and settings — including the theme each person has chosen, which continues to apply exactly as before.

Saving a theme is now checked more strictly for unsafe styling values. The check no longer accepts values that hide an unsafe fragment behind comments or escape sequences, and it rejects one further category that previously got through.

This release includes a database change. Apply it with the usual deployment step.

The "genealogy NSC" badge on the character list is now readable in the light theme. An earlier attempt at this fix shipped without effect; this one was confirmed against the deployed application.

Completeness badges on the vampire roster, and the matching badges on the admin data-import cards, are now readable in every theme. Their labels had been drawn in white on the amber "in progress" and green "complete" fills, which left the text at around 3:1 against its own background in six of the seven themes — well under the 4.5:1 that ordinary text needs. The amber badge was the worst of them and was close to unreadable in the dark themes.

Each theme now carries its own badge label colour: dark ink on the amber and green fills everywhere except Light, whose fills are already deep enough for white to read against. The red "not started" badge keeps its white label, which was never the problem — it clears the threshold in all seven themes.

The theme editor now warns you when a warning or success colour you have picked would leave its badge labels too faint, the same warning it already gives for the primary and error colours. The automated theme check has also been extended to measure these two pairs in every packaged theme, including Sabbat and Wraith's Veil, which it had not been covering at all.

Character sheets now use a short edit lease so two people editing the same character at once can no longer silently overwrite each other's changes. Opening a sheet someone else is already editing shows a read-only banner naming who holds it; a Game Master or Admin can take over an active or stale lease with an explicit confirmation, after which the previous editor can no longer save until they reload. A save that raced another change is now also rejected with a clear message asking you to reload, instead of overwriting the newer version.

The character sheet no longer reports unsaved changes after a save when nothing was changed. Previously, a field's own change handler completing slightly after you clicked Save could leave the "unsaved changes" notice showing on a sheet that was, in fact, already saved -- the notice is now based on whether the sheet's content genuinely differs from what was last saved, not on whether an edit event fired.

Character sheets can now be exported as a PDF. Open a character, click Export PDF, and a printable copy of the sheet downloads with a localized filename while you stay on the page.

The character sheet's Save button, the unsaved-changes note and the reason Save is unavailable now stay with you as you scroll instead of sitting at the very bottom of a very long page. If Save is unavailable because something still needs fixing, the sheet now says so where you can see it.

The small "+" button under every skill only appears once that skill is good enough to take a specialisation, rather than showing thirty greyed-out buttons that do nothing when clicked. It also has a proper name now, so screen-reader users are told which skill it belongs to.

Every rated trait on the character sheet now reads the same way. Attributes, skills, disciplines, backgrounds and virtues each used to lay their rows out slightly differently, so the names sat in columns of three different widths and the dots started wherever the name happened to end. The dots now line up in a column of their own, at the same place in every row, and a name too long for its column is shortened with an ellipsis instead of wrapping onto a second line and leaving the row ragged. Hovering a shortened name shows it in full, and it is still read out in full by a screen reader.

Two panels — Attributes and Disciplines — also stop insisting on a set width that was wider than a phone screen, so they give way properly on a narrow display.

This was attempted once before and withdrawn, because on the real sheet it made the page slide sideways rather than stop it. The reason is now understood and has been dealt with separately: the rows of dots would not become narrower than five full-size tap targets, so the new layout, which keeps the name column at a steady width, had nowhere to give. The dots can now shrink, none of them are hidden or cut off, and they return to full size wherever there is room.

Nothing about a character's values changed, and the sheet looks the same on a desktop screen.

Characters whose ratings run past the usual scale — elders and Methuselahs, mostly — now show every point they actually have. A Status of 7 used to be drawn on a six-dot row, so the seventh point simply had nowhere to appear, and a screen reader was told the rating was "7 of 6": one past a maximum the same announcement had just declared.

Rows now grow to fit the character in front of you, so nothing is hidden and nothing contradicts itself. Ordinary sheets are unchanged — a row only grows when the rating on it genuinely exceeds the usual scale.

The character sheet no longer slides sideways on a phone.

On a narrow screen the Backgrounds panel could not be made narrower than its widest row, so the whole sheet could be dragged left and right and part of every section sat off the edge. The panel now gives way when space is tight.

The same correction has been applied to the Lore and Genealogy panel, which is built the same way and had never been measured on a phone.

The Blood card on the character sheet is now a compact meter instead of a wall of boxes — at a full pool it used to run to about twenty rows. The current and maximum values, a slim progress bar, and the hunger tier line now fit in a handful of lines at every pool size, in both edit and read-only view.

The Health block's penalty (-1, -2, -5) now sits at the end of its row, after the damage boxes, instead of crowding the label with a large empty gap trailing behind it on a wide screen.

The Health section's legend is a single on-demand info button now, instead of a badge legend plus a second always-visible paragraph repeating the same text.

The "More Identity" section's collapsible heading is legible against its background again — it previously used near-invisible text and border colours.

The Essence summary panel's English heading no longer shows the German label, and virtues line up on one row like every other trait list instead of stacking their label above the rating.

A discipline rated high enough to wrap onto a second row of marks now keeps its name aligned with the first row instead of floating between the two.

On narrow screens, a long feeding-restriction value in the Essence panel no longer breaks mid-word.

Motion is now fully disabled site-wide, including scroll animations, for anyone with reduced-motion enabled at the OS level.

The Blood meter's fill and track are now visible against the card at every fill level, including a full pool, where the bar previously read as a plain decorative line rather than a bounded meter.

The current hunger tier is now its own labelled line on the Blood card, at the same size and prominence as the Pool and per-round values, instead of the trailing clause of a small reference paragraph.

The Blood card's reference text (the hunger-tier legend) now lives behind the same on-demand info button the Health section already uses, instead of an always-visible paragraph — Section IV now explains a control the same way everywhere on the sheet.

The Health track's damage penalty stays next to its own row of boxes at every screen width, including narrow phones, instead of sometimes landing on the label's line with the boxes a full row below it.

A trait rated high enough to wrap its rating onto a second row of marks now keeps its label aligned with the centre of the first row, at every screen width.

Every card in Section IV, the Attributes section, Backgrounds and Legacy now has a clean themed border, replacing the browser's plain default frame.

The Health section's discipline-bonus note now names the discipline the way the Disciplines list does and states its rating as a number, instead of a discipline's Latin name paired with a row of placeholder-looking letters.

The Essence panel's reference note now names the rulebook chapter instead of a source file name.

The Blood card's Pool and per-round values now line up with their own labels instead of sitting a few pixels further in.

The character name field on the character sheet now has a clearly visible brass frame and focus outline in both light and dark themes.

The health track no longer overflows its card on a phone, and the character name field keeps one frame colour across rest and hover instead of flashing grey.

Character sheet health boxes, section nav numerals, and info buttons are now readable and reachable at every screen width, with duplicate legends and headings cleaned up.

High attribute, skill, and virtue ratings on the character sheet now always show every mark, instead of some marks scrolling out of view depending on window width.

In narrow columns, a trait's name now sits on its own line above its marks instead of being squeezed onto the same line.

On wide screens, the Disciplines, Blood, Essence, and Health cards now fill their row's full width, with Disciplines twice as wide as each of the other three, though one card can still show empty space below its own content.

The Essence card on the character sheet no longer splits German words in the middle when its column narrows, and shows its labels and values in a smaller, still-readable type size at the narrowest widths.

The character sheet's Section IV cards now fill the row evenly at wide screens instead of Disciplines dropping to its own line and leaving the rest as a lopsided block underneath: Disciplines, Blood, Essence and Health share row one, and Virtues, Willpower and Humanity share row two (a later change moves that trio to its own section).

The masthead now reads name, then lineage line, then Clan/Generation/Chronicle in that order, instead of the lineage line sitting above the character's name.

Every section's card-header legend now insets into the card's own border the same way, instead of one section stretching its legend the full width of the card.

The "no entries yet" line under Backgrounds, Convictions, Chronicle Tenets and Advantages now reads the same way in every card, and the only way to add an entry is the add button shown while editing — never a second, duplicate invitation in the empty-state text itself.

Every icon-only button on the sheet now has a full-size tap target, instead of some buttons being easy to miss on a touch screen.

The character sheet's nine sections are now numbered and titled to match the game's own structure — Disciplines now share a section with Blood, Combat now precedes Backgrounds, and Drive is its own section — instead of a layout that grouped them differently from how the rules present them.

A sticky navigation rail under the header now shows every section's numeral, plus a short label on wider screens, scrolls sideways on a phone screen when the numerals don't all fit, and highlights whichever section is currently in view.

Wrapped German section headings now keep their second line indented under the heading text instead of under the numeral, and a card's own title no longer breaks the card's border when it wraps onto two lines on a narrow screen. Section headings, card titles and labels now follow the sheet's own type scale instead of one display face doing every job.

On a narrow screen, the app's name in the header now stays readable instead of getting cut off mid-word.

Fixed a round of layout defects on the character sheet's responsive card sections (Zustand and Antrieb).

Cards in a row now match height instead of a jagged skyline — Blood and Nature/Convictions used to be visibly shorter or taller than their row neighbours. The Blood card's meter is now followed by a visible caption instead of leaving dead space under it.

Every card's legend (title) now reads in the same style — uppercase, same size, weight and letter-spacing everywhere. Essenz used to be the odd one out with a smaller, styled title next to plain, unstyled ones on Health, Virtues, Willpower, Humanity, Ambition and Convictions.

The decorative corner-bracket frame is gone from the Essenz card.

Health track rows (level, boxes, penalty) no longer stack the label above its own boxes at very high resolutions — a box-size change in an underlying shared library was making rows wider than the layout expected.

At very high pixel density (around 2560px and up) the whole sheet now reads at a slightly larger base text size for readability.

The sticky Save/Back/PDF bar at the bottom of the page no longer overlaps the last row of a section when scrolling or jumping to it.

On wide screens (1536px and up) the Disciplines, Blood/Essenz and Health cards in the character sheet's state section now render as three equal-width columns instead of Health dropping to its own full-width row with a large empty gap next to its content.

Essenz values (for example "Menschen") no longer break mid-word on narrow screens; the label and value now stack instead of hyphenating.

Changed

  • A character's generation must now be unknown or between 1 and 16. A sheet carrying any other

value is rejected on save until a legal generation is chosen, instead of being stored with a generation the rulebook does not know.

  • Sheets now report when their stored blood points per round differ from the rulebook table for

the character's generation and age. The note is advisory and never blocks a save.

  • Importing a character whose source states a generation weaker than the sixteenth now stores

the sixteenth and records the adjustment in the character's import notes.

Added

  • Clan, Nature and Demeanor on the character sheet are now picked from the rulebook and corpus

catalogs. Each field is a filterable dropdown listing the known clans or archetypes; a stored value that is not in the rulebook (an imported clan or archetype outside the core list) still shows selected, marked "(eigen)" ("custom"), and is never rewritten just by opening the sheet.

  • Generation is now a dropdown of 1 to 16 plus "unbekannt" ("unknown") instead of a free-text

field. An imported sheet whose stored generation is outside that range shows it as a disabled, clearly marked entry and blocks saving until a legal value is picked. A generation inferred during import (rather than stated by the source) is marked with a small "abgeleitet" ("inferred") badge.

  • Bloodline keeps suggesting the usual antitribu names while staying free text.

Sheets saved before this change open exactly as they were: an untouched clan, nature, demeanor or generation is never rewritten just by opening and re-saving the sheet.

Added

  • The character sheet now shows an Essenz panel for Vampires and Ghouls: current Essenz, blood

pool maximum, blood per round, trait maximum, the two physical-attribute blood limits, the discipline roll bonus, and the feeding restriction, all read from the rulebook table for the character's generation. Each value explains which table row it came from. The panel is hidden entirely on Human sheets.

  • Blood per round is now a read-only value taken from that same table instead of a typed field.

If a sheet's stored blood-per-round differs from the table, the validation summary notes it, but saving is never blocked. An unset blood pool maximum now shows the table's default instead of zero.

  • Movement (walking through sprinting) and jump distances are now computed from height,

Dexterity, Athletics and Strength instead of being typed in by hand. Each value shows the formula that produced it, and the character's fixed height and speed in km/h are shown alongside the movement table.

Changed

  • Movement and jump values on a sheet saved before this change are left exactly as they were;

only the displayed value is now computed.

Added

  • The health track on the character sheet now grows with the Fortitudo discipline: raising

Fortitudo adds extra boxes to the Bruised and Verkrueppelt (Crippled) categories, following the rulebook table, and a caption under the track shows the current bonus (e.g. "Fortitudo O O O: +2 levels").

  • Lowering Fortitudo keeps existing damage safe: a filled box that no longer fits its category

moves to the next empty box of the same category. When no box is free, the damage stays exactly where it was and a warning explains that it could not be moved.

Fixed

  • Marking or clearing a box on the health track is now actually saved. Previously a health-track

change was silently dropped when the sheet was saved and reappeared unchanged after a reload.

Added

  • Weapons on the character sheet are now picked from the rulebook tables. Every melee and

ranged weapon row starts with a picker listing the weapons of the combat chapter; choosing one fills the row's stats from the table and shows them as fixed text. Choosing the custom entry ("Custom", in German "Eigene") keeps the row as a free-text weapon that can be typed as before.

  • The protection block gains an armour picker and a shield picker from the same tables. A picked

armour or shield shows its melee and ranged protection, its dexterity and strength penalties and its structure points, and the block sums armour and shield into the protection totals. Custom armour and a custom shield remain available with their own editable values.

Sheets saved before this change open exactly as they were: existing weapon rows stay editable free text and saving them again writes the same values.

Added

  • The character sheet gains section IX, an experience-point ledger. A total-EP field and an

inline "buy a dot" form record every purchase — category, target, the rating raised from and to, and the resulting cost — against attributes, skills, specializations, disciplines, backgrounds, merits, flaws and Via. A category table shows the ledger sum and the sheet's own cumulative value per category, and a remaining-EP line flags when the ledger spends more than the total was granted.

  • Buying a flaw now refunds experience points instead of costing nothing, and the merit cost per

dot was rebalanced. Ghoul sheets pay a higher discipline cost per dot, with a note explaining why. A background purchase beyond what the rulebook prices is recorded without a cost and flagged for review.

  • The ledger is append-only with delete; a stored purchase's cost is never recomputed. Purchases

do not themselves change the trait they record.

Sheets saved before this change open exactly as they were: a sheet that never used the ledger still saves without one.

Fixed

  • Creating a character now rejects an out-of-range armour structure-point or shield-protection

value the same way the character sheet's own Save button already did, instead of silently accepting it.

Added

  • Two sample vampires are now available for staging review: one with an elevated Fortitudo

rating and a marked wound box, and one carrying a matched melee weapon, armour and shield picked straight from the rulebook tables.

Changed

  • Discipline purchases on the experience ledger now cost 5 points per dot when the discipline is

one of the character's clan's three signature disciplines, and 7 points per dot otherwise (ghoul sheets are unaffected and still pay 10). A character whose clan has no recognised signature disciplines — a custom or homebrew clan — has every discipline purchase priced at the higher, out-of-clan rate, and the sheet notes this in its validation summary.

  • The background rating a new character sheet can be created with is now capped at 6, matching

the published rulebook table. A background rating above 6 already saved on an existing character is unaffected: the sheet still opens, still saves, and is flagged for review rather than rejected.

Corrected the German and English character-sheet and rulebook wording to match the approved glossary.

The third mental attribute is now Entschlossenheit (Resolve), and Willpower maximum follows Resilience + Resolve.

A blood pool of 7 now shows the Hunger tier Satiated, as the rulebook states.

All disciplines on the character sheet, including older imported names, now show their proper names in German and English.

When a vampire's generation changes, a blood pool maximum that still matched the old generation's table value now follows the new one. A maximum the player typed themselves is left alone, apart from the existing cap.

The character sheet now keeps its own look in every theme. Previously the sheet mixed two themes at once: its lettering came from the sheet's own parchment styling while the panels behind that lettering took their colour from whichever theme you had selected for the rest of the app. In the light theme the two collapsed into each other and the nine section headings — Identity, Attributes, Skills and the rest — became effectively invisible.

The same mismatch affected the dots, boxes and health tracks on the sheet, which drew their accent colour from the surrounding app theme rather than from the sheet.

Section headings now measure well above the accessibility contrast minimum in every theme, and the sheet looks the same whichever theme you pick.

Text of a chat message that is still sending is easier to read in the light theme.

The NPC data import screen is easier to read and to trust.

The "Import notes" button now uses the app's own action colour instead of a stock teal that was too faint against its white label to meet the accessibility contrast minimum.

A selected count tile is now clearly different from one your pointer is merely resting on. The two used to look almost identical, so it was not obvious which tile the listed entries actually belonged to.

On a phone the run's figures no longer break across lines between a label and its number, so "In quarantine" and the count it refers to stay together.

The last run's per-record skill mapping list is now folded away behind its own heading and opens with one click. It used to be printed in full underneath the run result, which pushed the outcome you came for into the top tenth of a very long page. Nothing was removed: the heading still states how many records the list holds. The card's heading now also carries the date and time of the run it describes, so it is no longer easy to mistake it for a second copy of the result above it.

Arriving at WoDVTT without a saved theme choice now shows Blood Moon, whatever your device's light or dark setting says. WoDVTT is a dark game and ships no light theme of its own, so a device set to light was previously served a generic pale interface that was never part of the app's design.

Your own theme choice is unaffected and still wins over everything. Nothing changes for anyone who has already picked a theme.

Fixed

  • Error pages no longer announce a theme they do not have. A page that does not

exist, and any page WoDVTT declines to show, arrived without naming a theme at all — which the browser reads not as "no preference" but as permission to re-apply whichever theme it last saw. It then applied one the error page defines no colours for, so the page painted in the stock component palette while claiming to be Blood Moon, Elysium or whatever the reader had chosen. Error pages now name, and use, the packaged dark theme. The same applies while the theme service is unreachable.

Fixed

The brief messages that appear after an action — confirming an import run, reporting a failed save, and the rest — now follow the theme you are using. They had adopted a fixed dark panel in every theme while keeping dark text, so in the light theme the import confirmation read at 2.53:1 against the 4.5:1 needed to read it comfortably, and its close button was effectively invisible.

They also look different everywhere, including in themes where nothing was wrong: the panel now takes the theme's raised-surface colour with a matching border, and whether a message is a success, a warning, an error or a note is shown by a coloured edge down its side rather than by tinting the words. The colours come from the shared design system rather than from this application, so the repair arrives with the newer version of it that this change takes.

A corrected field is now recognised as guarded no matter which spelling of its name reported it.

The import-notes card now shows its two groupings as two labelled rows, each carrying its own total, so the counts can be checked the way the card's own wording describes. Also removes a stray space before a comma in the data-import page introduction.

The import-notes summary tiles on the admin data-import page now follow the active theme. They previously kept one fixed grey background in every theme while their text followed the theme, which left the counts and labels close to unreadable on the dark themes.

The import notes card now shows a tile for every status, not just Open and Closed. The Informational count was read from the server and rendered nowhere, so the status tiles added up to less than the tiles above them with nothing on the page explaining the difference — on the current corpus the two rows differed by 2,698 of 5,625 entries. The card also states the total the two rows share, so the arithmetic can be checked at a glance.

Every tile now carries its own control affordance. They were already buttons, but they rendered as plain bordered boxes and relied on the sentence next to them to say so.

The NPC import preview now shows how sire relationships would be resolved. Previously that part of the report appeared only after a real import had already written to the database, so the preview — the step that exists to let you check the outcome before committing to it — was the one place it was missing. The preview still writes nothing.

The counts describe the records the run itself touches. Records that are already up to date are skipped and are not re-examined, as before.

On narrow screens the character roster's filter checkboxes now stay beside the words that name them, instead of the label wrapping onto its own line without its control.

Three imported characters carried page decoration in their names and now read correctly: Donatello Giovanni (previously shown with a run of dots, equals signs and an unreadable character in front of it), Margarite and Adnun (both previously shown with a leading apostrophe). The source document draws ornaments in the page corners, and the importer was reading them as part of the entry that followed.

Five characters also gain trait values that the same decoration had been swallowing, and the imported corpus now contains no unreadable characters at all.

Two characters now show their correct bloodline. Lamia — the founder of the Lamia bloodline — and Hipployta were both listed as Lasombra, because the source book prints them in a column whose heading the importer could not see. Both now read Lamia, and the Lamia bloodline appears in the corpus for the first time.

The other 135 characters in that part of the source really are Lasombra and are unchanged.

Imported characters now show their nature, demeanour, embrace year and apparent age as separate values. Previously, wherever the source printed the whole stat line on one line, everything after the first label was shown as the character's nature — for example a nature reading "Bravo Demeanour: Conformist Generation: 8th Embrace: 1565 Apparent Age: Mid-30s" — and the fields behind it were left blank. 927 characters are affected.

Across the corpus this fills in 390 more embrace years, 390 more apparent ages, 272 more demeanours, 84 more willpower values and 61 more morality values. Characters whose source says "unknown" now show nothing there instead of the word "unknown".

The "genealogy NSC" badge on the character list is now readable in the light theme. Its label was previously drawn in almost exactly the colour of the badge behind it, so on the default theme the badge appeared blank. The dark theme was unaffected, which is why the problem was easy to miss.

The character list also gains an automated check that measures the badge's readability rather than only checking that it is present, so this cannot come back unnoticed.

The admin import page now says when its "last run" was a dry run. Previously a rehearsal and a real import looked identical on that card: it reported how many characters were updated and moved its timestamp forward, with nothing to say that no data had actually changed.

A dry run's card now carries an explicit note and a dated "(dry run)" heading, so the numbers read as what a real import would have done rather than what it did.

Opening a bookmarked or shared link to the theme editor while signed out now takes you to the sign-in page and brings you back to the editor afterwards. It previously showed a blank page with nothing to click, leaving no way to sign in and continue.

Every other page in the app already behaved this way; the theme editor was the sole exception.

Fixed

  • The character sheet failed to open, showing an error message in place of the page. Because the

error message is rendered by the page frame rather than the page, it then stayed on screen for every other page visited in the same session, making the whole application look broken when only one page was.

  • An error of this kind now clears itself when you move to another page, so a single failing page

no longer takes the rest of the session with it. A page that is genuinely still broken shows the message again rather than hiding it.

  • Failures of this kind are now recorded at error level. They were recorded as warnings, which is

why nobody was alerted while the character sheet was unavailable.

Opening a page you are signed in for but not permitted to use now says so, on the address you asked for. It previously bounced you to the sign-in page — where signing in again changed nothing, because you were already signed in — and left no trace of what had actually been refused. The recycle bin at /groups/bin was the page this was found on.

Addresses that produce no page, such as a stale or mistyped link, now explain themselves too instead of rendering blank.

The notice on a character sheet that was imported as a genealogy or reference entry now reads as a sentence, in German or English to match the rest of the page. It previously showed an internal label in place of the explanation it was meant to give, so there was nothing on the sheet to say why the trait values were only defaults.

The willpower, humanity and blood trackers on a character sheet now announce themselves in your language to a screen reader. Their spoken labels stayed in English on a German sheet while the health tracker beside them was already translated. All four now follow the language you have selected.

The disciplines box on a character sheet now uses your language throughout. Its heading and the field for adding a new discipline stayed in English on a German sheet, and the short health legend beside the status badge did too. All three now follow the language you have selected.

A group's Game Master can now be changed from the group page. Until now the person who created a group was its Game Master permanently: there was no way to hand the role over, and no way to give a group a new one. A group whose Game Master had left could be recovered from the recycle bin and still be unusable, because nobody could run it.

On the members list, anyone who runs the group — and any administrator — can now set each member's role to Game Master or Player. Everyone else continues to see the roles, and the role is now shown in the reader's own language instead of an internal name.

A group cannot be left without a Game Master by accident: the last one on an active group cannot be demoted, and the control says why rather than simply refusing. Once the group is archived that restriction lifts, which is what allows an abandoned group to be given a new Game Master after it is recovered.

Archiving, removing, deleting and restoring a group now works for the people it was meant for. Until now every one of these actions was refused for everyone except an administrator, and the controls for them did not appear at all — the rights they needed had to be handed out one by one, and nothing in any running environment could hand them out.

Game Masters no longer need to be granted anything. Running a table is the qualification: the person who game-masters a group can archive it, un-archive it, remove it to the recycle bin and restore it from there, and the recycle bin entry appears for them. Someone who only plays at a table sees none of this, and the same account can run one group and merely play in another without the two being confused — the actions offered on each group card now follow that group.

Handing the role over takes effect immediately in both directions: a new Game Master can act at once, and a former one stops being able to on their very next action, with nothing to remember to withdraw.

Administrators keep every right they had, including immediate erasure, and each of the seven rights can still be withheld independently — now as part of an environment's configuration rather than per account. Immediate erasure continues to require an administrator in addition to the right itself, so granting that right elsewhere cannot by itself permit anyone to erase a group.

Keep the shared character-sheet health labels and penalties readable inside the nested dossier theme when the outer shell uses light mode. Include the populated health chart in both deployed theme-contrast checks.

Adds storage for the record of what an NPC import changed on a character, and for the fields a person has corrected so a later import leaves them alone. Nothing is visible yet — the record is written and read by later parts of this feature, and an empty record is a normal state.

This release includes a database change. Apply it with the usual deployment step.

The record of what an NPC import changed on a character is now kept up to date from one import to the next. A note that still applies stays a single note however many times it is reproduced, a note whose cause has gone away is marked as resolved rather than being deleted, and a note that comes back is reopened in place — so the note keeps its full history instead of turning into a pile of near-duplicates.

Each note now carries a trail of when it was raised, resolved and raised again. The trail keeps the fifty most recent events and drops the oldest beyond that, so a character that has been imported for years cannot grow one note without limit.

Nothing is visible yet: these notes are written and read by later parts of this feature, and a character with no notes remains a normal state.

Added

  • The import now records what it did to each character it maps, not only what that character turns

out to break: a trait the source gave two readings for, an ability it had to file under a different name, an ability it could not place at all, a discipline path folded into the discipline list, a Generation filed as a background, and a generation nobody stated that the ratings forced. Each is kept against the field it concerns, so a game master reading a sheet can see where its values came from.

Fixed

  • A value a game master corrected is no longer overwritten the next time the dataset is imported.

Corrections are put back before the import decides whether anything changed, so a corrected character stops being reported as updated on every single run while its untouched fields are still refreshed from source.

  • Importing the same dataset twice adds nothing the second time. Each deviation is one record for

the life of the character, brought forward by every run that still finds it, rather than a new row per run.

A dry run still reports what it found and writes none of it.

Changed

  • Editing a character now keeps its import notes honest. Saving re-checks the sheet against the

character rules, so a value you corrected stops being listed as a problem and a value you broke by hand is listed the same way an import-broken one is.

  • A field you correct is now remembered as yours. Once you have edited a field that carries an

import note, a later import leaves that field alone instead of overwriting your correction. This covers backgrounds that carry a scope, such as "Allies (Prague)", not only plain ones.

Saving is never refused for breaking a rule. Imported characters that already break the rules stay editable — that is the whole point of being able to see what the import did.

A save now records the sheet, its notes and its protected fields together, so an interrupted save can no longer leave a correction unprotected.

Added

  • The record of what an NPC import changed on a character can now be read back. A game master

can ask for one character's notes — which values the import adjusted, inferred, moved or dropped, and which rules it broke — with the entries that need attention listed first. A character the import left alone answers with an empty list rather than an error, because having nothing to report is an ordinary state and should read as one.

  • Administrators can read the same record across the whole imported cast, filtered by the kind

of change or by whether an entry still needs attention, and can see the counts per kind alongside which import run last touched them. The counts survive a restart of the service: they are read from the stored record itself, not from a summary held in memory.

Nothing new is visible in the application yet — these are the reads the character sheet panel and the import administration page will use once they land.

Added

  • A game master opening an imported character now sees an "Import notes" panel under the sheet

header saying what the import did to it. Values that still need attention are listed openly, each naming the field, what the source said, what the import put on the sheet and which rule was broken. Notes that only record a decision the import made — a value it adjusted, inferred, moved or dropped — sit in collapsed groups labelled with their counts, so they are there to read without being in the way.

  • Correcting a value and saving takes its entry out of the list. Nothing in the panel can be

dismissed or ticked off: an entry goes away because the value it describes was fixed.

Characters the import never touched show no panel at all, rather than an empty box. The panel is only shown to game masters, and it never blocks the sheet — a character remains readable and editable whether or not its notes could be loaded.

Added

  • The import notes now say what the import actually did. Every note the import writes — a trait the

source gave two readings for, an ability filed under another name or dropped entirely, a discipline path folded into the list, a Generation filed as a background, a generation derived from the character's ratings — is written out as a sentence in English and German instead of showing the field name and a rule id.

  • When the import notes cannot be read, the sheet says so in one line above the character rather

than quietly showing nothing. A game master is no longer at risk of reading an imported character as clean when the notes simply failed to load.

Fixed

  • An ability the rules have no standard slot for is no longer described as having lost that

slot to another ability. The note now says the model has no slot for it and names the entry it was kept under; before, it named a rival ability that did not exist.

  • Import notes left over from an earlier read are no longer shown after a read that failed. They

described the character as it was, not as it is, and there was nothing on screen to say so.

Added

  • The NPC Data Import page now offers an "Import notes" view of the whole imported corpus, not

just of one character. It shows how many notes exist of each kind — violations, adjustments, inferences, relocations and drops — alongside how many are still open and how many have been closed, and names the run that last reconciled them.

  • Selecting any of those counts lists the entries behind it, a page at a time, each naming the

character, the field, the rule, what the source said and what the import applied. Selecting the same count again puts the list away.

The counts cover everything the journal holds rather than only the last run, so they still answer "what is outstanding" after a re-import. A kind and a status are separate choices and never combine, so a list is always exactly the set its heading names. An import that produced no notes says so instead of showing a row of zeroes, and a view that cannot be loaded says that too rather than looking like a clean corpus.

The view is part of the existing NPC Data Import page and is available to the same administrators, through the same menu entry, as the import itself.

Changed

  • Game masters can now open the imported cast. An imported NPC belongs to no one, and the

character read path only ever answered to a character's owner or to an administrator — so every game master who was not also an administrator was refused every imported NPC, and with it the record of what the import changed. Anyone who runs at least one table can now read those characters.

Characters that belong to a player are untouched: they still answer to their owner and to an administrator alone. Running a table does not make someone else's character readable.

The data-import page now says outright when the last-run card is describing the run just completed, rather than showing the same figures twice under two headings. Alerts on narrow screens also get the full width of their card.

The NPC import now reports its result in the language you are using. Finishing a run showed its confirmation in English on an otherwise German page, and the warning shown before a live re-import — the last thing you read before the character data is rewritten — was English too. Both now follow your language setting.

The import-notes tiles no longer say "1 Ableitungen". A count of one now reads with the singular form of its label.

Information badges and buttons are readable again. Their label was white on a mid-blue fill in every theme except Light, too faint to read comfortably — the same problem the warning and success badges had fixed some time ago, on the one severity that had been missed. Each theme now sets its own label colour for information surfaces, the way it already does for warnings and successes.

Error badges and buttons are readable in the default dark theme, where the label was also white and also too faint. The five story themes and the light theme were already fine and are unchanged. The app now states the label colour for error surfaces explicitly rather than leaving it to a default, so a future change to the shared design tokens cannot quietly alter it.

Withdrawing an invitation on the Einladungen page now works. Confirming the withdrawal previously showed a layout error and left the invitation live.

Characters described in more than one source now keep the record of what the import corrected about them. Where a character appeared in both the Word document and the genealogy site, the two descriptions were combined into one — and the notes each side had recorded ("sire truncated", "morality cleaned", "generation range resolved", and the like) were dropped in the process. The character also lost its "needs review" marking, so a value the import had rewritten looked untouched.

Both sides' notes are now kept together, and the review marking follows from them again. 408 more characters are flagged for review, and the number carrying a correction record rises from 571 to 979. No existing record was shortened or removed.

The published dataset is regenerated with this change; no character's name, clan, lineage or game values move.

Imported characters no longer carry three separate details crushed into one. Where the source ran a character's sire, Nature and Demeanour together on a single line, all three ended up stored as the sire — so the sire read as nonsense like "Absimiliard Nature: Fanatic Demeanour: Monster", and the Nature and Demeanour fields sat empty even though the source had supplied them.

522 characters were affected. Their sire now reads as a name again, and roughly a thousand Nature and Demeanour values that were present in the source all along now appear where they belong. Nature is filled in on 810 characters instead of 303, and Demeanour on 553 instead of 47.

Because a sire is now a name rather than a sentence, it can be matched to the character it refers to: 51 characters regained the lineage links to their childer that the run-together text had hidden.

Existing imported characters keep their current values until the data is imported again.

Two imported characters no longer carry a trait rating that no character can hold. Amalia of Thrace, Penitent had Potence 34, and Violet Mary, the Invisible Harpy had Courage 23 — values that appeared on their sheets as rows of 34 and 23 marks. In both cases two digits had run together in the source book, so the rating was never a rating at all.

Every other rating in the collection is between 0 and 9, with nothing whatsoever in between 9 and 23, so there was no scale these two could sit at the top of. Nothing in the sources says which of the two digits was meant, so the lower one was chosen: Potence 3 and Courage 2. Both characters are marked as needing review and carry a note saying what was changed and why, so the choice is visible rather than silent.

The import now also checks every rating when the collection is published, and says so when one is out of range, instead of letting it through to a character sheet.

Existing imported characters keep their current values until the data is imported again.

The character roster gives the name column 53 more pixels, reclaimed from the slack the other five columns were carrying, so fewer names are shortened on screen.

The import journal on the NPC Data Import page now names each entry's kind and status in the language you are reading the page in. Previously the summary tiles above the list were translated while the list's own Kind and Status columns were not, so selecting the "Verstöße" tile produced a list in which every row said "Violation" — the same word twice, in two languages, on the same card.

Entries with nothing to resolve are now labelled "Hinweis" in German rather than "Informational".

A kind or status this build has no translation for still shows its stored name, so nothing in the journal becomes unreadable.

The character roster now names an absent owner in the reader's own language instead of printing the English word "Unknown" on every imported NPC row.

A shortened owner name in the character roster now reveals its full value on hover, as the other columns already did.

You can now invite someone to a group by e-mail address. Open the group, choose to invite a player, type the address, and they receive an invitation link. Previously the only way to add somebody was to paste their internal user identifier, which nobody using the app can see.

Any member of a group can invite, not only the group master — an invitation issued by a member waits for the group master's approval before it grants membership.

The group page now lists the invitations that are still open, who issued each one, and how long it remains valid. Each row also states whether the invitation e-mail actually went out, so an invitation that could not be delivered is visible instead of appearing to be on its way. Where no mail is configured at all, the invitation is reported as undelivered rather than as still being on its way, so it can be sent again once mail is available.

There is now an "Einladungen" entry in the navigation. It opens one page showing every invitation that still matters to you, in both directions: the ones you have received, and the ones you have sent. Each row names the group and how much longer the invitation is valid.

Invitations you sent also show whether the e-mail actually reached the recipient, and you can withdraw one at any time. Withdrawing takes it back immediately — the link in the e-mail stops working, and the invitation disappears from the other person's list as well.

Only invitations that are still open appear here. Expired ones, and ones that have already been dealt with, drop off the page by themselves.

An invitation whose e-mail did not get through no longer sits there quietly. The row on "Einladungen" says so plainly and now carries a "Erneut senden" button, so you can put the invitation back on its way without starting over.

Sending again issues a new link, which means any earlier link stops working. That costs nothing in practice — the button appears only after a delivery failed, so there is no link in anyone's hands to lose. The invitation keeps its original validity: sending it again does not buy it another two weeks. To stop accidental double-sends, the button waits a minute between attempts and says so if you are too quick.

Undelivered invitations are also re-tried on their own in the background, with growing gaps between attempts and a limit on how many times. If those attempts do not get through either, the invitation stays visibly undelivered and the button is still there.

An invitation can now be accepted. Following the link from an invitation e-mail opens a page that names the group, who invited you and how much longer the invitation is valid, and offers one button to accept it.

If the link is no longer valid — withdrawn, already used, or simply too old — the page says so straight away, without asking you to sign in first. You are only taken to sign-in when there is genuinely an invitation waiting for you. If the page is temporarily unable to check the link at all, it says that instead, rather than telling you an invitation you are holding has expired.

Once you accept, what happens next depends on who invited you. An invitation from the group's leader makes you a member immediately, and the page offers the group's next session if one is scheduled. An invitation from another member is passed to the group's leader for approval, and the page says you are waiting rather than offering anything to join.

You no longer need the e-mail at all: an invitation you can see on the "Einladungen" page now carries an "Öffnen" button that accepts it in place. Once accepted, that row shows the outcome and the button is gone, so the same invitation cannot be accepted twice.

If your connection drops at the moment you accept, accepting the same invitation again simply confirms what already happened, whichever of the two ways you use — you are never told an invitation you have already taken up has gone.

A group's leader now decides who actually joins when somebody else did the inviting.

Any member of a group can invite a player, but an invitation issued by an ordinary member no longer puts anyone into the group on its own. The invited person accepts as usual and is told plainly that they are waiting for the group's leader; nothing is offered to them to join until that decision is made.

The group page now shows the leader a waiting list whenever somebody is waiting there. Each entry names the person who accepted, the member who invited them, and how long they have been waiting, with two choices: let them in, or decline. Letting them in makes them a member immediately and clears the entry. Declining ends the invitation quietly — the link stops working, nobody is notified, and the same person can be invited again at any time. Because a decline cannot be undone and leaves no trace anyone can see, it asks for confirmation first.

The waiting list is visible only to the group's leader, and only while somebody is actually waiting. It never shows the e-mail address the invitation was sent to: by that point the person who arrived is known by name, and the address is not the leader's to learn.

Group leaders are also sent an e-mail when somebody starts waiting, so a decision does not sit unnoticed until the next time the group page happens to be open.

Finished invitations are now tidied away automatically once they are more than thirty days past their last change, so an old group's invitation history stops growing without limit. Nothing you can still act on is affected: a live invitation is kept until it expires, and an expired one is kept for the full window afterwards, so a recently finished invitation can still be looked at.

An invitation that is waiting for a group master's approval is never tidied away, however long it has been waiting. Approval has no deadline, so a waiting entry stays in the group master's list until somebody decides on it.

Inviting a player takes one click again. Typing an address into the invitation dialog left "Einladen" greyed out until the field lost focus, so the first click on it did nothing at all and the address only went out on a second attempt. The button now becomes available as soon as there is something to send.

Fixed

  • Alerts, table rows, icon-only buttons and password fields now pick up the

corrected shared styling. The corrections were released earlier in the shared look-and-feel package, but this app was still pinned to the previous release, so none of them were reaching the screen.

The app now opens in Blood Moon rather than in the platform's stock dark theme. WoDVTT had never marked any of its own themes as the default, so anyone arriving without a saved preference was shown a neutral platform palette instead of the game's. Your own theme choice is untouched — if you have picked one, you keep seeing it.

Warning badges and warning text are readable again on the Light and Dark themes. They were painted white on amber, which was too faint to read; they now use a dark ink on that fill.

Filled Secondary buttons and badges on the Dark theme have been adjusted so their edge stays visible against the panels they sit on.

Light and Dark themselves are unchanged in colour: they are the platform's shared themes, and WoDVTT's own palettes have always been its five named themes — Blood Moon, Elysium, Kindred Codex, Sabbat and Wraith's Veil.

The character roster's column headings are readable again. On the German roster two of the six read "Kl…" and "Erst…" on every page, so the columns of clan names and dates were labelled with words nobody could finish. Their full names are back.

The heading is now the part of the column that is never abbreviated, and it holds whichever system font the reader's machine renders the page in. All six headings - not only the two that were visibly broken - now have room for a font noticeably wider than the one this was designed on, so a heading no longer becomes unreadable just because of what fonts a particular machine happens to have. A very long character name may still be shortened to fit, as it was before; hovering it, or opening the character, still shows it in full.

The character list's row actions ("Ansehen", "Bearbeiten", "Löschen") now show their whole labels instead of being cut mid-word, the grid reflows into readable stacked rows on a phone so the classification badge and the row actions stay reachable, the pager states which page of how many you are on at every width, and the classification badge is labelled with the same "Genealogie" vocabulary as the filter that produces it.

Roster row actions on phones now present full-size tap targets with room between them, so the delete action beside them is harder to hit by accident.

The character list fits its columns again. The row actions are now icon buttons with tooltips and accessible names instead of full-width labels, which returns the space the "Klan", "Besitzer" and "Erstellt" columns had lost — their headings and values were being cut to "Kl…", "Besi…", "Ers…", "Ventr…" and "2026-…" — and gives the classification badge room to print its whole label rather than "GENEALOGIE-NS". On a phone each row is now a two-line card carrying the name, the classification, the clan, the created date and the three actions, instead of six stacked cells per row: the list is about a fifth of its previous height and the classification and actions are still on screen without scrolling sideways. The owner column is hidden below tablet width and returns above it.

The one-off script that moves existing accounts onto the shared sign-in system now also carries over group invitations and saved themes. Previously the person who sent an invitation, or who created a theme, would have been left pointing at an account that no longer exists after the move — so an invitation would show no sender, and a theme no author.

The script now refuses to finish when anything is left behind, instead of reporting the problem and completing anyway. If a single row cannot be accounted for, the whole move is undone and nothing changes, so a partially migrated database is no longer a possible outcome.

Database updates are now applied by the deployment itself, before the new version of the app starts serving. If an update cannot be applied, the deployment stops and the previous version keeps running, rather than the new one starting against a database that was never brought up to date.

Database updates no longer run while the app is starting up. They are applied by the deployment beforehand, so a failed update now stops the deployment with the previous version still serving, instead of leaving the app unable to start.

Loading older chat history now keeps the message you were reading in the same place, including in browsers that apply their own scroll anchoring while the history arrives.

Make deployed session-chat E2E evidence retry a transient second-reader identity-provider stall consistently, with a fixed diagnostic timeout.

Session Chat staging validation now retrieves its protected test credentials reliably.

Chat messages now keep a readable text column in both light and dark themes. Ordinary messages and timestamps no longer collapse into character-by-character wrapping when the ownership controls share a message row.

Loading older session-chat history now preserves the reader's anchored message even when the page request causes an intermediate render.

Session chat now keeps the reader's place when loading history, jumps reliably to new messages, shows confirmed reactions immediately, and reaches the composer before transcript actions by keyboard.

Session Chat now reports a visible error when a reaction cannot be saved.

Session Chat staging validation now waits for the reader-visible settled scroll state after asynchronous scroll interop.

Session Chat now preserves the reader's position when older history reaches the browser after the server render callback.

Session chat history now remains inside the session panel instead of expanding the page, so readers can scroll conversations and receive new-message notices.

Session chat history now stays in its panel and scrolls independently. This keeps readers in place when new messages arrive and allows the new-messages indicator to appear.

Loading older chat history now reliably keeps the reader on the same message.

Loading older chat history now keeps the reader on the same visible message instead of jumping to a different point in the conversation.

Loading older chat history now preserves the message being read.

Older chat history now loads without duplicating messages sent at the same time.

Loading older chat history now preserves the reader's position instead of applying the scroll adjustment twice.

Loading older chat history now keeps the reader's place while the browser settles its scroll anchor.

Loading older chat history now keeps the line being read fixed even when the browser applies its own delayed scroll adjustment.

Keep a reader's visible chat message fixed when loading older history or when a message is removed.

Administrators can now permanently erase a session that is sitting in the removal bin — either after a grace period, or straight away. Erasing straight away requires a written justification, such as the regulatory request that made waiting impossible, and is refused without one. Leaving the grace period unspecified applies the default rather than erasing immediately, so a session cannot be destroyed by an omitted field.

What is erased is the session and everything that belonged to it: its chat, reactions, dice rolls and participants. Characters are unaffected. A session that has not been removed cannot be erased at all — it goes to the bin first, so the recovery window is never skipped by accident.

Every erasure, and every decision to schedule one, is written to a durable record that outlives the session itself and does not depend on whether audit logging is switched on.

Operators can run the retention pass on demand instead of waiting for its next scheduled run. The on-demand run is available only to callers holding a service key issued for this app; it erases exactly what the scheduled pass would erase at that moment, and where no key has been issued the facility is unavailable rather than open.

The default grace period between a session being marked for erasure and it being erased is 30 days, and is configurable.

The release notes page now uses the shared platform component, so it renders formatted releases instead of raw text.

Keep long German Essence labels inside the mobile character sheet. Align the accessibility check with the approved identity layout and wait for settled layout before validating native screenshot visibility.

Wait for the character sheet's real interactive renderer and initial route focus before browser acceptance. Native evidence fits between the measured sticky navigation and action bars, retaining obstruction checks.

Scope the corrected card foreground to Radzen fieldsets, preserving the other panels' existing foreground contracts.

Declare the interactive server renderer in the shared component-test context so readiness-aware pages exercise the same rendering mode as user interactions.

Keep character-sheet cards and their headings readable when the surrounding app uses the light theme. Radzen card surfaces now resolve in the sheet's existing Kindred Codex theme alongside their text.

Picking a theme while signed out now sticks. The choice was being overridden by WoDVTT's default on every page load, so a visitor who selected Sabbat saw Blood Moon again on the next navigation.

This restores the promise the previous release made — that your own theme choice wins over everything — which held for signed-in users but not for anyone browsing signed out.

On staging, theme requests that the API serves on the platform's behalf (the shared theme stylesheet and the personal theme) work again. The API had been left pointing at a placeholder platform address, so those requests failed with a gateway error on every page load.

Group invitations in staging now use the authenticated SMTP relay rather than the internal mail sandbox, so an invitation e-mail is sent to its actual recipient.

The profile-menu theme picker no longer ignores the first click on a theme when the account's saved theme is not the packaged default. It now waits for the account's theme state to load before it renders, instead of showing a placeholder selection that a click could not change.

Fixed

  • Error pages now carry the theme definitions the rest of the site is built

from. A page that does not exist, and any page WoDVTT declines to show, was served with an empty block where those definitions belong, so it painted in the stock component palette however the reader had the site set up. The companion change already stopped those pages naming a theme they could not produce; this is the other half, and it arrives with an updated shared theming package. Error pages still use the packaged dark palette rather than a chosen one.

Access to the personal theme editor is now a grant that can be given or withdrawn per environment, rather than something every signed-in account has permanently. Nothing changes for anyone today — the grant ships held by every signed-in account, exactly as before — but an operator can now withdraw it without a code change.

Where the editor is withheld, it disappears from the account menu instead of appearing and then refusing the click, and opening its address directly is refused outright rather than asking a signed-in person to sign in again.

Session times you enter when scheduling are now understood as your own local time and stored as a real moment, so everyone at the table sees the correct hour wherever they are. The create form states the time zone it is reading your entry in.

The two clock-change nights are handled explicitly: a time in the hour that daylight saving removes is refused with a message naming the change, leaving your entry as you typed it, and a time in the hour that occurs twice is saved as the earlier one, with the form saying so before you submit.

Fixed

  • Creating a session straight after signing in no longer saves it at the wrong hour. The create

form now establishes your time zone itself instead of relying on a value the session list had already worked out, so a game master who has not opened a list yet is no longer scheduled in UTC. Until the zone is known the form declines to save and says why, rather than guessing.

Fixed

  • Session start times in the session list now carry an unambiguous machine-readable value, the

way the session detail page already did. Screen readers announce the time with the zone it is in, and copying a session into a calendar keeps the right moment instead of a bare number whose zone anyone reading it has to guess.

Trait rows (attributes, abilities, backgrounds, disciplines, virtues, willpower, humanity) now draw their rating marks from the shared component library's own mark grammar: 11px marks, 3px gap within a group, 6px total gap at the 5th/6th-mark boundary for the 10-cap rows (Disciplines, Willpower, Humanity); a 5-cap row is simply one group of five, with no boundary gap. Setting a rating is now a row-level pointer/keyboard target — the whole row is the control (one focusable role="slider" per trait), not one target per mark.

The automated interface checks now open a real character sheet.

Until now they browsed the application as a signed-out visitor, so the character sheet — which requires an account — was never opened at all. A signed-out request is answered with the sign-in page, and that page renders perfectly well, so a clean result could be reported for a screen nobody had actually looked at.

The checks now sign in with a synthetic account, open a character that has data in it, and confirm the sheet lays out without sideways scrolling on both a desktop and a phone-sized screen. If no synthetic account is available, or if it cannot open that character, the run fails and says which — rather than passing quietly having looked at nothing.

Fixed

  • Signed-in people are now always shown their own data. When more than one person used

the application within a short window, a request made by one account could be answered with another account's information — an invitations screen, for instance, could list invitations the viewer had never sent, along with the controls to act on them, while the viewer's own list appeared empty. Every request to the application's services now carries the identity of the person who made it, and a request that cannot be attributed to a signed-in person is made without an identity rather than reusing anyone else's.

Operators can now confirm which build each part of the application is running, rather than only the part that answers API calls. Where the two are updated independently, a release that touched only the user-facing part previously looked as though it had not shipped at all.

Automated staging evidence now names the build that actually served the pages it tested, and says so when the two parts are on different builds.

The character sheet is now checked against the three kinds of character it has to display: a brand new one with nothing filled in, an ordinary character, and an ancient one whose ratings run well past the usual maximum. Previously only the empty sheet was ever exercised, so a display problem that only showed up on a real or a very old character could reach players unnoticed.

The character sheet's numeral strip now moves the page to the section you picked. It used to reload the sheet, jump back to the top, and in one case leave the page entirely and take unsaved edits with it. The strip also stays on screen while you scroll instead of sliding under the app bar, and a section you jump to no longer stops with its heading hidden behind it.

A sheet you have just opened no longer shows validation errors for fields you have not filled in yet; they appear once you start editing or try to save.

Adding an alias, a derangement, a conviction or a chronicle tenet no longer renders a full-width button that overlaps its own heading.

The checks for very old characters now use a real character from the system rather than an invented one, so they reflect what the sheet actually has to display — including a sheet that shows three different rating scales at once, and a value that sits above the scale it is drawn on.

Two error messages on a group's page now speak German to German readers. When saving a change to a group's name or description fails, and when removing a member fails, the notice that appears was still written in English no matter which language the rest of the page was in.

Both now read in the reader's own language, and both say the same thing the other failure notices on that page already said: what did not work, and that it is worth trying again.

Groups that were removed or deleted are now reachable again. A new **Recycle bin** page, opened from the toolbar on the Groups list, lists them with who put them there, when, how many sessions and members they hold, and — for a deleted group — the date it will be erased for good. Removed groups carry no such date and say so rather than showing one.

You see only your own half of the bin: a Game Master finds the groups they removed, an administrator finds the ones that were deleted. Restore is offered only for the groups you are entitled to recover, and immediate erasure only to an administrator who holds that right specifically. Anyone else is refused the page outright rather than being asked to sign in again.

The bin says nothing about what is inside a group beyond those counts — no session titles, no chat, no member list.

On a phone the table becomes one labelled card per group, so every value stays readable and named instead of being cut off.

The German wording on the group, recycle-bin and lifecycle-confirmation screens is now checked automatically. Nothing about those screens changes; the check exists so that a missing translation, or a stray English phrase, cannot reach them unnoticed in future.

It also pins down one word. A group is a "Gruppe" throughout, never a "Runde" — that second word belongs to a play session, and mixing the two would quietly rename the thing being archived or deleted.

Group lifecycle management is now switched on for everyone. Game Masters can archive a group that has finished and bring it back later, and administrators can remove one to the recycle bin, restore it, or erase it for good against a written reason. A group whose last Game Master has left can be recovered and handed to somebody new.

Until now this was available only on the test environment. Nothing about how it works has changed — the same screens, the same confirmations, the same thirty-day window before a removed group is erased for good.

The two group-membership operations that were already in the application — changing a member's role, and leaving a group yourself — are now described in the published API reference, where they had been missing.

The last Game Master of an archived group can now leave it. Until now the Leave button stayed greyed out for them whatever state the group was in, with a note saying they were the only Game Master — so a group could be put away for good and still keep the one person who no longer wanted to run it.

Leaving an active group is unchanged: its last Game Master still cannot walk away and strand the players, and the button still says why. The restriction only lifts once the group has been archived, which is the point at which nobody is playing any more.

This also restores the way an abandoned group is meant to be rescued: a group whose Game Master has left can be recovered and handed to somebody new, and until now the first half of that story could not actually happen.

Leaving a group now asks in the reader's own language. The confirmation that appears when you leave — its question, its heading and both of its buttons — was written in English only, so a German reader was asked in English to confirm something they cannot undo on their own: getting back in needs a fresh invitation. The two messages that follow, when leaving is refused or fails, were English-only for the same reason and are now translated as well.

The note on a greyed-out Leave button has also been brought into line with the rest of the group screens, which address the reader informally and call the role "Spielleitung".

Align character-sheet browser acceptance with the single accessible, editable Name in the masthead and the twelve remaining Identity fields. Add an opt-in local Aspire entry point for the same seven TypeScript acceptance cases.

Regelwerk: Alle Anhänge A–C (Ontologie-Updates, Design-Entscheidungen, Inkonsistenzen) wurden aus den 8 Kapiteldateien in drei konsolidierte SL-Dokumente extrahiert: 00_Ontologie.yaml, 00_Design_Entscheidungen.md, 00_Inkonsistenzen.md. Anhang D in Kapitel 3 (Herleitung der Kostenstruktur) bleibt erhalten.

Loading older chat history now keeps the message you were reading in place without disrupting normal reaction, edit, or tombstone updates.

The theme editor's token table, form fields, and live-preview panels now render correctly through scoped CSS instead of inline style overrides. The preview swatches and elevated-surface cards on the theme editor page are styled consistently with the design-token system.

The character sheet's attribute, discipline, skill, and health sections now render through the design-token system instead of per-element overrides. Dice input fields on the session detail page size correctly, dice history rows align consistently, and the session list grid cursor shows the right affordance.

Theme names are now checked in full when a curated theme is created, including any trailing blank line, so a name that does not fit the documented lowercase slug format is rejected on save instead of being stored. The generated theme stylesheet renders names inertly as well, so it stays well-formed whatever a stored name contains. Existing themes, and any name already following the documented format, are unaffected.

Changed

  • The theme editor is now the same one the rest of the platform uses, reached

from a "Your theme" entry in your profile menu. The previous editor had no menu entry at all, so you could only find it if you knew the address — and it refused to save whenever it warned about colour contrast. It now warns without blocking, so a combination you have chosen deliberately is still yours to keep.

A personal theme you saved is now actually applied. Choosing your own theme previously left the page on the default dark palette: the theme was saved, it appeared in the theme picker, and selecting it changed nothing on screen. Your colours now apply on every page and survive signing out and back in.

A saved theme that cannot be rendered no longer appears in the picker at all, rather than appearing and doing nothing when chosen.

If you have saved your own theme, you can now delete it and return to the theme the app would otherwise apply. Deleting takes two clicks and cannot happen by accident.

Typing a colour code by hand now works. Previously the colour picker closed while you typed, so a code entered by keyboard was lost without any message.

Small status labels shown in the secondary colour are now comfortably readable in every theme. Their lettering was previously set against the theme's accent colour in a way this app could not influence, so in five of the seven themes the text sat too faintly on its background to meet the readability standard the rest of the interface already meets.

Buttons in the same secondary colour were corrected earlier; the labels lagged behind because the colour was being fixed for them further upstream. That is now resolved, and the two match again.

Theme colours themselves are unchanged. Nothing looks different apart from the lettering on those labels, which is now chosen per theme for legibility.

Changed

  • The two application images are now built from an explicit list of the files the build

actually reads, instead of copying the entire repository into the build. Nothing about the shipped applications changes — they are assembled from the same sources and carry the same version metadata — but two things follow from the narrower input. An image is now rebuilt only when one of its own inputs changed, so an edit to an unrelated project, test or document no longer discards the cached build and pays for a full rebuild. And a reference added to one project but not to the build's input list now fails the image build immediately, rather than being satisfied by the wholesale copy and going unnoticed until something else exposed it.

Invitation mail can no longer hold a request open for a hundred seconds when the mail relay accepts a connection and then goes silent, and mail addressed to a reserved domain such as .local or .test is no longer handed to the real relay at all — those addresses cannot hold a mailbox, so every such message was a guaranteed bounce against the live sending account. Such an invitation is now recorded as failed rather than delivered, so the inviter is never told a message reached someone it never could. The staging mail provider is now named SmtpRelay, which is what it has actually been since the relay settings were added.

An invitation link now asks whether you already have an account instead of assuming you do, and offers to create one.

Fixed

  • The API now answers in the language you ask for. Error messages from the groups, sessions, chat,

dice, theme and user endpoints were English for everyone; they now follow the Accept-Language of the request, and each response says which language it used.

  • Invitation and approval e-mails were being written in the server's language rather than the

recipient's. The German versions had shipped some time ago and were simply unreachable, because nothing in the API ever worked out what language a request was in.

  • Sign-in, registration and password messages gained their German versions.

Fixed

  • Buttons, form labels, placeholders and table column headings across the sessions, groups, GM

prep, theme and font surfaces now follow the language you selected. They previously showed English words on a German page regardless of that setting, because the text was written into the markup instead of being looked up.

The wording follows the terms the rest of the product already uses — a session is a "Sitzung", a group is a "Gruppe", a non-player character is an "NSC", and the appearance settings are called "Design". Words that are the same in both languages, such as "Name" and the dice panel's "Hunger", are unchanged and are now recorded as a deliberate choice rather than left to chance.

Fixed

  • Headings, page descriptions, empty and error states, browser tab titles and the labels a screen

reader speaks now follow the language you selected. Buttons and form fields already did, so a German page could show a translated button under an English heading — the sessions page said "Sitzung beenden" beneath "GM Actions", and the dice panel labelled its fields in German under "Dice Roll". Around 130 of these strings across 20 screens now come from the translation files.

  • The changelog page and the "nothing at this address" page had no translations at all and were

English whatever you had selected. They do now.

  • Two theme-editor fields whose placeholder read "(inherits from base)", and the "GM Prep" entry in

the navigation, were missed by the earlier sweep because of the shape of the words rather than anything about the words themselves. They are translated now, and the check that looks for this no longer depends on how a string happens to be capitalised.

Words that are the same in both languages — "Chat", "Token", "WCAG", "Details", "Changelog" and the dice panel's "Hunger" — stay as they are, and are recorded as a deliberate choice. The product name, the roman numerals on the character sheet, the developer-only environment notice and the filter expressions an operator copies verbatim are likewise left alone, each with its reason written down.

Run local browser journeys against an isolated real platform sign-in service and seeded character corpus. Capture tall character-sheet regions in complete native tiles so evidence checks work when a resource card exceeds the viewport.

Kapitel 4 (Merits & Flaws) rebalanciert: Sprachbegabt, Albträume, Scheu, Weichherzig, Willensschwach, Orakelfähigkeit, Kreuzabwehr, Dunkles Geheimnis und Erschaffers Groll angepasst, plus Umlaut-Korrekturen.

Opening this app from the platform now arrives signed in. Previously the launch landed on the front page, which is readable without an account, so the app never learned who you were — the navigation showed only Home, and everything you have access to stayed hidden until you signed in a second time here.

Reaching the app directly by its own address is unchanged: the front page is still open to anyone, and nothing new is required to view it.

After signing in, you are now always returned to a page inside this app. A few unusual spellings of the "return to" address were previously accepted and could send you to a different site once sign-in finished; those are now ignored and you land on the home page instead.

Ordinary return links are unaffected, including ones that point at a particular section of a page.

The session chat now uses the shared chat component for its message timeline, composer, actions, reactions, and live-update behaviour. This keeps scrolling, unread messages, and dark-mode presentation consistent wherever chat is used.

Changed

  • Session chat messages now say who wrote them. A message is attributed to the sender's account

display name, or to their e-mail address where no display name is set, worked out when the transcript is read rather than copied at the moment of sending — so someone who later changes their display name is shown under the new one on old messages too. Where no name can be established at all, the message carries none and each reader's own client supplies the wording, in that reader's language.

  • A deleted message now keeps its place in the conversation instead of vanishing from it. The

transcript returns the message stripped of its text and of its reactions, so the deleted wording is not sent to anyone, and participants who already have the conversation open see the message turn into a "deleted" placeholder without having to reload the page.

Changed

  • The session chat now reads as a conversation. Each message carries its author's name and a

small coloured circle with their initials — the same colour for the same person every time, so a glance down the column is enough to see who is speaking. Consecutive messages from one person sent within a few minutes are gathered under a single heading instead of repeating the name on every line, and a different speaker always starts a fresh block.

  • Your own messages are tinted and marked with a coloured edge, so you can find your own

contributions without reading the names. They stay in the same column as everyone else's: a table has four to six people at it, and pushing one person's messages to the opposite side would make it look like a conversation between two.

  • Timestamps read the way you would say them. A message from the last hour says how long ago it

arrived; anything older shows the time of day, with the full date and time available by hovering over it. Days are separated by a line naming the day, worked out from your own calendar rather than the server's — so a conversation that runs past midnight where you are is split where you would split it.

  • Long messages now wrap at a comfortable reading width, and a single very long unbroken run of

characters can no longer stretch the page sideways.

Changed

  • The session chat no longer keeps its controls on screen at all times. Editing and deleting your

own message now appear when you hover over it or move the keyboard onto it, and step out of the way again afterwards, so a conversation reads as a conversation rather than as a row of buttons. On a phone or tablet, where there is nothing to hover over, pressing and holding a message opens the same short list of actions.

  • Deleting a message now asks first. Once confirmed, the message is replaced by a quiet note that

it was deleted, in the place it always occupied — the conversation around it does not shift, the note is still there after a reload, and everyone else at the table sees it appear straight away without refreshing.

  • Reactions are shown only once somebody has actually made one. An unreacted message carries

nothing beneath it, and the thumbs-up and thumbs-down you can add live with the other actions rather than sitting under every line. Your own reaction is outlined and marked as pressed, so screen readers announce it as well as showing it.

Changed

  • The session chat now follows the conversation only when you are actually at the end of it. If you

have scrolled up to read something, a message arriving from someone else no longer leaves you guessing: the transcript stays exactly where you put it, and a button appears at the foot of the panel telling you how many messages came in. Selecting it — or simply sending a message yourself — takes you back to the latest, and the count clears. If you were already at the bottom, the new message scrolls into view as before and nothing extra appears.

  • Loading older messages now keeps your place. The line you were reading stays under your eyes

instead of sliding down the screen by the height of the page that just arrived.

  • A chat with nothing in it yet says so, rather than showing an empty box, and the message field

stays available so you can start the scene.

  • A short conversation now sits at the foot of the chat panel, next to where you type, instead of

being stranded at the top of an otherwise empty area.

Changed

  • Sending a chat message now shows it straight away. The message appears in the conversation the

moment you send it, slightly faded until the server confirms it, instead of the whole conversation being fetched again and nothing happening until it comes back.

  • A message that fails to send stays where you wrote it, marked as not sent, with a Retry beside

it. Your text is no longer lost with the request. Retry checks first whether the message actually arrived, so retrying something that did land does not post it a second time.

  • Enter now sends on a desktop-sized window, and Shift+Enter starts a new line. On a narrow

window Enter starts a new line and the send button is the way to send. Ctrl+Enter still sends everywhere. Pressing Enter to confirm a word suggested by an input method no longer sends the half-typed message.

  • The message field grows with what you write, up to six lines, and then scrolls. The send

control now sits inside the field, stays reachable by keyboard, and is greyed out while there is nothing to send.

  • The field stops at 2000 characters — the same limit the server enforces — and tells you how

many you have used once you are past 1800, rather than refusing the message after you have written it.

  • An unsent message you were in the middle of writing is kept per session, so opening a

character sheet and coming back no longer discards it.

Changed

  • A screen reader now announces new chat messages as they arrive, instead of the conversation

being a silent block that has to be re-read to find out what changed. Only arrivals are announced — a deleted message is replaced in place without the surrounding conversation being read out again.

  • Reaction buttons are now announced by name rather than as an emoji and a number, and the

button that jumps to the latest messages says how many are waiting.

  • Pressing Escape while editing a message now abandons the edit, the same way Escape already

closes the delete confirmation.

  • Tab now stays inside the delete confirmation while it is open. Previously it walked out onto

the conversation behind the dialog, leaving the two choices unreachable.

  • The message field you are editing is now labelled for screen readers.

Changed

  • The chat panel's remaining English text now follows your language setting: the notice shown

when a session is read-only, and the messages that appear when loading older messages, editing, or deleting fails.

Fixed

  • In a session that is closed or archived, the reaction buttons under a chat message are no

longer clickable. They still show who reacted and how many, so the history reads back in full — they simply no longer offer an action the session can no longer accept.

Session chat now tells you when it is not receiving live updates. Until now, a session whose real-time connection had failed looked exactly like a quiet one: the transcript loaded, sending worked, and messages other people wrote simply never appeared, with nothing on screen to say so. A short note above the transcript now names that state, and the transcript and composer stay usable — a reload still brings everything in.

The cause was that the server refused the connection's request to follow a session by dropping the connection outright, which the app could only observe as an unexplained network fault. A refusal is now answered with a reason, so a failure to follow a session is recorded plainly instead of being mistaken for a slow network.

Live updates in session chat now connect as the person who is signed in. The real-time connection was reusing a single sign-in for everyone, so it was refused for every reader: chat history and sending worked, but nothing another participant wrote or deleted ever appeared until the page was reloaded.

No one saw anyone else's session as a result — the server rejected the mismatched sign-in rather than honouring it, which is why this showed up as missing updates rather than as the wrong person's messages.

The session list no longer cuts values off. Start times previously lost their tail to an ellipsis, and the part that disappeared was the time zone — so a session scheduled for 20:00 in Berlin reached a reader in New York as a bare "2:00 PM" with nothing on screen to say which clock that was. Read as your own local time, that is a session missed by hours.

Columns are now wide enough for the values they hold, and anything longer wraps onto a second line instead of being trimmed. This applies on phones and tablets too, where the narrower screen made the trimming worse.

The join button's label was affected by the same limit and is now shown in full.

Fixed

  • Buttons and links near the top of a page can be clicked again. The bar across the top of the

application floated above the page instead of sitting above it, so it covered roughly the first 85 pixels of every page and swallowed any click that landed there. On the group overview this meant the "Create Group" button did not respond at all, and the page could not be scrolled to move the button clear.

Changed

  • The application now uses a newer release of the shared application frame. Alongside the fix

above it carries the shared-frame work of the past weeks, so some surfaces may look slightly different.

A round of visual-consistency housekeeping across the sign-in, sign-out, admin data-import, fonts and theme-editor screens: the styling of these areas now follows your selected theme's spacing and shape settings instead of fixed per-element values, so they stay consistent when a theme changes. No change to what the screens look like was intended.

The theme toggle uses the browser's current light or dark scheme when clicked. Delayed initialization and earlier saves no longer overwrite a newer theme choice. Adopts AppPlatform.Theming 2.3.3.

Personal themes now live entirely in the shared, cross-app theme store. The app-local copy has been removed now that every existing personal theme was moved and verified there, so your saved theme, its export/import, and its CSS preview all come from the same place regardless of which app you are using.

Deleting an uploaded font no longer edits personal themes that reference it. A theme built around a since-deleted font keeps its saved value and your browser falls back to a similar font automatically.

Added an operator-run tool to clear out a personal theme locally once it has already been moved and confirmed in the shared theme store, so that move can be completed safely. No change to how personal themes look, save, or apply for users.

Added an operator-run tool to move existing personal themes into the shared theme store. No change to how personal themes look, save, or apply for users.

Personal themes are now stored in the platform's shared per-user theme store instead of staying local to this app, using the same sign-in you already use here. Your existing custom theme keeps working exactly as before while this change rolls out.

The personal theme editor's export and import are on the way: exporting your theme to a file, and importing one back with a preview before anything is overwritten, land in a following release once the shared editor component picks up the new store.

Personal themes now actually apply. Choosing your own colours previously failed at every step: the editor opened with empty swatches instead of the theme you were looking at, saving was rejected outright, and a colour that did reach storage left the page unchanged. All three had one cause, and it is fixed.

Every built-in theme now states a complete palette. Where a theme did not define a colour, it fell through to a platform default that could sit oddly against the rest of the theme; those colours are now part of the theme itself. A few of them are still the generic defaults and will be designed per theme in follow-up work.

Themes you or an administrator saved earlier keep working exactly as before — nothing stored was changed or migrated.

When the platform is unreachable, the palette shown while it recovers is now the current one rather than an older set of colours that no longer had any effect.

Reopening your personal theme editor now shows your current colours instead of empty swatches. Once you were using your own theme, the editor could no longer tell what you were looking at and opened blank, so every colour you had not changed appeared unset.

An individual signed-in account can now be excluded from the personal theme editor while every other player keeps normal access. This lets an operator hold back the editor from one specific account for verification, without changing what any real player can do.

Signing out now works reliably from the sign-out page in automated checks; no change to how signing out behaves for you.

Fixed

  • The build identifier the application reports about itself now names the commit it was

built from once, instead of repeating it twice joined by a dot. The version itself was always correct, but anyone checking which build is running — by hand or by an automated check comparing it against the source — got a value that matched nothing, so a deployment could not be confirmed as up to date. The reported identifier is now the plain commit again, and it is also cleaned up when read, so a build that repeats it cannot reach the outside again.

Fixed

  • The colours shown for the fraction of a second before the page finishes loading now match the

colours the page actually settles on. They were drawn from a set of names the application had stopped using, so the very first paint could look wrong before correcting itself. A corrected set was published a while ago and this application had not picked it up.

The shared sign-in components this app is built on move up to their current release. Nothing about signing in, staying signed in, or managing your account changes: the same screens do the same things, and no one is signed out by the update.

Operators: the update brings a database change with it, which is applied automatically on deploy. It only adds — no table is renamed, no column is removed and no row is rewritten, so it can run against a database that is already in use without a maintenance window. Sessions that were already open before the update stay open and keep working.

Two pieces of housekeeping travel with it. Accounts can now record which language they would like to be written to in, so that address can be honoured once the surfaces that send mail start reading it; existing accounts have nothing recorded and continue to be written to in the language of the request, exactly as before. And an open session can now be told apart from the other sessions of the same account, which is what a "these are your signed-in devices" view would eventually be built on. Neither is exposed anywhere yet — this update only puts the foundations in place.

Changed

  • Added diagnostics that record how the application decides who is signed in as a page becomes

interactive. Nothing changes on screen; the entries go to the operational logs and exist to settle why a signed-in visitor can still see a brief flash on entry. An error while carrying that decision across is now recorded rather than silently discarded.

Fixed

  • The WoDVTT tile in the platform app catalog no longer shows scrambled

characters. Its dice icon rendered as a run of stray letters and symbols; the German descriptions lost every umlaut, so "Würfelsystem" read as "Würfelsystem" and "für" as "für"; and the English and default summaries showed "—" where their dash belonged. The text had been stored twice-encoded, so each accented character was saved as the meaningless characters that its own encoding looks like when read the wrong way. The icon and every affected description now hold the characters they are meant to be, in both languages, matching what the tile has always shown elsewhere.

Attribute labels on the character sheet remain readable when switching between light and dark mode.

Character sheets now lead with the character's name, clan, generation and chronicle. Edit the name directly in the heading area, with readable text and controls on narrow screens.

Keep Health labels and controls inside the Character Sheet panel on narrow screens, preserving full labels and touch targets.

The character sheet's Disciplines/Blood/Essence/Health, Attributes and Antrieb (Nature/Virtues/Willpower/Humanity/Ambition/Convictions) sections now lay out on the shared platform grid instead of a page-specific hand-rolled one, so cards in a row line up with equal heights and one consistent label size at every screen width, from a phone up to a 4K monitor. (Skills/section III keeps its existing skill-group layout this round; it was not part of this grid conversion.)

Every card on the sheet now uses the same title style — including Combat, Backgrounds and Legacy, which used to keep an older, more decorated card style — so Disciplines, Blood, Health, Melee Weapons, Advantages and the rest all read as one family instead of several slightly different ones. Every discipline name (including "Thaumaturgy") stays fully readable at every screen width instead of being cut off with "…", and the Health track no longer wraps its label onto its own line at any screen width.

Test alignment: the local and staging visual-acceptance checks for this page had drifted out of date since an earlier round moved Wesen/Verhalten to a different section — they still expected the old field count. Brought back in step with what the page actually renders.

Character identity values are readable in view mode, including long names and notes. Attribute and skill labels follow the selected German or English language.

Rulebook clarification: clan discipline triads in the Vampire chapter now use the same discipline names as the Disciplines chapter, and every clan lists a complete set of three. The Essenz age-increase table now states the actual age steps used by the game, replacing outdated figures.

The shared theming package behind the profile-menu theme picker is updated so the picker follows the account's theme at the component level as well. WoDVTT already waits for the account's theme state before showing the picker, so nothing changes on screen; the update takes the fix at its source rather than relying on that wait alone.

Changed

  • CI pipeline consolidated: the seven separate workflow checkouts (secret-scan,

pointer-mode-guard, quality-gate, docker-gate, migration-gate, css-token-guard, and validate-ci-conventions) each paid ~7s of runner time for checkout and classification, even when the classifier skipped every heavy step. A single ci-gate-orchestrator workflow now performs the checkout and classification once and fans the result out to individual gate jobs, preserving the five required status check names (secret-scan, pointer-mode-guard, quality-gate, docker-gate, migration-gate) as job IDs. The old workflow files remain in place for now; this is the first step — once the orchestrator is verified green they can be removed.

Signing in is now the only thing that decides what you can reach. Any page or request that does not say it is public requires you to be signed in, so nothing can end up reachable by accident. The pages you would expect to see signed out — the welcome page, sign-in, sign-out, the changelog, an invitation link and the error page — are unchanged.

Changing your sign-in details now happens only on your account page at the platform, where it always belonged, and the app no longer offers its own version. The in-app screens already pointed there, and the in-app e-mail change could not finish in any case: it sent a confirmation link to a page that no longer exists, so the address never changed while two e-mails still went out. One account, one place to manage it.

Running a game is now something you earn by running a table. Create a group and you are its game master, which unlocks the GM prep workspace and creating non-player characters. This previously depended on a role nobody could be given, so the workspace and NPC creation were open to administrators only — everyone else was quietly handed an ordinary character when they asked for an NPC.

Signing in for the first time no longer needs anything prepared in advance: your profile is created on your first visit instead of the app reporting that you do not exist.

Operators: one previously mandatory configuration value is no longer read and can be removed from the deployment environment after this release. The configuration matrix in the project documentation lists it and where it is still set.

Fixed

  • Session chat now reconnects to its live update group after a temporary connection loss and clearly reports when live updates are unavailable.

Session chat now consumes the shared chat panel release that keeps its message controls and content readable in the active theme. Its deployed quality checks also verify the public RCL selectors and prove that a second open reader had the deleted message before the live update.

Keep chat history anchored while late row layout settles.

Keep history anchoring in scrollport coordinates and retain toolbar interactivity while moving the pointer into it.

Keep chat history anchoring correct through delayed message-row layout, and keep revealed message actions within their row so their controls remain reachable.

Session chat's staging quality check now verifies readable timestamps and reliable delete actions alongside the dark-mode and responsive-layout guard.

Session chat's staging quality checks now execute the responsive readability and durable-delete journeys reliably before reporting their result.

Session chat now keeps ordinary messages readable on supported desktop and mobile viewports and exposes delete actions reliably for both keyboard and pointer users.

Session chat now gives message prose the remaining panel width, while staging validation distinguishes an immediately visible sender-side failure from a missed real-time update.

Session chat now protects readable desktop width when session details grow and opens delete confirmation reliably from keyboard activation.

Changed

  • The application now keeps a much smaller record of the people who use it. It signs everyone in

with their platform account and never handles a password itself, yet its database still held a place for one, along with places for a lock-out counter, a two-factor setting and several related tables — none of which it could fill in or act on. All of that is gone. What remains is who the person is, enough of their profile to show a name and a picture, and the link the application's own data hangs off.

  • Nobody has to do anything, and nothing anyone can do changes. Signing in, creating characters,

group membership, invitations and theme preferences all behave exactly as before, because the removed storage was never what decided any of them: permissions have always come from the platform account the person signed in with.

  • Existing accounts are carried over rather than recreated. Everyone keeps their display name,

their picture, their language, their characters and everything else attached to them; only the unused credential storage is removed from around them.

  • Two consequences are visible if you look closely. The list of roles shown for the signed-in

person now reflects the account they actually signed in with, where previously it was always empty. And the personal-data download no longer includes two entries — a list of linked external sign-in providers and a two-factor key — that were always blank here; both belong to the platform account, which offers its own download.

Changed

  • The keys that protect sign-in cookies, form-submission tokens, and the one-time links

used to reset a password, confirm an address, or unlock an account are now stored encrypted when the operator supplies a key-protection certificate for the environment. Until now they were kept in readable form alongside the application's own data, so a copy of that data — a backup, an export, a restored snapshot — carried everything needed to impersonate a signed-in person. Startup now also states plainly whether the protection is in effect, so an environment that is still missing it is visible rather than silent.

Styling updates now reach people who have visited before. Previously the browser could keep using its saved copy of the shared styling after an update shipped, so a returning visitor might continue seeing the old appearance for a while. Each update is now published under its own address, so browsers pick it up immediately while still caching aggressively between updates. This also completes the recent control-outline work for returning visitors, and picks up the typography groundwork that lets a chosen typeface reach the interface components.

Ticked checkboxes and selected radio buttons stay clearly visible when you point at them. Previously the coloured fill of a ticked checkbox faded to a barely-there tint as soon as the pointer reached it, leaving its white tick almost invisible; a selected radio button faded the same way, and because the filled dot is the only thing marking a radio button as chosen, it briefly looked as though the selection had been lost.

In the dark appearance the accent colour used for selected and focused controls is also a touch brighter. It had been very slightly too dark to stand out against the raised panels that cards, tiles, and dialogs are drawn on, so a tick box, a selected radio button, a switch, or a slider inside one of those panels was harder to pick out than it should have been. The keyboard focus outline benefits most: it is the only thing showing which control you have moved to with the keyboard, and inside a panel it had been right at the edge of being too faint to see.

Button labels remain just as legible, the light appearance is unchanged, and selected rows in tables, highlighted text, and slider and progress tracks keep the soft tint they have always had.

Form fields, dropdowns, checkboxes, radio buttons, and outlined buttons now have a clearly visible outline in both light and dark themes, meeting the accessibility contrast requirement for control boundaries. Previously these outlines were too faint to separate a control from the surface behind it, and because the dark appearance is what most people see by default, the weaker of the two was the common case. Pointing at one of these controls in the dark appearance now brightens its outline instead of making it vanish, so it stays clear which one is under the pointer. Dividers, table rules, and panel outlines are deliberately unchanged and stay understated.

Security

  • A developer-only diagnostic that lists the service's available operations is now restricted to

local development machines. It was previously switched on by a testing option alone, so a deployed environment that had that option turned on for any reason would have offered the listing to callers who were not signed in. No deployed environment is known to have had it turned on, and nothing else changes: the diagnostic keeps working locally, and no other behaviour is affected.

Removed

  • The app no longer keeps a store for sign-in credentials it does not issue. Since it moved to

central sign-in it has had a table for them that nothing could ever put anything in, and that had to be carried along by every future database change.

  • With it goes a way of identifying a caller from a browser cookie instead of the credential they

signed in with. It could not have worked — the store it consulted was always empty — but it was a second, weaker route to answering "who is this?" sitting alongside the real one.

  • Also gone: a background job that woke up regularly to tidy that same empty store.

The change refuses to run if the store turns out to hold anything, rather than discarding it.

A session page no longer offers everyone at the table the game master's controls. "Close Session" and "Archive Session" were shown to every player of an active session and then refused when used, because the page decided from the state of the session rather than from who was reading it. The controls now appear only for the people who may actually use them — the group's game master, the person who created the session, and an administrator.

Fixed

  • A group's member list showed each person as a fragment of an internal identifier rather than

their name, so a Storyteller looking at their own coterie could not tell who was in it. It now shows the name people already see everywhere else in the application; the identifier is still available by hovering, for anyone who needs it to ask for support.

Changed

  • The shared identity library moved from 4.3.0 to 8.1.0, four major versions in

one step. Nothing this app does changes: every breaking change in between alters interfaces for session and admin management that this app neither implements nor calls, the database model is byte-identical so no migration is needed, and all 6797 tests pass unchanged. The upgrade is groundwork — the library release that lets invitation mail reach a real mailbox is built on this line, and arriving there from 4.3.0 later would have meant doing this jump under time pressure instead.

The admin import journal's Violations tile and its own filter caption were already shown in your language. The rows underneath it were not: pressing a filter tile in German produced a caption reading "Verstöße" above a grid whose Kind and Status columns still read "Violation" and "Open" in English regardless of your language setting. Both columns now follow the same language as everything else on the page.

Fixed

  • Nine out of ten imported NPCs could not be saved. Opening a published character, changing

anything and pressing save was rejected, because the sheet the import produced broke rules the application enforces on the way back in — and in almost every case the rules were the ones at fault, not the character. Willpower had to equal a figure derived from the character's attributes, so any value the books actually printed counted as wrong. The three Humanity virtues were demanded of every character, including the four fifths whose source material never lists them. A bibliography citing one book at six different pages counted as six duplicates. Elder holdings and virtues were capped below the range the books publish, and the earliest embrace dates fell outside the accepted window entirely.

  • Willpower, virtues and backgrounds now keep what the source gave them, characters without

stated virtues receive the standard three at zero — a real rating, unlike an absent one — and a virtue of zero is allowed, since a character wholly without Conscience is a legitimate one. Of the 2,950 characters that failed, 22 remain: over-long biography text, one sheet whose specializations sit on skills too low to carry them, and one mis-read rating. Those are errors in the source data rather than in the rules, and are left visible for the import clean-up pass.

Fixed

  • An imported NPC whose source material never states a generation, but describes them with a

rating only an elder could have, no longer lands on a sheet their own abilities are too strong for. Such a character was filed as a young vampire, whose ceiling sits below the rating the books gave them, and the sheet was rejected the moment anyone tried to save it. The import now reads the generation from the character's own ratings when the source omits it, claiming no more antiquity than those ratings require, and marks the value as inferred so it is not mistaken for something the books said. One character in the published dataset is affected — Elimelech the Twice Damned, now a fifth-generation elder rather than an unsaveable neonate with a 9 in History.

Everyone signed in when this reaches staging is signed out once, and any password-reset or email-confirmation link sent before that point stops working. Signing in again is all that is needed; nothing about a character, a chronicle or an account is affected.

The keys that sign wodvtt's session cookies used to be kept in two separate places under two different names: the web side kept its own set, the API kept another, and the two had no idea the other existed. Only one of them was ever looked after. They are now one set, kept in one place that is backed up, cannot be quietly discarded when the app is busy, and is checked from the same place every other app in the estate is checked from.

Moving the keys is what ends the current sessions — the new ones are not the old ones. It happens once.

Added

You can now leave a group. A "Leave Group" button sits at the top of the group page for anyone who belongs to it, asks you to confirm, and returns you to your list of groups.

Until now a group you had joined stayed with you permanently. A Game Master could remove other people from a group but had no way to remove themselves, so the one person able to empty a group was the one person who could not walk away from it.

The group's only Game Master is the single exception: the button is shown but refused, explaining that someone else has to become a Game Master first. A group with nobody able to run it would be stuck that way, because groups still cannot be deleted.

Leaving is not the same as being deleted from the group's history. Sessions you took part in and messages you wrote stay where they are; you simply lose access to the group, and a Game Master has to invite you back if you want to return.

Five automated checks over the lore and genealogy part of the character sheet pressed an add button, or typed into an alias box, and then asked whether anything had happened without waiting for the page to handle the press. Most of the time the page was quick enough; on a busy machine it was not, and a whole run could be reported as broken when nothing about the sheet was wrong. The five now wait for the press to be handled before they look. The sheet itself is unchanged, and the checks are exactly as strict as they were.

Discipline names on the character sheet are readable in the light theme again. They previously rendered in a colour meant for the light shell's own background while sitting on the character sheet's always-dark codex card surface, making them all but invisible.

The character sheet's section-navigation strip no longer widens the page past the viewport on narrow phones. A hidden accessibility label on each off-screen nav item used to sit outside the visible area instead of collapsing onto its own link.

Changed

  • The database migration step now runs the same published application image as the rest of

the deployment, instead of two separate images built just for it. Those two had drifted into a state where they could no longer be built at all, and because nothing built them routinely, that went unnoticed. The migration behaviour itself is unchanged — the step runs the same command it always did — but it now travels on an image that is rebuilt and checked on every change, so the same kind of rot cannot recur silently.

Fixed

  • Opening the application while already signed in no longer flashes a "you must be logged in"

message and bounces you back out through the sign-in provider before letting you in. The page is built twice — once on the server, then again once it becomes interactive — and only the first of those could see your sign-in cookie. The second pass therefore concluded you were a stranger, said so, and started a full round trip to sign you back in, which succeeded immediately because you had never actually been signed out. The answer the first pass works out is now handed to the second, so both agree from the first frame. Genuinely signed-out visitors are still sent to sign in, but quietly, without being told off on the way.

Fixed

  • Imported NPCs no longer come out of the import weaker than their source material. The

published sources describe characters with 221 different ability names, while a sheet holds 30 skills, so several abilities routinely land on the same one — an occultist with ten kinds of lore, a scholar with eight fields of study. The import used to keep only the highest of each cluster and delete the rest, which quietly removed about a sixth of every skill rating in the catalog: one NPC lost 88 rating points, another 33 on a single skill. Each ability now keeps its own rating, listed under the skill it belongs to, and abilities the import previously did not recognise at all — Ride, Hearth Wisdom, Area Knowledge and others — have a home as well. Re-importing the published dataset now carries 35,595 of its 35,600 rating points onto sheets, against 29,253 before.

Changed

  • The admin data-import page now reports what actually happened to the source abilities

— how many were dropped, merged, or kept alongside their canonical skill, and how many rating points were lost — instead of a single unexplained "conflicts" number. A dry run also publishes its report, so the per-record detail can be reviewed before committing an import rather than only afterwards in the logs.

Changed

  • Outside a developer's own machine, the application now refuses to start when single

sign-on has not been set up, and says so plainly in the startup error. Until now it started anyway and quietly fell back to checking sign-ins on its own instead of against the identity provider — so a deployment that had simply been left unconfigured looked healthy while single sign-on was not actually in effect. A misconfiguration that used to pass unnoticed is now visible at the first attempt to start.

The tests that need a real PostgreSQL now share one, instead of each test booting a database engine of its own. A full run of those five suites went from nineteen container starts to one and from thirty-seven seconds to eight, and the Docker connection storm that occasionally failed a run on Windows with a named-pipe timeout no longer has nineteen chances per run to happen. Every test still gets its own empty database, so the suites prove exactly what they proved before.

Added

  • The one-time cleanup of key ring entries written before the protecting

certificate existed can now be carried out. Reporting that those entries were still unprotected was already in place, but there was no way to act on the report: the work has to happen on the staging host, and nobody — operator or agent — has a shell there. It is now a dispatched maintenance job that inspects both rings, takes a verified backup, and, once told the consequence out loud, removes the unprotected entries and restarts both services.

The restart is included rather than left to whoever runs it. A running service keeps its key ring in memory for up to a day and carries on signing with a key that has just been removed, so a removal on its own looks like it did nothing and then signs everyone out at whatever later moment the service happens to recycle.

The job refuses to remove anything when no backup exists, and refuses when no protecting certificate is configured — in that case the replacement key would be written unprotected as well, leaving things exactly as exposed as they started, in exchange for signing every user out.

Fixed

  • You can download the data this application holds about you again. The download itself never

stopped working, but the page that offered it was removed a while ago and nothing replaced the link, so there was no way to reach it. It is now in the profile menu, next to Account. It covers what this application stores — your name, your e-mail address, your picture and your theme choice. Everything belonging to your platform account is offered separately by the platform, which has its own download.

The readiness answer is now capable of reporting a problem on the part of the application that serves pages, rather than always coming back well.

Readiness is reported over a set of checks. That part of the application registers no checks of its own, so the set was empty — and an empty set is always well, so the answer was the same whether it was healthy or not. Automated monitoring read a steady all-clear it could never have read any other way. Its own state is now always included, so the answer reflects something real. The part that talks to a database was unaffected and continues to report exactly as before.

The addresses, their responses and their meaning are unchanged; only what the answer is computed over has changed.

The application now refuses to start in its deployed environments if the keys behind sign-in are not protected where they are stored.

Those keys secure the cookie that keeps someone signed in, the tokens that guard form submissions, and the links sent out to reset a password, unlock an account or confirm an address. They are kept in the application's own database. Encrypting them there requires a certificate, and until now a deployment that had not been given one simply carried on without it — everything looked and behaved normally, so the only sign was a line in a log nobody had reason to read. Anyone able to read the database, or a copy of it made for some unrelated reason, could have used those keys to impersonate a signed-in person or to forge an account-recovery link.

Staging and production now stop rather than run in that state, and say which setting is missing. Development and test environments are unaffected and continue to run without a certificate.

A related sharp edge is gone too: a certificate setting left blank or containing only spaces — what a half-finished setup tends to leave behind — used to fail at start-up with an unhelpful message about corrupted data. It is now treated as not provided at all, which produces the same clear explanation as leaving it out.

This protects keys from the point a certificate is provided onward. Where the application has already run without one, the keys written earlier remain as they were and are still in use, so such an installation is only partly protected until those are replaced — a separate step.

Balancing-Matrix fuer Merits und Flaws: 134 Eintraege klassifiziert nach Scope, Magnitude, Frequency.

Disziplinen: Willenskraft als Widerstandspool durch Resilienz + Entschlossenheit ersetzt. Geistbeeinflussende Disziplinen (Animalis, Auspex, Dementatium, Dominatium, Majesty, Nightmare) vereinheitlicht auf vergleichende Würfe statt Differenz-Subtraktion. WP-Maximum-Formel korrigiert (Resilienz + Entschlossenheit statt Resilienz + Intelligenz). Patzer/Fehlschlag/Erfolg-Einträge bei passiven Fähigkeiten ohne Prüfwurf entfernt (Auspex 2.1, Obtenebra 8.1, Proteanum 11.1). Dementatium 4.5 Super-Erfolg: Widerstandspool Resilienz + Entschlossenheit statt Intelligenz + Selbstbeherrschung.

EP-Kosten für Merits und Flaws von ×4 auf ×3 reduziert. Balancing-Matrix eingeführt.

Kapitel 4 (Merits & Flaws) überarbeitet: 134 Einträge aus drei Quellen, 5-Stufen-Kosten-System.

Added

  • Both services now report at start-up how much of their key ring is still

stored without protection, instead of reporting only that a protecting certificate is configured. The two are not the same thing: the certificate protects entries as they are created, and a service that already had a valid key carries on using it, so an installation can be fully configured and still hold every key unprotected for months. Where that is the case the report now says so, and says how many. The API service said nothing at all about its key ring before this, which left half the picture invisible.

Fixed

  • Three style rules named a design value that does not exist, and a browser given an unknown

value does not complain — it quietly throws the whole rule away. The result was spacing and lettering that had been written down but never actually applied: the sign-in card sat with its text pressed against its own border, the panels of a character sheet stacked flush against one another with nothing between them, and the group name on an invitation page was set in the ordinary body face instead of the heading face used everywhere else. All three now render as they were always meant to.

Removed

  • The one-off scripts written to move existing accounts onto the shared platform sign-in have been

withdrawn. They were built for a situation that never arose — this application has no live deployment and no real accounts — and a rehearsal showed they could not have completed as written. The database is being rebuilt instead, and accounts are created the first time each person signs in. The scripts remain in version history for any application that does have accounts to move.

Fixed

  • Staying signed in while a page sits open for hours no longer risks signing you out of

everything. Each time the app renewed your session in the background, the identity provider handed back a fresh credential and the app failed to record it — so a second browser tab would present the spent one. The identity provider treats a reused credential as a sign the session may have been stolen and can end every session you have in response, which is the right instinct but was being triggered by ordinary use.

Changed

  • Signed-in sessions are now kept on the server rather than entirely in the browser cookie, and

encrypted where they are stored. **Everyone signed in when this ships is signed out once** and will need to sign in again; sessions opened afterwards are unaffected.

Removed

  • Two leftovers from before the app moved to central sign-in: a place it looked for a credential

in a browser cookie that nothing has written for some time, and an unused piece of the old machinery for issuing its own credentials.

Secondary buttons and badges now take their colour from the theme you have chosen, and their labels are readable in all of them. They had been stuck on a stock teal that belonged to no theme at all: it never changed when you switched appearance, and its white lettering sat at 3.17:1 against the fill, below the 4.5:1 that normal text needs to stay comfortably legible. The "Retry" button inside an error message, "Open sessions" on the home page, "Join" in the session list, and the "Past" marker on a finished session were all affected.

Each theme now carries its own secondary action colour, deepened where it had to be so white lettering reads clearly on it — and kept as it was for Light and Sabbat, whose accents were already dark enough. The brighter accent each theme is known for is untouched: member avatars, the character sheet's brass rules, and the swatches in the theme picker all look exactly as they did. Secondary buttons simply sit a shade deeper than the accent beside them, which is also what tells them apart from the primary action.

Themes you have built yourself are unchanged and keep using their own secondary colour as before. The theme editor now warns you when that colour would leave its button labels too faint, the same warning it already gives for the primary and error colours, and its preview pane shows the secondary button as it will really appear. Saving a theme also no longer quietly discards the parts of it the editor does not show — until now that silently dropped a theme's other colour definitions every time it was saved.

Secondary buttons and badges are now readable on the Dark theme too. The previous release gave each theme its own secondary action colour, but two themes never received it: Dark and Light are supplied by the shared platform rather than by WoDVTT, and the change reached neither. Dark was the one that mattered — it is the default, and its secondary buttons kept the old teal at 3.00:1 against their white lettering, below the 4.5:1 that normal text needs. Light happened to clear the bar already, but by luck rather than by design.

Both now use the same deepened gold as the rest of the themes, so the "Retry" button inside an error message, "Open sessions" on the home page, "Join" in the session list and the "Past" marker on a finished session read clearly whichever theme you are on.

Nothing else about Dark and Light changes: their backgrounds, text and accent colours still come from the shared platform exactly as before.

Session-chat staging evidence now validates both follow-at-bottom and held-scroll behavior, and reports a stalled identity sign-in promptly before retrying a new, isolated login.

Session-chat checks now wait for the interactive transcript to settle before they verify loading older messages or use message actions. This makes the release evidence reliable on slower deployed environments.

Loading older session-chat messages now keeps the reader's visible message in place when the history control disappears. Session-chat E2E evidence also reliably positions an existing off-screen target before it uses its actions.

On the character sheet, skill rating dots no longer jump left and right between rows in the same section: a row with a specialization button reserves the same space for it as a row without one, so every row's dots start at the same position. The specialization button also no longer wraps onto its own line at narrower widths.

Changed

  • The database's change history has been collapsed into a single starting point. Previously the

database was built by replaying every schema change made since the project began; it is now created in one step from the current design. Nothing about the resulting structure changes, with one exception noted below. This has no effect on how the application behaves, but it does require the existing staging database to be recreated, which discards its contents. That is deliberate and was chosen now precisely because no real accounts or data exist yet.

Removed

  • A leftover table for locally stored sign-in keys has been dropped. It was created by an early

schema change but had already been removed from the application's design when sign-in moved to the shared platform account service, so it held no data and nothing read from it.

The session and member tallies on a group page are now translated and agree with the number beside them, instead of always reading English plurals.

The rest of the automated checks that press a button or type into a field were looking at the screen before the page had been given the chance to react — the chat composer and its delete and retry controls, the invite dialog, the character sheet's spinners and name field, and the skill specialisation button. On a quiet machine the page was always quick enough; on a busy one it was not, and a whole run could be reported as broken while the app itself was fine. Thirty-two of those presses and keystrokes now wait for the page to handle them. Two deliberately do not: they are the checks that hold the server's answer open on purpose, and waiting there would mean waiting for something that is never coming. Nothing about the app changed, and every check demands exactly what it demanded before.

Six more automated checks — five over the groups toolbar, one over a reaction on a read-only session — pressed a control and then looked at the result on the very next line, without waiting for the page to act on the press first. On a machine busy with the rest of the run the press was still waiting its turn, so the check read the page, or the list of requests the page had made, as it had been a moment earlier. All six now wait for the press to be handled.

The reaction one was the worst of them, because what it demands is that nothing was sent. A press that had not been handled yet satisfied that on its own, so the check could report success without ever reaching the rule it exists to prove.

What each one demands is unchanged and just as strict, and neither the groups toolbar nor the session chat was ever at fault.

The character sheet no longer slides sideways. Rows of trait marks in the narrower panels — Virtues, Willpower and Humanity — insisted on their full width however little room the panel gave them, and the surplus spilled out and pushed the whole page left and right. It happened at every window size, not only on a phone, and nothing on the page looked out of place: every panel appeared to fit, and only the marks that had spilled past the edge were holding the page open.

The marks now shrink to fit the space they are given. None are hidden or cut off, and where there is room they stay at their normal size.

The automated browser checks that verify pages on the staging site used to fail outright whenever a page answered with a server error, or with no answer at all, during the brief window right after a change goes live while the site restarts. A single automatic retry could land inside that same window, so a failure there was often the same restart measured twice rather than a real problem with the page.

These checks now retry a server error, or an unanswered connection, with a short wait that grows between attempts, for up to two and a half minutes by default, which covers one full restart, before reporting the original failure unchanged. Responses below 500 are not retried.

Fixed

  • The check that tells the operator whether the one-time move of existing accounts to the

central sign-in finished correctly was reading the wrong way round. It asked whether each piece of data still pointed at an old account, but a successful move is precisely what makes that stop being true — so it reported every correctly moved record as a failure, and could only report success when nothing had moved at all. The written procedure said not to finish the move until the count reached zero, which it never could.

Added

  • A dry run the operator can execute beforehand. It changes nothing, and reports what the move

would touch, whose data it cannot account for, and — the one that matters — whether anyone currently exists under both their old and new account at once. That situation stops the move halfway through, and until now there was no way to look for it in advance.

  • Dice-roll history is now moved along with everything else. The plan of record listed it as

covered and the procedure never touched it, so every past roll would have stayed attached to an account that no longer signs anyone in.

  • A way to build the old-account-to-new-account pairing from the email address both systems

already hold, instead of transcribing pairs of identifiers by hand. A single mistyped character in that list would have handed one person's characters, groups and chat history to someone else, without anything reporting a problem.

One check over the character list failed about once in twenty-five complete runs and passed every single time it ran on its own, so a healthy build occasionally looked broken and the failure could not be pinned down. The check asked the page to open a character for reading and then looked at the address the browser had moved to, but it never waited for the page to act on the request first — on a loaded machine it sometimes read the address from the moment before. It now waits. What it demands is unchanged and just as strict, and the character list itself was never at fault.

Keep destructive action labels readable when theme error-text colors change, including hover states, while preserving badge and outlined-action colors.

Each of the five app themes — Blood Moon, Elysium, Kindred Codex, Sabbat and Wraith's Veil — now carries its own values for a set of design tokens that previously fell back to the platform's generic grey-and-blue palette. Borders, danger-action buttons, and text drawn on tinted accent backgrounds now match each theme's own colour family instead of looking the same across every theme. All values were verified against WCAG AA contrast requirements.

Group secondary character identity in an expandable section and show ambition and desire once. Give disciplines their own row above equal-width resource cards that fit the space beside the navigation, label blood values consistently, and translate health status into German. Protect staging test sessions from overlapping deployments and reject changed versions in acceptance reports. Automatically check these sheet layouts after deployment and retain native unobstructed screenshots for visual review.

[0.4.12] - 2026-08-01

Changed

  • Chat messages and dice rolls inside a session now show the time on your own

clock. Until now they showed the server's time, so a message sent at 21:57 in Berlin was listed as 19:57 — with nothing on screen to suggest the number was not yours.

  • A session's start and end are shown in your own time zone as well, and the

zone is always named next to them, so a time on this page is never a number you have to guess the meaning of.

  • When you are in a different time zone from the game master, the session page

also states their local time — "19:00 (20:00 for the game master)" — so you can confirm you have understood rather than trust arithmetic you cannot see.

  • In the session list the time zone is named only when yours differs from the

one the session was scheduled in. A group that all plays in one zone sees no extra label on every row.

Note

  • Times now also carry a machine-readable value with their offset, so screen

readers and copy-and-paste get an unambiguous moment rather than a bare number.

  • Sessions created before time zones were recorded keep displaying exactly as

they do today, with no zone named — there is none to name.

[0.4.11] - 2026-08-01

Changed

  • Session start times in your session list are now shown in your own time zone,

detected automatically from your device with nothing to set up. A game master in Berlin who schedules an evening for 20:00 and a player reading the same session in London now each see that moment on their own clock.

  • A session starting within the next week is introduced with "today",

"tomorrow" or "in N days" alongside the exact time — never instead of it.

Note

  • On a first visit the start time shows a short placeholder until your time zone

is known, rather than briefly showing a time in the wrong zone and correcting itself a moment later. Returning visits show your local time immediately.

  • Sessions created before this change carry no recorded time zone and keep

displaying exactly as they do today, unchanged.

[0.4.10] - 2026-08-01

Added

  • Sessions can now carry the time zone they were scheduled in. Nothing looks

different yet: this is the groundwork for showing session times in each reader's own local time. Sessions that already exist are untouched and keep displaying exactly as they do today.

Operator note

  • Deploying this version applies a small database change that adds one empty

column to the sessions table. No existing data is read, rewritten, or removed, and the step can be reversed by removing the column again.

[0.4.9] - 2026-07-31

Fixed

  • Your sessions now actually appear under "My sessions". The page built the

table but never asked for its contents, so the list stayed empty for everyone, no matter how many sessions they had. Nothing was ever lost — the sessions were there the whole time, and they show up again with no further action.

  • Joining a session from the list works again. The "Join" button only exists on

a row, so while the list was empty there was no way to join from it.

Changed

  • "My sessions" now speaks plainly and in your language. Column headings and

session states are translated into German and English instead of showing internal English terms, and the two state columns no longer both read "Active" — one now tells you *when* (planned, happening now, over) and the other tells you what the game master has set (draft, scheduled, open, finished, archived).

  • If you have no sessions yet, the page now says so in a sentence and offers to

create your first one, instead of showing an empty table with the technical note "No records to display".

  • If the list cannot be loaded, the page now says what happened, reassures you

that nothing is lost, and offers a "Try again" button rather than leaving you on a dead end.

  • Start times are shown in your language's usual date format instead of a

technical timestamp.

[0.4.8] - 2026-07-31

Fixed

  • The character sheet really does stay within the screen width on a phone now.

The previous release fixed one of the two causes; the sheet still slid sideways because the hidden label that tells a screen reader what the delete column is for sat outside its table's scrolling area and quietly held the page open to the full width of the widest weapon table. The Combat and the Merits & Flaws tables continue to scroll on their own, so every column stays reachable. The automated check that was meant to catch this now reproduces the hidden label as well, and fails if it is ever left out again.

[0.4.7] - 2026-07-30

Fixed

  • The character sheet no longer scrolls sideways on phone-sized screens. The

Combat and the Merits & Flaws panels now stay within the screen width, and their wide tables scroll on their own, so every column remains reachable.

[0.4.6] - 2026-07-30

Changed

  • The start page is now a proper welcome screen. It greets you, points you

straight at your characters and your sessions, and shows a short preview of a character's traits.

  • The start page no longer displays internal development notes and reference

codes that were never meant for players and storytellers.

  • Wording on the start page is now fully available in German and English,

including the short descriptions on the overview cards.

[0.4.5] - 2026-07-30

Fixed

  • Saving a brand-new character no longer leaves the "unsaved changes" note on

screen. Once the character is saved the note disappears and the Save button stays inactive until the next edit.

  • A character you have just created is no longer presented as a

genealogy/reference entry. Its row in the character list shows the usual completeness indicator, and opening its sheet no longer starts with the reference-entry notice. Imported genealogy entries keep that label exactly as before.

[0.4.4] - 2026-07-30

Fixed

  • Pages now render in the intended gothic and serif typefaces. The typefaces the

design calls for were referenced by the stylesheet but never shipped with the app, so every page quietly fell back to the reader's default system fonts and wasted a handful of failed downloads on each visit. The typefaces are now included and served directly by the app, with no third-party font service involved. All of them are published under the SIL Open Font License, and their licence texts are shipped alongside them.

[0.4.3] - 2026-07-21

Security

  • Updated an internal cryptography dependency to address published security

advisories.

Changed

  • Centrally declared dependency versions are now applied consistently across the

whole application, including parts that previously kept older versions indirectly.

[0.4.2] - 2026-07-20

Fixed

  • The app now stays usable and falls back to its packaged themes when the theme

service is slow or temporarily unavailable, instead of failing the page.

  • Error pages now carry the same browser security protections as regular pages.

[0.4.1] - 2026-07-07

Added

  • Added an administrator data-import experience for reloading the bundled

vampire dataset with reviewable results.

  • Added data-quality improvements that make incomplete lore entries, duplicates,

and import issues easier to identify.

  • Added public release visibility through version metadata and the changelog

page.

Changed

  • Updated shared character-sheet controls and app shell styling for a more

consistent experience.

  • Improved browser cache behavior for shared styling updates.
  • Improved app-switcher loading performance.

Fixed

  • Fixed session-expiry handling so browser sessions are not kept or discarded at

the wrong time.

  • Fixed pagination edge cases for very large page values.
  • Refreshed outdated testing documentation.

Security

  • Strengthened development-only authentication safeguards so local sign-in

helpers cannot be enabled outside development.

  • Reduced refresh-session lifetime.
  • Updated an internal dependency to address a published security advisory.
  • Kept dice results generated on the server.

[0.4.0] - 2026-06-25

Added

  • Established 0.4.0 as the first WoDVTT SemVer baseline.
  • Added account sign-in, email confirmation, two-factor setup, recovery codes,

passkeys, account management, and administrator user management.

  • Added player and game-master character-sheet management with role-aware

access and soft delete support.

  • Added Vampire character sheets with attributes, skills, disciplines, hunger,

willpower, convictions, and chronicle-tenet fields.

  • Added theme selection, theme import/export, and gothic visual styling for the

app experience.

  • Added English and German localization foundations.
  • Added deployed service version metadata and health information.
  • Added public API documentation and structured error responses for client

integrations.

  • Added automated tests for core API and user-interface behavior.

Changed

  • Adopted an interactive server app model for the web experience.
  • Simplified deployment routing around the current hosting model.