style: mobile — hoist buy CTA above the title, split product-right

The right column was a single .product-right that wrapped the buy zone
(price + Add to Cart + preview + auction/offer) AND the related content
(comments link, price history, watch queue, related products) in one
node. Hoisting it to top of the mobile stack would put the related grid
above the title — too much chrome before the title even appears.

Split into two sibling sections, both still carrying .product-right
so existing button/cinema/centering rules apply unchanged:
  - section.product-purchase — buy zone
  - section.product-related  — comments link, price history, watch
                                queue, related products

Mobile (≤800px) order:
  0 product-purchase   ← above the title
  1 product-images     (title + cover + thumbnails)
  2 product-description
  3 product-comments
  4 product-related    (no longer crowding the title)

Desktop (≥960px) grid-template-areas:
  "images       purchase"
  "description  related"
  "comments     related"

So purchase sits at the top of the right column (where the price has
always lived) and related spans the two rows below — the page hierarchy
stays identical to before on desktop, but mobile finally gets the CTA
to the top of fold.

Cinema-mode grid + watch-mode mobile/desktop rules updated to address
both new grid areas. CLAUDE.md Mobile Layout section rewritten.

Product/watch/cinema test slice (37 tests) still green.
This commit is contained in:
russell@unturf.com 2026-05-15 11:53:03 -04:00
parent 81c051e3fc
commit 8b25285647
No known key found for this signature in database
3 changed files with 82 additions and 49 deletions

View file

@ -389,15 +389,28 @@ normal redirect flow on any error.
## Mobile Layout
On mobile (`max-width: 800px`), our product page reorders to single column
with the **buy CTA promoted up** so a tablet/phone buyer doesn't have to
scroll past the description and comment thread to find Add to Cart.
Desktop two-column already keeps `product-right` visible as a side panel;
this mobile rule mirrors that intent:
with the **buy CTA promoted ABOVE the title** so a phone buyer sees price
+ Add to Cart immediately, before scrolling. The old right-column section
was split into two siblings (`.product-purchase` + `.product-related`,
both also carry `.product-right` so legacy styling still applies). Order:
1. `product-images` (order 1) — sticky in watch mode
2. `product-right` (order 2) — price, Add to Cart, Preview, related content
3. `product-description` (order 3)
4. `product-comments` (order 4)
1. `product-purchase` (order 0) — price, Add to Cart, preview, auction/offer
2. `product-images` (order 1) — title, cover image, thumbnails; sticky in watch mode
3. `product-description` (order 2)
4. `product-comments` (order 3)
5. `product-related` (order 4) — comments link, price history, watch queue, related products
Desktop (`@media (min-width: 960px)`) uses `grid-template-areas`:
```
"images purchase"
"description related"
"comments related"
```
So `product-purchase` sits at the top of the right column next to the
image (where the price has always lived) and `product-related` spans the
rows below.
Cinema mode is a no-op on mobile (gated on `@media (min-width: 800px)`).
The normal watch-mode mobile stack already gives full-viewport-width media

View file

