# Advertising

Quaeriva can display advertisements in five places on the site. Each advertisement is a row you create in the admin panel; you can have several per placement and rotate between them.

**Administration → Advertisements.**

---

## 1. Turning it on

Advertising is off by default. Two switches matter:

1. **Enable advertising** — the global switch. With it off, nothing renders anywhere, whatever else is configured.
2. Each advertisement's own **enabled** state, toggled directly from the list.

---

## 2. Placements

| Placement | Where it appears |
|---|---|
| Header | Below the header bar, on every page |
| Footer | Top of the footer, on every page |
| Home page | In the home page body — rendered through the `ad_slot` widget in the `homepage_main` region |
| Below the question | Between the question and the answers |
| Between answers | Between answers on a question page |

**Between answers** has two extra controls, on the main advertisements screen:

- **Interval** — insert after every N answers.
- **Maximum insertions** — never insert more than this many times on one page.

An advertisement is never placed after the *last* answer, whatever the interval — that position is already covered by "Below the question".

---

## 3. One advertisement, either format

There is no format selector. Each advertisement has both an ad-code field and image fields, and the renderer decides:

- If **ad code** is filled, it is used.
- Otherwise, if an **image URL** is valid, the image is used.
- Otherwise nothing renders — and no empty wrapper is emitted either.

So an AdSense or other network tag goes in the ad-code box, and a self-hosted banner goes in the image fields. Moving a slot from your own banner to a network is a matter of pasting into one field.

### Ad code

Paste the provider's snippet unchanged — AdSense, or any other network. The code is **not sanitised**: providers need `<script>` and `<ins>`, and sanitising it would break every one of them.

This is a deliberate trusted path, which is why:

- only **administrators** can write it, never moderators;
- PHP tags (`<?`, `?>`) are refused, at save time and again at render time;
- there is a 100 KB size cap.

Treat the field with the same care as FTP access: whatever you paste executes in every visitor's browser.

### Image advertisements

Fill the image URL, a link URL, alternative text, and **width and height**. Set the dimensions — without them the page jumps as the image loads, which is the one advertising behaviour readers actually notice.

Paid links automatically carry `rel="sponsored"`, and external ones also get `noopener noreferrer` and open in a new tab.

---

## 4. Rotation and weights

Each placement has a **rotation** switch, on the main advertisements screen.

**Rotation off** — the placement runs one advertisement. Enabling a second one in that placement is refused, with a message naming the one already running. This is enforced both in the admin and in the renderer, so the rule holds however a row was created.

**Rotation on** — every eligible advertisement in that placement enters a weighted draw, once per page load.

**Weight** decides the share. Two advertisements with weights 3 and 1 appear roughly 75% and 25% of the time. Equal weights mean equal shares. Weight is ignored entirely when rotation is off.

---

## 5. Targeting

Each advertisement can be restricted. Empty means no restriction.

| Restriction | Effect |
|---|---|
| **Devices** | Desktop, tablet, mobile — any combination |
| **Languages** | A comma-separated list of locale codes |
| **Visible to** | Everyone, signed-out visitors, or signed-in members |
| **Starts / Ends** | Optional schedule |

**Leave the start date empty to begin immediately. Leave the end date empty so it never expires.**

An end date before the start date is refused rather than saved as an advertisement that can never appear.

### Device targeting is decided on the server

A restricted advertisement is **not sent to the browser at all** on a device it excludes — not sent and then hidden with CSS.

This matters for two reasons. Hiding with CSS still downloads and executes the provider's script, on the connections least able to afford it. And the network still records an impression your advertiser is billed for, on a screen where nobody could have seen the ad.

When any advertisement is device-restricted, responses carry `Vary: User-Agent` so a shared cache cannot serve one device's page to another.

---

## 6. Status

The list shows each advertisement as:

| Status | Meaning |
|---|---|
| Running | Enabled and within its schedule |
| Scheduled | Enabled, start date in the future |
| Expired | Enabled, end date in the past |
| Disabled | Switched off |

---

## 7. What this system does not do

Deliberately, to keep it simple:

- **No impressions, clicks or reporting.** Use your ad network's own dashboard, which counts more accurately than a PHP counter can.
- **No campaigns, advertisers or billing.**
- **No A/B testing** beyond rotation weights.
- **No consent banner.** If you need one for GDPR, add it through Administration → Custom code.
- **No `ads.txt`.** Serve it as a static file if your network requires one.

---

Ad placements and custom code can be scoped by canonical page type. Custom code is a super-admin trust boundary and may contain third-party scripts; review CSP requirements before enabling external providers.
