diff --git a/doc/_quarto.yml b/doc/_quarto.yml
index dd94735d..eef5d377 100644
--- a/doc/_quarto.yml
+++ b/doc/_quarto.yml
@@ -47,7 +47,8 @@ website:
href: syntax/clause/project.qmd
- text: "`LABEL`"
href: syntax/clause/label.qmd
- - examples.qmd
+ - text: Gallery
+ href: gallery/index.qmd
- href: faq.qmd
text: FAQ
tools:
diff --git a/doc/gallery/examples/bar-chart.qmd b/doc/gallery/examples/bar-chart.qmd
new file mode 100644
index 00000000..42ab2382
--- /dev/null
+++ b/doc/gallery/examples/bar-chart.qmd
@@ -0,0 +1,62 @@
+---
+title: "Bar Chart"
+description: "Categorical comparisons using bars"
+image: thumbnails/bar-chart.svg
+categories: [basic, bar]
+order: 3
+---
+
+Bar charts display categorical data with rectangular bars. The height of each bar represents the value for that category.
+
+## Code
+
+```{ggsql}
+SELECT species, COUNT(*) as count FROM ggsql:penguins
+GROUP BY species
+VISUALISE species AS x, count AS y, species AS fill
+DRAW bar
+LABEL
+ title => 'Penguin Count by Species',
+ x => 'Species',
+ y => 'Count'
+```
+
+## Explanation
+
+- The SQL query aggregates penguin counts by species
+- `species AS x` places species names on the x-axis
+- `count AS y` sets the bar height based on the count
+- `species AS fill` colors each bar by species
+- `DRAW bar` creates vertical bars
+
+## Variations
+
+### Horizontal Bars
+
+Use `PROJECT y, x TO cartesian` to flip the axes for horizontal bars:
+
+```{ggsql}
+SELECT species, COUNT(*) as count FROM ggsql:penguins
+GROUP BY species
+VISUALISE species AS x, count AS y, species AS fill
+DRAW bar
+PROJECT y, x TO cartesian
+LABEL
+ title => 'Penguin Count by Species',
+ x => 'Species',
+ y => 'Count'
+```
+
+### Auto-Count Bar Chart
+
+When you don't specify a y aesthetic, ggsql automatically counts occurrences:
+
+```{ggsql}
+SELECT species FROM ggsql:penguins
+VISUALISE species AS x
+DRAW bar
+LABEL
+ title => 'Penguin Distribution',
+ x => 'Species',
+ y => 'Count'
+```
diff --git a/doc/gallery/examples/faceted.qmd b/doc/gallery/examples/faceted.qmd
new file mode 100644
index 00000000..84e204c2
--- /dev/null
+++ b/doc/gallery/examples/faceted.qmd
@@ -0,0 +1,60 @@
+---
+title: "Faceted Plot"
+description: "Small multiples showing data split by category"
+image: thumbnails/faceted.svg
+categories: [faceted, advanced]
+order: 6
+---
+
+Faceted plots (small multiples) split data into separate panels by one or more categorical variables. This makes it easy to compare patterns across groups.
+
+## Code
+
+```{ggsql}
+SELECT bill_len, bill_dep, species FROM ggsql:penguins
+VISUALISE bill_len AS x, bill_dep AS y
+DRAW point
+FACET species
+LABEL
+ title => 'Bill Dimensions by Species',
+ x => 'Bill Length (mm)',
+ y => 'Bill Depth (mm)'
+```
+
+## Explanation
+
+- `FACET species` creates a separate panel for each penguin species
+- Each panel shows the same scatter plot, filtered to that species
+- This reveals species-specific patterns that might be hidden in a combined view
+
+## Variations
+
+### Grid Layout with Two Variables
+
+Use `FACET rows BY cols` to create a grid layout:
+
+```{ggsql}
+SELECT bill_len, bill_dep, species, island FROM ggsql:penguins
+VISUALISE bill_len AS x, bill_dep AS y
+DRAW point
+FACET species BY island
+LABEL
+ title => 'Bill Dimensions by Species and Island',
+ x => 'Bill Length (mm)',
+ y => 'Bill Depth (mm)'
+```
+
+### Free Scales
+
+Allow each facet to have independent axis scales with `SETTING free`:
+
+```{ggsql}
+SELECT bill_len, bill_dep, species FROM ggsql:penguins
+VISUALISE bill_len AS x, bill_dep AS y
+DRAW point
+FACET species SETTING free => 'y'
+LABEL
+ title => 'Bill Dimensions (Free Y Scale)',
+ x => 'Bill Length (mm)',
+ y => 'Bill Depth (mm)'
+```
diff --git a/doc/gallery/examples/histogram.qmd b/doc/gallery/examples/histogram.qmd
new file mode 100644
index 00000000..b2479317
--- /dev/null
+++ b/doc/gallery/examples/histogram.qmd
@@ -0,0 +1,74 @@
+---
+title: "Histogram"
+description: "Distribution of a single numeric variable"
+image: thumbnails/histogram.svg
+categories: [statistical, distribution]
+order: 4
+---
+
+Histograms show the distribution of a numeric variable by grouping values into bins and counting occurrences in each bin.
+
+## Code
+
+```{ggsql}
+SELECT Temp FROM ggsql:airquality
+VISUALISE Temp AS x
+DRAW histogram
+LABEL
+ title => 'Temperature Distribution',
+ x => 'Temperature (F)',
+ y => 'Count'
+```
+
+## Explanation
+
+- `VISUALISE Temp AS x` specifies the variable to bin
+- `DRAW histogram` automatically computes bins and counts
+- No y mapping is needed - ggsql computes the count automatically
+
+## Variations
+
+### Custom Bin Count
+
+Control the number of bins with `SETTING bins`:
+
+```{ggsql}
+SELECT Temp FROM ggsql:airquality
+VISUALISE Temp AS x
+DRAW histogram
+ SETTING bins => 15
+LABEL
+ title => 'Temperature Distribution (15 bins)',
+ x => 'Temperature (F)',
+ y => 'Count'
+```
+
+### Custom Bin Width
+
+Set explicit bin width instead of count:
+
+```{ggsql}
+SELECT Temp FROM ggsql:airquality
+VISUALISE Temp AS x
+DRAW histogram
+ SETTING binwidth => 5
+LABEL
+ title => 'Temperature Distribution (5 degree bins)',
+ x => 'Temperature (F)',
+ y => 'Count'
+```
+
+### Density Instead of Count
+
+Use `REMAPPING` to show density (proportion) instead of count:
+
+```{ggsql}
+SELECT Temp FROM ggsql:airquality
+VISUALISE Temp AS x
+DRAW histogram
+ REMAPPING density AS y
+LABEL
+ title => 'Temperature Density',
+ x => 'Temperature (F)',
+ y => 'Density'
+```
diff --git a/doc/gallery/examples/line-chart.qmd b/doc/gallery/examples/line-chart.qmd
new file mode 100644
index 00000000..e988113b
--- /dev/null
+++ b/doc/gallery/examples/line-chart.qmd
@@ -0,0 +1,45 @@
+---
+title: "Line Chart"
+description: "Time series visualization with proper date scaling"
+image: thumbnails/line-chart.svg
+categories: [basic, line, time-series]
+order: 2
+---
+
+Line charts are ideal for showing trends over time. The `SCALE x VIA date` clause ensures proper date formatting on the axis.
+
+## Code
+
+```{ggsql}
+SELECT Date, Temp FROM ggsql:airquality
+VISUALISE Date AS x, Temp AS y
+DRAW line
+SCALE x VIA date
+LABEL
+ title => 'Daily Temperature',
+ x => 'Date',
+ y => 'Temperature (F)'
+```
+
+## Explanation
+
+- `SELECT ... FROM ggsql:airquality` queries the built-in air quality dataset
+- `VISUALISE Date AS x, Temp AS y` maps the date column to x and temperature to y
+- `DRAW line` connects data points with lines
+- `SCALE x VIA date` ensures the x-axis is formatted as dates with appropriate tick marks
+- `LABEL` provides descriptive titles for the chart and axes
+
+## Variations
+
+### Multiple Lines by Category
+
+```{ggsql}
+SELECT Date, Temp, Month FROM ggsql:airquality
+VISUALISE Date AS x, Temp AS y, Month AS color
+DRAW line
+SCALE x VIA date
+LABEL
+ title => 'Daily Temperature by Month',
+ x => 'Date',
+ y => 'Temperature (F)'
+```
diff --git a/doc/gallery/examples/multi-layer.qmd b/doc/gallery/examples/multi-layer.qmd
new file mode 100644
index 00000000..b0a4924f
--- /dev/null
+++ b/doc/gallery/examples/multi-layer.qmd
@@ -0,0 +1,76 @@
+---
+title: "Multi-Layer Plot"
+description: "Combining multiple geometric layers in one visualization"
+image: thumbnails/multi-layer.svg
+categories: [layers, advanced]
+order: 5
+---
+
+Multi-layer plots combine different geometric elements (geoms) to create richer visualizations. Each `DRAW` clause adds a new layer.
+
+## Code
+
+```{ggsql}
+SELECT Date, Temp FROM ggsql:airquality
+VISUALISE Date AS x, Temp AS y
+DRAW line
+ SETTING color => 'steelblue'
+DRAW point
+ SETTING size => 4, color => 'darkblue'
+SCALE x VIA date
+LABEL
+ title => 'Temperature with Line and Points',
+ x => 'Date',
+ y => 'Temperature (F)'
+```
+
+## Explanation
+
+- The first `DRAW line` creates a line connecting all points
+- The second `DRAW point` adds point markers at each data point
+- `SETTING` on each layer controls that layer's visual properties
+- Both layers share the same x and y mappings from `VISUALISE`
+
+## Variations
+
+### Different Aesthetics Per Layer
+
+Each layer can have its own aesthetic mappings using `MAPPING`:
+
+```{ggsql}
+SELECT Date, Temp, Ozone FROM ggsql:airquality
+VISUALISE Date AS x
+DRAW line
+ MAPPING Temp AS y, 'Temperature' AS color
+DRAW line
+ MAPPING Ozone AS y, 'Ozone' AS color
+SCALE x VIA date
+LABEL
+ title => 'Temperature and Ozone Over Time',
+ x => 'Date',
+ y => 'Value'
+```
+
+### Layers from Different Data Sources
+
+Use `MAPPING ... FROM` to pull each layer from different CTEs:
+
+```{ggsql}
+WITH temps AS (
+ SELECT Date, Temp as value FROM ggsql:airquality
+),
+ozone AS (
+ SELECT Date, Ozone as value FROM ggsql:airquality WHERE Ozone IS NOT NULL
+)
+VISUALISE
+DRAW line
+ MAPPING Date AS x, value AS y, 'Temperature' AS color FROM temps
+DRAW point
+ MAPPING Date AS x, value AS y, 'Ozone' AS color FROM ozone
+ SETTING size => 3
+SCALE x VIA date
+LABEL
+ title => 'Temperature vs Ozone',
+ x => 'Date',
+ y => 'Value'
+```
diff --git a/doc/gallery/examples/scatterplot.qmd b/doc/gallery/examples/scatterplot.qmd
new file mode 100644
index 00000000..e3d59100
--- /dev/null
+++ b/doc/gallery/examples/scatterplot.qmd
@@ -0,0 +1,40 @@
+---
+title: "Scatter Plot"
+description: "Basic scatter plot mapping two numeric variables to position"
+image: thumbnails/scatterplot.svg
+categories: [basic, point]
+order: 1
+---
+
+A scatter plot displays the relationship between two numeric variables by mapping them to x and y positions. This is one of the most fundamental visualization types for exploring correlations and patterns.
+
+## Code
+
+```{ggsql}
+VISUALISE bill_len AS x, bill_dep AS y FROM ggsql:penguins
+DRAW point
+LABEL
+ title => 'Penguin Bill Dimensions',
+ x => 'Bill Length (mm)',
+ y => 'Bill Depth (mm)'
+```
+
+## Explanation
+
+- `VISUALISE ... FROM ggsql:penguins` loads the built-in penguins dataset
+- `bill_len AS x, bill_dep AS y` maps bill length to the x-axis and bill depth to the y-axis
+- `DRAW point` creates a scatter plot using points
+- `LABEL` adds descriptive axis labels and a title
+
+## Variations
+
+### With Color by Species
+
+```{ggsql}
+VISUALISE bill_len AS x, bill_dep AS y, species AS color FROM ggsql:penguins
+DRAW point
+LABEL
+ title => 'Penguin Bill Dimensions by Species',
+ x => 'Bill Length (mm)',
+ y => 'Bill Depth (mm)'
+```
diff --git a/doc/gallery/examples/thumbnails/bar-chart.svg b/doc/gallery/examples/thumbnails/bar-chart.svg
new file mode 100644
index 00000000..19dfe657
--- /dev/null
+++ b/doc/gallery/examples/thumbnails/bar-chart.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/doc/gallery/examples/thumbnails/faceted.svg b/doc/gallery/examples/thumbnails/faceted.svg
new file mode 100644
index 00000000..e0d14c16
--- /dev/null
+++ b/doc/gallery/examples/thumbnails/faceted.svg
@@ -0,0 +1 @@
+
diff --git a/doc/gallery/examples/thumbnails/histogram.svg b/doc/gallery/examples/thumbnails/histogram.svg
new file mode 100644
index 00000000..fe5e2d58
--- /dev/null
+++ b/doc/gallery/examples/thumbnails/histogram.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/doc/gallery/examples/thumbnails/line-chart.svg b/doc/gallery/examples/thumbnails/line-chart.svg
new file mode 100644
index 00000000..16edd43f
--- /dev/null
+++ b/doc/gallery/examples/thumbnails/line-chart.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/doc/gallery/examples/thumbnails/multi-layer.svg b/doc/gallery/examples/thumbnails/multi-layer.svg
new file mode 100644
index 00000000..5d43bc93
--- /dev/null
+++ b/doc/gallery/examples/thumbnails/multi-layer.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/doc/gallery/examples/thumbnails/scatterplot.svg b/doc/gallery/examples/thumbnails/scatterplot.svg
new file mode 100644
index 00000000..6df1bc98
--- /dev/null
+++ b/doc/gallery/examples/thumbnails/scatterplot.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/doc/gallery/index.qmd b/doc/gallery/index.qmd
new file mode 100644
index 00000000..5acd6f8a
--- /dev/null
+++ b/doc/gallery/index.qmd
@@ -0,0 +1,13 @@
+---
+title: Example Gallery
+listing:
+ contents: examples
+ type: grid
+ grid-columns: 3
+ image-height: 200px
+ fields: [image, title, description]
+ sort: "order"
+ categories: true
+---
+
+Browse examples of ggsql visualizations. Click any example to see the full code and explanation.
diff --git a/doc/styles.scss b/doc/styles.scss
index edf1138b..d0265c52 100644
--- a/doc/styles.scss
+++ b/doc/styles.scss
@@ -369,6 +369,15 @@ code {
}
}
+// Hide category badges on gallery example pages
+.quarto-listing-category {
+ // Keep category sidebar for filtering
+}
+
+body:has(.quarto-title) .quarto-categories {
+ display: none;
+}
+
.btn {
display: inline-block;
padding: 0.75rem 1.5rem;