diff --git a/app/(home)/search/[collection]/page.js b/app/(home)/search/[collection]/page.js
index 8a2f510de..b13445aae 100644
--- a/app/(home)/search/[collection]/page.js
+++ b/app/(home)/search/[collection]/page.js
@@ -1,4 +1,4 @@
-import { getCollections } from 'lib/shopify';
+import { getCollections } from 'commerce/shopify';
 
 import { HomeProductsList } from '/components/home';
 
diff --git a/app/(page)/layout.js b/app/(page)/layout.js
index c0ebc6566..760809b04 100644
--- a/app/(page)/layout.js
+++ b/app/(page)/layout.js
@@ -1,7 +1,9 @@
+import styles from './styles.module.scss';
+
 export default function PageLayout({ children }) {
     return (
         <>
-            <main>{children}</main>
+            <main className={styles.main}>{children}</main>
         </>
     );
 }
diff --git a/app/(page)/product/[handle]/page.js b/app/(page)/product/[handle]/page.js
index aa43f46b3..825f91f37 100644
--- a/app/(page)/product/[handle]/page.js
+++ b/app/(page)/product/[handle]/page.js
@@ -2,9 +2,77 @@ import Image from 'next/image';
 
 import xss from 'xss';
 
-import { getProducts, getProduct } from 'lib/shopify';
+import { getProducts, getProduct } from 'commerce/shopify';
 
+import styles from './styles.module.scss';
 import PurchaseInput from '/components/product/purchase-input.js';