@ -2634,13 +2634,21 @@ textarea.markup-editor-textarea {
padding-right: 4px;
}
/* Section order on tablet + phone: images first, then product-right
(price, Add to Cart, Preview, Up Next) so the buy CTA is at the
top not buried below description + comments. Desktop two-column
keeps product-right as a side panel where the CTA is always
visible alongside the image; mobile lifts it up so a buyer doesn't
have to scroll past the entire description and comment thread to
find the button. */
/* Section order on tablet + phone: the buy zone (price + Add To
Cart + preview / auction / offer buttons) is hoisted ABOVE the
title that title lives inside .product-images, so order:0 on
.product-purchase lifts the well above the entire image block.
Related content (comments link, price history, related products)
falls to the bottom so it doesn't push the title off-screen. */
section.product-purchase {
order: 0;
width: 100%;
}
section.product-purchase .well {
margin-top: var(--space-2, 8px);
margin-bottom: var(--space-3, 12px);
}
.product-images {
order: 1;
max-width: 100%;
@ -2655,25 +2663,17 @@ textarea.markup-editor-textarea {
height: auto;
}
section.product-right {
order: 2;
width: 100%;
}
section.product-right .well {
/* Tighten the top margin on mobile the well now sits directly
under the product image instead of below description + comments,
so it doesn't need the 30px gap that separated it from the
comment thread in the old order. */
margin-top: var(--space-3, 12px);
}
.product-description {
order: 3;
order: 2;
}
.product-comments {
order: 3;
}
section.product-related {
order: 4;
width: 100%;
}
/* Watch mode mobile: dissolve watch-left wrapper so sticky video sticks
@ -2691,15 +2691,15 @@ textarea.markup-editor-textarea {
.watch-mode .product-description,
.watch-mode .product-comments,
.watch-mode section.product-right {
.watch-mode section.product-purchase,
.watch-mode section.product-related {
position: relative;
z-index: 5;
background: var(--bg-primary, #ffffff);
}
.watch-mode section.product-right {
order: 2;
}
.watch-mode section.product-purchase { order: 0; }
.watch-mode section.product-related { order: 4; }
/* On mobile, show only 7 next items in related content */
.related-content-row.related-content-overflow {
@ -2766,7 +2766,8 @@ textarea.markup-editor-textarea {
grid-template-columns: 2fr 1fr;
grid-template-areas:
"images images"
"content purchase";
"content purchase"
"content related";
gap: 24px;
max-width: 100%;
padding-left: 20px;
@ -2788,7 +2789,8 @@ textarea.markup-editor-textarea {
}
section.two-column.cinema-mode.cinema-wide .product-images { grid-area: images; }
section.two-column.cinema-mode.cinema-wide section.product-right { grid-area: purchase; align-self: start; }
section.two-column.cinema-mode.cinema-wide section.product-purchase { grid-area: purchase; align-self: start; }
section.two-column.cinema-mode.cinema-wide section.product-related { grid-area: related; align-self: start; }
section.two-column.cinema-mode.cinema-wide .watch-video-container {
max-height: 92vh;
@ -2887,9 +2889,9 @@ textarea.markup-editor-textarea {
display: grid;
grid-template-columns: minmax(0, 2fr) minmax(280px, 1fr);
grid-template-areas:
"images purchase"
"description purchase"
"comments purchase";
"images purchase"
"description related"
"comments related";
gap: 40px;
max-width: 1200px;
margin-left: auto;
@ -2898,27 +2900,34 @@ textarea.markup-editor-textarea {
padding-right: 40px;
align-items: start;
}
.product-images {
grid-area: images;
}
section.product-right {
section.product-purchase {
grid-area: purchase;
}
section.product-related {
grid-area: related;
}
.product-description {
grid-area: description;
}
.product-comments {
grid-area: comments;
}
/* Watch mode: two-column layout with left wrapper for sticky video */
/* Watch mode: two-column layout with left wrapper for sticky video.
Right column stacks purchase (top) and related (below). */
section.two-column.watch-mode {
grid-template-columns: 2fr 1fr;
grid-template-areas: "left purchase";
grid-template-areas:
"left purchase"
"left related";
align-items: stretch;
}
@ -2942,8 +2951,8 @@ textarea.markup-editor-textarea {
background: var(--bg-primary, #ffffff);
}
.watch-mode section.product-right {
grid-area: purchase;
.watch-mode section.product-purchase,
.watch-mode section.product-related {
align-self: start;
}

View file

@ -186,8 +186,13 @@
{% if request.shop.watch_mode_enabled %}</div>{% endif %}
<section class="product-right">
<br/>
{# The buy zone is its own section so we can hoist it above the
title on mobile/tablet (fox: "moved close to top of fold maybe even
above the title"). On desktop it still occupies the top of the
right column via grid-template-areas. .product-right is kept as a
second class so the existing right-column styling (centering, button
widths, cinema-mode task bar, etc.) still applies to both halves. #}
<section class="product-purchase product-right">
<div class="well">
<h1>${{ '{:,.2f}'.format(product.price) }}</h1>
@ -306,6 +311,12 @@
{% endif %}
</div>
</section>
{# Related-content + comments-link + price-history sit below the buy
zone on desktop (right column) and at the bottom of the stack on
mobile, so they don't push the title off-screen. #}
<section class="product-related product-right">
{% if shop.comments_enabled %}
<a href="#comments" class="product-comments-link">Comments ({{ product.public_comment_count }})</a>