diff --git a/resources/templates/transaction-bulk-code/account-row.html b/resources/templates/transaction-bulk-code/account-row.html
index 82d468d5..f30997d3 100644
--- a/resources/templates/transaction-bulk-code/account-row.html
+++ b/resources/templates/transaction-bulk-code/account-row.html
@@ -1,27 +1,34 @@
-{# One expense-account row, read from a loop-bound `row` view-model (account-row-vm). The
- account typeahead, location select, and remove button all reuse the shared component
- partials (typeahead.html / location-select.html / a-icon-button via its ctx); only the
- table layout is inline. The location cell (#account-location-N) swaps just itself on
+{# One expense-account row from a loop-bound `row` view-model. All structure, wiring, and
+ field names are literal here, built from `row.index` + the shared `urls`; only data (the
+ Alpine x-data, db/id, errors, and the typeahead / location / money control contexts)
+ comes from the view-model. The location cell (#account-location-N) swaps just itself on
account change; the remove button swaps the whole #bulk-code-form. #}
-
-
+
+
-
-
{% with width=row.account.width x_data=row.account.x_data x_model=row.account.x_model key=row.account.key disabled=row.account.disabled a_xinit=row.account.a_xinit placeholder=row.account.placeholder hidden_attrs=row.account.hidden_attrs %}{% include "templates/components/typeahead.html" %}{% endwith %}
+
+
{% with width="" x_data=row.account.x_data x_model=row.account.x_model key=row.account.key disabled=row.account.disabled a_xinit=row.account.a_xinit placeholder=row.account.placeholder hidden_attrs=row.account.hidden_attrs %}{% include "templates/components/typeahead.html" %}{% endwith %}
{{ row.account_error }}
-
-
- {% with name=row.location.name variant=row.location.variant options=row.location.options %}{% include "templates/components/location-select.html" %}{% endwith %}
+
+
+ {% with name=row.location.name variant="w-full" options=row.location.options %}{% include "templates/components/location-select.html" %}{% endwith %}
{{ row.location_error }}
-
+
{% with variant=row.pct.variant attrs=row.pct.attrs %}{% include "templates/components/money-input.html" %}{% endwith %}
{{ row.pct_error }}
-
{% with extra=row.remove.extra attrs=row.remove.attrs body=row.remove.body %}{% include "templates/components/a-icon-button.html" %}{% endwith %}
diff --git a/resources/templates/transaction-bulk-code/body.html b/resources/templates/transaction-bulk-code/body.html
index dc5e7a79..6e16e34d 100644
--- a/resources/templates/transaction-bulk-code/body.html
+++ b/resources/templates/transaction-bulk-code/body.html
@@ -1,26 +1,32 @@
{# Bulk-code modal body: vendor typeahead (a change repopulates the default account via a
- whole-form swap), status select, and the expense-account grid. Each field inlines the
- validated-field wrapper (label + error
) and pulls its control in from the shared
- component partials; all data comes from the form view-model. #}
+ whole-form swap), status select, and the expense-account grid. All wiring, the status
+ options, and the field-wrapper classes are literal here; only data (selected values,
+ resolved labels, errors) comes from the view-model. #}
-
-
+
+
- {% with width=vendor.ta.width x_data=vendor.ta.x_data x_model=vendor.ta.x_model key=vendor.ta.key disabled=vendor.ta.disabled a_xinit=vendor.ta.a_xinit placeholder=vendor.ta.placeholder hidden_attrs=vendor.ta.hidden_attrs %}{% include "templates/components/typeahead.html" %}{% endwith %}
+ {% with width="w-96" x_data=vendor.ta.x_data x_model=vendor.ta.x_model key=vendor.ta.key disabled=vendor.ta.disabled a_xinit=vendor.ta.a_xinit placeholder=vendor.ta.placeholder hidden_attrs=vendor.ta.hidden_attrs %}{% include "templates/components/typeahead.html" %}{% endwith %}
{{ vendor.error }}
-
+
- {% with name=status.name variant=status.variant attrs=status.attrs options=status.options %}{% include "templates/components/select.html" %}{% endwith %}
+
{{ status.error }}
Expense Accounts
-
+
{% include "templates/transaction-bulk-code/account-grid.html" %}
{{ accounts.error }}
diff --git a/resources/templates/transaction-bulk-code/form.html b/resources/templates/transaction-bulk-code/form.html
index 78d1f5ee..be9a5041 100644
--- a/resources/templates/transaction-bulk-code/form.html
+++ b/resources/templates/transaction-bulk-code/form.html
@@ -2,4 +2,4 @@
every sub-template composes from it via includes/blocks. The resolved (not-locked)
transaction id set rides in hidden ids[] fields so the selection survives
form-changed / submit posts without an EDN snapshot or a filter round-trip. #}
-
+
diff --git a/src/clj/auto_ap/ssr/components/selmer.clj b/src/clj/auto_ap/ssr/components/selmer.clj
index 03bc59a0..6f985703 100644
--- a/src/clj/auto_ap/ssr/components/selmer.clj
+++ b/src/clj/auto_ap/ssr/components/selmer.clj
@@ -103,14 +103,6 @@
;; --- field wrapper ---------------------------------------------------------------
-(defn validated-field-classes
- "The wrapping-div class string for a validated field (group + optional has-error +
- caller class). Split out so a template-driven row can stamp the same classes."
- [{:keys [errors] :as params}]
- (cond-> (or (:class params) "")
- (sequential? errors) (hh/add-class "has-error")
- :always (hh/add-class "group")))
-
(defn errors-str
"Comma-join the string errors at a field (nil/empty -> empty string), matching the
validated-field error
."
diff --git a/src/clj/auto_ap/ssr/transaction/bulk_code.clj b/src/clj/auto_ap/ssr/transaction/bulk_code.clj
index e8725fd8..02326dee 100644
--- a/src/clj/auto_ap/ssr/transaction/bulk_code.clj
+++ b/src/clj/auto_ap/ssr/transaction/bulk_code.clj
@@ -113,63 +113,35 @@
;; ---------------------------------------------------------------------------
(defn- account-row-vm
- "Build the plain-data view-model for one expense-account row (rendered by
- account-row.html). Attribute maps are pre-serialized to HTML attribute strings; the
- account typeahead, location