+import { getTags, listTags } from '/util';
+
+//TODO: NumberInput
+
+const ImageScroll = ({ images }) => (
+    <div className={styles.imageScroll}>
+        <div className={styles.horizScroll}>
+            {images?.length > 1 && (
+                <p className={styles.scrollMessage}>Scroll to right ( → )</p>
+            )}
+            <div className={styles.imageContainer}>
+                {images?.map(image => (
+                    <Image
+                        key={image?.url}
+                        src={image?.url}
+                        alt={image?.altText}
+                        width={image?.width}
+                        height={image?.height}
+                    />
+                ))}
+                <div>
+                    <div className={styles.spacer} />
+                </div>
+            </div>
+        </div>
+    </div>
+);
+
+const ProductPane = async ({ product }) => {
+    const tags = await getTags({ product });
+
+    return (
+        <div className={styles.productPane}>
+            {product?.handle ? (
+                <div className={styles.topBottom}>
+                    <div className={styles.description}>
+                        <h1>{product?.title}</h1>
+                        {tags && tags.length > 0 && (
+                            <h2 className={styles.collections}>
+                                {listTags({ tags })}
+                            </h2>
+                        )}
+                        <div
+                            dangerouslySetInnerHTML={{
+                                __html: xss(product.descriptionHtml),
+                            }}
+                        />
+                    </div>
+                    <PurchaseInput product={product} />
+                </div>
+            ) : (
+                <p>Product not found</p>
+            )}
+        </div>
+    );
+};
+
+export default async function ProductPage({ params: { handle } }) {
+    const product = await getProduct(handle);
+
+    return (
+        <div className={styles.productPage}>
+            <ImageScroll images={product.images} />
+            <ProductPane {...{ product }} />
+        </div>
+    );
+}
 
 export async function generateStaticParams() {
     const products = await getProducts({
@@ -15,37 +83,3 @@ export async function generateStaticParams() {
 
     return products.map(product => ({ product: product.handle }));
 }
-
-//TODO: NumberInput
-
-export default async function ProductPage({ params: { handle } }) {
-    const product = await getProduct(handle);
-
-    return (
-        <>
-            {product?.handle ? (
-                <>
-                    <h1>{product?.title}</h1>
-                    <div
-                        dangerouslySetInnerHTML={{
-                            __html: xss(product.descriptionHtml),
-                        }}
-                    />
-                    <PurchaseInput product={product} />
-                </>
-            ) : (
-                <p>Product not found</p>
-            )}
-            <p>Scroll to right ( → )</p>
-            {product?.images?.map(image => (
-                <Image
-                    key={image?.url}
-                    src={image?.url}
-                    alt={image?.altText}
-                    width={image?.width}
-                    height={image?.height}
-                />
-            ))}
-        </>
-    );
-}
diff --git a/app/(page)/product/[handle]/styles.module.scss b/app/(page)/product/[handle]/styles.module.scss
new file mode 100644
index 000000000..1729a7a74
--- /dev/null
+++ b/app/(page)/product/[handle]/styles.module.scss
@@ -0,0 +1,93 @@
+@use 'styles/_spacing';
+@use 'styles/_typography';
+
+$spacer-width: calc(100vw - 100vh);
+
+.imageScroll {
+    position: relative;
+
+    left: 0;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    height: 0;
+    overflow-y: visible;
+
+    .horizScroll {
+        height: 100vh;
+        overflow-x: scroll;
+        position: relative;
+
+        .scrollMessage {
+            @include typography.subheader;
+
+            position: absolute;
+
+            left: 30px;
+            bottom: spacing.$page-bottom-baseline;
+
+            z-index: 3;
+        }
+
+        .imageContainer {
+            display: flex;
+            flex-direction: row;
+
+            height: 100%;
+
+            > * {
+                height: 100%;
+                width: auto;
+            }
+
+            img {
+                z-index: 2;
+            }
+
+            .spacer {
+                padding-right: $spacer-width;
+                height: 0;
+            }
+        }
+    }
+}
+
+.productPane {
+    padding-left: calc(calc(100vw - $spacer-width) + spacing.$grid-column-gap);
+    padding-right: spacing.$page-margin-x;
+    padding-top: 59px;
+    padding-bottom: spacing.$page-bottom-baseline;
+
+    height: 100vh;
+
+    .topBottom {
+        * {
+            z-index: 1;
+        }
+
+        height: 100%;
+
+        display: flex;
+        flex-direction: column;
+
+        .description {
+            @include typography.body-content;
+        }
+    }
+}
+
+.productPage {
+    position: absolute;
+
+    height: 100vh;
+    width: 100vw;
+    overflow: hidden;
+}
+
+.productPage {
+    position: absolute;
+
+    height: 100vh;
+    width: 100vw;
+    overflow: hidden;
+}
diff --git a/app/(page)/styles.module.scss b/app/(page)/styles.module.scss
new file mode 100644
index 000000000..60598a9fa
--- /dev/null
+++ b/app/(page)/styles.module.scss
@@ -0,0 +1,5 @@
+@use 'styles/_spacing';
+
+.main {
+    // padding: 0 spacing.$page-margin-x;
+}
diff --git a/commerce/constants.ts b/commerce/constants.ts
new file mode 100644
index 000000000..2f965aa67
--- /dev/null
+++ b/commerce/constants.ts
@@ -0,0 +1,50 @@
+export type SortFilterItem = {
+    title: string;
+    slug: string | null;
+    sortKey: 'RELEVANCE' | 'BEST_SELLING' | 'CREATED_AT' | 'PRICE';
+    reverse: boolean;
+};
+
+export const defaultSort: SortFilterItem = {
+    title: 'Relevance',
+    slug: null,
+    sortKey: 'RELEVANCE',
+    reverse: false,
+};
+
+export const sorting: SortFilterItem[] = [
+    defaultSort,
+    {
+        title: 'Trending',
+        slug: 'trending-desc',
+        sortKey: 'BEST_SELLING',
+        reverse: false,
+    }, // asc
+    {
+        title: 'Latest arrivals',
+        slug: 'latest-desc',
+        sortKey: 'CREATED_AT',
+        reverse: true,
+    },
+    {
+        title: 'Price: Low to high',
+        slug: 'price-asc',
+        sortKey: 'PRICE',
+        reverse: false,
+    }, // asc
+    {
+        title: 'Price: High to low',
+        slug: 'price-desc',
+        sortKey: 'PRICE',
+        reverse: true,
+    },
+];
+
+export const TAGS = {
+    collections: 'collections',
+    products: 'products',
+};
+
+export const HIDDEN_PRODUCT_TAG = 'nextjs-frontend-hidden';
+export const DEFAULT_OPTION = 'Default Title';
+export const SHOPIFY_GRAPHQL_API_ENDPOINT = '/api/2023-01/graphql.json';
diff --git a/commerce/shopify/fragments/cart.ts b/commerce/shopify/fragments/cart.ts
new file mode 100644
index 000000000..335128b9f
--- /dev/null
+++ b/commerce/shopify/fragments/cart.ts
@@ -0,0 +1,53 @@
+import productFragment from './product';
+
+const cartFragment = /* GraphQL */ `
+    fragment cart on Cart {
+        id
+        checkoutUrl
+        cost {
+            subtotalAmount {
+                amount
+                currencyCode
+            }
+            totalAmount {
+                amount
+                currencyCode
+            }
+            totalTaxAmount {
+                amount
+                currencyCode
+            }
+        }
+        lines(first: 100) {
+            edges {
+                node {
+                    id
+                    quantity
+                    cost {
+                        totalAmount {
+                            amount
+                            currencyCode
+                        }
+                    }
+                    merchandise {
+                        ... on ProductVariant {
+                            id
+                            title
+                            selectedOptions {
+                                name
+                                value
+                            }
+                            product {
+                                ...product
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        totalQuantity
+    }
+    ${productFragment}
+`;
+
+export default cartFragment;
diff --git a/commerce/shopify/fragments/image.ts b/commerce/shopify/fragments/image.ts
new file mode 100644
index 000000000..46b61a86d
--- /dev/null
+++ b/commerce/shopify/fragments/image.ts
@@ -0,0 +1,10 @@
+const imageFragment = /* GraphQL */ `
+    fragment image on Image {
+        url
+        altText
+        width
+        height
+    }
+`;
+
+export default imageFragment;
diff --git a/lib/shopify/fragments/product.ts b/commerce/shopify/fragments/product.ts
similarity index 100%
rename from lib/shopify/fragments/product.ts
rename to commerce/shopify/fragments/product.ts
diff --git a/lib/shopify/fragments/seo.ts b/commerce/shopify/fragments/seo.ts
similarity index 50%
rename from lib/shopify/fragments/seo.ts
rename to commerce/shopify/fragments/seo.ts
index 2d4786c4f..f41b8bcfd 100644
--- a/lib/shopify/fragments/seo.ts
+++ b/commerce/shopify/fragments/seo.ts
@@ -1,8 +1,8 @@
 const seoFragment = /* GraphQL */ `
-  fragment seo on SEO {
-    description
-    title
-  }
+    fragment seo on SEO {
+        description
+        title
+    }
 `;
 
 export default seoFragment;
diff --git a/commerce/shopify/index.ts b/commerce/shopify/index.ts
new file mode 100644
index 000000000..d04053fdb
--- /dev/null
+++ b/commerce/shopify/index.ts
@@ -0,0 +1,424 @@
+import {
+    HIDDEN_PRODUCT_TAG,
+    SHOPIFY_GRAPHQL_API_ENDPOINT,
+    TAGS,
+} from 'commerce/constants';
+import { isShopifyError } from 'commerce/type-guards';
+import {
+    addToCartMutation,
+    createCartMutation,
+    editCartItemsMutation,
+    removeFromCartMutation,
+} from './mutations/cart';
+import { getCartQuery } from './queries/cart';
+import {
+    getCollectionProductsQuery,
+    getCollectionQuery,
+    getCollectionsQuery,
+} from './queries/collection';
+import { getMenuQuery } from './queries/menu';
+import { getPageQuery, getPagesQuery } from './queries/page';
+import {
+    getProductQuery,
+    getProductRecommendationsQuery,
+    getProductsQuery,
+} from './queries/product';
+import {
+    Cart,
+    Collection,
+    Connection,
+    Menu,
+    Page,
+    Product,
+    ShopifyAddToCartOperation,
+    ShopifyCart,
+    ShopifyCartOperation,
+    ShopifyCollection,
+    ShopifyCollectionOperation,
+    ShopifyCollectionProductsOperation,
+    ShopifyCollectionsOperation,
+    ShopifyCreateCartOperation,
+    ShopifyMenuOperation,
+    ShopifyPageOperation,
+    ShopifyPagesOperation,
+    ShopifyProduct,
+    ShopifyProductOperation,
+    ShopifyProductRecommendationsOperation,
+    ShopifyProductsOperation,
+    ShopifyRemoveFromCartOperation,
+    ShopifyUpdateCartOperation,
+} from './types';
+
+const domain = `https://${process.env.SHOPIFY_STORE_DOMAIN!}`;
+const endpoint = `${domain}${SHOPIFY_GRAPHQL_API_ENDPOINT}`;
+const key = process.env.SHOPIFY_STOREFRONT_ACCESS_TOKEN!;
+
+type ExtractVariables<T> = T extends { variables: object }
+    ? T['variables']
+    : never;
+
+export async function shopifyFetch<T>({
+    cache = 'force-cache',
+    headers,
+    query,
+    tags,
+    variables,
+}: {
+    cache?: RequestCache;
+    headers?: HeadersInit;
+    query: string;
+    tags?: string[];
+    variables?: ExtractVariables<T>;
+}): Promise<{ status: number; body: T } | never> {
+    try {
+        const result = await fetch(endpoint, {
+            method: 'POST',
+            headers: {
+                'Content-Type': 'application/json',
+                'X-Shopify-Storefront-Access-Token': key,
+                ...headers,
+            },
+            body: JSON.stringify({
+                ...(query && { query }),
+                ...(variables && { variables }),
+            }),
+            cache,
+            ...(tags && { next: { tags } }),
+        });
+
+        const body = await result.json();
+
+        if (body.errors) {
+            throw body.errors[0];
+        }
+
+        return {
+            status: result.status,
+            body,
+        };
+    } catch (e) {
+        if (isShopifyError(e)) {
+            throw {
+                status: e.status || 500,
+                message: e.message,
+                query,
+            };
+        }
+
+        throw {
+            error: e,
+            query,
+        };
+    }
+}
+
+const removeEdgesAndNodes = (array: Connection<any>) => {
+    return array.edges.map(edge => edge?.node);
+};
+
+const reshapeCart = (cart: ShopifyCart): Cart => {
+    if (!cart.cost?.totalTaxAmount) {
+        cart.cost.totalTaxAmount = {
+            amount: '0.0',
+            currencyCode: 'USD',
+        };
+    }
+
+    return {
+        ...cart,
+        lines: removeEdgesAndNodes(cart.lines),
+    };
+};
+
+const reshapeCollection = (
+    collection: ShopifyCollection
+): Collection | undefined => {
+    if (!collection) {
+        return undefined;
+    }
+
+    return {
+        ...collection,
+        path: `/search/${collection.handle}`,
+    };
+};
+
+const reshapeCollections = (collections: ShopifyCollection[]) => {
+    const reshapedCollections = [];
+
+    for (const collection of collections) {
+        if (collection) {
+            const reshapedCollection = reshapeCollection(collection);
+
+            if (reshapedCollection) {
+                reshapedCollections.push(reshapedCollection);
+            }
+        }
+    }
+
+    return reshapedCollections;
+};
+
+const reshapeProduct = (
+    product: ShopifyProduct,
+    filterHiddenProducts: boolean = true
+) => {
+    if (
+        !product ||
+        (filterHiddenProducts && product.tags.includes(HIDDEN_PRODUCT_TAG))
+    ) {
+        return undefined;
+    }
+
+    const { images, variants, ...rest } = product;
+
+    return {
+        ...rest,
+        images: removeEdgesAndNodes(images),
+        variants: removeEdgesAndNodes(variants),
+    };
+};
+
+const reshapeProducts = (products: ShopifyProduct[]) => {
+    const reshapedProducts = [];
+
+    for (const product of products) {
+        if (product) {
+            const reshapedProduct = reshapeProduct(product);
+
+            if (reshapedProduct) {
+                reshapedProducts.push(reshapedProduct);
+            }
+        }
+    }
+
+    return reshapedProducts;
+};
+
+export async function createCart(): Promise<Cart> {
+    const res = await shopifyFetch<ShopifyCreateCartOperation>({
+        query: createCartMutation,
+        cache: 'no-store',
+    });
+
+    return reshapeCart(res.body.data.cartCreate.cart);
+}
+
+export async function addToCart(
+    cartId: string,
+    lines: { merchandiseId: string; quantity: number }[]
+): Promise<Cart> {
+    const res = await shopifyFetch<ShopifyAddToCartOperation>({
+        query: addToCartMutation,
+        variables: {
+            cartId,
+            lines,
+        },
+        cache: 'no-store',
+    });
+    return reshapeCart(res.body.data.cartLinesAdd.cart);
+}
+
+export async function removeFromCart(
+    cartId: string,
+    lineIds: string[]
+): Promise<Cart> {
+    const res = await shopifyFetch<ShopifyRemoveFromCartOperation>({
+        query: removeFromCartMutation,
+        variables: {
+            cartId,
+            lineIds,
+        },
+        cache: 'no-store',
+    });
+
+    return reshapeCart(res.body.data.cartLinesRemove.cart);
+}
+
+export async function updateCart(
+    cartId: string,
+    lines: { id: string; merchandiseId: string; quantity: number }[]
+): Promise<Cart> {
+    const res = await shopifyFetch<ShopifyUpdateCartOperation>({
+        query: editCartItemsMutation,
+        variables: {
+            cartId,
+            lines,
+        },
+        cache: 'no-store',
+    });
+
+    return reshapeCart(res.body.data.cartLinesUpdate.cart);
+}
+
+export async function getCart(cartId: string): Promise<Cart | null> {
+    const res = await shopifyFetch<ShopifyCartOperation>({
+        query: getCartQuery,
+        variables: { cartId },
+        cache: 'no-store',
+    });
+
+    if (!res.body.data.cart) {
+        return null;
+    }
+
+    return reshapeCart(res.body.data.cart);
+}
+
+export async function getCollection(
+    handle: string
+): Promise<Collection | undefined> {
+    const res = await shopifyFetch<ShopifyCollectionOperation>({
+        query: getCollectionQuery,
+        tags: [TAGS.collections],
+        variables: {
+            handle,
+        },
+    });
+
+    return reshapeCollection(res.body.data.collection);
+}
+
+export async function getCollectionProducts({
+    collection,
+    reverse,
+    sortKey,
+}: {
+    collection: string;
+    reverse?: boolean;
+    sortKey?: string;
+}): Promise<Product[]> {
+    const res = await shopifyFetch<ShopifyCollectionProductsOperation>({
+        query: getCollectionProductsQuery,
+        tags: [TAGS.collections, TAGS.products],
+        variables: {
+            handle: collection,
+            reverse,
+            sortKey,
+        },
+    });
+
+    if (!res.body.data.collection) {
+        console.log(`No collection found for \`${collection}\``);
+        return [];
+    }
+
+    return reshapeProducts(
+        removeEdgesAndNodes(res.body.data.collection.products)
+    );
+}
+
+export async function getCollections(): Promise<Collection[]> {
+    const res = await shopifyFetch<ShopifyCollectionsOperation>({
+        query: getCollectionsQuery,
+        tags: [TAGS.collections],
+    });
+    const shopifyCollections = removeEdgesAndNodes(res.body?.data?.collections);
+    const collections = [
+        {
+            handle: '',
+            title: 'All',
+            description: 'All products',
+            seo: {
+                title: 'All',
+                description: 'All products',
+            },
+            path: '/search',
+            updatedAt: new Date().toISOString(),
+        },
+        // Filter out the `hidden` collections.
+        // Collections that start with `hidden-*` need to be hidden on the search page.
+        ...reshapeCollections(shopifyCollections).filter(
+            collection => !collection.handle.startsWith('hidden')
+        ),
+    ];
+
+    return collections;
+}
+
+export async function getMenu(handle: string): Promise<Menu[]> {
+    const res = await shopifyFetch<ShopifyMenuOperation>({
+        query: getMenuQuery,
+        tags: [TAGS.collections],
+        variables: {
+            handle,
+        },
+    });
+
+    return (
+        res.body?.data?.menu?.items.map(
+            (item: { title: string; url: string }) => ({
+                title: item.title,
+                path: item.url
+                    .replace(domain, '')
+                    .replace('/collections', '/search')
+                    .replace('/pages', ''),
+            })
+        ) || []
+    );
+}
+
+export async function getPage(handle: string): Promise<Page> {
+    const res = await shopifyFetch<ShopifyPageOperation>({
+        query: getPageQuery,
+        variables: { handle },
+    });
+
+    return res.body.data.pageByHandle;
+}
+
+export async function getPages(): Promise<Page[]> {
+    const res = await shopifyFetch<ShopifyPagesOperation>({
+        query: getPagesQuery,
+    });
+
+    return removeEdgesAndNodes(res.body.data.pages);
+}
+
+export async function getProduct(handle: string): Promise<Product | undefined> {
+    const res = await shopifyFetch<ShopifyProductOperation>({
+        query: getProductQuery,
+        tags: [TAGS.products],
+        variables: {
+            handle,
+        },
+    });
+
+    return reshapeProduct(res.body.data.product, false);
+}
+
+export async function getProductRecommendations(
+    productId: string
+): Promise<Product[]> {
+    const res = await shopifyFetch<ShopifyProductRecommendationsOperation>({
+        query: getProductRecommendationsQuery,
+        tags: [TAGS.products],
+        variables: {
+            productId,
+        },
+    });
+
+    return reshapeProducts(res.body.data.productRecommendations);
+}
+
+export async function getProducts({
+    query,
+    reverse,
+    sortKey,
+}: {
+    query?: string;
+    reverse?: boolean;
+    sortKey?: string;
+}): Promise<Product[]> {
+    const res = await shopifyFetch<ShopifyProductsOperation>({
+        query: getProductsQuery,
+        tags: [TAGS.products],
+        variables: {
+            query,
+            reverse,
+            sortKey,
+        },
+    });
+
+    return reshapeProducts(removeEdgesAndNodes(res.body.data.products));
+}
diff --git a/commerce/shopify/mutations/cart.ts b/commerce/shopify/mutations/cart.ts
new file mode 100644
index 000000000..f59aeb85b
--- /dev/null
+++ b/commerce/shopify/mutations/cart.ts
@@ -0,0 +1,45 @@
+import cartFragment from '../fragments/cart';
+
+export const addToCartMutation = /* GraphQL */ `
+    mutation addToCart($cartId: ID!, $lines: [CartLineInput!]!) {
+        cartLinesAdd(cartId: $cartId, lines: $lines) {
+            cart {
+                ...cart
+            }
+        }
+    }
+    ${cartFragment}
+`;
+
+export const createCartMutation = /* GraphQL */ `
+    mutation createCart($lineItems: [CartLineInput!]) {
+        cartCreate(input: { lines: $lineItems }) {
+            cart {
+                ...cart
+            }
+        }
+    }
+    ${cartFragment}
+`;
+
+export const editCartItemsMutation = /* GraphQL */ `
+    mutation editCartItems($cartId: ID!, $lines: [CartLineUpdateInput!]!) {
+        cartLinesUpdate(cartId: $cartId, lines: $lines) {
+            cart {
+                ...cart
+            }
+        }
+    }
+    ${cartFragment}
+`;
+
+export const removeFromCartMutation = /* GraphQL */ `
+    mutation removeFromCart($cartId: ID!, $lineIds: [ID!]!) {
+        cartLinesRemove(cartId: $cartId, lineIds: $lineIds) {
+            cart {
+                ...cart
+            }
+        }
+    }
+    ${cartFragment}
+`;
diff --git a/commerce/shopify/queries/cart.ts b/commerce/shopify/queries/cart.ts
new file mode 100644
index 000000000..14054332a
--- /dev/null
+++ b/commerce/shopify/queries/cart.ts
@@ -0,0 +1,10 @@
+import cartFragment from '../fragments/cart';
+
+export const getCartQuery = /* GraphQL */ `
+    query getCart($cartId: ID!) {
+        cart(id: $cartId) {
+            ...cart
+        }
+    }
+    ${cartFragment}
+`;
diff --git a/commerce/shopify/queries/collection.ts b/commerce/shopify/queries/collection.ts
new file mode 100644
index 000000000..60bcfb427
--- /dev/null
+++ b/commerce/shopify/queries/collection.ts
@@ -0,0 +1,56 @@
+import productFragment from '../fragments/product';
+import seoFragment from '../fragments/seo';
+
+const collectionFragment = /* GraphQL */ `
+    fragment collection on Collection {
+        handle
+        title
+        description
+        seo {
+            ...seo
+        }
+        updatedAt
+    }
+    ${seoFragment}
+`;
+
+export const getCollectionQuery = /* GraphQL */ `
+    query getCollection($handle: String!) {
+        collection(handle: $handle) {
+            ...collection
+        }
+    }
+    ${collectionFragment}
+`;
+
+export const getCollectionsQuery = /* GraphQL */ `
+    query getCollections {
+        collections(first: 100, sortKey: TITLE) {
+            edges {
+                node {
+                    ...collection
+                }
+            }
+        }
+    }
+    ${collectionFragment}
+`;
+
+export const getCollectionProductsQuery = /* GraphQL */ `
+    query getCollectionProducts(
+        $handle: String!
+        $sortKey: ProductCollectionSortKeys
+        $reverse: Boolean
+    ) {
+        collection(handle: $handle) {
+            products(sortKey: $sortKey, reverse: $reverse, first: 100) {
+                edges {
+                    node {
+                        ...product
+                    }
+                }
+            }
+        }
+    }
+    ${productFragment}
+`;
diff --git a/commerce/shopify/queries/menu.ts b/commerce/shopify/queries/menu.ts
new file mode 100644
index 000000000..6401b794d
--- /dev/null
+++ b/commerce/shopify/queries/menu.ts
@@ -0,0 +1,10 @@
+export const getMenuQuery = /* GraphQL */ `
+    query getMenu($handle: String!) {
+        menu(handle: $handle) {
+            items {
+                title
+                url
+            }
+        }
+    }
+`;
diff --git a/commerce/shopify/queries/page.ts b/commerce/shopify/queries/page.ts
new file mode 100644
index 000000000..0c50c9ec8
--- /dev/null
+++ b/commerce/shopify/queries/page.ts
@@ -0,0 +1,41 @@
+import seoFragment from '../fragments/seo';
+
+const pageFragment = /* GraphQL */ `
+    fragment page on Page {
+        ... on Page {
+            id
+            title
+            handle
+            body
+            bodySummary
+            seo {
+                ...seo
+            }
+            createdAt
+            updatedAt
+        }
+    }
+    ${seoFragment}
+`;
+
+export const getPageQuery = /* GraphQL */ `
+    query getPage($handle: String!) {
+        pageByHandle(handle: $handle) {
+            ...page
+        }
+    }
+    ${pageFragment}
+`;
+
+export const getPagesQuery = /* GraphQL */ `
+    query getPages {
+        pages(first: 100) {
+            edges {
+                node {
+                    ...page
+                }
+            }
+        }
+    }
+    ${pageFragment}
+`;
diff --git a/commerce/shopify/queries/product.ts b/commerce/shopify/queries/product.ts
new file mode 100644
index 000000000..83901440d
--- /dev/null
+++ b/commerce/shopify/queries/product.ts
@@ -0,0 +1,41 @@
+import productFragment from '../fragments/product';
+
+export const getProductQuery = /* GraphQL */ `
+    query getProduct($handle: String!) {
+        product(handle: $handle) {
+            ...product
+        }
+    }
+    ${productFragment}
+`;
+
+export const getProductsQuery = /* GraphQL */ `
+    query getProducts(
+        $sortKey: ProductSortKeys
+        $reverse: Boolean
+        $query: String
+    ) {
+        products(
+            sortKey: $sortKey
+            reverse: $reverse
+            query: $query
+            first: 100
+        ) {
+            edges {
+                node {
+                    ...product
+                }
+            }
+        }
+    }
+    ${productFragment}
+`;
+
+export const getProductRecommendationsQuery = /* GraphQL */ `
+    query getProductRecommendations($productId: ID!) {
+        productRecommendations(productId: $productId) {
+            ...product
+        }
+    }
+    ${productFragment}
+`;
diff --git a/commerce/shopify/types.ts b/commerce/shopify/types.ts
new file mode 100644
index 000000000..1cbc3be96
--- /dev/null
+++ b/commerce/shopify/types.ts
@@ -0,0 +1,265 @@
+export type Maybe<T> = T | null;
+
+export type Connection<T> = {
+    edges: Array<Edge<T>>;
+};
+
+export type Edge<T> = {
+    node: T;
+};
+
+export type Cart = Omit<ShopifyCart, 'lines'> & {
+    lines: CartItem[];
+};
+
+export type CartItem = {
+    id: string;
+    quantity: number;
+    cost: {
+        totalAmount: Money;
+    };
+    merchandise: {
+        id: string;
+        title: string;
+        selectedOptions: {
+            name: string;
+            value: string;
+        }[];
+        product: Product;
+    };
+};
+
+export type Collection = ShopifyCollection & {
+    path: string;
+};
+
+export type Image = {
+    url: string;
+    altText: string;
+    width: number;
+    height: number;
+};
+
+export type Menu = {
+    title: string;
+    path: string;
+};
+
+export type Money = {
+    amount: string;
+    currencyCode: string;
+};
+
+export type Page = {
+    id: string;
+    title: string;
+    handle: string;
+    body: string;
+    bodySummary: string;
+    seo?: SEO;
+    createdAt: string;
+    updatedAt: string;
+};
+
+export type Product = Omit<ShopifyProduct, 'variants' | 'images'> & {
+    variants: ProductVariant[];
+    images: Image[];
+};
+
+export type ProductOption = {
+    id: string;
+    name: string;
+    values: string[];
+};
+
+export type ProductVariant = {
+    id: string;
+    title: string;
+    availableForSale: boolean;
+    selectedOptions: {
+        name: string;
+        value: string;
+    }[];
+    price: Money;
+};
+
+export type SEO = {
+    title: string;
+    description: string;
+};
+
+export type ShopifyCart = {
+    id: string;
+    checkoutUrl: string;
+    cost: {
+        subtotalAmount: Money;
+        totalAmount: Money;
+        totalTaxAmount: Money;
+    };
+    lines: Connection<CartItem>;
+    totalQuantity: number;
+};
+
+export type ShopifyCollection = {
+    handle: string;
+    title: string;
+    description: string;
+    seo: SEO;
+    updatedAt: string;
+};
+
+export type ShopifyProduct = {
+    id: string;
+    handle: string;
+    availableForSale: boolean;
+    title: string;
+    description: string;
+    descriptionHtml: string;
+    options: ProductOption[];
+    priceRange: {
+        maxVariantPrice: Money;
+        minVariantPrice: Money;
+    };
+    variants: Connection<ProductVariant>;
+    featuredImage: Image;
+    images: Connection<Image>;
+    seo: SEO;
+    tags: string[];
+    updatedAt: string;
+};
+
+export type ShopifyCartOperation = {
+    data: {
+        cart: ShopifyCart;
+    };
+    variables: {
+        cartId: string;
+    };
+};
+
+export type ShopifyCreateCartOperation = {
+    data: { cartCreate: { cart: ShopifyCart } };
+};
+
+export type ShopifyAddToCartOperation = {
+    data: {
+        cartLinesAdd: {
+            cart: ShopifyCart;
+        };
+    };
+    variables: {
+        cartId: string;
+        lines: {
+            merchandiseId: string;
+            quantity: number;
+        }[];
+    };
+};
+
+export type ShopifyRemoveFromCartOperation = {
+    data: {
+        cartLinesRemove: {
+            cart: ShopifyCart;
+        };
+    };
+    variables: {
+        cartId: string;
+        lineIds: string[];
+    };
+};
+
+export type ShopifyUpdateCartOperation = {
+    data: {
+        cartLinesUpdate: {
+            cart: ShopifyCart;
+        };
+    };
+    variables: {
+        cartId: string;
+        lines: {
+            id: string;
+            merchandiseId: string;
+            quantity: number;
+        }[];
+    };
+};
+
+export type ShopifyCollectionOperation = {
+    data: {
+        collection: ShopifyCollection;
+    };
+    variables: {
+        handle: string;
+    };
+};
+
+export type ShopifyCollectionProductsOperation = {
+    data: {
+        collection: {
+            products: Connection<ShopifyProduct>;
+        };
+    };
+    variables: {
+        handle: string;
+        reverse?: boolean;
+        sortKey?: string;
+    };
+};
+
+export type ShopifyCollectionsOperation = {
+    data: {
+        collections: Connection<ShopifyCollection>;
+    };
+};
+
+export type ShopifyMenuOperation = {
+    data: {
+        menu?: {
+            items: {
+                title: string;
+                url: string;
+            }[];
+        };
+    };
+    variables: {
+        handle: string;
+    };
+};
+
+export type ShopifyPageOperation = {
+    data: { pageByHandle: Page };
+    variables: { handle: string };
+};
+
+export type ShopifyPagesOperation = {
+    data: {
+        pages: Connection<Page>;
+    };
+};
+
+export type ShopifyProductOperation = {
+    data: { product: ShopifyProduct };
+    variables: {
+        handle: string;
+    };
+};
+
+export type ShopifyProductRecommendationsOperation = {
+    data: {
+        productRecommendations: ShopifyProduct[];
+    };
+    variables: {
+        productId: string;
+    };
+};
+
+export type ShopifyProductsOperation = {
+    data: {
+        products: Connection<ShopifyProduct>;
+    };
+    variables: {
+        query?: string;
+        reverse?: boolean;
+        sortKey?: string;
+    };
+};
diff --git a/commerce/type-guards.ts b/commerce/type-guards.ts
new file mode 100644
index 000000000..0141a6970
--- /dev/null
+++ b/commerce/type-guards.ts
@@ -0,0 +1,30 @@
+export interface ShopifyErrorLike {
+    status: number;
+    message: Error;
+}
+
+export const isObject = (
+    object: unknown
+): object is Record<string, unknown> => {
+    return (
+        typeof object === 'object' && object !== null && !Array.isArray(object)
+    );
+};
+
+export const isShopifyError = (error: unknown): error is ShopifyErrorLike => {
+    if (!isObject(error)) return false;
+
+    if (error instanceof Error) return true;
+
+    return findError(error);
+};
+
+function findError<T extends object>(error: T): boolean {
+    if (Object.prototype.toString.call(error) === '[object Error]') {
+        return true;
+    }
+
+    const prototype = Object.getPrototypeOf(error) as T | null;
+
+    return prototype === null ? false : findError(prototype);
+}
diff --git a/commerce/utils.ts b/commerce/utils.ts
new file mode 100644
index 000000000..2d736e737
--- /dev/null
+++ b/commerce/utils.ts
@@ -0,0 +1,11 @@
+import { ReadonlyURLSearchParams } from 'next/navigation';
+
+export const createUrl = (
+    pathname: string,
+    params: URLSearchParams | ReadonlyURLSearchParams
+) => {
+    const paramsString = params.toString();
+    const queryString = `${paramsString.length ? '?' : ''}${paramsString}`;
+
+    return `${pathname}${queryString}`;
+};
diff --git a/components/home/index.js b/components/home/index.js
index 7b372436d..f2458b564 100644
--- a/components/home/index.js
+++ b/components/home/index.js
@@ -3,19 +3,15 @@ import 'server-only';
 import Link from 'next/link';
 import Image from 'next/image';
 
-import { getCollectionProducts, getMenu } from 'lib/shopify';
+import { getCollectionProducts, getMenu } from 'commerce/shopify';
 
 import styles from './styles.module.scss';
 import { PriceRanges } from '/components/price';
+import { getTags, listTags } from '/util';
 
 export async function HomeProduct({ product }) {
-    const typesMenu = await getMenu('types-nav');
-
-    const types = typesMenu?.map(item => /search\/(\w+)/.exec(item?.path)?.[1]);
     const featuredImage = product?.images?.[0];
-    const collections = product?.collections?.nodes
-        ?.map(col => col?.title)
-        ?.filter(col => types?.includes(col?.toLowerCase()));
+    const tags = await getTags({ product });
 
     return (
         <Link
@@ -34,10 +30,8 @@ export async function HomeProduct({ product }) {
             </div>
             <div>
                 <p className={styles.title}>{product?.title}</p>
-                {collections && collections.length > 0 && (
-                    <p className={styles.collections}>{`(${collections.join(
-                        ', '
-                    )})`}</p>
+                {tags && tags.length > 0 && (
+                    <p className={styles.collections}>{listTags({ tags })}</p>
                 )}
             </div>
             <PriceRanges product={product} />
diff --git a/components/home/styles.module.scss b/components/home/styles.module.scss
index f21fe93e3..806889eda 100644
--- a/components/home/styles.module.scss
+++ b/components/home/styles.module.scss
@@ -2,12 +2,6 @@
 @use 'styles/_spacing';
 @use 'styles/_colors';
 
-@mixin home-grid {
-    display: grid;
-    grid-template-columns: repeat(24, 1fr);
-    column-gap: 10px;
-}
-
 .homeNav {
     padding: (51px - spacing.$home-spacer-y) 115px 22px 115px;
 
@@ -59,7 +53,7 @@
 }
 
 .homeProductsList {
-    @include home-grid;
+    @include spacing.home-grid;
 
     row-gap: 20px;
     padding-bottom: 364px;
@@ -132,7 +126,7 @@
         padding-top: 20px;
         padding-bottom: 30px;
 
-        @include home-grid;
+        @include spacing.home-grid;
 
         > p {
             @include typography.body;
diff --git a/components/price/index.js b/components/price/index.js
index 10ec2739a..fbf47a78b 100644
--- a/components/price/index.js
+++ b/components/price/index.js
@@ -70,12 +70,13 @@ export const VariantPrice = ({ variant, quantity }) => {
     const onSale = variantOnSale(variant);
 
     return variant ? (
-        <div>
+        <div className={styles.variantPrice}>
             {availableForSale ? (
                 <>
                     <>
                         {onSale && (
-                            <p className={'original-price'}>
+                            <p className={styles.originalPrice}>
+                                Retail:{' '}
                                 {formatPrice({
                                     amount:
                                         (variant?.compareAtPrice?.amount ?? 0) *
@@ -86,7 +87,7 @@ export const VariantPrice = ({ variant, quantity }) => {
                             </p>
                         )}
                     </>
-                    <p>
+                    <p className={styles.actualPrice}>
                         {formatPrice({
                             amount: (variant?.price?.amount ?? 0) * quantity,
                             currencyCode: variant?.price?.currencyCode,
@@ -94,8 +95,7 @@ export const VariantPrice = ({ variant, quantity }) => {
                     </p>
                 </>
             ) : (
-                // TODO: this can just say "Sold Out" in the future
-                <p>Variant Sold Out</p>
+                <p className={styles.actualPrice}>Sold Out</p>
             )}
         </div>
     ) : (
diff --git a/components/price/styles.module.scss b/components/price/styles.module.scss
index e2170f724..6d9e6d0c3 100644
--- a/components/price/styles.module.scss
+++ b/components/price/styles.module.scss
@@ -10,3 +10,15 @@
         text-decoration-line: line-through;
     }
 }
+
+.variantPrice {
+    .originalPrice {
+        @include typography.body;
+
+        text-decoration-line: line-through;
+    }
+
+    .actualPrice {
+        @include typography.title;
+    }
+}
diff --git a/components/product/purchase-input.js b/components/product/purchase-input.js
index 8eddb518d..4010ba3ab 100644
--- a/components/product/purchase-input.js
+++ b/components/product/purchase-input.js
@@ -3,6 +3,7 @@
 import { useState } from 'react';
 import Link from 'next/link';
 
+import styles from './styles.module.scss';
 import { Option, Select, NumberInput } from '/components/input';
 import {
     productAvailableForSale,
@@ -41,6 +42,7 @@ export const productVariant = ({ product, selectedOptions }) => {
     }
 };
 
+// TODO: check availability against stock ?
 export default function PurchaseInput({ product }) {
     const hasOptions = productHasOptions(product);
     const isForSale = productIsForSale(product);
@@ -56,47 +58,73 @@ export default function PurchaseInput({ product }) {
         ? productVariant({ product, selectedOptions })
         : product?.variants?.[0];
 
-    return availableForSale ? (
-        isForSale && (
-            <>
-                <NumberInput
-                    min='1'
-                    value={qty}
-                    id='quantity'
-                    label='Qty'
-                    onChange={e => setQty(e.target.value)}
-                />
-                <>
-                    {hasOptions &&
-                        product?.options?.map((option, i) => (
-                            <Select
-                                key={option?.id}
-                                id={option?.name}
-                                label={option?.name}
-                                value={selectedOptions[i]}
-                                onChange={e =>
-                                    setSelectedOptions(
-                                        selectedOptions.map((value, ii) =>
-                                            i == ii ? e.target.value : value
-                                        )
-                                    )
-                                }
+    return (
+        <div className={styles.purchaseInput}>
+            {isForSale && (
+                <div className={styles.topBottom}>
+                    <div>
+                        {availableForSale && (
+                            <>
+                                <NumberInput
+                                    min='1'
+                                    value={qty}
+                                    id='quantity'
+                                    label='Qty'
+                                    onChange={e => setQty(e.target.value)}
+                                />
+                                <>
+                                    {hasOptions &&
+                                        product?.options?.map((option, i) => (
+                                            <Select
+                                                key={option?.id}
+                                                id={option?.name}
+                                                label={option?.name}
+                                                value={selectedOptions[i]}
+                                                onChange={e =>
+                                                    setSelectedOptions(
+                                                        selectedOptions.map(
+                                                            (value, ii) =>
+                                                                i == ii
+                                                                    ? e.target
+                                                                          .value
+                                                                    : value
+                                                        )
+                                                    )
+                                                }
+                                            >
+                                                {option?.values?.map(value => (
+                                                    <Option
+                                                        key={value}
+                                                        value={value}
+                                                    >
+                                                        {value}
+                                                    </Option>
+                                                ))}
+                                            </Select>
+                                        ))}
+                                </>
+                            </>
+                        )}
+                    </div>
+                    <div>
+                        <VariantPrice variant={variant} quantity={qty} />
+                        <div className={styles.ctas}>
+                            {/* TODO: add to cart on click */}
+                            <button
+                                className={`${styles.buyNow} ${
+                                    !availableForSale && styles.inactive
+                                }`}
+                                type='button'
                             >
-                                {option?.values?.map(value => (
-                                    <Option key={value} value={value}>
-                                        {value}
-                                    </Option>
-                                ))}
-                            </Select>
-                        ))}
-                </>
-                <VariantPrice variant={variant} quantity={qty} />
-                {/* TODO: add to cart on click */}
-                <button type='button'>Buy Now!</button>
-                <Link href='/checkout'>Checkout?</Link>
-            </>
-        )
-    ) : (
-        <p>Sold Out</p>
+                                Buy Now!
+                            </button>
+                            <Link href='/checkout' className={styles.checkout}>
+                                Checkout?
+                            </Link>
+                        </div>
+                    </div>
+                </div>
+            )}
+        </div>
     );
 }
diff --git a/components/product/styles.module.scss b/components/product/styles.module.scss
new file mode 100644
index 000000000..4ac99fc8f
--- /dev/null
+++ b/components/product/styles.module.scss
@@ -0,0 +1,32 @@
+@use 'styles/_typography';
+@use 'styles/_spacing';
+
+.purchaseInput {
+    padding-left: spacing.$list-padding;
+    height: 100%;
+
+    .topBottom {
+        height: 100%;
+
+        display: flex;
+        flex-direction: column;
+        justify-content: space-between;
+
+        .ctas {
+            display: flex;
+            flex-direction: row;
+            gap: 20px;
+
+            .buyNow,
+            .checkout {
+                @include typography.header-cta;
+
+                text-decoration-line: underline;
+            }
+
+            .buyNow.inactive {
+                text-decoration-line: line-through;
+            }
+        }
+    }
+}
diff --git a/lib/constants.ts b/lib/constants.ts
deleted file mode 100644
index 99711221a..000000000
--- a/lib/constants.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-export type SortFilterItem = {
-  title: string;
-  slug: string | null;
-  sortKey: 'RELEVANCE' | 'BEST_SELLING' | 'CREATED_AT' | 'PRICE';
-  reverse: boolean;
-};
-
-export const defaultSort: SortFilterItem = {
-  title: 'Relevance',
-  slug: null,
-  sortKey: 'RELEVANCE',
-  reverse: false
-};
-
-export const sorting: SortFilterItem[] = [
-  defaultSort,
-  { title: 'Trending', slug: 'trending-desc', sortKey: 'BEST_SELLING', reverse: false }, // asc
-  { title: 'Latest arrivals', slug: 'latest-desc', sortKey: 'CREATED_AT', reverse: true },
-  { title: 'Price: Low to high', slug: 'price-asc', sortKey: 'PRICE', reverse: false }, // asc
-  { title: 'Price: High to low', slug: 'price-desc', sortKey: 'PRICE', reverse: true }
-];
-
-export const TAGS = {
-  collections: 'collections',
-  products: 'products'
-};
-
-export const HIDDEN_PRODUCT_TAG = 'nextjs-frontend-hidden';
-export const DEFAULT_OPTION = 'Default Title';
-export const SHOPIFY_GRAPHQL_API_ENDPOINT = '/api/2023-01/graphql.json';
diff --git a/lib/shopify/fragments/cart.ts b/lib/shopify/fragments/cart.ts
deleted file mode 100644
index fc5c838dd..000000000
--- a/lib/shopify/fragments/cart.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-import productFragment from './product';
-
-const cartFragment = /* GraphQL */ `
-  fragment cart on Cart {
-    id
-    checkoutUrl
-    cost {
-      subtotalAmount {
-        amount
-        currencyCode
-      }
-      totalAmount {
-        amount
-        currencyCode
-      }
-      totalTaxAmount {
-        amount
-        currencyCode
-      }
-    }
-    lines(first: 100) {
-      edges {
-        node {
-          id
-          quantity
-          cost {
-            totalAmount {
-              amount
-              currencyCode
-            }
-          }
-          merchandise {
-            ... on ProductVariant {
-              id
-              title
-              selectedOptions {
-                name
-                value
-              }
-              product {
-                ...product
-              }
-            }
-          }
-        }
-      }
-    }
-    totalQuantity
-  }
-  ${productFragment}
-`;
-
-export default cartFragment;
diff --git a/lib/shopify/fragments/image.ts b/lib/shopify/fragments/image.ts
deleted file mode 100644
index 5d002f175..000000000
--- a/lib/shopify/fragments/image.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-const imageFragment = /* GraphQL */ `
-  fragment image on Image {
-    url
-    altText
-    width
-    height
-  }
-`;
-
-export default imageFragment;
diff --git a/lib/shopify/index.ts b/lib/shopify/index.ts
deleted file mode 100644
index 2e23a7f78..000000000
--- a/lib/shopify/index.ts
+++ /dev/null
@@ -1,396 +0,0 @@
-import { HIDDEN_PRODUCT_TAG, SHOPIFY_GRAPHQL_API_ENDPOINT, TAGS } from 'lib/constants';
-import { isShopifyError } from 'lib/type-guards';
-import {
-  addToCartMutation,
-  createCartMutation,
-  editCartItemsMutation,
-  removeFromCartMutation
-} from './mutations/cart';
-import { getCartQuery } from './queries/cart';
-import {
-  getCollectionProductsQuery,
-  getCollectionQuery,
-  getCollectionsQuery
-} from './queries/collection';
-import { getMenuQuery } from './queries/menu';
-import { getPageQuery, getPagesQuery } from './queries/page';
-import {
-  getProductQuery,
-  getProductRecommendationsQuery,
-  getProductsQuery
-} from './queries/product';
-import {
-  Cart,
-  Collection,
-  Connection,
-  Menu,
-  Page,
-  Product,
-  ShopifyAddToCartOperation,
-  ShopifyCart,
-  ShopifyCartOperation,
-  ShopifyCollection,
-  ShopifyCollectionOperation,
-  ShopifyCollectionProductsOperation,
-  ShopifyCollectionsOperation,
-  ShopifyCreateCartOperation,
-  ShopifyMenuOperation,
-  ShopifyPageOperation,
-  ShopifyPagesOperation,
-  ShopifyProduct,
-  ShopifyProductOperation,
-  ShopifyProductRecommendationsOperation,
-  ShopifyProductsOperation,
-  ShopifyRemoveFromCartOperation,
-  ShopifyUpdateCartOperation
-} from './types';
-
-const domain = `https://${process.env.SHOPIFY_STORE_DOMAIN!}`;
-const endpoint = `${domain}${SHOPIFY_GRAPHQL_API_ENDPOINT}`;
-const key = process.env.SHOPIFY_STOREFRONT_ACCESS_TOKEN!;
-
-type ExtractVariables<T> = T extends { variables: object } ? T['variables'] : never;
-
-export async function shopifyFetch<T>({
-  cache = 'force-cache',
-  headers,
-  query,
-  tags,
-  variables
-}: {
-  cache?: RequestCache;
-  headers?: HeadersInit;
-  query: string;
-  tags?: string[];
-  variables?: ExtractVariables<T>;
-}): Promise<{ status: number; body: T } | never> {
-  try {
-    const result = await fetch(endpoint, {
-      method: 'POST',
-      headers: {
-        'Content-Type': 'application/json',
-        'X-Shopify-Storefront-Access-Token': key,
-        ...headers
-      },
-      body: JSON.stringify({
-        ...(query && { query }),
-        ...(variables && { variables })
-      }),
-      cache,
-      ...(tags && { next: { tags } })
-    });
-
-    const body = await result.json();
-
-    if (body.errors) {
-      throw body.errors[0];
-    }
-
-    return {
-      status: result.status,
-      body
-    };
-  } catch (e) {
-    if (isShopifyError(e)) {
-      throw {
-        status: e.status || 500,
-        message: e.message,
-        query
-      };
-    }
-
-    throw {
-      error: e,
-      query
-    };
-  }
-}
-
-const removeEdgesAndNodes = (array: Connection<any>) => {
-  return array.edges.map((edge) => edge?.node);
-};
-
-const reshapeCart = (cart: ShopifyCart): Cart => {
-  if (!cart.cost?.totalTaxAmount) {
-    cart.cost.totalTaxAmount = {
-      amount: '0.0',
-      currencyCode: 'USD'
-    };
-  }
-
-  return {
-    ...cart,
-    lines: removeEdgesAndNodes(cart.lines)
-  };
-};
-
-const reshapeCollection = (collection: ShopifyCollection): Collection | undefined => {
-  if (!collection) {
-    return undefined;
-  }
-
-  return {
-    ...collection,
-    path: `/search/${collection.handle}`
-  };
-};
-
-const reshapeCollections = (collections: ShopifyCollection[]) => {
-  const reshapedCollections = [];
-
-  for (const collection of collections) {
-    if (collection) {
-      const reshapedCollection = reshapeCollection(collection);
-
-      if (reshapedCollection) {
-        reshapedCollections.push(reshapedCollection);
-      }
-    }
-  }
-
-  return reshapedCollections;
-};
-
-const reshapeProduct = (product: ShopifyProduct, filterHiddenProducts: boolean = true) => {
-  if (!product || (filterHiddenProducts && product.tags.includes(HIDDEN_PRODUCT_TAG))) {
-    return undefined;
-  }
-
-  const { images, variants, ...rest } = product;
-
-  return {
-    ...rest,
-    images: removeEdgesAndNodes(images),
-    variants: removeEdgesAndNodes(variants)
-  };
-};
-
-const reshapeProducts = (products: ShopifyProduct[]) => {
-  const reshapedProducts = [];
-
-  for (const product of products) {
-    if (product) {
-      const reshapedProduct = reshapeProduct(product);
-
-      if (reshapedProduct) {
-        reshapedProducts.push(reshapedProduct);
-      }
-    }
-  }
-
-  return reshapedProducts;
-};
-
-export async function createCart(): Promise<Cart> {
-  const res = await shopifyFetch<ShopifyCreateCartOperation>({
-    query: createCartMutation,
-    cache: 'no-store'
-  });
-
-  return reshapeCart(res.body.data.cartCreate.cart);
-}
-
-export async function addToCart(
-  cartId: string,
-  lines: { merchandiseId: string; quantity: number }[]
-): Promise<Cart> {
-  const res = await shopifyFetch<ShopifyAddToCartOperation>({
-    query: addToCartMutation,
-    variables: {
-      cartId,
-      lines
-    },
-    cache: 'no-store'
-  });
-  return reshapeCart(res.body.data.cartLinesAdd.cart);
-}
-
-export async function removeFromCart(cartId: string, lineIds: string[]): Promise<Cart> {
-  const res = await shopifyFetch<ShopifyRemoveFromCartOperation>({
-    query: removeFromCartMutation,
-    variables: {
-      cartId,
-      lineIds
-    },
-    cache: 'no-store'
-  });
-
-  return reshapeCart(res.body.data.cartLinesRemove.cart);
-}
-
-export async function updateCart(
-  cartId: string,
-  lines: { id: string; merchandiseId: string; quantity: number }[]
-): Promise<Cart> {
-  const res = await shopifyFetch<ShopifyUpdateCartOperation>({
-    query: editCartItemsMutation,
-    variables: {
-      cartId,
-      lines
-    },
-    cache: 'no-store'
-  });
-
-  return reshapeCart(res.body.data.cartLinesUpdate.cart);
-}
-
-export async function getCart(cartId: string): Promise<Cart | null> {
-  const res = await shopifyFetch<ShopifyCartOperation>({
-    query: getCartQuery,
-    variables: { cartId },
-    cache: 'no-store'
-  });
-
-  if (!res.body.data.cart) {
-    return null;
-  }
-
-  return reshapeCart(res.body.data.cart);
-}
-
-export async function getCollection(handle: string): Promise<Collection | undefined> {
-  const res = await shopifyFetch<ShopifyCollectionOperation>({
-    query: getCollectionQuery,
-    tags: [TAGS.collections],
-    variables: {
-      handle
-    }
-  });
-
-  return reshapeCollection(res.body.data.collection);
-}
-
-export async function getCollectionProducts({
-  collection,
-  reverse,
-  sortKey
-}: {
-  collection: string;
-  reverse?: boolean;
-  sortKey?: string;
-}): Promise<Product[]> {
-  const res = await shopifyFetch<ShopifyCollectionProductsOperation>({
-    query: getCollectionProductsQuery,
-    tags: [TAGS.collections, TAGS.products],
-    variables: {
-      handle: collection,
-      reverse,
-      sortKey
-    }
-  });
-
-  if (!res.body.data.collection) {
-    console.log(`No collection found for \`${collection}\``);
-    return [];
-  }
-
-  return reshapeProducts(removeEdgesAndNodes(res.body.data.collection.products));
-}
-
-export async function getCollections(): Promise<Collection[]> {
-  const res = await shopifyFetch<ShopifyCollectionsOperation>({
-    query: getCollectionsQuery,
-    tags: [TAGS.collections]
-  });
-  const shopifyCollections = removeEdgesAndNodes(res.body?.data?.collections);
-  const collections = [
-    {
-      handle: '',
-      title: 'All',
-      description: 'All products',
-      seo: {
-        title: 'All',
-        description: 'All products'
-      },
-      path: '/search',
-      updatedAt: new Date().toISOString()
-    },
-    // Filter out the `hidden` collections.
-    // Collections that start with `hidden-*` need to be hidden on the search page.
-    ...reshapeCollections(shopifyCollections).filter(
-      (collection) => !collection.handle.startsWith('hidden')
-    )
-  ];
-
-  return collections;
-}
-
-export async function getMenu(handle: string): Promise<Menu[]> {
-  const res = await shopifyFetch<ShopifyMenuOperation>({
-    query: getMenuQuery,
-    tags: [TAGS.collections],
-    variables: {
-      handle
-    }
-  });
-
-  return (
-    res.body?.data?.menu?.items.map((item: { title: string; url: string }) => ({
-      title: item.title,
-      path: item.url.replace(domain, '').replace('/collections', '/search').replace('/pages', '')
-    })) || []
-  );
-}
-
-export async function getPage(handle: string): Promise<Page> {
-  const res = await shopifyFetch<ShopifyPageOperation>({
-    query: getPageQuery,
-    variables: { handle }
-  });
-
-  return res.body.data.pageByHandle;
-}
-
-export async function getPages(): Promise<Page[]> {
-  const res = await shopifyFetch<ShopifyPagesOperation>({
-    query: getPagesQuery
-  });
-
-  return removeEdgesAndNodes(res.body.data.pages);
-}
-
-export async function getProduct(handle: string): Promise<Product | undefined> {
-  const res = await shopifyFetch<ShopifyProductOperation>({
-    query: getProductQuery,
-    tags: [TAGS.products],
-    variables: {
-      handle
-    }
-  });
-
-  return reshapeProduct(res.body.data.product, false);
-}
-
-export async function getProductRecommendations(productId: string): Promise<Product[]> {
-  const res = await shopifyFetch<ShopifyProductRecommendationsOperation>({
-    query: getProductRecommendationsQuery,
-    tags: [TAGS.products],
-    variables: {
-      productId
-    }
-  });
-
-  return reshapeProducts(res.body.data.productRecommendations);
-}
-
-export async function getProducts({
-  query,
-  reverse,
-  sortKey
-}: {
-  query?: string;
-  reverse?: boolean;
-  sortKey?: string;
-}): Promise<Product[]> {
-  const res = await shopifyFetch<ShopifyProductsOperation>({
-    query: getProductsQuery,
-    tags: [TAGS.products],
-    variables: {
-      query,
-      reverse,
-      sortKey
-    }
-  });
-
-  return reshapeProducts(removeEdgesAndNodes(res.body.data.products));
-}
diff --git a/lib/shopify/mutations/cart.ts b/lib/shopify/mutations/cart.ts
deleted file mode 100644
index 4cc1b5ac6..000000000
--- a/lib/shopify/mutations/cart.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import cartFragment from '../fragments/cart';
-
-export const addToCartMutation = /* GraphQL */ `
-  mutation addToCart($cartId: ID!, $lines: [CartLineInput!]!) {
-    cartLinesAdd(cartId: $cartId, lines: $lines) {
-      cart {
-        ...cart
-      }
-    }
-  }
-  ${cartFragment}
-`;
-
-export const createCartMutation = /* GraphQL */ `
-  mutation createCart($lineItems: [CartLineInput!]) {
-    cartCreate(input: { lines: $lineItems }) {
-      cart {
-        ...cart
-      }
-    }
-  }
-  ${cartFragment}
-`;
-
-export const editCartItemsMutation = /* GraphQL */ `
-  mutation editCartItems($cartId: ID!, $lines: [CartLineUpdateInput!]!) {
-    cartLinesUpdate(cartId: $cartId, lines: $lines) {
-      cart {
-        ...cart
-      }
-    }
-  }
-  ${cartFragment}
-`;
-
-export const removeFromCartMutation = /* GraphQL */ `
-  mutation removeFromCart($cartId: ID!, $lineIds: [ID!]!) {
-    cartLinesRemove(cartId: $cartId, lineIds: $lineIds) {
-      cart {
-        ...cart
-      }
-    }
-  }
-  ${cartFragment}
-`;
diff --git a/lib/shopify/queries/cart.ts b/lib/shopify/queries/cart.ts
deleted file mode 100644
index 044e47f66..000000000
--- a/lib/shopify/queries/cart.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import cartFragment from '../fragments/cart';
-
-export const getCartQuery = /* GraphQL */ `
-  query getCart($cartId: ID!) {
-    cart(id: $cartId) {
-      ...cart
-    }
-  }
-  ${cartFragment}
-`;
diff --git a/lib/shopify/queries/collection.ts b/lib/shopify/queries/collection.ts
deleted file mode 100644
index 6396ff8eb..000000000
--- a/lib/shopify/queries/collection.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-import productFragment from '../fragments/product';
-import seoFragment from '../fragments/seo';
-
-const collectionFragment = /* GraphQL */ `
-  fragment collection on Collection {
-    handle
-    title
-    description
-    seo {
-      ...seo
-    }
-    updatedAt
-  }
-  ${seoFragment}
-`;
-
-export const getCollectionQuery = /* GraphQL */ `
-  query getCollection($handle: String!) {
-    collection(handle: $handle) {
-      ...collection
-    }
-  }
-  ${collectionFragment}
-`;
-
-export const getCollectionsQuery = /* GraphQL */ `
-  query getCollections {
-    collections(first: 100, sortKey: TITLE) {
-      edges {
-        node {
-          ...collection
-        }
-      }
-    }
-  }
-  ${collectionFragment}
-`;
-
-export const getCollectionProductsQuery = /* GraphQL */ `
-  query getCollectionProducts(
-    $handle: String!
-    $sortKey: ProductCollectionSortKeys
-    $reverse: Boolean
-  ) {
-    collection(handle: $handle) {
-      products(sortKey: $sortKey, reverse: $reverse, first: 100) {
-        edges {
-          node {
-            ...product
-          }
-        }
-      }
-    }
-  }
-  ${productFragment}
-`;
diff --git a/lib/shopify/queries/menu.ts b/lib/shopify/queries/menu.ts
deleted file mode 100644
index d05b09949..000000000
--- a/lib/shopify/queries/menu.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-export const getMenuQuery = /* GraphQL */ `
-  query getMenu($handle: String!) {
-    menu(handle: $handle) {
-      items {
-        title
-        url
-      }
-    }
-  }
-`;
diff --git a/lib/shopify/queries/page.ts b/lib/shopify/queries/page.ts
deleted file mode 100644
index ac6f6f986..000000000
--- a/lib/shopify/queries/page.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import seoFragment from '../fragments/seo';
-
-const pageFragment = /* GraphQL */ `
-  fragment page on Page {
-    ... on Page {
-      id
-      title
-      handle
-      body
-      bodySummary
-      seo {
-        ...seo
-      }
-      createdAt
-      updatedAt
-    }
-  }
-  ${seoFragment}
-`;
-
-export const getPageQuery = /* GraphQL */ `
-  query getPage($handle: String!) {
-    pageByHandle(handle: $handle) {
-      ...page
-    }
-  }
-  ${pageFragment}
-`;
-
-export const getPagesQuery = /* GraphQL */ `
-  query getPages {
-    pages(first: 100) {
-      edges {
-        node {
-          ...page
-        }
-      }
-    }
-  }
-  ${pageFragment}
-`;
diff --git a/lib/shopify/queries/product.ts b/lib/shopify/queries/product.ts
deleted file mode 100644
index d3f12bd0f..000000000
--- a/lib/shopify/queries/product.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import productFragment from '../fragments/product';
-
-export const getProductQuery = /* GraphQL */ `
-  query getProduct($handle: String!) {
-    product(handle: $handle) {
-      ...product
-    }
-  }
-  ${productFragment}
-`;
-
-export const getProductsQuery = /* GraphQL */ `
-  query getProducts($sortKey: ProductSortKeys, $reverse: Boolean, $query: String) {
-    products(sortKey: $sortKey, reverse: $reverse, query: $query, first: 100) {
-      edges {
-        node {
-          ...product
-        }
-      }
-    }
-  }
-  ${productFragment}
-`;
-
-export const getProductRecommendationsQuery = /* GraphQL */ `
-  query getProductRecommendations($productId: ID!) {
-    productRecommendations(productId: $productId) {
-      ...product
-    }
-  }
-  ${productFragment}
-`;
diff --git a/lib/shopify/types.ts b/lib/shopify/types.ts
deleted file mode 100644
index 23dc02d46..000000000
--- a/lib/shopify/types.ts
+++ /dev/null
@@ -1,265 +0,0 @@
-export type Maybe<T> = T | null;
-
-export type Connection<T> = {
-  edges: Array<Edge<T>>;
-};
-
-export type Edge<T> = {
-  node: T;
-};
-
-export type Cart = Omit<ShopifyCart, 'lines'> & {
-  lines: CartItem[];
-};
-
-export type CartItem = {
-  id: string;
-  quantity: number;
-  cost: {
-    totalAmount: Money;
-  };
-  merchandise: {
-    id: string;
-    title: string;
-    selectedOptions: {
-      name: string;
-      value: string;
-    }[];
-    product: Product;
-  };
-};
-
-export type Collection = ShopifyCollection & {
-  path: string;
-};
-
-export type Image = {
-  url: string;
-  altText: string;
-  width: number;
-  height: number;
-};
-
-export type Menu = {
-  title: string;
-  path: string;
-};
-
-export type Money = {
-  amount: string;
-  currencyCode: string;
-};
-
-export type Page = {
-  id: string;
-  title: string;
-  handle: string;
-  body: string;
-  bodySummary: string;
-  seo?: SEO;
-  createdAt: string;
-  updatedAt: string;
-};
-
-export type Product = Omit<ShopifyProduct, 'variants' | 'images'> & {
-  variants: ProductVariant[];
-  images: Image[];
-};
-
-export type ProductOption = {
-  id: string;
-  name: string;
-  values: string[];
-};
-
-export type ProductVariant = {
-  id: string;
-  title: string;
-  availableForSale: boolean;
-  selectedOptions: {
-    name: string;
-    value: string;
-  }[];
-  price: Money;
-};
-
-export type SEO = {
-  title: string;
-  description: string;
-};
-
-export type ShopifyCart = {
-  id: string;
-  checkoutUrl: string;
-  cost: {
-    subtotalAmount: Money;
-    totalAmount: Money;
-    totalTaxAmount: Money;
-  };
-  lines: Connection<CartItem>;
-  totalQuantity: number;
-};
-
-export type ShopifyCollection = {
-  handle: string;
-  title: string;
-  description: string;
-  seo: SEO;
-  updatedAt: string;
-};
-
-export type ShopifyProduct = {
-  id: string;
-  handle: string;
-  availableForSale: boolean;
-  title: string;
-  description: string;
-  descriptionHtml: string;
-  options: ProductOption[];
-  priceRange: {
-    maxVariantPrice: Money;
-    minVariantPrice: Money;
-  };
-  variants: Connection<ProductVariant>;
-  featuredImage: Image;
-  images: Connection<Image>;
-  seo: SEO;
-  tags: string[];
-  updatedAt: string;
-};
-
-export type ShopifyCartOperation = {
-  data: {
-    cart: ShopifyCart;
-  };
-  variables: {
-    cartId: string;
-  };
-};
-
-export type ShopifyCreateCartOperation = {
-  data: { cartCreate: { cart: ShopifyCart } };
-};
-
-export type ShopifyAddToCartOperation = {
-  data: {
-    cartLinesAdd: {
-      cart: ShopifyCart;
-    };
-  };
-  variables: {
-    cartId: string;
-    lines: {
-      merchandiseId: string;
-      quantity: number;
-    }[];
-  };
-};
-
-export type ShopifyRemoveFromCartOperation = {
-  data: {
-    cartLinesRemove: {
-      cart: ShopifyCart;
-    };
-  };
-  variables: {
-    cartId: string;
-    lineIds: string[];
-  };
-};
-
-export type ShopifyUpdateCartOperation = {
-  data: {
-    cartLinesUpdate: {
-      cart: ShopifyCart;
-    };
-  };
-  variables: {
-    cartId: string;
-    lines: {
-      id: string;
-      merchandiseId: string;
-      quantity: number;
-    }[];
-  };
-};
-
-export type ShopifyCollectionOperation = {
-  data: {
-    collection: ShopifyCollection;
-  };
-  variables: {
-    handle: string;
-  };
-};
-
-export type ShopifyCollectionProductsOperation = {
-  data: {
-    collection: {
-      products: Connection<ShopifyProduct>;
-    };
-  };
-  variables: {
-    handle: string;
-    reverse?: boolean;
-    sortKey?: string;
-  };
-};
-
-export type ShopifyCollectionsOperation = {
-  data: {
-    collections: Connection<ShopifyCollection>;
-  };
-};
-
-export type ShopifyMenuOperation = {
-  data: {
-    menu?: {
-      items: {
-        title: string;
-        url: string;
-      }[];
-    };
-  };
-  variables: {
-    handle: string;
-  };
-};
-
-export type ShopifyPageOperation = {
-  data: { pageByHandle: Page };
-  variables: { handle: string };
-};
-
-export type ShopifyPagesOperation = {
-  data: {
-    pages: Connection<Page>;
-  };
-};
-
-export type ShopifyProductOperation = {
-  data: { product: ShopifyProduct };
-  variables: {
-    handle: string;
-  };
-};
-
-export type ShopifyProductRecommendationsOperation = {
-  data: {
-    productRecommendations: ShopifyProduct[];
-  };
-  variables: {
-    productId: string;
-  };
-};
-
-export type ShopifyProductsOperation = {
-  data: {
-    products: Connection<ShopifyProduct>;
-  };
-  variables: {
-    query?: string;
-    reverse?: boolean;
-    sortKey?: string;
-  };
-};
diff --git a/lib/type-guards.ts b/lib/type-guards.ts
deleted file mode 100644
index 1b7e7af5a..000000000
--- a/lib/type-guards.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-export interface ShopifyErrorLike {
-  status: number;
-  message: Error;
-}
-
-export const isObject = (object: unknown): object is Record<string, unknown> => {
-  return typeof object === 'object' && object !== null && !Array.isArray(object);
-};
-
-export const isShopifyError = (error: unknown): error is ShopifyErrorLike => {
-  if (!isObject(error)) return false;
-
-  if (error instanceof Error) return true;
-
-  return findError(error);
-};
-
-function findError<T extends object>(error: T): boolean {
-  if (Object.prototype.toString.call(error) === '[object Error]') {
-    return true;
-  }
-
-  const prototype = Object.getPrototypeOf(error) as T | null;
-
-  return prototype === null ? false : findError(prototype);
-}
diff --git a/lib/utils.ts b/lib/utils.ts
deleted file mode 100644
index 3fa32280b..000000000
--- a/lib/utils.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { ReadonlyURLSearchParams } from 'next/navigation';
-
-export const createUrl = (pathname: string, params: URLSearchParams | ReadonlyURLSearchParams) => {
-  const paramsString = params.toString();
-  const queryString = `${paramsString.length ? '?' : ''}${paramsString}`;
-
-  return `${pathname}${queryString}`;
-};
diff --git a/styles/_spacing.scss b/styles/_spacing.scss
index f4233362b..c35877d6c 100644
--- a/styles/_spacing.scss
+++ b/styles/_spacing.scss
@@ -1,2 +1,11 @@
 $page-margin-x: 60px;
 $home-spacer-y: 13px + 12px;
+$grid-column-gap: 10px;
+$list-padding: 50px;
+$page-bottom-baseline: 40px;
+
+@mixin home-grid {
+    display: grid;
+    grid-template-columns: repeat(24, 1fr);
+    column-gap: $grid-column-gap;
+}
diff --git a/styles/_typography.scss b/styles/_typography.scss
index e068011bb..61f470a4f 100644
--- a/styles/_typography.scss
+++ b/styles/_typography.scss
@@ -1,3 +1,5 @@
+@use 'styles/_spacing';
+
 @mixin title {
     font-family: var(--font-century-nova);
     font-size: 95px;
@@ -37,6 +39,8 @@
     font-weight: 300;
     line-height: 30px; /* 120% */
     letter-spacing: -0.75px;
+    text-decoration-thickness: 3%;
+    text-underline-offset: 7%;
 
     a,
     a:visited,
@@ -45,7 +49,6 @@
         font-weight: 100;
         letter-spacing: -0.25px;
 
-        text-decoration-line: underline;
         text-decoration-thickness: 3%;
         text-underline-offset: 7%;
     }
@@ -69,38 +72,74 @@
     text-transform: uppercase;
 }
 
-@mixin header-cta($decoration: underline) {
+@mixin header-cta {
     font-family: var(--font-century-nova);
     font-size: 35px;
     font-style: normal;
     font-weight: 100;
     line-height: 35px; /* 100% */
     letter-spacing: -1.4px;
-    text-decoration-line: $decoration;
 
     text-decoration-thickness: 5%;
     text-underline-offset: 7%;
 }
 
-@mixin body-cta($decoration: underline) {
+@mixin body-cta {
     font-family: var(--font-dia);
     font-size: 25px;
     font-style: normal;
     font-weight: 100;
     line-height: 30px;
     letter-spacing: -0.25px;
-    text-decoration-line: $decoration;
 
     text-decoration-thickness: 4%;
     text-underline-offset: 4%;
 }
 
-@mixin list-cta($decoration: underline) {
+@mixin list-cta {
     font-family: var(--font-dia);
     font-size: 18px;
     font-style: normal;
     font-weight: 100;
     line-height: 20px; /* 111.111% */
     text-transform: uppercase;
-    text-decoration-line: $decoration;
+}
+
+@mixin body-content {
+    h1,
+    h2,
+    h3,
+    h4,
+    h5,
+    h6 {
+        @include subheader;
+    }
+
+    p {
+        @include body;
+
+        margin: spacing.$grid-column-gap 0;
+    }
+
+    ul {
+        list-style: disc;
+    }
+
+    ul,
+    ol {
+        @include list;
+
+        padding-left: spacing.$list-padding;
+    }
+
+    li {
+        margin: spacing.$grid-column-gap 0;
+    }
+
+    span {
+        text-decoration-line: underline;
+
+        text-decoration-thickness: 4%;
+        text-underline-offset: 5%;
+    }
 }
diff --git a/util/index.js b/util/index.js
new file mode 100644
index 000000000..436e901a1
--- /dev/null
+++ b/util/index.js
@@ -0,0 +1,14 @@
+import { getMenu } from 'commerce/shopify';
+
+export const getTags = async ({ product }) => {
+    const typesMenu = await getMenu('types-nav');
+
+    const types = typesMenu?.map(item => /search\/(\w+)/.exec(item?.path)?.[1]);
+    const tags = product?.collections?.nodes
+        ?.map(col => col?.title)
+        ?.filter(col => types?.includes(col?.toLowerCase()));
+
+    return tags;
+};
+
+export const listTags = ({ tags }) => `(${tags.join(', ')})`;