tiles--halves.html
<section class="tiles">
    <div class="container">
        <div class="tiles__grid -featured">
            <!-- 
                Halves grid: 4 columns at desktop
                Add any number of tiles, any combination of modifiers
            -->
            <div class="tiles__set -halves">
                <div class="tiles__tile -featured">
                    <div class="tiles__media">
                        <img src="https://images.unsplash.com/photo-1557682250-33bd709cbe85?w=800&h=800&fit=crop" alt="">
                    </div>
                    <button class="tiles__more">
                        <svg class="icon" aria-hidden="true" role="presentation">
                            <use href="/main-icons-sprite.svg#images" />
                        </svg>
                        View 10 More Images
                    </button>
                </div>

                <div class="tiles__tile">
                    <div class="tiles__media">
                        <img src="https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=400&h=400&fit=crop" alt="">
                    </div>
                </div>

                <div class="tiles__tile">
                    <div class="tiles__media">
                        <img src="https://images.unsplash.com/photo-1614850523296-d8c1af93d400?w=400&h=400&fit=crop" alt="">
                    </div>
                </div>

                <div class="tiles__tile -wide">
                    <div class="tiles__media">
                        <img src="https://images.unsplash.com/photo-1557682224-5b8590cd9ec5?w=600&h=300&fit=crop" alt="">
                    </div>
                </div>
            </div>

            <!-- 
                4-column grid: Small + Wide + Small (1+2+1 = 4 cols)
                Visually continues from the section above (no extra gap)
            -->
            <div class="tiles__set -halves">
                <div class="tiles__tile">
                    <div class="tiles__media">
                        <img src="https://images.unsplash.com/photo-1501854140801-50d01698950b?w=400&h=400&fit=crop" alt="">
                    </div>
                </div>

                <div class="tiles__tile -wide">
                    <div class="tiles__media">
                        <img src="https://images.unsplash.com/photo-1433086966358-54859d0ed716?w=800&h=400&fit=crop" alt="">
                    </div>
                </div>

                <div class="tiles__tile">
                    <div class="tiles__media">
                        <img src="https://images.unsplash.com/photo-1439066615861-d1af74d74000?w=400&h=400&fit=crop" alt="">
                    </div>
                </div>
            </div>
        </div>
    </div>
</section>
index.scss
@use "@/core" as *;

