diff --git a/make_post_sell/static/css/common.css b/make_post_sell/static/css/common.css
index 33123d7..84a2c80 100644
--- a/make_post_sell/static/css/common.css
+++ b/make_post_sell/static/css/common.css
@@ -1430,6 +1430,39 @@ a.tag-chip-active:hover {
background: var(--dark-button-bg-hover, #3a4658);
}
+/* Sort dropdown — shown on tag detail and filtered shop home above
+ the grid. Right-aligned on a single row at desktop, full width on
+ mobile (label above select). Grid only — no flexbox. */
+form.serp-sort-form {
+ display: grid;
+ grid-template-columns: auto auto auto;
+ justify-content: end;
+ align-items: center;
+ gap: var(--space-2, 8px);
+ margin: 0 0 var(--space-3, 12px) 0;
+ padding: 0 var(--space-1, 4px);
+}
+form.serp-sort-form .serp-sort-label {
+ color: var(--text-muted, #666);
+ font-size: var(--type-body-sm-size, 0.875rem);
+}
+form.serp-sort-form select {
+ padding: var(--space-1, 4px) var(--space-2, 8px);
+ border-radius: var(--radius-sm, 4px);
+ border: 1px solid var(--border-color, #dee2e6);
+ background: var(--surface-color, #fff);
+ color: var(--text-color, #222);
+ font-size: var(--type-body-sm-size, 0.875rem);
+}
+@media (max-width: 600px) {
+ /* On narrow screens, label sits above the select instead of
+ crowding the row. Still right-aligned. */
+ form.serp-sort-form {
+ grid-template-columns: 1fr;
+ justify-items: end;
+ }
+}
+
/* Sectioned-lanes layout — one lane per top tag. Netflix-style
horizontal row with scroll-snap. */
section.tag-lane {
diff --git a/make_post_sell/templates/home.j2 b/make_post_sell/templates/home.j2
index fdc34a2..201a2c4 100644
--- a/make_post_sell/templates/home.j2
+++ b/make_post_sell/templates/home.j2
@@ -129,6 +129,22 @@
when no tag filter is active. #}
{% set grid_products = filtered_products if filtered_products is defined and filtered_products is not none else products %}
{% if grid_products %}
+ {% if active_tag and sort_options is defined %}
+ {# Sort dropdown — only shown on filtered views (when a tag is
+ active). Lane home doesn't show this; lanes have a fixed editorial
+ order. Capability-driven: plain GET form works without JS,
+ auto-submits on change with JS. The ?tag= param is preserved. #}
+
+ {% endif %}
{% for product in grid_products %}
diff --git a/make_post_sell/templates/shop_tag.j2 b/make_post_sell/templates/shop_tag.j2
index 4132228..61195d4 100644
--- a/make_post_sell/templates/shop_tag.j2
+++ b/make_post_sell/templates/shop_tag.j2
@@ -22,6 +22,18 @@
{% endif %}
+{# Sort dropdown — capability-driven: works as a plain GET form
+ without JS, auto-submits on change with JS. #}
+
+
{% for product in products %}
{% if product.is_ready %}
diff --git a/make_post_sell/views/shop.py b/make_post_sell/views/shop.py
index 4abec1a..51fc472 100644
--- a/make_post_sell/views/shop.py
+++ b/make_post_sell/views/shop.py
@@ -217,6 +217,86 @@ def get_shop_from_matchdict(request, prefetched_shop=None):
return get_shop_by_id(request.dbsession, request.matchdict["shop_id"])
+# MPS-24: sort options surfaced as a