Changelog

All notable changes to this store, one entry per merged pull request.

  • feat #126

    turn the error page into a recovery page

    The 404 rendered a product-list bound to collection: all. That collection is unsorted, so the first products shown were papan bunga condolence boards — a visitor who hit a broken link was met with a funeral wreath reading *"Turut Berduka"*, in yellow and blue, on a brand positioned as premium editorial.

    That is the most off-brand surface on the site, shown to people already having a bad experience.

    @wilthung
  • fix #125

    correct delivery coverage to nationwide, not Jabodetabek

    The About page claimed delivery "across Jabodetabek" in three places, including the FAQPage JSON-LD that Google reads. Outerbloom ships nationwide across Indonesia, with same-day delivery nationwide for fresh flowers and papan bunga.

    This was factually wrong, materially understated coverage, and was live in structured data. My error from the earlier About build.

    @wilthung
  • feat #124

    publish open roles to Google Jobs via JobPosting schema

    The careers page lists six open roles that were invisible to Google Jobs — a surface candidates search directly. This adds JobPosting structured data for each.

    @wilthung
  • feat #123

    signature pieces, preserved-flower pillars, FAQ schema, corporate bridge

    Deepens the About page using the Bloom Book — the brand's primary branding asset, which was almost entirely unused on the site.

    @wilthung
  • fix #119

    add honeypot + time-trap spam guard to partner forms

    • Both the CP Lead Form and CP Contact form (corporate partnership pages) submit straight to the API with zero bot protection, and have been getting hit by automated form spam.
    • Add a hidden honeypot field (cp_hp) and a minimum-fill-time check (3s) to each form.
    • A submission tripping either guard is silently dropped without hitting the API — no company row, no notification — so bots get no signal to adapt against.
    @diyesh
  • feat #120

    add native replacement for the HubSpot embed form

    • /pages/be-our-partner currently embeds a HubSpot Free Forms widget (Nama Pemilik, Nama Perusahaan, Website/IG/Facebook, Email, No Telp (WA), Pesan) with no spam protection, and it's been getting hit hard by bot spam.
    • Add a new be-our-partner-form section replicating the same fields/copy, protected with the same honeypot + time-trap guard used on the CP Lead Form and CP Contact form (see #119).
    • Add page.be-our-partner template so it can be assigned to the page from the theme…
    @diyesh
  • fix #122

    stop mobile horizontal overflow in proof-points grid

    The About page scrolls sideways on mobile. Document width is 1520px on a 390px viewport — about 1130px of overflow.

    @wilthung
  • feat #121

    rebuild Tentang Kami as English editorial About page

    Rebuilds /pages/tentang-kami as a proper About page using native Horizon sections, replacing the empty page.about placeholder template.

    @wilthung
  • fix #118

    accept the shop's custom domain in the checkout-URL safety check

    Found via live end-to-end testing of #117 with a real Storefront API token against the production store (outerbloom1.myshopify.com, custom domain outerbloom.com) -- before the storefront_api_token setting was ever turned on in production.

    cartCreate's checkoutUrl comes back on the shop's primary/custom domain (outerbloom.com), not shop.permanent_domain (outerbloom1.myshopify.com). isSafeCheckoutUrl only allowlisted the latter, so it would have rejected every…

    @diyesh
  • feat #117

    client-side Storefront API prefill, replacing web-raku as primary

    Recreated from #116, which GitHub permanently closed when the base branch (fix/checkout-city-lookup-shadowed-by-plain-product, now merged as #115) was deleted -- GitHub refuses to reopen or retarget a PR once its base branch is gone. Same commit, same content, now based directly on main (which already includes #115).

    Adds a client-side Storefront API cartCreate as the new primary checkout-address-prefill mechanism, ahead of the existing external web-raku

    @diyesh
  • fix #115

    stop plain products from shadowing the delivery-city lookup

    Regression from #114 (merged 2026-08-10).

    decideCheckoutNavigation() in assets/checkout-fallback.js picks the cart line to read the delivery address from by taking the last item with properties.type === 'product'. Before #114, only lines that actually carried personalization/delivery data ever got type: 'product'. #114 made that tag unconditional on every add-to-cart, so a plain product (e.g. jewelry) added to the cart *after* a bouquet/board now also matches…

    @diyesh
  • feat #114

    tag every add-to-cart with properties[type]=product

    • Extends the existing PatchedFormData bridge in blocks/buy-buttons.liquid (previously only fired for papan-bunga personalized products) so every add-to-cart form submission always carries properties[type]=product.
    • Adds the same properties: { type: 'product' } to the JSON batch-add path in assets/product-form.js (used for queued variant-change adds).
    • Leaves assets/pb-card.js's companion greeting-card line item as type: 'card' untouched, since checkout-fallback.js
    @diyesh
  • feat #113

    jarring separation between role accordions

    Follow-up to #112 — the accordion shipped there still read as a soft list; direct feedback was "separation between each roles is still not there... make it more obvious and jarring."

    • Divider between roles: 1px hairline -> 3px solid ink rule (the hairline was the same weight as the internal dividers used for skill tags / nice-to-have, so it didn't register as a boundary between roles).
    • Vertical padding around each role's summary: 56/88px (was 40/56px).
    • Added a large ghost index…
    @wilthung
  • feat #112

    accordion roles, bigger titles, richer JD

    Replaces #111 (dedicated sub-page per role, closed unmerged) with an in-page accordion, per direction change after reviewing a screenshot of the sub-page approach.

    • Each role is now a native <details>/<summary> accordion (no JS, accessible by default) instead of a plain list row.
    • Title size: 18/20px -> 26/32px, plus a new department/location/type eyebrow line above it. Each role reads as its own section now, not a row in a dense list — this was the specific UX gap flagged in…
    @wilthung
  • feat #110

    homepage-matched tokens, candidate-facing JD, role anchors

    • Replaces the placeholder palette from KARIR_REDESIGN_PLAN.md with the real homepage design tokens (#212121 ink / #6b6b68 muted / #e2e4e6 hairline / #640000 accent / #fcfaf7 warm surface) and matching H2–H6 type scale, across all 5 career sections.
    • Removes the "Why Outerbloom" proof row (career-proof.liquid, deleted). It sold the brand to customers (same-day delivery, city count) — not relevant to a candidate. Flagged by @wilthung during review.
    • Improves the role JD,…
    @wilthung
  • revert #109

    pull the same-day product-card badge from live

    • Same-day badge ("Bisa hari ini", A3 / BBB2-6579) shipped to production today via PR #105, bundled inside commit d4cb86d with three unrelated fixes.
    • It needs deeper team review before being live (merchant-tag semantics, badge design/copy sign-off) — this reverts just the badge.
    • WhatsApp enquiry (D1), the customElements.define guards, and the cart display hygiene fix (B4) from that same commit are untouched.
    @wilthung
  • #108

    Karir page: editorial redesign — bespoke sections, warm palette

    • Full redesign of /pages/karir, this time as bespoke Liquid sections rather than Horizon group/text blocks — following the existing sections/corporate-partner-*.liquid pattern already in this repo.
    • Root cause fixed: config/settings_data.json sets --font-h1..h4--case: uppercase at theme level. This silently overrode the sentence-case headline shipped in #107 — the live page kept shouting no matter what copy was set. Bespoke markup is the only way to actually own text…
    @wilthung
  • #107

    Karir page: full revamp — spacious role cards, punchier copy

    • Full UI/UX revamp of /pages/karir, aimed at a more modern, premium feel and a more direct/confident brand voice per request.
    • Positions (the main change): replaced the accordion job listings with distinct bordered cards — department pill, big role title, one confident left-aligned "Apply Now" CTA that goes full-width on mobile. Each role now gets real visual separation instead of collapsed rows. Responsibilities/requirements/apply links carried over verbatim — only…
    @wilthung
  • #106

    Karir page: illustrated values icons (from FAVO career page)

    • Replaces the generic Shopify icon-in-circle badge on the /pages/karir "Our Values" cards with custom line-art illustrations (fist bump / growth chart / rice stalk), adapted from favo.id/pages/career and recolored to Outerbloom's monochrome brand black (#212121) to match the site's existing "modern & sophisticated" palette.
    • Removed the 64px black circle badge treatment in the page's custom CSS (career-styles section) — it was sized for tiny glyphs and would have clipped the…
    @wilthung
  • docs #103

    track the Jira export and Notion migration tooling

    The Outerbloom Horizon backlog — epic BBB2-6557, 46 issues — is moving to Notion so Anggia, Putri and Dona can own their own work, and the Jira issues will be deleted afterwards. These files are what make that safe to do.

    Documentation and tooling only. Nothing here is served by the theme, and it touches no Liquid, JS, or settings.

    @wilthung
  • feat #105

    WhatsApp enquiry, same-day card badge, custom-element guards, cart polish

    _Recreated from #102 — GitHub auto-closed it when #104's base branch was deleted on merge. Same branch/commits, retargeted to main._

    @diyesh
  • feat #104

    show the delivery promise on PDPs and delivery facts in the cart

    _Recreated from #101 — GitHub auto-closed it when #100's base branch was deleted on merge. Same branch/commits, retargeted to main._

    @diyesh
  • fix #99

    Safari/in-app-webview resilience + PDP/collection image loading (audit quick wins)

    The 2026-08-09 migration audit (qa-system/docs/AUDIT_HANDOFF.md) found that both purchase flows work, but they leak revenue at the edges: the checkout button dies silently on storage-restricted browsers (exactly where Instagram/WhatsApp traffic lives), and eager image loading pushes the papan bunga PDP to a 19.4s lab LCP. This PR is the small, high-confidence, independently-revertible subset — no template JSON, no settings, no app code.

    @wilthung
  • fix #100

    escape braces so a PR body quoting Liquid can't break the theme

    /changelog is broken on the live theme, and shopify theme dev refuses to serve any page at all for anyone on the team. Shopify rejects the *entire theme upload* when one Liquid file has a syntax error:

    `` Liquid syntax error (line 116): Variable '{{ request.origin | append: '/search?q={search_term_string}' was not properly terminated ``

    I hit this trying to preview an unrelated change locally — every route returned 500.

    @wilthung
  • feat #98

    add smooth floating checkout button on mobile cart page

    On the mobile cart page, a long items list pushes the checkout button below the fold with no way to reach it without scrolling all the way down.

    @diyesh
  • fix #95

    correct trust-badge typos on product and collection pages

    Two copy typos in the trust-badge strip that renders on every product and collection page, directly above the buy area:

    | Was | Now | Occurrences | |---|---|---| | PREPARED WITH CARED | PREPARED WITH CARE | 3 | | ON- TIME DELIVERY (stray space) | ON-TIME DELIVERY | 6 |

    Files: templates/product.json, templates/product.ff.json, templates/product.papan-bunga.json, templates/collection.json, templates/collection.campaign.json

    @wilthung
  • fix #97

    detect active variant via Horizon's variant:update event

    • Custom Variant block (blocks/ai_gen_block_477e93e.liquid) detected the active variant by polling input[name=\"id\"] every 400ms — a Dawn-era pattern. In this theme's Horizon web-component product form, that input only updates asynchronously after the variant:update event fires, so the block kept using the initially-loaded variant title and the Photo field never appeared for variants like "Custom logo & photo for frame".
    • Replaced the polling/DOM-reading logic with a…
    @diyesh
  • feat #94

    drive carousel via theme slideshow

    Section collection menu list (di halaman collection) sekarang memakai fitur slideshow yang sama dengan section *featured collection product* di homepage.

    Sebelumnya section ini punya carousel custom (collection-menu-carousel + overflow-scroll). Sekarang dirender lewat snippet bersama resource-list — jalur yang sama dipakai section product-list di homepage — sehingga slider mobile menjadi slideshow-component bawaan tema: snap antar-slide, panah navigasi, peek slide…

    @diyesh
  • fix #93

    stop the carousel row from claiming vertical touches

    Reported again after PR #91: the mobile "Our Selections" collection carousel still traps vertical touch scrolling — the shopper has to touch outside the carousel section to scroll the page up/down.

    @diyesh
  • fix #92

    raise BitChat launcher on ALL product pages (personalize CTA overlap)

    Follow-up dari #91. Setelah #91 di-merge, launcher chat BitChat masih menutupi tombol "PERSONALIZE THIS BOARD/BOUQUET" di product mobile.

    @diyesh
  • fix #91 @diyesh
  • fix #90

    drop featured content from the mobile menu

    The mobile menu drawer no longer renders menu-drawer__featured-content — the horizontal strip of collection/product cards that appeared at the bottom of the drawer and again inside each expanded submenu.

    One file, snippets/header-drawer.liquid, two guards.

    @diyesh
  • fix #89

    use base link colour, size read-more with body preset

    Two fixes to the Collection description section, both driven by settings the merchant already controls.

    @diyesh
  • perf #88

    serve images sized to their rendered box

    Images across the theme were downloaded at their full intrinsic size regardless of how small they render. Worst case: a 1122×1402 source in a 156×156 slot ().

    I audited every template in headless Chrome at 1440@1x and 390@2x — index, collection (+ campaign / ff-collection / filter-feed), product (+ ff / papan-bunga), cart, search, blog, article, page (+ 5 variants) and 404 — comparing each image's intrinsic size against its measured rendered box. Templates with no…

    @diyesh
  • fix #87

    stop literal Liquid samples from breaking the upload

    snippets/changelog-entries.liquid has never reached any theme. Shopify rejects the whole file on upload, so /changelog is broken on the Horizon theme right now, and shopify theme dev refuses to serve *any* page until it is fixed.

    `` Liquid syntax error (line 246): Variable '{{ request.origin | append: '/search?q={search_term_string}' was not properly terminated with regexp: /\}\}/ ``

    Two changelog entries quote Liquid source as prose, and Liquid parses it rather than…

    @diyesh
  • fix #86

    give ARIA labels a role that permits them, and size the policy trigger

    Empat kegagalan accessibility dari audit Lighthouse theme ini (2026-07-31, 6 laporan: home/collection/product × mobile/desktop). Semuanya kasus di mana markup-nya sudah berniat benar tapi atributnya dibuang browser.

    @diyesh
  • fix #85

    match <ul> tags that carry attributes

    Block Description point menampilkan "Deskripsi produk tidak memiliki format list (ul)" pada produk yang jelas punya bullet list.

    @diyesh
  • perf #84

    drop Sentry tracing bundle and keep the hero video off the LCP path

    Dua perbaikan performa dari hasil Lighthouse 2026-07-31 (6 laporan: home/collection/product x mobile/desktop — mobile 32–33, desktop 48–64). Keduanya biaya yang kita timbulkan sendiri dan bisa diperbaiki di theme.

    @diyesh
  • feat #83

    send submissions to the dashboard and Google Chat

    [sections/corporate-partner-lead-form.liquid](sections/corporate-partner-lead-form.liquid)

    Sebelumnya submit form lead korporat hanya membuka WhatsApp — datanya tidak tersimpan di mana pun dan tidak ada notifikasi, jadi pengunjung yang menutup tab WhatsApp tidak meninggalkan jejak apa pun. Sekarang form ikut mengirim ke endpoint yang sama dengan form registrasi CP Contact:

    POST /api/companies?source=web → baris masuk ke sheet Company, dan source=web membuat web-raku…

    @diyesh
  • feat #82

    make every form field switchable in the editor

    [sections/corporate-partner-contact.liquid](sections/corporate-partner-contact.liquid) + [assets/corporate-partner-base.css](assets/corporate-partner-base.css)

    Form registrasi korporat sebelumnya terkunci di 18 field. Sekarang tiap field punya toggle sendiri di theme editor:

    • Header schema baru "Fields" dengan 18 checkbox, urutannya mengikuti urutan field di form, semua default true.
    • Field yang dimatikan dihapus dari DOM, bukan disembunyikan lewat CSS. Ini yang penting:…
    @diyesh
  • feat #81

    let each logo block use an uploaded image

    [sections/corporate-partner-proof.liquid](sections/corporate-partner-proof.liquid) — block "Logo / Wordmark" sekarang bisa pakai logo asli, bukan cuma teks.

    • Setting baru per block: Logo (image picker) dan Logo max height (12–96px, default 28px) — pola yang sama dengan block company di CP Trusted Companies.
    • Kalau logo diisi → yang dirender gambar, teks wordmark/chip diabaikan sepenuhnya (sesuai permintaan: "text nya akan berganti ke image yang di upload").
    • Field Text tetap berg…
    @diyesh
  • feat #80

    add vendor hero as the first page section

    Port blok HERO dari desain corporate landing (corporate-partnership.html) menjadi satu section mandiri, lalu dipasang di page.corporate-partner sebagai section pertama, di atas hero gifting yang sekarang.

    • sections/corporate-partner-hero-vendor.liquid (baru) — nama di editor CP Hero Vendor (nama "CP Hero" sudah dipakai section hero lama berbahasa Inggris yang statusnya disabled di page ini; section itu tidak disentuh).
    • Badge, H1 dengan bagian <em> terpisah (heading +…
    @diyesh
  • feat #79

    give each Occasion block its own product filter and card list

    Occasion di block Card kembali jadi child block, bukan dari menu linklist lagi. Setting Occasion menu (link_list) dan loop-nya dihapus dari blocks/card.liquid.

    Linklist (#74) memang bikin occasion admin-editable, tapi cuma bisa menentukan *chip mana, urutan apa*. Kartu tiap occasion selalu diambil dari satu aturan hardcoded: produk di koleksi greeting-card-all yang ber-tag occasions-<name>. Tidak ada cara memilih kartu per occasion, dan tidak ada cara menampilkan set…

    @diyesh
  • fix #78

    size the hero by its media, capped at the screen

    Hero homepage-nya sekarang mengambil tinggi dari media yang di-upload, bukan dari setting Height, dengan cap setinggi layar.

    @diyesh
  • fix #77

    show mobile hero video at source aspect ratio

    Video di section Hero homepage terlihat ter-zoom di layar mobile, walaupun source video sudah 4:5.

    @diyesh
  • fix #76

    single "Custom..." prefix, and stop wrapping content_for in a condition

    Two fixes. The second is the answer to "the Occasion menu setting still isn't there".

    @diyesh
  • fix #75 @diyesh
  • feat #74

    drive occasion chips and message prefixes from a link list

    The Card block's occasions were child blocks — one _occasion block per occasion, each with a hand-typed name. Adding or reordering an occasion meant editing the block tree on every product template. And the PREFIX dropdown was a hardcoded list of three, shared by every occasion, so a Sympathy card and a Birthday card offered the same openings.

    Occasions now come from a link list, the same way the raku-app personalized-ob extension already does it, and each menu item's…

    @diyesh
  • revert #73

    Additional information block dynamic name

    Membatalkan task 4 dari PR #72 (yang sudah ter-merge). Nama schema block "Additional information" dikembalikan ke nilai statis "Additional information", menghapus dynamic name "{{ filter_values }}".

    Hanya menyentuh blocks/ai_gen_block_b3e6234.liquid (1 baris). Perubahan lain dari #72 (harga Estimated total 20px, z-index drawer, Card media-gate) tidak terpengaruh.

    @diyesh
  • feat #72

    card media gate + cart/drawer/block-name tweaks

    Empat perubahan kecil sesuai permintaan:

    • Harga Estimated total (cart).cart-totals__total-value di snippets/cart-summary.liquid sekarang font-size: 20px (desktop & mobile), sebelumnya pakai token --cart-font-size--xl / --font-size--lg.
    • Drawer filter di collection page.filter-collection__drawer (sections/filter-collection.liquid) z-index naik dari 99999999 supaya di atas overlay lain.
    • Block "Card" (product page) — Foto & Video — panel step 4 (Enhance Your Message /…
    @diyesh
  • fix #71

    drop the placeholder collection cards from the budget menu

    The "Shop by Budget" panel rendered two collection cards that were pure placeholder graphics. Its links point at price-bracket collections ("< 300.000", "300.000 - 500.000", …) and none of them carry an image, so every card fell back to the same stock illustration — identical tiles that told a shopper nothing, took the full width of the panel, and squeezed the bracket list beside them.

    That menu is now text-only, using the same downgrade (menu_content_type = 'text') the…

    @diyesh
  • feat #70

    hide zero-priced products and cover the remaining lists

    Builds on 3584981, which introduced product-hidden for [US] and Shipping Cost utility SKUs and applied it to seven listing loops.

    1. Third rule — a product priced 0.

    ``liquid elsif product.price == 0 assign hidden = true ``

    product.price is the cheapest variant, which is the figure the card shows, so this matches what a shopper would have seen. Confirmed against the live catalogue: several "Extra Shipping Cost" entries sit at 0.

    2. Coverage extended from 7 to 13 loops. These…

    @diyesh
  • perf #69

    load the reviews widget on product pages only

    The Stamped widget was loaded unconditionally from the layout, so every page paid ~51KB for it — cart, search, blog, 404 included — and threw Uncaught TypeError: e is not a function on startup where it had nothing to attach to.

    It is now gated on template.name == 'product', which covers every product template including product.ff.json.

    @diyesh
  • fix #68

    resolve the homepage accessibility audit failures

    A Lighthouse run of the homepage scored accessibility 83, with four axe rules failing across 51 elements. This fixes all four.

    | Rule | Elements | Impact | File | | --- | --- | --- | --- | | aria-hidden-focus | 30 | serious | assets/slideshow.js | | link-name | 9 | serious | snippets/group.liquid | | list | 3 | serious | blocks/_header-menu.liquid | | target-size | 8 | — | snippets/collection-card.liquid |

    @diyesh
  • perf #67

    download one video rendition instead of the largest

    A Lighthouse audit of the homepage showed the hero video as the single biggest payload on the page: 4.4MB, 44% of the 10.8MB total.

    The poster-first facade (#64) already deferred the sources, but on hydration every <source> was given a src, and a plain <video> takes the first playable one in document order. Shopify lists its MP4 ladder largest first, so the 1080p file was downloaded for every viewport regardless of how small the box actually was.

    Deferring the sources is what…

    @diyesh
  • fix #66

    remove black flash between the video cover and playback

    Reported via Jam: a black flash appears in the hero during the cover → video transition ("Transisi antara cover dan video ada jeda space hitam").

    @diyesh
  • feat #65

    add desktop + mobile cover uploads across all video surfaces

    Every section/block with an uploaded-video setting now exposes "Video cover (desktop)" and "Video cover (mobile)" image pickers. The cover is the poster painted before the video, with an art-directed mobile crop when set. Covers fall back to the video's preview frame, so existing content renders unchanged.

    Follows the hero poster-first work (#64) and extends the same idea — merchant-controlled covers — to the rest of the theme's video surfaces.

    @diyesh
  • perf #64

    render hero videos with a poster-first facade

    A Lighthouse audit of the homepage (mobile) scored Performance 27, with LCP 12.7s and Speed Index 15.3s. The single largest payload was a 4.4 MB hero video loaded on the critical path.

    Root cause in sections/hero.liquid: hero videos rendered with Shopify's native video_tag, which downloads the full video immediately. And with custom_mobile_media off, the mobile fallback reused the desktop video at full size — so mobile downloaded the 4.4 MB clip too.

    @diyesh
  • fix #63

    stop a literal brace from making the whole section fail to upload

    sections/header.liquid tidak pernah sampai ke theme mana pun sejak af65082 (PR #49, 17 Juli 2026). Shopify menolak file-nya saat upload:

    `` Liquid syntax error (line 315): Variable '{{ request.origin | append: '/search?q={search_term_string}' was not properly terminated with regexp: /\}\}/ ``

    @diyesh
  • style #62

    enlarge the line item title and line total on the cart page

    Perbesar dua angka di cart page: judul line item dan harga line item yang sudah dikalikan quantity.

    | Elemen | Sebelum | Sesudah | | --- | --- | --- | | .cart-items__title (judul produk) | --font-size--md (1rem) | --font-size--lg (1.125rem) | | .cart-items__price (total baris = harga satuan x qty) | --font-size--sm (0.875rem) | --font-size--md (1rem) |

    @diyesh
  • feat #61

    add a Custom body size to the typography presets

    Adds a Custom option to *Body preset* on the collection description section. Choosing it reveals a px size field, default 14px.

    @diyesh
  • fix #60

    cover the full section width and defeat pasted text colours

    Fixes the SEO copy below the footer on collection pages: a white strip down the left of the section, and body text that was almost invisible against the dark scheme. Two unrelated causes.

    @diyesh
  • style #59

    square off the pagination corners

    Removes the rounded corners from pagination — normal, active and hover states all square.

    @diyesh
  • refactor #58

    move the Money Bouquet denom into the variant picker

    Follow-up to #57. The denomination stepper shipped as its own block that had to be placed by hand — so after #57 merged, nothing appeared on the storefront until someone added it.

    It belongs with the variant picker: the sheet count comes from the selected variant, so the two are one control. A separate block could be positioned away from the picker, or left out entirely.

    @diyesh
  • feat #57

    add Money Bouquet denomination block

    Adds a Money Bouquet denomination stepper: on products tagged Money Bouquet, the shopper splits the selected variant's lembar (sheet) count between Rp 50.000 and Rp 100.000 notes, with a running total and the payment-transfer note.

    Brings the workflow from raku-app's personalize-bouquet block to Horizon, matching the snippet of the same name shipping in web-outerbloom-shopify.

    @diyesh
  • style #56

    square corners on bordered elements (remove border-radius)

    Menghilangkan sudut membulat pada elemen ber-border: setiap rule CSS yang mendeklarasikan border terlihat (style solid/dashed/dll) kini border-radius-nya 0.

    Elemen lingkaran/pil/spinner (50%, 999px, 9999px, inherit, calc()) dipertahankan agar spinner & tombol bulat tetap normal.

    @diyesh
  • feat #55

    discount percentage & stacked layout on product page

    Menyeragamkan tampilan harga di product page agar sama seperti product card / product list:

    • Compare price di atas, dengan persentase diskon (xx%) di sebelahnya
    • Regular (sale) price di bawahnya, tersusun vertikal (stacked)

    Sebelumnya product page memakai layout inline tanpa persentase diskon, sedangkan product card sudah stacked + persentase.

    @diyesh
  • style #54

    use regular weight for compare price & discount percentage

    Memastikan compare price (harga coret) dan persentase diskon selalu tampil dengan bobot regular (tidak bold).

    Sebelumnya kedua elemen tidak menyetel font-weight sendiri, sehingga mewarisi bobot bold dari harga/heading induknya. Sekarang keduanya dipin ke bobot body regular theme (--font-paragraph--weight).

    @diyesh
  • fix #53

    rework the mobile footer grid and tighten its gap

    Reworks the mobile (<750px) footer layout in sections/footer.liquid and drops the mobile gap to 10px.

    Before, the two-column mobile footer put INFO OUTERBLOOM and BANTUAN stacked in column 1 and gave the entire B2B group column 2 (spanning both rows). Now:

    `` INFO OUTERBLOOM | BANTUAN B2B PROGRAMS | LAYANAN CUSTOMERS JOIN OUR EMAIL LIST (full width) ``

    How it works:

    • Standalone blocks (the two menus) are placed by grid auto-flow, one per column.
    • A group block is a single grid…
    @diyesh
  • fix #52

    keep mega menu open when moving within the active item

    Hovering a header nav item sometimes failed to show the mega menu — the panel flickered open then immediately closed, and it was especially reproducible when approaching a menu item from below.

    Reported via Jam:

    • https://jam.dev/c/c7d7c470-f438-4ed8-a8e0-3d2aca754e12 ("kadang-kadang tidak keluar")
    • https://jam.dev/c/ff82d8d5-d5fc-4944-bd12-e5cfb58412d1 ("tidak muncul apabila cursor dari bawah")
    @diyesh
  • feat #51

    Card & Message block with Occasion child block

    Adds the native Card & Message theme block (blocks/card.liquid) to the product page — a single-step personalization flow ported from the raku-app personalize-bouquet app-embed, plus its Occasion child block, supporting snippets, and behaviour JS. All files are new (nothing pre-existed on main).

    @diyesh
  • feat #50

    collection, search & 404 title rules

    Follow-up to #49. Extends the <title> logic in snippets/meta-tags.liquid to collection, search and 404 pages, replicating the live store's buy-intent copy while fixing bugs the live theme's version carried. Only the <title> block changes; product and every other page type render identically.

    @diyesh
  • feat #49

    meta description fallback + BreadcrumbList schema

    Two additive SEO gaps found while auditing the theme's SEO surface. Both are new signals for Google — nothing existing is removed.

    @diyesh
  • docs #48

    stop before committing unless asked

    Adds a "stop before committing" rule to this repo's agent guide.

    Agents have been finishing a task by committing, pushing and opening a PR unasked. That removes the one cheap checkpoint the reviewer has: reading the diff while it is still a working-tree change. It matters most on the theme repos, where a merge to main deploys straight to the live storefront — an unreviewed commit is effectively an unreviewed deploy.

    The rule:

    > Finish the edits, run the verification, report…

    @diyesh
  • feat #47 @diyesh
  • feat #46

    typography preset settings

    The Collection description section (the SEO copy rendered below the footer on collection pages) could only override heading sizes in px. There was no way to point it at one of the theme's typography presets, so its body copy always inherited the global type and headings could be resized but not restyled.

    Adds a Typography group to the section schema with two selects:

    | Setting | Applies to | Options | | --- | --- | --- | | Body preset | p, li, blockquote, dd, dt,…

    @diyesh
  • perf #45

    load components on demand instead of on every page

    snippets/scripts.liquid loaded 27 JS modules unconditionally on every template — ~250KB raw / ~58KB gzip. Product-only components (product-form, variant-picker, media-gallery, volume-pricing, price-per-item, quantity-selector, product-inventory, product-sku, …) were downloaded and parsed on the homepage, blog, search and policy pages, and slideshow.js + layered-slideshow.js (46KB together) shipped everywhere whether or not a slideshow existed.

    Every one of those modules does…

    @diyesh
  • feat #44

    image upload, per-section padding, sticky offer, width & corner-radius controls

    Merchant-facing controls for the corporate-partner page, added iteratively. All new settings default to the current appearance — nothing changes on the live page until a merchant edits a value. Touches sections/corporate-partner-*.liquid only; no editor-owned JSON, routes, or shared assets.

    @diyesh
  • feat #43

    collection description below footer + cart, footer, filter, pagination, drawer UI refinements

    Six requested storefront UI refinements, one commit per concern.

    @diyesh
  • feat moment

    recipient share experience — share card, brand type, HEIC

    The Moment recipient experience (moment.outerbloom.com — shown when the recipient scans the flower card) shipped several updates: a shareable Moment card (share your photo + the sender’s words as a portrait image to Instagram, X and Threads, Spotify-Wrapped style); Save as an image (fixed on mobile); on-brand design with a bigger photo and Outerbloom’s Source Sans 3 type; iPhone HEIC photos; an IG/TikTok-style mute icon; and no clipping of long recipient names.

    @wilthung
  • feat #42

    add global Hover style setting for text links

    Adds a global Hover style theme setting so link hover feedback is controlled in one place instead of hardcoded per section.

    New Hover settings group (config/settings_schema.json):

    • global_hover_style: Default / Underline / Underline (animated) / Change color / Bold / Thin
    • global_hover_color: color used by *Change color* and the animated underline (falls back to the link's current color)

    A body class global-hover-style-<value> (layout/theme.liquid) drives CSS in…

    @diyesh
  • feat #41

    add 7 vendor-focused page sections

    Adds Why Vendor, Role Segmentation, Product Scope, Coverage Map, Proof, Offer, and Lead Form as self-contained sections ported from the corporate landing design, and wires them into the page.corporate-partner template after enterprise_procurement.

    Each section follows the existing corporate-partner-* convention: settings- driven CSS variables, editable blocks, and presets prefilled with the source copy. Bespoke flower/map SVGs are preserved (symbol + gradient ids namespaced…

    @diyesh
  • feat #40

    instrument the web-raku order-flow dependency

    Storefront bergantung pada web-raku (app.asmaraku.com) di dua titik kritis untuk konversi, dan keduanya gagal tanpa terlihat:

    • Checkout. routes.checkout_create_url di-POST setiap klik "Check out" (assets/component-cart-items.js). web-raku mengembalikan Storefront checkout URL dengan alamat penerima sudah terisi. Ini bukan plumbing opsional — Shopify mematikan mutation checkoutCreate pada 2025-04-01 dan Checkout Extensibility membuang param…
    @wilthung
  • docs #39

    track CLAUDE.md + require pre-push sync with origin/main

    CLAUDE.md (agent working guide for this theme repo) was untracked, so its workflow rules weren't actually part of the repo and didn't reliably reach agents/Codex. This commits it and tightens one rule.

    @diyesh
  • feat #38

    full-width heading + dedicated description section

    Collection-page template layout work, in two commits.

    1. Collection heading & menu list

    • blocks/image.liquid: on the storefront, an image block with no image (e.g. a collection missing the main_banner metafield) now renders nothing, so the sibling fill group (the title/subtitle) expands to full width instead of leaving a gap for the placeholder. The onboarding placeholder still renders in the theme editor.
    • sections/collection-menu-list.liquid: hide the section on the…
    @diyesh
  • feat #37

    set checkout_create_url so delivery prefill actually runs

    Sets Theme.routes.checkout_create_url in snippets/scripts.liquid to the web-raku delivery-prefill endpoint, so the cart's custom checkout flow (address prefill + the recipient picker from #36) actually runs.

    @diyesh
  • feat #36

    recipient picker for multi-recipient checkout prefill

    When a cart holds gifts for more than one recipient, clicking Check out now opens a modal to choose which recipient's address pre-fills the Shopify checkout, and sends that choice to the delivery-prefill create service.

    A single Shopify checkout carries one shipping address. A gift cart groups each recipient's lines by a shared id line property, with the address on that group's type: 'product' line (Alamat Penerima). Previously the service just used the first product…

    @diyesh
  • feat #35

    render collection descriptions + product-title SEO rule (P0 migration blockers)

    Closes the two P0 migration blockers from the SEO/GEO audit.

    • Collection SEO copy + internal links — Horizon rendered collection.description nowhere, dropping hand-written SEO copy and the internal backlinks embedded in it across ~1,098 collections. New snippets/collection-seo-description.liquid, rendered from main-collection.liquid after <results-list> (so the custom filter's show/hide never affects it). Because the internal links are inline <a> tags inside the…
    @wilthung
  • feat #34

    "Board Details" for Papan Bunga / Standing Flower items

    Cart line items whose product type is Papan Bunga or Standing Flower now show a Board Details box, built from their line-item properties — the board equivalent of the existing "Greeting Card Detail" box.

    | Label | Line-item property | | --- | --- | | Recipient | Papan Nama Penerima | | Message | Papan Ucapan | | Sender | Papan Nama Pengirim | | Logo | Papan Logo — rendered as an <img> only when the value contains https:// |

    @diyesh
  • feat #33

    hide "[US]" and "Shipping Cost" products from listings

    Products whose title contains [US] or Shipping Cost are utility/helper SKUs (e.g. shipping-fee add-ons). They must stay reachable by direct link, but should not appear when customers browse. This hides them from every storefront product list.

    @diyesh
  • ci #32

    auto-apply settings-change label on editor-owned JSON PRs

    PR Guard's settings-guard check fails whenever a PR touches theme-editor-owned files (config/settings_data.json, templates/*.json) until someone manually adds the settings-change label — the exact toil we just hit on #31. This adds a companion workflow that applies the label automatically.

    @diyesh
  • feat #31

    real "You may also like" product recommendations

    The cart page's "You may also like" was a *static* Featured collection (a product-list section hard-pointed at the all collection). This makes it the same dynamic product-recommendations section the product page uses — matching both its style and its settings — so cart shoppers see genuine related products instead of a fixed collection.

    @diyesh
  • feat #30

    product review ratings + WebSite/SearchAction + Organization fixes

    Fills the biggest SEO/AEO gaps found in the review (structured data was carrying no review rating despite 4.9★/774 reviews being shown).

    • Product: hand-author the Product JSON-LD so it includes aggregateRating from the reviews.rating / reviews.rating_count metafields, plus AggregateOffer (low/high price, currency, availability), brand, sku, images. Emitted only when a real rating + non-zero count exist (no fake stars). → review stars in Google + citeable by AI answer engines.
    @wilthung
  • perf #29

    stop feed pagination early on the first empty batch

    Follow-up to #27/#28. products_count can exceed storefront-visible products (drafts/hidden), so trailing 50-per-page feed pages return empty. Verified: a collection with products_count 961 has 544 visible products = 11 data pages + 9 empty. Break on the first all-empty batch to skip the wasted requests. Products paginate in order, so this can't drop mid-collection items. Theme Check clean; JS syntax checked; parity with the old products.json path confirmed (544 = 544).

    @wilthung
  • fix #28

    drop Accept header that made the Option B feed return zero products

    Follow-up to #27. The feed fetch sent Accept: application/json, which makes Shopify serve its built-in collection JSON API ({"collection":...}) instead of the ?view=filter-feed template — so the client parsed a non-array and rendered zero products.

    Removing the header fixes it. Verified on the preview theme:

    • ?view=filter-feed returns the correct product array.
    • Prices match products.json exactly (385000 / 800000 compare-at, etc.) — the divided_by: 100 assumption is…
    @wilthung
  • perf #27

    replace ~5MB products.json fetch with a compact server feed (Option B)

    Implements Option B: keeps the custom filter drawer, but swaps its data source from /products.json (~5 MB on a large collection) to a compact server-rendered JSON view.

    • New templates/collection.filter-feed.liquid (?view=filter-feed) emits only the fields the filter reads — no body_html — paginated by 50. Transfer drops to ~150–250 KB.
    • fetchAllProducts now pulls the feed with bounded concurrency (6) and one-retry-then-throw per page (fixes a latent bug where a failed…
    @wilthung
  • docs #26

    add an Open Items roadmap section

    • Adds a hand-maintained Open items section to the /changelog page, below the auto-generated ledger.
    • Grouped by area (Performance, Reliability & cart, Security & backend, Content & merchant), each item carries a status tag (in progress / planned / needs review / external / merchant).
    • Gated by a show_open_items setting (default on) with an editable heading. CSS-only, no JS.
    @wilthung
  • perf #25

    harden the collection filter data layer (Option B)

    Keeps the custom filter UI and fixes its correctness, safety, and staleness. This is Option B — it does not remove the full-collection products.json fetch (~5 MB on a large collection); that is inherent to client-side filtering and needs native facets (Option A) or a server-rendered dataset.

    • Stale price/inventory: cache now has a 10-minute TTL (key v1v2), so a price/availability change that doesn't change the product count self-heals instead of serving stale data all session.
    @wilthung
  • fix #24

    show video poster so the homepage hero is never blank

    • Fixes a blank homepage hero (major above-the-fold conversion issue): the hero background video rendered with poster: nil, so with preload="metadata" and no poster the first ~665px stayed white until the video buffered — and mobile browsers routinely defer autoplay video, so it could stay blank entirely.
    • Sets poster to each video's own preview_image for all four hero slots (desktop/mobile × slot 1/2), so the cover frame shows instantly.
    @wilthung
  • chore #23

    image dimensions on personalization blocks; SHA-pin GitHub Actions

    • Adds width/height to settings-backed personalization images (greeting-card default image, board preview images) to reduce layout shift (CLS).
    • Pins actions/checkout and actions/github-script to full commit SHAs so CI can't silently pull a re-tagged action.
    @wilthung
  • fix #22

    never fall through to /undefined; guarantee native Shopify checkout fallback

    • Fixes the P0 where clicking Check out navigated to /undefined (404) or threw TypeError: Cannot read properties of undefined (reading 'domain').
    • Native Shopify checkout is now the guaranteed fallback for every missing-service, failure, or unsafe-URL case, while the custom delivery-prefill flow is preserved when the app supplies its endpoint.
    • Escapes customer greeting-card properties (recipient, sender, message, e-card link) rendered in the cart, closing a permalink/stored XSS.
    @wilthung
  • perf #21

    fetch filter pages in parallel and cache them

    fetchAllProducts in sections/filter-collection.liquid had await inside a for loop, so it fetched every page of products.json one after another even though the pages are independent.

    Measured against the live store (fast desktop connection, so mobile is worse — each sequential page adds a full round-trip of latency):

    | Collection | Products | Pages | Before | After | | |---|---|---|---|---|---| | all | 4,012 | 17 | 5.48s | 0.47s | 11.7× | | papan-bunga | 1,321 | 6 |…

    @wilthung
  • feat #20

    rebuild the page as a scannable ledger

    The Changelog is an internal page read to answer *"what shipped, when, by whom"*, but nothing about it supported scanning: every entry was an identical card, titles carried feat(article): prefixes, and the theme's 72px <h1> pushed the list below the fold.

    • Rows are now an aligned ledgerDate / Type / PR / Change / By — sharing fixed columns with a header row. A real <table> was the obvious move, but descriptions are multi-paragraph with bullets and code and would…
    @wilthung
  • fix #18

    align grid with page width when all slides fit

    • Fixes the Jam report from the review team (jam.dev/c/67bfb2c0, "grid image diratakan dengan page"): in the homepage "Moments that Matters (IG tagged)" section, the 4-image grid stops ~48px short of the right page margin while the heading and the "See more stories" button align with it.
    • Root cause: the Horizon carousel always subtracts --peek-next-slide-size (3rem = 48px) from each slide width so the next slide peeks in — but this section has exactly as many slides (4) as…
    @diyesh
  • fix #19

    render PR markdown as HTML instead of literal text

    • The Changelog page rendered raw Markdown (## Ringkasan, - bullet, **bold**) as literal text, and cut entries mid-word. Root cause is in changelog.yml: it took only the PR body's first paragraph, HTML-escaped it verbatim, and hard-truncated at 400 characters.
    • Worst case was #15, which rendered as nothing but ## Ringkasan — its real content sat in a later paragraph that the first-paragraph rule discarded entirely.
    • New .github/scripts/changelog-markdown.js converts the PR body…
    @wilthung
  • feat #17

    add mobile banner image upload to Article (legacy)

    • Fixes the Jam report from the review team (jam.dev/c/83989377, "Banner mobile tidak ada field uploadnya"): the Banner group on the Article (legacy) section only offers a desktop banner upload, so the wide 1450x240 image renders squished on mobile.
    • Adds a Banner image (mobile) image picker to sections/main-article-legacy.liquid (Banner settings group, right under the desktop Banner image).
    • The banner now renders as a <picture>: the mobile image is served on screens narrower…
    @diyesh
  • fix #16

    make blog page typography & buttons follow Horizon theme settings

    • Fixes the Jam report from the review team (jam.dev/c/2da00587): typography and buttons on the blog page ("Inspirations") must follow the settings already configured in the Horizon theme editor instead of the hardcoded legacy pixel styles.
    • sections/main-blog-legacy.liquid: page title now uses the theme h5 type preset (was hardcoded 28px/capitalize), card titles the h6 preset (was 22px/1px letter-spacing); "Baca selengkapnya" links are now theme .button-secondary;…
    @diyesh
  • #15

    Add tag-based collection filter (ported from old theme)

    Menambahkan filter collection ke theme Horizon baru, mengikuti cara kerja filter di theme lama (Horizon AI - Old/sections/filter-collection.liquid) sebagai referensi:

    • Section baru sections/filter-collection.liquid — bar label filter horizontal (SORT, DELIVERY TO, TYPE, STYLE, OCCASION, COLOR, PRICE RANGE) + drawer kanan berisi checkbox per grup, badge filter aktif, preset harga cepat, tombol CLEAR ALL / VIEW.
    • Sumber data sama dengan theme lama: tag produk berprefix…
    @diyesh
  • feat #14

    port corporate-partner page template from the old theme

    • Adds templates/page.corporate-partner.json plus the 14 corporate-partner-* sections it references, copied verbatim from the old theme (Horizon AI - Old) including all initial settings (stats, badge, copy, colors, company logos via shopify://shop_images/..., meeting link, etc.).
    • The sections are fully self-contained: no snippet/global-setting/translation/form dependencies, so nothing else in the theme is touched.
    • Verified on theme dev: /pages/corporate-partnership (the store…
    @diyesh
  • feat #13

    match policy pages to the global page template layout

    • Shopify generates /policies/* pages itself — no JSON template or sections exist for them — so layout/theme.liquid now wraps content_for_layout (only when request.page_type == 'policy') in the same layout structure as the global page template: narrow (90rem) container, two-column grid, sticky sidebar.
    • New snippets/policy-page-sidebar.liquid renders the sidebar card with the footer menus (footer / Info Outerbloom and bantuan), mirroring templates/page.json's sidebar,…
    @diyesh
  • feat #12

    add content width setting, widen default page template

    • sections/main-page.liquid hardcoded the page-width-content class — a 42rem (~672px) reading width — so every page using the section rendered far narrower than other templates (collection, blog, etc. use --normal-page-width).
    • Adds a Content width select (Reading / Narrow / Normal / Wide) to the section schema. The default stays content (current behavior), so page.career, page.contact, and page.personalize templates are completely unaffected.
    • templates/page.json (the global…
    @diyesh
  • feat #11

    restyle blog & article pages to legacy design on OS 2.0

    • New sections/main-blog-legacy.liquid replicating the old theme's blog page (templates/blog.liquid + sections/Blog.liquid in Horizon AI - Old) as a native OS 2.0 section:
    • H1 with the legacy 160px primary-color underline accent and RSS feed link; shows the active tag (with kategori_ prefix stripped) when filtering.
    • Horizontal bordered article cards (image left / content right, 50-50, min-height 300px): date + Indonesian reading-time meta ("X menit membaca", chars/1100), 22px…
    @diyesh
  • ci #10

    add PR guard for stale branches and theme-editor settings files

    • New .github/workflows/pr-guard.yml with two advisory checks on every PR into main:
    • Branch is up to date with main — fails when the PR branch is behind main, meaning it may be missing Update from Shopify... theme-editor sync commits. Rebase or merge origin/main, then push again.
    • Theme-editor settings untouched — fails when the PR diff touches files the Shopify theme editor owns (config/settings_data.json, templates/*.json), so a stale copy of merchant settings never…
    @diyesh
  • docs #9

    backfill entry for career page restyle

    • The career page restyle (templates/page.career.json) reached main through the Shopify GitHub integration sync commit 3bcf487 instead of a pull request, so the Changelog workflow never fired for it.
    • Backfill the entry by hand in CHANGELOG.md and snippets/changelog-entries.liquid, matching the auto-generated format and linking to the sync commit (same approach as PR #4 did for #3).
    @diyesh
  • feat #8

    add heading font weight & case settings

    Footer headings previously rendered with inconsistent weights: menu block headings were forced to the heading font weight by a hardcoded CSS rule, while text block headings followed the h6 preset and were wrapped in <strong>.

    • Add "Font weight" and "Case" selects to the footer section schema (Footer > Typography), applied to all headings in the footer
    • Neutralize <strong>/<b> inside headings when a weight is selected
    • Remove the hardcoded menu heading font-weight override
    @diyesh
  • feat #7

    add collection menu list section

    • New sections/collection-menu-list.liquid: renders a linked menu of sub-collections on the collection template.
    • Link source resolution, in priority order: (1) a menu explicitly chosen in section settings, (2) a linklist whose handle matches the current collection handle, (3) the children of the matching item in a configured parent navigation menu.
    • Supports grid and carousel layouts, with placeholder cards in the theme editor when no menu matches so the section stays visible.
    @diyesh
  • fix #6

    top-align occasions grid so icons and labels line up

    • Change the "Occasions" group in templates/index.json from vertical_alignment: center to flex-start.
    • Items with two-line labels ("Sympathy & Funeral", "Grand Opening") were taller than their siblings, so center alignment pushed the other icons out of line on the wrapped mobile grid (and on desktop). Top-aligning the row keeps all icons on one line with labels starting at the same height.
    @diyesh
  • ci #5

    rely on native GitHub sync, add PR description to changelog

    Remove deploy-theme.yml — the whole-theme shopify theme push is redundant now that branch main is connected to the theme via Shopify's native GitHub integration, and running both risks double deploys / clobbering theme-editor changes.

    In changelog.yml, drop the "Deploy changelog to Shopify" step for the same reason; the changelog commit reaches the theme through native sync. Also enrich each entry with a concise description pulled from the PR body (first paragraph, HTML…

    @diyesh
  • #4

    Backfill changelog entry for PR #3

    Manually adds the changelog entry for #3, which the Changelog workflow missed (no run was created for its pull_request.closed event).

    • snippets/changelog-entries.liquid: adds the pr-3 entry at the top.
    • CHANGELOG.md: adds the - #3 — line.

    🤖 Generated with Claude Code

    @diyesh
  • #3

    Add full theme deploy workflow

    Adds .github/workflows/deploy-theme.yml that pushes the whole theme to the target Shopify theme on every push to main (and via manual dispatch).

    • Uses secrets SHOPIFY_CLI_THEME_TOKEN, SHOPIFY_STORE, SHOPIFY_THEME_ID.
    • --nodelete keeps theme-only files.
    • Changelog bot commits use [skip ci], so they don't trigger a redundant full deploy.

    ⚠️ A full push overwrites config/settings_data.json and templates/*.json on the theme with the repo versions. Add them to --ignore if…

    @diyesh
  • #2

    Bump CI actions to Node 24 runtimes

    Upgrades workflow actions to clear the Node 20 deprecation warning:

    • actions/checkout@v4@v5
    • actions/github-script@v7@v8

    Both now run on Node 24. No behavioral change to the changelog automation.

    🤖 Generated with Claude Code

    @diyesh
  • #1

    Document the changelog automation flow

    Adds docs/changelog.md explaining how merged PRs flow into the storefront Changelog page and deploy to Shopify.

    This PR also serves as an end-to-end test of the changelog automation:

    • verifies the Changelog workflow inserts an entry for this PR,
    • verifies the PR number links back to this pull request,
    • verifies the deploy step pushes the changelog files to Shopify.

    🤖 Generated with Claude Code

    @diyesh
  • style 3bcf487

    restyle career page to legacy design on Horizon color schemes

    Restyles templates/page.career.json to match the legacy career page while keeping Horizon color schemes: new career-styles custom-liquid section with scoped CSS (muted subtitles, equal-height value cards with soft shadow and round icon badges, card-style job accordions with dot bullets and divider footers, dark CTA), value cards switched from height: fit to fill, and spacing added before the first job category. Landed via the Shopify GitHub sync commit, so it has no…

    @diyesh
  • init

    Initial theme setup — homepage, header, footer, and settings synced from Horizon AI

Open items

Work identified but not yet shipped, tracked here for visibility. Not a commitment or timeline.

Performance

  • in progress Collection filter downloads the full catalogue on each filter. A compact server-rendered feed (Option B) cuts the transfer ~95% while keeping the current drawer.
  • planned Native facet filtering (Option A) would remove the client fetch entirely and add crawlable filter URLs and a no-JS fallback.

Reliability & cart

  • needs review Sticky add-to-cart uses a legacy event model — it can overlap the chat widget and double-fire on a fast tap.
  • needs review Required personalisation is not enforced before accelerated (Shop Pay) checkout.
  • needs review Cart section-render race and timestamp-based line grouping need hardening.

Security & backend

  • external Harden the upload endpoints (auth, rate-limit, private storage) for gift photos and corporate KTP/NPWP documents.
  • external Review e-card token security (entropy, expiry, no-index) on the e-card domain.
  • external Confirm what provides the custom checkout service on the published theme, and remove any dev-tunnel app embeds before go-live.

Content & merchant

  • merchant Trust-badge copy fixes ("Prepared with cared" → "care"; "On- time delivery" spacing) — edit in the theme editor.
  • merchant Condolence products default to celebratory composer copy; the default should follow product intent.
  • merchant Require an explicit delivery-city selection and show the delivery fee before checkout.