.tiles {
    $b: &;

    padding: 2rem 0;

    &__grid {
        --columns: 1;
        --gap: var(--root-gap, 1rem);

        display: grid;
        gap: var(--gap);
        grid-template-columns: repeat(var(--columns), 1fr);
        grid-auto-flow: dense;

        @include at(md) {
            --columns: 2;
        }

        @include at(lg) {
            --columns: 4;
        }
    }

    &__tile {
        background-color: var(--tile-bg, var(--color-gray-100, #f5f5f5));
        display: grid;
        overflow: hidden;
        position: relative;

        // Position for content tiles
        &.-has-content {
            align-items: end;
        }
    }

    &__media {
        height: 100%;
        left: 0;
        position: absolute;
        top: 0;
        width: 100%;

        img,
        video {
            height: 100%;
            object-fit: cover;
            width: 100%;
        }
    }

    &__content {
        display: flex;
        flex-direction: column;
        gap: 1.5rem;
        max-width: 32rem;
        padding: 2rem;
        position: relative;
        z-index: 1;
    }

    &__heading {
        color: inherit;
        font-family: var(--root-font-family-heading);
        font-size: clamp(2rem, 5vw, 3.5rem);
        font-weight: 700;
        line-height: 1.1;
        margin: 0;
    }

    &__description {
        line-height: 1.5;
        margin: 0;
    }

    &__actions {
        display: flex;
        flex-wrap: wrap;
        gap: 1rem;
        margin-block-start: 0.5rem;
    }

    // Dark theme for tiles with dark backgrounds
    &__tile.-dark {
        color: #fff;

        #{$b}__heading,
        #{$b}__description {
            color: inherit;
        }

        .button.-outline {
            border-color: currentColor;
            color: inherit;

            &:hover {
                background-color: #fff;
                border-color: #fff;
                color: #000;
            }
        }
    }

    // Light theme for tiles with light backgrounds
    &__tile.-light {
        color: var(--root-color, #333);
    }

    // Featured layout container
    &__grid.-featured {
        display: flex;
        flex-direction: column;
        gap: var(--gap);
    }

    // ==========================================================================
    // Tile Sets - Flexible grid layouts
    // 
    // Set modifiers define the column structure:
    //   -thirds  : 3-column grid at desktop (for 1/3 + 2/3 style layouts)
    //   -halves  : 4-column grid at desktop (for 50/50 style layouts)
    //
    // Tile modifiers control spanning (work in any set):
    //   -featured : Spans 2 columns × 2 rows (large square)
    //   -wide     : Spans 2 columns × 1 row (horizontal banner)
    //   -tall     : Spans 1 column × 2 rows (vertical banner)
    //   (default) : 1 column × 1 row (small square)
    //
    // Tiles flow in DOM order with dense packing to fill gaps.
    // Add as many tiles as needed - the grid adapts automatically.
    // ==========================================================================
    &__set {
        container-type: inline-size;
        display: grid;
        gap: var(--gap);
        grid-auto-flow: dense;

        // Thirds: 3-column grid
        &.-thirds {
            --_tile-size: 100cqi; // Mobile: full width
            grid-template-columns: 1fr;

            @include at(sm) {
                --_tile-size: calc((100cqi - var(--gap)) / 2);
                grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
            }

            @include at(md) {
                --_tile-size: calc((100cqi - var(--gap) * 2) / 3);
                grid-template-columns: repeat(3, 1fr);
            }

            // Staggered layout: uses -short (1 row) and -fill (2 rows) tiles
            // No explicit row heights - tiles define their own heights via aspect-ratio/min-height
            &.-staggered {
                // Row-major flow: dense backfills holes and can strand late tiles in implicit/zero-sized tracks
                // with rowspan + mixed spans (last items “missing” in some engines).
                grid-auto-columns: minmax(0, 1fr);
                grid-auto-flow: row;

                // Short height = tile-width × 9/16
                --_short-height: calc(var(--_tile-size) * 0.5625);
                
                #{$b}__tile.-fill {
                    // Fill = 2 shorts + 1 gap
                    min-height: calc(var(--_short-height) * 2 + var(--gap));
                    aspect-ratio: auto;
                }

                // Absolute .tiles__media gives no in-flow height — tile needs intrinsic block size.
                // aspect-ratio:auto + height:100% below md was cyclic → 0× height in some rows.
                #{$b}__tile.-short {
                    align-self: stretch;
                    aspect-ratio: 16 / 9;
                    height: auto;
                    justify-self: stretch;
                    min-height: 0;
                    width: 100%;

                    @include at(md) {
                        align-self: start;
                    }
                }

                // Sandwich last two -short tiles between fills (demo = 6 tiles). md+ uses DOM order.
                > .tiles__tile:nth-child(1) {
                    order: 1;
                }

                > .tiles__tile:nth-child(2) {
                    order: 6;
                }

                > .tiles__tile:nth-child(3) {
                    order: 2;
                }

                > .tiles__tile:nth-child(4) {
                    order: 5;
                }

                > .tiles__tile:nth-child(5) {
                    order: 3;
                }

                > .tiles__tile:nth-child(6) {
                    order: 4;
                }

                @include at(md) {
                    > .tiles__tile:nth-child(1),
                    > .tiles__tile:nth-child(2),
                    > .tiles__tile:nth-child(3),
                    > .tiles__tile:nth-child(4),
                    > .tiles__tile:nth-child(5),
                    > .tiles__tile:nth-child(6) {
                        order: 0;
                    }
                }
            }
        }

        // Halves: 4-column grid (good for 50/50 layouts with sub-divisions)
        &.-halves {
            --_tile-size: 100cqi; // Mobile: full width
            grid-template-columns: 1fr;

            @include at(sm) {
                --_tile-size: calc((100cqi - var(--gap)) / 2);
                // Allow columns to shrink more before dropping
                grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
            }

            @include at(md) {
                --_tile-size: calc((100cqi - var(--gap) * 3) / 4);
                grid-template-columns: repeat(4, 1fr);
            }
        }

        // =======================================================================
        // Tile modifiers - control spanning behavior (mobile-first)
        // Default: 1×1 square tile on all screens
        // Modifiers add spanning at md breakpoint and above
        // =======================================================================

        // Default: 1×1 square tile
        #{$b}__tile {
            aspect-ratio: 1;
        }

        // Featured: Large 2×2 tile at md+ (square on mobile)
        #{$b}__tile.-featured {
            @include at(sm) {
                // Span all available columns at sm to avoid implicit tracks.
                grid-column: 1 / -1;
                grid-row: span 2;
            }

            @include at(md) {
                grid-column: span 2;
                grid-row: span 2;
            }
        }

        // Wide: Horizontal 2×1 tile at md+ (square on mobile)
        #{$b}__tile.-wide {
            @include at(sm) {
                // Span all available columns at md to avoid implicit tracks.
                grid-column: 1 / -1;
                aspect-ratio: auto;
                align-self: stretch;
                min-height: var(--_tile-size, 200px);
            }

            @include at(md) {
                grid-column: span 2;
            }
        }

        // Tall: Vertical 1×2 tile at md+ (square on mobile)
        #{$b}__tile.-tall {
            @include at(sm) {
                grid-row: span 2;
                aspect-ratio: auto;
                align-self: stretch;
                min-height: calc(var(--_tile-size, 200px) * 2 + var(--gap));
            }
        }

        // Short: default compact landscape from sm (square below sm via &__tile); staggered overrides below md
        #{$b}__tile.-short {
            @include at(sm) {
                aspect-ratio: 16 / 9;
            }
        }

        // Fill: Spans 2 grid rows at md+, pairs with -short tiles (square on mobile)
        #{$b}__tile.-fill {
            @include at(md) {
                grid-row: span 2;
                aspect-ratio: auto;
                align-self: stretch;
            }
        }
    }

    // "View more" button - positioned within tile content
    &__more {
        align-items: center;
        align-self: flex-end;
        background: #fff;
        border-radius: var(--root-border-radius);
        border: none;
        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
        color: var(--link-color, var(--color-blue));
        cursor: pointer;
        display: inline-flex;
        font-size: 0.875rem;
        font-weight: 500;
        gap: 0.5rem;
        margin: auto 2rem 2rem auto; // Push to bottom-right with tile padding
        padding: 0.5rem 1rem;
        position: relative;
        transition: box-shadow var(--root-ease-out-fast);
        white-space: nowrap;
        z-index: 2;

        &:hover {
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
        }

        svg,
        .icon {
            fill: currentColor;
            height: 1em;
            width: 1em;
        }
    }

    // Gradient overlay for better text readability
    &__overlay {
        background: linear-gradient(
            to top,
            rgba(0, 0, 0, 0.7) 0%,
            rgba(0, 0, 0, 0.3) 50%,
            rgba(0, 0, 0, 0) 100%
        );
        inset: 0;
        position: absolute;
        z-index: 0;
    }
}