diff --git a/lang/en.json b/lang/en.json
index c62d621..2b6d02c 100644
--- a/lang/en.json
+++ b/lang/en.json
@@ -168,6 +168,7 @@
"DG.Roll.For": "For",
"DG.Roll.Damage": "Damage",
"DG.Roll.Roll": "Roll",
+ "DG.Roll.CannotRollStat": "A statistic of 0 cannot be rolled.",
"DG.Physical.Wounds": "Wounds",
"DG.Physical.FirstAidLabel": "First Aid Has Already Been Attempted: ",
"DG.Physical.ExhaustionPenalty": "Penalty:",
@@ -237,7 +238,7 @@
"DG.Gear.Armor": "ARMOR",
"DG.Gear.OtherGear": "OTHER GEAR",
"DG.Gear.Name": "Name",
- "DG.Gear.DamageOrLethality": "Damage/Lethality",
+ "DG.Gear.DamageOrLethality": "Damage/ Lethality",
"DG.Gear.ArmorPiercing": "Armor Piercing",
"DG.Gear.ArmorRating": "Armor Rating",
"DG.Gear.Equipped": "Equipped",
@@ -328,6 +329,13 @@
"DG.ModifyDamageRollDialogue.Weapon": "Weapon:",
"DG.ModifyDamageRollDialogue.OuterModifierTooltip": "Use this field to modify the outer part of the formula, such as to double the result.",
"DG.ModifyDamageRollDialogue.InnerModifierTooltip": "Use this field to add a flat modifier such as '+1' or another dice term such as '+1d4'.",
+ "DG.DamageOrLethalityRoll.Title": "Roll Damage or Lethality",
+ "DG.DamageOrLethalityRoll.Intro": "This weapon has both damage and lethality. Choose which to roll.",
+ "DG.DamageOrLethalityRoll.RollDamage": "Roll Damage",
+ "DG.DamageOrLethalityRoll.RollLethality": "Roll Lethality",
+ "DG.EditStats.Title": "Edit Statistics",
+ "DG.EditStats.Button": "Edit Statistics",
+ "DG.EditStats.DistinguishingFeature": "Distinguishing Feature",
"DG.Roll.Inhuman": "Inhuman",
"DG.Tooltip.BreakPoint": "The current breaking point, reset when current sanity falls to or below this value. The starting breaking point is SAN - POW.",
"DG.Skills.ritual": "Ritual",
diff --git a/module/MODULES.md b/module/MODULES.md
index e111154..69c1e35 100644
--- a/module/MODULES.md
+++ b/module/MODULES.md
@@ -4,19 +4,19 @@ Entry point: [`deltagreen.js`](deltagreen.js) (registered in `system.json`).
## Folder responsibilities
-| Path | Purpose |
-| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| [`config/`](config/) | System constants (`DG`, skill list, template paths) |
-| [`data/`](data/) | TypeDataModels (`actor/`, `item/`, `derived/`) |
-| [`active-effect/`](active-effect/) | Active effect metadata, data model, document class, runtime sync |
+| Path | Purpose |
+| ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| [`config/`](config/) | System constants (`DG`, skill list, template paths) |
+| [`data/`](data/) | TypeDataModels (`actor/`, `item/`, `derived/`) |
+| [`active-effect/`](active-effect/) | Active effect metadata, data model, document class, runtime sync |
| [`profession/`](profession/) | Profession catalog, validation, character-creation math, deferred commit ([`commit-character-creation.js`](profession/commit-character-creation.js)), Damaged Veteran modifiers ([`damaged-veteran-modifiers.js`](profession/damaged-veteran-modifiers.js)), disorder IDs ([`disorders.js`](profession/disorders.js)) |
-| [`roll/`](roll/) | Dice roll subclasses, roll pipeline, and roll UX. [`roll/roll.js`](roll/roll.js) is the public API. Internal Roll subclasses live in [`roll/classes/`](roll/classes/). |
-| [`sheets/`](sheets/) | ApplicationV2 sheets and mixins |
-| [`applications/`](applications/) | Standalone dialogs, chargen orchestrator ([`profession-setup-flow.js`](applications/profession-setup-flow.js)), shared dialog UI ([`character-creation-ui.js`](applications/character-creation-ui.js)), and dialog factory ([`dg-dialog.js`](applications/dg-dialog.js)) |
-| [`actor/`](actor/), [`item/`](item/) | Document subclasses |
-| [`chat/`](chat/) | Chat cards ([`dg-chat-card.js`](chat/dg-chat-card.js)) and inline actions |
-| [`integrations/`](integrations/) | Optional module hooks (Dice So Nice) |
-| [`utils/`](utils/) | Generic helpers only (Handlebars, migration, skill layout/tooltips) |
+| [`roll/`](roll/) | Dice roll subclasses, roll pipeline, and roll UX. [`roll/roll.js`](roll/roll.js) is the public API. Internal Roll subclasses live in [`roll/classes/`](roll/classes/). |
+| [`sheets/`](sheets/) | ApplicationV2 sheets and mixins |
+| [`applications/`](applications/) | Standalone dialogs, chargen orchestrator ([`profession-setup-flow.js`](applications/profession-setup-flow.js)), shared dialog UI ([`character-creation-ui.js`](applications/character-creation-ui.js)), and dialog factory ([`dg-dialog.js`](applications/dg-dialog.js)) |
+| [`actor/`](actor/), [`item/`](item/) | Document subclasses |
+| [`chat/`](chat/) | Chat cards ([`dg-chat-card.js`](chat/dg-chat-card.js)) and inline actions |
+| [`integrations/`](integrations/) | Optional module hooks (Dice So Nice) |
+| [`utils/`](utils/) | Generic helpers only (Handlebars, migration, skill layout/tooltips) |
Root-level modules: [`settings.js`](settings.js), [`templates.js`](templates.js).
diff --git a/module/applications/edit-stats.js b/module/applications/edit-stats.js
index d4d05b7..cc9e7c1 100644
--- a/module/applications/edit-stats.js
+++ b/module/applications/edit-stats.js
@@ -1,4 +1,5 @@
import DG, { BASE_TEMPLATE_PATH } from "../config/index.js";
+import { applyDialogTheme } from "./dg-dialog.js";
export default class ActorEditStatForm extends foundry.applications.api.HandlebarsApplicationMixin(
foundry.applications.api.ApplicationV2,
@@ -11,9 +12,13 @@ export default class ActorEditStatForm extends foundry.applications.api.Handleba
/** @override */
static DEFAULT_OPTIONS = {
tag: "form",
- classes: [DG.ID, "edit-stat-form"],
- window: { title: "Edit statistics form", resizable: true },
- position: { width: 400, height: 200 },
+ classes: [DG.ID, "edit-stat-form", "dg-dialog-app"],
+ window: {
+ title: "DG.EditStats.Title",
+ resizable: true,
+ contentClasses: ["standard-form"],
+ },
+ position: { width: 600, height: "auto" },
actions: {},
form: {
handler: this.formHandler,
@@ -28,11 +33,18 @@ export default class ActorEditStatForm extends foundry.applications.api.Handleba
},
});
+ /** @override */
+ async _onRender(context, options) {
+ await super._onRender(context, options);
+ applyDialogTheme(this);
+ }
+
/** @override */
async _prepareContext(options) {
return {
...(await super._prepareContext(options)),
actor: this.actor,
+ statKeys: DG.statistics,
sourceStatistics:
this.actor._source?.system?.statistics ?? this.actor.system.statistics,
};
diff --git a/module/roll/classes/dg-percentile-roll.js b/module/roll/classes/dg-percentile-roll.js
index 57106a6..a247b81 100644
--- a/module/roll/classes/dg-percentile-roll.js
+++ b/module/roll/classes/dg-percentile-roll.js
@@ -255,6 +255,20 @@ export class DGPercentileRoll extends DGRoll {
return { target, localizedKey, skillPath };
}
+ /**
+ * i18n key for a user-facing warning when this roll must not be made, or null if allowed.
+ * @returns {string|null}
+ */
+ get blockedRollMessage() {
+ if (this.type !== "stat") return null;
+
+ const x5 = Number(this.target);
+ if (!Number.isFinite(x5) || x5 < 1) {
+ return "DG.Roll.CannotRollStat";
+ }
+ return null;
+ }
+
/**
* Create label based on result of roll
*
@@ -392,7 +406,7 @@ export class DGPercentileRoll extends DGRoll {
const { rollTargetModifier } = this;
- if (!this.target || Number.isNaN(this.target)) {
+ if (this.target == null || Number.isNaN(this.target)) {
return null;
}
diff --git a/module/roll/roll-dialogs.js b/module/roll/roll-dialogs.js
index 98c36b0..c45c4fa 100644
--- a/module/roll/roll-dialogs.js
+++ b/module/roll/roll-dialogs.js
@@ -7,6 +7,8 @@ const PERCENTILE_ROLL_DIALOG_TEMPLATE =
"systems/deltagreen/templates/dialog/modify-percentile-roll.html";
const DAMAGE_ROLL_DIALOG_TEMPLATE =
"systems/deltagreen/templates/dialog/modify-damage-roll.html";
+const DAMAGE_OR_LETHALITY_ROLL_DIALOG_TEMPLATE =
+ "systems/deltagreen/templates/dialog/damage-or-lethality-roll.html";
const QUICK_MODIFIER_PRESETS = [
{
@@ -215,3 +217,41 @@ export async function showDamageRollModifyDialog({ itemName, formula }) {
],
});
}
+
+/**
+ * @param {object} options
+ * @param {string} options.itemName
+ * @returns {Promise<"damage"|"lethality"|void>}
+ */
+export async function showDamageOrLethalityChoiceDialog({ itemName }) {
+ const content = await renderTemplate(
+ DAMAGE_OR_LETHALITY_ROLL_DIALOG_TEMPLATE,
+ {
+ itemName,
+ },
+ );
+
+ return showDgDialog({
+ modifier: "damage-or-lethality",
+ content,
+ window: {
+ title: game.i18n.localize("DG.DamageOrLethalityRoll.Title"),
+ },
+ close: () => null,
+ buttons: [
+ {
+ default: true,
+ label: game.i18n.localize("DG.DamageOrLethalityRoll.RollDamage"),
+ action: "damage",
+ icon: "fas fa-dice",
+ callback: () => "damage",
+ },
+ {
+ label: game.i18n.localize("DG.DamageOrLethalityRoll.RollLethality"),
+ action: "lethality",
+ icon: "fas fa-skull-crossbones",
+ callback: () => "lethality",
+ },
+ ],
+ });
+}
diff --git a/module/roll/roll.js b/module/roll/roll.js
index d622b66..e1ff5c5 100644
--- a/module/roll/roll.js
+++ b/module/roll/roll.js
@@ -100,6 +100,11 @@ export async function processDGRoll(event, roll) {
const shiftKey = event?.shiftKey ?? false;
const which = event?.which ?? 0;
+ if (roll instanceof DGPercentileRoll && roll.blockedRollMessage) {
+ ui.notifications.warn(roll.blockedRollMessage, { localize: true });
+ return;
+ }
+
if (shiftKey || which === 3) {
if (!(roll instanceof DGSanityDamageRoll)) {
const dialogData = await roll.showDialog();
diff --git a/module/sheets/mixins/roll-sheet-mixin.js b/module/sheets/mixins/roll-sheet-mixin.js
index 7367ac4..cb91e39 100644
--- a/module/sheets/mixins/roll-sheet-mixin.js
+++ b/module/sheets/mixins/roll-sheet-mixin.js
@@ -1,4 +1,5 @@
import { getDGRollToken } from "../../chat/dg-chat-card.js";
+import { showDamageOrLethalityChoiceDialog } from "../../roll/roll-dialogs.js";
import { createDGRollFromDataset, processDGRoll } from "../../roll/roll.js";
/** @param {typeof foundry.applications.api.ApplicationV2} Base */
@@ -36,11 +37,27 @@ export default function RollSheetMixin(Base) {
* @private
*/
static async _onRoll(event, target) {
- if (target.classList.contains("not-rollable") || event.which === 2)
+ if (event.which === 2) return;
+
+ if (target.classList.contains("not-rollable")) {
+ if (target.dataset.rolltype === "stat") {
+ ui.notifications.warn("DG.Roll.CannotRollStat", { localize: true });
+ }
return;
+ }
const item = this.actor.items.get(target.dataset.iid);
- const roll = createDGRollFromDataset(target.dataset, {
+ const rollDataset = { ...target.dataset };
+
+ if (rollDataset.rolltype === "damage-or-lethality") {
+ const choice = await showDamageOrLethalityChoiceDialog({
+ itemName: item?.name ?? "",
+ });
+ if (!choice) return;
+ rollDataset.rolltype = choice;
+ }
+
+ const roll = createDGRollFromDataset(rollDataset, {
actor: this.actor,
item,
element: target,
diff --git a/module/utils/register-helpers.js b/module/utils/register-helpers.js
index 4448418..6a44602 100644
--- a/module/utils/register-helpers.js
+++ b/module/utils/register-helpers.js
@@ -122,4 +122,24 @@ export default function registerHandlebarsHelpers() {
return result;
});
+
+ Handlebars.registerHelper("hasWeaponDamage", (damage) => {
+ const trimmed = String(damage ?? "").trim();
+ return trimmed !== "" && trimmed !== "0";
+ });
+
+ Handlebars.registerHelper("hasWeaponLethality", (lethality) => {
+ const value = Number(lethality);
+ return Number.isFinite(value) && value > 0;
+ });
+
+ Handlebars.registerHelper("hasWeaponDamageAndLethality", (item) => {
+ const damage = item?.system?.damage;
+ const lethality = item?.system?.lethality;
+ const trimmed = String(damage ?? "").trim();
+ const hasDamage = trimmed !== "" && trimmed !== "0";
+ const hasLethality =
+ Number.isFinite(Number(lethality)) && Number(lethality) > 0;
+ return hasDamage && hasLethality;
+ });
}
diff --git a/module/utils/roll-target-tooltip.js b/module/utils/roll-target-tooltip.js
index 4dfd37e..8fb1206 100644
--- a/module/utils/roll-target-tooltip.js
+++ b/module/utils/roll-target-tooltip.js
@@ -177,6 +177,22 @@ export function appendRollTargetTooltipSection(
return `${existingTooltip}
${appendix}`;
}
+/**
+ * @param {string} existingTooltip
+ * @param {string} [feature]
+ * @returns {string}
+ */
+export function appendDistinguishingFeatureSection(existingTooltip, feature) {
+ const trimmed = String(feature ?? "").trim();
+ if (!trimmed) return existingTooltip;
+
+ const label = game.i18n.localize("DG.EditStats.DistinguishingFeature");
+ const escaped = foundry.utils.escapeHTML(trimmed);
+ const section = `
${foundry.utils.escapeHTML(label)}: ${escaped}`;
+ if (!existingTooltip) return section;
+ return `${existingTooltip}${section}`;
+}
+
/**
* Stat and sanity roll-target tooltips for the agent sheet left bar (not skills).
*
@@ -191,8 +207,12 @@ export function prepareAgentStatSanityTooltips(actor) {
for (const [key, stat] of Object.entries(system.statistics ?? {})) {
const base = Number(stat.x5) || 0;
const existing = game.i18n.localize(`DG.Attributes.Tooltip.${key}`);
- stat.tooltip = appendRollTargetTooltipSection(
+ const withFeature = appendDistinguishingFeatureSection(
existing,
+ stat.distinguishing_feature,
+ );
+ stat.tooltip = appendRollTargetTooltipSection(
+ withFeature,
actor,
"system.rollTarget.statistics",
base,
diff --git a/scss/_utilities.scss b/scss/_utilities.scss
index e6528a2..bede06a 100644
--- a/scss/_utilities.scss
+++ b/scss/_utilities.scss
@@ -69,7 +69,9 @@
}
button.rollable.inline-roll {
- display: inline;
+ display: inline-block;
+ width: max-content;
+ max-width: 100%;
margin: 0;
padding: 0;
border: none;
@@ -78,6 +80,8 @@ button.rollable.inline-roll {
color: var(--dg-ink);
line-height: inherit;
vertical-align: baseline;
+ text-align: inherit;
+ box-sizing: border-box;
> i {
color: inherit;
diff --git a/scss/deltagreen/_agent-sheet.scss b/scss/deltagreen/_agent-sheet.scss
index 6f7382f..ba2a69f 100644
--- a/scss/deltagreen/_agent-sheet.scss
+++ b/scss/deltagreen/_agent-sheet.scss
@@ -1,6 +1,7 @@
.agent-sheet {
--dg-agent-section-gap: 8px;
--dg-agent-list-row-border: 1px solid rgba(33, 33, 33, 0.25);
+ --weapons-col-damage-trail: 60px;
.window-content {
display: grid;
@@ -794,10 +795,42 @@
.tab.combat .items-list .item:not(.item-header) {
display: grid;
grid-template-columns:
- 24px minmax(0, 1fr) minmax(4rem, auto) minmax(3rem, auto)
+ 24px minmax(0, 1fr)
+ minmax(0, calc(4.25rem + var(--weapons-col-damage-trail)))
+ minmax(0, 3.5rem)
86px;
align-items: center;
- column-gap: 5px;
+ column-gap: 10px;
+ }
+
+ .tab.combat .items-list .weapons-col-damage,
+ .tab.combat .items-list .weapons-col-armor-piercing {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ min-width: 0;
+ text-align: center;
+ white-space: normal;
+ line-height: 1.2;
+ overflow-wrap: break-word;
+ box-sizing: border-box;
+ }
+
+ .tab.combat .items-list .weapons-col-damage {
+ padding: 0 calc(2px + var(--weapons-col-damage-trail)) 0 2px;
+ }
+
+ .tab.combat .items-list .weapons-col-armor-piercing {
+ padding: 0 2px;
+ }
+
+ .tab.combat .items-list .weapons-col-damage[data-action="roll"],
+ .tab.gear .items-list .weapons-col-damage[data-action="roll"] {
+ cursor: pointer;
+ flex-wrap: nowrap;
+ white-space: nowrap;
+ overflow-wrap: normal;
+ gap: 0.15em;
}
.tab.combat .items-list .item:not(.item-header) > .flexrow {
@@ -840,8 +873,35 @@
margin-top: var(--dg-agent-section-gap);
}
- .tab.gear .items-list .centered-item-property {
- flex: 0 0 auto;
+ .tab.gear .items-list .weapons-col-damage {
+ flex: 0 0 calc(4.25rem + var(--weapons-col-damage-trail));
+ max-width: calc(4.25rem + var(--weapons-col-damage-trail));
+ }
+
+ .tab.gear .items-list .weapons-col-armor-piercing {
+ flex: 0 0 3.5rem;
+ max-width: 3.5rem;
+ }
+
+ .tab.gear .items-list .weapons-col-damage,
+ .tab.gear .items-list .weapons-col-armor-piercing {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ min-width: 0;
+ text-align: center;
+ white-space: normal;
+ line-height: 1.2;
+ overflow-wrap: break-word;
+ box-sizing: border-box;
+ }
+
+ .tab.gear .items-list .weapons-col-damage {
+ padding: 0 calc(2px + var(--weapons-col-damage-trail)) 0 2px;
+ }
+
+ .tab.gear .items-list .weapons-col-armor-piercing {
+ padding: 0 2px;
}
/* Personal tab */
@@ -945,9 +1005,21 @@
border-radius: 2.5px;
background-color: rgba(0, 0, 0, 0.185);
font-weight: bold;
+ color: var(--dg-ink);
+
+ &:hover,
+ &:focus-visible {
+ color: var(--color-text-dark-header, #406539);
+ text-decoration: underline;
+ }
+
+ &:visited {
+ color: var(--dg-ink);
+ }
i {
flex: 0 0 auto;
+ color: inherit;
}
span {
diff --git a/scss/deltagreen/_character-sheet-cont.scss b/scss/deltagreen/_character-sheet-cont.scss
index b71b850..0b8ab3b 100644
--- a/scss/deltagreen/_character-sheet-cont.scss
+++ b/scss/deltagreen/_character-sheet-cont.scss
@@ -191,16 +191,42 @@ div .exhausted-section {
margin-bottom: 1rem;
}
-.tab.about .dg-about-links a {
+.deltagreen .tab.about .dg-about-links a {
display: inline-flex;
align-items: center;
gap: 0.5rem;
width: fit-content;
max-width: 100%;
+ color: var(--dg-ink);
+
+ &:hover,
+ &:focus-visible {
+ color: var(--color-text-dark-header, #406539);
+ text-decoration: underline;
+ }
+
+ &:visited {
+ color: var(--dg-ink);
+ }
}
-.tab.about .dg-about-links a i {
+.deltagreen .tab.about .dg-about-links a i {
flex: 0 0 auto;
+ color: inherit;
+}
+
+.deltagreen .tab.about .dg-about-legal a {
+ color: var(--dg-ink);
+
+ &:hover,
+ &:focus-visible {
+ color: var(--color-text-dark-header, #406539);
+ text-decoration: underline;
+ }
+
+ &:visited {
+ color: var(--dg-ink);
+ }
}
.tab.about .dg-about-links a span {
diff --git a/scss/deltagreen/_character-sheet.scss b/scss/deltagreen/_character-sheet.scss
index 0f4f4f4..e764476 100644
--- a/scss/deltagreen/_character-sheet.scss
+++ b/scss/deltagreen/_character-sheet.scss
@@ -205,6 +205,23 @@
}
}
+.deltagreen[id^="dg-settings"] section.window-content {
+ color: var(--color-text-primary);
+
+ .form-group > label {
+ color: var(--color-form-label);
+ }
+
+ .hint {
+ color: var(--color-form-hint);
+ }
+
+ input[type="checkbox"]:checked::before,
+ input[type="radio"]:checked::before {
+ color: var(--color-text-primary);
+ }
+}
+
a.btn-tiny {
height: 26px;
margin-left: 5px;
diff --git a/scss/deltagreen/_stats-edit.scss b/scss/deltagreen/_stats-edit.scss
index 545154a..86f7542 100644
--- a/scss/deltagreen/_stats-edit.scss
+++ b/scss/deltagreen/_stats-edit.scss
@@ -5,8 +5,8 @@
/* Edit icon */
.stats-edit-btn {
position: absolute;
- top: -10px;
- right: 0;
+ top: 50%;
+ right: -12px;
width: 24px;
height: 24px;
@@ -26,12 +26,13 @@
transition:
opacity 0.15s ease,
transform 0.15s ease;
+ transform: translateY(-50%) scale(1);
}
/* Show on hover of whole stats block */
.stats-wrapper:hover .stats-edit-btn {
opacity: 1;
- transform: scale(1.05);
+ transform: translateY(-50%) scale(1.05);
}
/* Hover feedback */
@@ -39,12 +40,6 @@
background: var(--color-border-highlight, #c9a227);
}
-.edit-stat-form {
- label {
- color: var(--color-text-primary);
- }
-}
-
.physical-stats-outer-grid-2col {
display: grid;
grid-template-columns: 1fr 1fr;
diff --git a/scss/dg-dialog/_dialogs.scss b/scss/dg-dialog/_dialogs.scss
index 966829b..2c4b768 100644
--- a/scss/dg-dialog/_dialogs.scss
+++ b/scss/dg-dialog/_dialogs.scss
@@ -814,9 +814,15 @@
line-height: 1.4;
}
-.dg-dialog--damaged-veterans label.checkbox.damaged-veterans-option > input[type="radio"],
-.dg-dialog--things-disorder label.checkbox.things-disorder-option > input[type="radio"],
-.dg-dialog--hard-experience label.checkbox.hard-experience-bond-option > input[type="radio"] {
+.dg-dialog--damaged-veterans
+ label.checkbox.damaged-veterans-option
+ > input[type="radio"],
+.dg-dialog--things-disorder
+ label.checkbox.things-disorder-option
+ > input[type="radio"],
+.dg-dialog--hard-experience
+ label.checkbox.hard-experience-bond-option
+ > input[type="radio"] {
margin-top: 0.15rem;
}
@@ -847,9 +853,7 @@
overflow: hidden;
}
-.dg-dialog-app.dg-dialog-app--hard-experience
- .dialog-form
- > .dialog-content {
+.dg-dialog-app.dg-dialog-app--hard-experience .dialog-form > .dialog-content {
display: flex;
flex-direction: column;
flex: 1 1 auto;
@@ -881,3 +885,46 @@
padding: 0;
border: 0;
}
+
+/* Edit statistics */
+.dg-dialog--edit-stats .edit-stats-grid {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 0.75rem 0.5rem;
+}
+
+.dg-dialog--edit-stats .edit-stats-stat-block {
+ display: flex;
+ flex-direction: column;
+ gap: 0.35rem;
+ min-width: 0;
+}
+
+.dg-dialog--edit-stats .edit-stats-value-row {
+ display: flex;
+ flex-direction: column;
+ gap: 0.25rem;
+ align-items: stretch;
+}
+
+.dg-dialog--edit-stats .edit-stats-value-row input {
+ width: 100%;
+ text-align: center;
+}
+
+.dg-dialog--edit-stats .edit-stats-feature-row {
+ display: flex;
+ flex-direction: column;
+ gap: 0.25rem;
+}
+
+.dg-dialog--edit-stats .edit-stats-feature-row textarea {
+ width: 100%;
+ min-height: 2.5rem;
+ resize: vertical;
+}
+
+/* Damage or lethality roll choice */
+.dg-dialog--damage-or-lethality .damage-or-lethality-weapon {
+ margin: 0.5rem 0 0;
+}
diff --git a/templates/actor/edit-stats.html b/templates/actor/edit-stats.html
index ee4ccef..19757fc 100644
--- a/templates/actor/edit-stats.html
+++ b/templates/actor/edit-stats.html
@@ -1,26 +1,26 @@
-