Every webpage you have ever visited has a background. Sometimes it is a plain colour · like the white page of a news article. Sometimes it is a beautiful photograph spanning the full screen. Sometimes it is a subtle repeating pattern that gives texture to the design. All of these effects are created with one family of CSS properties: the background properties.
Think of an HTML element as a picture frame. The content (text, images, buttons) is the artwork inside the frame. The background is the wall behind the artwork · the surface you see wherever there is no content covering it.
In this lesson you will master every background property CSS offers, one at a time, from the simplest to the most sophisticated. By the end you will be able to:
- Set a solid background colour for any element
- Place a background image behind any element
- Control whether an image repeats (tiles) or appears only once
- Control the position of a background image
- Decide whether the background scrolls with the page or stays fixed
- Write all background properties in a single line using the shorthand property
- Combine everything into a polished, real-world webpage section
What is a CSS property?
A CSS property is a specific visual characteristic you can control. For example, color controls text colour, font-size controls text size, and background-color controls the background colour of an element.
What does "behind" mean in CSS?
HTML elements are stacked in layers. The background of an element sits behind its text and child elements. So if you set a red background on a <div> that contains a paragraph, the red colour appears under the paragraph text · not on top of it.
What is a URL in CSS?
When you reference an image file in CSS, you wrap its path in url():
background-image: url("photo.jpg");This tells the browser: "Go find the file called photo.jpg and use it here." The path can be a file path ("images/banner.jpg") or a full web address ("https://example.com/img/pattern.png").
What is it?
background-color sets the solid fill colour of an element's background area · the space behind the element's content and padding.
Why does it exist?
Before images or gradients, colour is the most fundamental background tool. Solid background colours are used everywhere: page backgrounds, navigation bars, buttons, cards, alerts, hero banners, table rows, and more.
How colour values work
You can write colour values in several ways in CSS:
| Format | Example | Meaning |
|---|---|---|
| Named colour | red, blue, navy | English colour name |
| HEX code | #ff0000 | 6-digit hexadecimal value |
| Short HEX | #f00 | 3-digit shorthand for HEX |
| RGB | rgb(255, 0, 0) | Red, Green, Blue 0 · 255 |
| RGBA | rgba(255, 0, 0, 0.5) | RGB + alpha (transparency) |
| HSL | hsl(0, 100%, 50%) | Hue, Saturation, Lightness |
All of these formats work with background-color.
Simple Example 1 · Colour the whole page body
HTML + CSS:
<!DOCTYPE html>
<html>
<head>
<style>
body {
background-color: lightblue;
}
</style>
</head>
<body>
<h1>Hello, World!</h1>
<p>This page has a light blue background.</p>
</body>
</html>Expected Output: The entire visible webpage background turns light blue. The heading and paragraph text sit on top of this colour.
Line-by-line explanation:
body· selects the<body>element, which represents the entire visible page areabackground-color: lightblue;· fills the body's background with the named colour "lightblue"
Simple Example 2 · Colour individual elements differently
You can set different background colours for different elements on the same page:
<style>
body {
background-color: #f0f0f0; /* light grey page */
}
h1 {
background-color: navy;
color: white;
padding: 10px;
}
p {
background-color: #fff9c4; /* pale yellow */
padding: 8px;
}
</style>Expected Output:
- The page body has a light grey background
- The
<h1>heading has a navy background with white text - Each
<p>paragraph has a pale yellow background
Thinking Prompt: The
bodyhas grey background. Theh1has navy. What colour do you see around theh1? Is it grey or navy? (Answer: You see grey around it · the body grey shows where the h1 does not cover.)
Simple Example 3 · Using HEX and RGBA
div {
background-color: #2c3e50; /* dark blue-grey HEX */
color: white;
padding: 20px;
}
.transparent-box {
background-color: rgba(0, 0, 255, 0.2); /* 20% opacity blue */
}Expected Output:
- The
<div>has a rich dark navy background - The
.transparent-boxhas a very faint, see-through blue background (you can see content behind it)
The opacity property vs RGBA
There are two ways to make a background semi-transparent:
Method 1: RGBA background (preferred · only the background becomes transparent):
div {
background-color: rgba(0, 0, 255, 0.3);
}Method 2: opacity property (entire element becomes transparent · including text!):
div {
background-color: blue;
opacity: 0.3; /* Text also becomes faded! */
}⚠️ Important difference: RGBA only makes the background colour transparent. The
opacityproperty makes the entire element · including its text and child elements · transparent. Use RGBA when you only want to affect the background.
Real-World Use Case
Almost every professional website uses background-color extensively:
/* Navigation bar */
nav {
background-color: #1a1a2e;
}
/* Alert/notification box */
.alert-success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
padding: 12px 16px;
}
/* Button */
.btn-primary {
background-color: #007bff;
color: white;
padding: 10px 20px;
}background-color: Painting With a Solid Colour. Open your editor, type the examples above by hand, modify them, and observe what changes.What is it?
background-image places an image (or a gradient) in the background of an element · behind its text and content.
Why does it exist?
Flat colours are great for many uses, but sometimes you need photography, textures, patterns, or gradients as a backdrop. Background images make hero sections, full-screen landing pages, patterned cards, and decorative headers possible.
How it works
element {
background-image: url("path/to/image.jpg");
}The url() function points to the image file. The path can be:
- Relative:
"images/banner.jpg"· relative to your CSS file's location - Absolute:
"https://example.com/img/photo.jpg"· a full web URL
Simple Example 1 · Set a background image on the body
body {
background-image: url("paper.gif");
}Expected Output: The image paper.gif tiles (repeats) to fill the entire page background by default.
Why does it repeat? By default, CSS tiles (repeats) background images in both directions to fill the available space · like wallpaper. You will learn to control this in Part 3.
Simple Example 2 · Background image on a specific element
<style>
.hero {
background-image: url("mountain.jpg");
height: 400px;
width: 100%;
}
</style>
<div class="hero">
<h1>Welcome to My Website</h1>
</div>Expected Output: A 400px tall section with the mountain photo as a background. The heading "Welcome to My Website" appears on top of the image.
Line-by-line explanation:
background-image: url("mountain.jpg");· loads the mountain photo as the backgroundheight: 400px;· the div needs an explicit height, otherwise it would collapse if it has no content height to fillwidth: 100%;· makes the div span the full width of its container
Simple Example 3 · Stacking a background image on top of a background colour
A very useful technique: set both a background-color and background-image. The colour acts as a fallback · it shows if the image fails to load:
.hero {
background-image: url("banner.jpg");
background-color: #2c3e50; /* shows if image doesn't load */
height: 400px;
}Expected Output: If banner.jpg loads successfully, the photo appears. If the image file cannot be found or takes too long to load, the user sees a dark navy background instead of a broken/blank area.
Pro Tip: Always set a
background-colorfallback when usingbackground-image. This is essential for accessibility and robustness in real projects.
Important: Background Image vs <img> Tag
You might wonder: when should you use background-image in CSS versus an <img> tag in HTML?
Use background-image (CSS) | Use <img> (HTML) |
|---|---|
| Decorative images (textures, hero backdrops) | Content images (product photos, diagrams) |
| When you want text layered on top | When the image IS the content |
| Patterns and design elements | Profile pictures, logos |
Images that don't need alt text | Images that need accessibility descriptions |
background-image: Placing an Image Behind Your Content. Open your editor, type the examples above by hand, modify them, and observe what changes.What is it?
By default, CSS tiles (repeats) background images in both the horizontal and vertical directions to fill the available space. background-repeat lets you control · or stop · this tiling behaviour.
Why does it exist?
Tiling is perfect for small texture and pattern images, but it is a disaster for large photos. Imagine a beautiful landscape photo tiled across your page dozens of times · it would look broken and ugly. background-repeat gives you control over how (and whether) the image repeats.
The four main values
| Value | What it does |
|---|---|
repeat | Repeats in both X and Y directions (default) |
repeat-x | Repeats only horizontally (left to right) |
repeat-y | Repeats only vertically (top to bottom) |
no-repeat | Image appears exactly once · no tiling |
Simple Example 1 · Default behaviour (repeat in both directions)
body {
background-image: url("small-pattern.png");
background-repeat: repeat; /* this is the default */
}Expected Output: The small-pattern.png tiles like wallpaper across the entire page in rows and columns.
Simple Example 2 · Repeat only horizontally
A horizontal stripe effect · great for decorative top/bottom borders:
body {
background-image: url("gradient-stripe.png");
background-repeat: repeat-x;
}Expected Output: The image repeats from left to right in a single row across the top of the page. It does NOT repeat downward. Below the first row, the background colour (or white default) shows.
Simple Example 3 · Repeat only vertically
body {
background-image: url("side-pattern.png");
background-repeat: repeat-y;
}Expected Output: The image repeats from top to bottom in a single column on the left side of the page. It does NOT repeat horizontally.
Simple Example 4 · No repeat (most common for photo backgrounds)
body {
background-image: url("mountain-photo.jpg");
background-repeat: no-repeat;
}Expected Output: The mountain photo appears exactly once in the top-left corner of the page. The rest of the page background uses the default white (or whatever background-color is set).
Thinking Prompt: After seeing the photo in the corner, you will learn in Part 4 how to position it differently · in the centre, or stretched to fill the whole page.
background-position · Placing the Image Precisely
When using no-repeat, the image defaults to the top-left corner. background-position lets you move it anywhere.
Syntax:
background-position: horizontal vertical;Keyword values:
- Horizontal:
left,center,right - Vertical:
top,center,bottom
Pixel values:
background-position: 50px 100px;· 50px from left, 100px from top
Percentage values:
background-position: 50% 50%;· perfectly centred
Simple Example · Centre a background image
body {
background-image: url("mountain-photo.jpg");
background-repeat: no-repeat;
background-position: center top;
}Expected Output: The mountain photo appears once, centred horizontally, aligned to the top of the page.
Simple Example · Centre both ways
body {
background-image: url("logo-watermark.png");
background-repeat: no-repeat;
background-position: center center;
}Expected Output: The image appears exactly once, perfectly centred both horizontally and vertically on the page.
Real-World Use Case
Patterns (small repeating images) are commonly used for subtle textures:
.card {
background-image: url("subtle-dots.png");
background-repeat: repeat;
background-color: #ffffff;
padding: 30px;
}Large hero photos always use no-repeat:
.hero-section {
background-image: url("hero-photo.jpg");
background-repeat: no-repeat;
background-position: center center;
height: 600px;
}background-repeat: Controlling the Tile Behaviour. Open your editor, type the examples above by hand, modify them, and observe what changes.What is it?
background-attachment controls whether a background image moves with the page as you scroll, or stays fixed in place while the content scrolls over it.
Why does it exist?
This single property creates one of the most impressive visual effects in web design: the parallax effect · where the background appears to stay still while foreground content scrolls past it. It creates an illusion of depth and is widely used in modern landing pages.
The two main values
| Value | What it does |
|---|---|
scroll | Background moves with the page as you scroll (default behaviour) |
fixed | Background stays fixed in the viewport; content scrolls over it (parallax effect) |
There is also a third value local which fixes the image relative to the element's own scrolling, but scroll and fixed cover 95% of real use cases.
Simple Example 1 · Default scroll behaviour
body {
background-image: url("nature.jpg");
background-attachment: scroll; /* this is the default */
background-repeat: no-repeat;
}Expected Output: When you scroll the page, the background image moves upward along with the rest of the content · everything scrolls together as one unit.
Simple Example 2 · Fixed background (parallax effect)
body {
background-image: url("nature.jpg");
background-attachment: fixed;
background-repeat: no-repeat;
background-position: center center;
}Expected Output: The background image stays perfectly stationary as you scroll the page. The text and other content move over the image, creating a layered, three-dimensional sensation. This is the classic parallax effect.
Seeing the Difference
The difference between scroll and fixed is most noticeable on long pages:
<style>
body {
background-image: url("forest.jpg");
background-attachment: fixed;
background-size: cover;
background-position: center;
}
.content-section {
background-color: rgba(255, 255, 255, 0.85);
margin: 60px auto;
max-width: 800px;
padding: 40px;
}
</style>
<div class="content-section">
<h2>Section One</h2>
<p>Lots of text here...</p>
</div>
<div class="content-section">
<h2>Section Two</h2>
<p>More text here...</p>
</div>Expected Output: The forest photo fills the page background and stays fixed. The semi-transparent white sections (.content-section) scroll over it smoothly, creating a beautiful parallax effect.
background-size · Making Images Fill the Element
Introduced alongside modern CSS, background-size controls how large the background image is rendered. While not part of the original four properties, it is essential for using background images well.
| Value | What it does |
|---|---|
auto | Image displays at its natural size (default) |
cover | Scales image to cover the entire element · may crop edges |
contain | Scales image to fit entirely within the element · may show gaps |
100px 200px | Sets exact width and height |
50% auto | Sets percentage width, auto height |
.hero {
background-image: url("banner.jpg");
background-size: cover; /* fills the entire hero section */
background-position: center;
background-repeat: no-repeat;
height: 500px;
}Expected Output: The banner image scales to completely cover the 500px hero section. If the image's aspect ratio differs from the container, the image is cropped on the edges but never distorted.
covervscontainanalogy:coveris like filling a frame by zooming in · the whole frame is covered but parts of the photo may be cut off.containis like fitting a photo inside a frame without cropping · all of the photo is visible but there may be empty space around it.
background-attachment: Fixed or Scrolling Background. Open your editor, type the examples above by hand, modify them, and observe what changes.What is it?
Instead of writing five or six separate background property declarations, CSS lets you combine them all into a single line using the background shorthand property.
Why does it exist?
Shorthand properties exist to make code shorter, faster to write, and easier to read. Once you are comfortable with each individual property, the shorthand becomes the natural and preferred way to write backgrounds in professional code.
The full shorthand syntax
background: color image position/size repeat attachment;You can include any or all of these values in a single declaration. The order matters for some values (particularly position/size).
Understanding the Syntax Step by Step
Here is the longhand version:
body {
background-color: #ffffff;
background-image: url("paper.png");
background-position: right top;
background-size: auto;
background-repeat: no-repeat;
background-attachment: fixed;
}And here is the exact same thing as a shorthand:
body {
background: #ffffff url("paper.png") right top / auto no-repeat fixed;
}The / (forward slash) separates background-position from background-size. Everything to the left of / is the position; everything to the right is the size.
Simple Example 1 · Colour and image only
The most minimal shorthand · colour + image:
body {
background: #f4f4f4 url("texture.png");
}This is equivalent to:
body {
background-color: #f4f4f4;
background-image: url("texture.png");
}Expected Output: The body has a light grey colour (fallback), and the texture image tiles over it by default.
Simple Example 2 · Full shorthand with all main values
body {
background: #ffffff url("mountain.jpg") center center / cover no-repeat fixed;
}Breaking this down word by word:
#ffffff→background-color: #ffffff(white fallback)url("mountain.jpg")→background-image: url("mountain.jpg")center center→background-position: center center(centred)/→ separator between position and sizecover→background-size: cover(fill the container)no-repeat→background-repeat: no-repeatfixed→background-attachment: fixed(parallax)
Expected Output: The mountain image covers the full page background, is centred, doesn't repeat, and stays fixed as you scroll · the complete parallax hero effect · all in one line.
Simple Example 3 · Shorthand for a card with a texture
.card {
background: #fff url("dots-pattern.png") repeat;
border: 1px solid #ddd;
padding: 20px;
}Expected Output: Each .card element has a white background with a repeating dot texture on top of it.
What Happens to Values You Don't Specify?
When you use the shorthand and omit a value, CSS resets that property to its initial (default) value. This is an important gotcha:
/* Full longhand set previously: */
div {
background-color: navy;
background-image: url("pattern.png");
background-repeat: no-repeat;
}
/* Then you override with shorthand: */
div {
background: red; /* ← Only sets color; ALL OTHER values reset to default! */
}Result: The image is gone. Repeat is back to repeat. Only background-color: red remains.
⚠️ Important: The
backgroundshorthand resets all unspecified background sub-properties to their defaults. This can cause unexpected results if you use the shorthand to update only one property. When you only want to change one value, use the individual property instead.
All CSS Background Properties · Reference Table
| Property | Purpose | Common Values |
|---|---|---|
background-color | Sets solid background colour | Named colour, HEX, RGB, RGBA |
background-image | Sets background image or gradient | url("file.jpg"), none |
background-repeat | Controls image tiling | repeat, no-repeat, repeat-x, repeat-y |
background-position | Positions the background image | top left, center center, 50% 50%, 0px 0px |
background-size | Controls image size | auto, cover, contain, pixel/percentage values |
background-attachment | Scroll vs fixed behaviour | scroll, fixed, local |
background | Shorthand for all the above | See shorthand syntax |
background Shorthand Property. Open your editor, type the examples above by hand, modify them, and observe what changes.Exercise 1 · Styled Blog Post Header
Objective: Practice background-color, background-image, no-repeat, background-position, and background-size.
Scenario: You are building the header section of a travel blog. The header should display a stunning full-width photo with the blog title on top.
HTML (given):
<!DOCTYPE html>
<html>
<head>
<title>Travel Blog</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header class="blog-header">
<h1>Discover the World</h1>
<p>Adventures, tips and stories from a passionate traveller</p>
</header>
<main class="content">
<article>
<h2>My Trip to the Mountains</h2>
<p>The air was crisp and the views were breathtaking...</p>
</article>
<article>
<h2>Street Food in Lagos</h2>
<p>The flavours of suya and puff puff never get old...</p>
</article>
</main>
</body>
</html>Your Task · write the CSS:
- Style
.blog-headerwith a background image of your choice (use any image URL or a placeholder like"banner.jpg"),no-repeat,coversize, centred position, and a dark navy fallback colour (#1a1a2e). Give it aheightof400px. - Make the text in
.blog-headerwhite so it shows up against the dark image. - Give the
bodyabackground-colorof#f5f5f5(light grey). - Give each
articleabackground-colorof white, some padding (20px), and a bottom margin (16px). - Bonus: Add
background-attachment: fixedto the header to create a parallax effect.
Solution:
/* Task 3 — body background */
body {
background-color: #f5f5f5;
margin: 0;
font-family: Arial, sans-serif;
}
/* Task 1 — header background */
.blog-header {
background-color: #1a1a2e; /* fallback colour */
background-image: url("banner.jpg");
background-repeat: no-repeat;
background-size: cover;
background-position: center center;
background-attachment: fixed; /* bonus parallax */
height: 400px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
/* Task 2 — header text */
.blog-header h1,
.blog-header p {
color: white;
text-align: center;
}
/* Task 4 — article cards */
article {
background-color: white;
padding: 20px;
margin-bottom: 16px;
max-width: 800px;
margin-left: auto;
margin-right: auto;
}Self-check Questions:
- What colour would you see in the header if
banner.jpgcould not be found? - Why does the header need an explicit
height? - What would change if you switched
background-size: covertobackground-size: contain?
Exercise 2 · Pattern Card Grid
Objective: Practice background-repeat, background-color as fallback, and the background shorthand.
Scenario: You are building a grid of feature cards for a design agency website. Each card should have a distinct background personality.
HTML (given):
<div class="card-grid">
<div class="card card-plain">
<h3>Strategy</h3>
<p>We craft brand strategies that resonate.</p>
</div>
<div class="card card-pattern">
<h3>Design</h3>
<p>Clean, modern interfaces tailored to you.</p>
</div>
<div class="card card-dark">
<h3>Development</h3>
<p>Fast, accessible, and beautiful websites.</p>
</div>
</div>Your Task:
- Give
.cardbase styles:padding: 30px,border-radius: 8px,margin: 10px. - Use the
backgroundshorthand to style.card-plainwith only a solid colour:#e8f4fd. - Style
.card-patternusing the shorthand with a white fallback colour, a small repeating pattern image ("dots.png"· imagine it exists), andrepeat. - Style
.card-darkusing the shorthand with#2c3e50background colour and white text.
Solution:
/* Task 1 — Base card */
.card {
padding: 30px;
border-radius: 8px;
margin: 10px;
}
/* Task 2 — Plain card (shorthand, colour only) */
.card-plain {
background: #e8f4fd;
}
/* Task 3 — Pattern card (shorthand with image) */
.card-pattern {
background: #ffffff url("dots.png") repeat;
}
/* Task 4 — Dark card */
.card-dark {
background: #2c3e50;
color: white;
}Exercise 3 · Converting Longhand to Shorthand
Objective: Practice reading and writing the background shorthand.
Given longhand CSS · convert each to shorthand:
Set A:
/* Longhand */
div.box-a {
background-color: #ffeeba;
background-image: url("stripe.png");
background-repeat: repeat-x;
background-position: top left;
}Set B:
/* Longhand */
section.hero {
background-color: #000;
background-image: url("hero.jpg");
background-size: cover;
background-repeat: no-repeat;
background-position: center center;
background-attachment: fixed;
}Solutions:
/* Set A — shorthand */
div.box-a {
background: #ffeeba url("stripe.png") top left repeat-x;
}
/* Set B — shorthand */
section.hero {
background: #000 url("hero.jpg") center center / cover no-repeat fixed;
}Note on Set B: The
/betweencenter centerandcoveris required · it separates position from size. This is mandatory CSS syntax.
Mistake 1 · Background image not showing (wrong path)
Wrong:
body {
background-image: url(mountain.jpg); /* no quotes */
}
/* or */
body {
background-image: url("images/mountain.jpg"); /* file is actually in root folder */
}Why it's wrong: A missing or incorrect file path means the browser cannot find the image · it silently fails with no error shown on the page.
Correct:
body {
background-image: url("mountain.jpg"); /* quotes are recommended */
}Debugging tip: Open browser DevTools (F12), go to the Network tab, reload the page, and look for the image file. A red status (404) means the path is wrong.
Mistake 2 · Background image invisible because element has no height
Wrong:
.hero {
background-image: url("banner.jpg");
background-size: cover;
}HTML:
<div class="hero"></div> <!-- empty div! -->Why it's wrong: An empty <div> has height: 0 by default. With no content to expand it, the div collapses to zero height · the background is technically there but invisible because the element is zero pixels tall.
Correct:
.hero {
background-image: url("banner.jpg");
background-size: cover;
height: 500px; /* explicit height required */
}Mistake 3 · Forgetting background-repeat: no-repeat on photos
Wrong:
body {
background-image: url("portrait-photo.jpg");
/* missing no-repeat! */
}Why it's wrong: Without no-repeat, the photo tiles repeatedly across the page, which almost never looks good for actual photographs.
Correct:
body {
background-image: url("portrait-photo.jpg");
background-repeat: no-repeat;
background-size: cover;
background-position: center;
}Mistake 4 · Using opacity when you only want to fade the background
Wrong:
.overlay {
background-color: black;
opacity: 0.5; /* this fades EVERYTHING — text becomes unreadable too! */
}Correct:
.overlay {
background-color: rgba(0, 0, 0, 0.5); /* only the background is faded */
}Mistake 5 · Wrong shorthand order for position/size (missing the slash)
Wrong:
body {
background: url("hero.jpg") center center cover no-repeat;
/* Missing / between position and size! */
}Why it's wrong: CSS does not know where the position ends and the size begins without the / separator.
Correct:
body {
background: url("hero.jpg") center center / cover no-repeat;
/* position ↑ / ↑ size */
}Mistake 6 · Overwriting background properties accidentally with shorthand
Wrong:
.card {
background-image: url("texture.png");
background-repeat: repeat;
}
/* Later, you add: */
.card {
background: white; /* THIS REMOVES the image and repeat settings! */
}Why it's wrong: The background shorthand resets all unspecified sub-properties to their initial defaults. The image and repeat settings are wiped out.
Correct: If you only want to update the colour, use the individual property:
.card {
background-color: white; /* Only changes the colour; image/repeat unchanged */
}Build a polished, multi-section landing page that uses every background property you have learned.
Project Brief
You are building the homepage for a fictional eco-travel company called "Verdant Journeys." The page should have three distinct sections, each with a different background treatment.
Stage 1 · HTML Structure
Create index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Verdant Journeys — Eco Travel</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- Section 1: Hero -->
<section id="hero">
<div class="hero-content">
<h1>Travel. Sustainably.</h1>
<p>Discover the world without leaving it worse than you found it.</p>
<a href="#" class="btn">Explore Tours</a>
</div>
</section>
<!-- Section 2: Features -->
<section id="features">
<h2>Why Choose Verdant?</h2>
<div class="features-grid">
<div class="feature-card">
<h3>🌿 Carbon Neutral</h3>
<p>All our tours are offset 100% through certified reforestation.</p>
</div>
<div class="feature-card">
<h3>🗺️ Local Guides</h3>
<p>Expert guides from the communities you visit.</p>
</div>
<div class="feature-card">
<h3>🌍 Small Groups</h3>
<p>Maximum 12 people per tour. Intimate and impactful.</p>
</div>
</div>
</section>
<!-- Section 3: Testimonial with parallax -->
<section id="testimonial">
<div class="testimonial-content">
<blockquote>"The most transformative trip of my life. Verdant didn't just show me places — they showed me people."</blockquote>
<cite>— Ngozi A., Lagos</cite>
</div>
</section>
<!-- Section 4: Footer -->
<footer id="site-footer">
<p>© 2025 Verdant Journeys. Travelling responsibly since 2015.</p>
</footer>
</body>
</html>