Add legal policy pages and shared layout component
Some checks are pending
linter / quality (push) Waiting to run
tests / ci (8.3) (push) Waiting to run
tests / ci (8.4) (push) Waiting to run
tests / ci (8.5) (push) Waiting to run

- Add Cookie Policy view documenting essential cookies (session, CSRF, remember_me, fa_location) and cookieless Umami analytics
- Add Privacy Policy view covering UK GDPR compliance, data categories, lawful bases, processors, retention, and user rights
- Add Refund & Cancellation Policy view explaining 14-day cooling-off period under Consumer Contracts Regulations 2013 and express-consent flow
- Add Terms of Service view defining account rules, subscription billing, and governing law
- Create shared legal layout component with FuelAlert header, footer with cross-links, and consistent typography
- Add feature tests covering all four legal pages and their cross-links
- All policies include placeholders for ICO registration number, email, and hosting/email providers pending production config
This commit is contained in:
Ovidiu U
2026-05-14 17:43:53 +01:00
parent 598ef04645
commit ecd45588e9
9 changed files with 1170 additions and 3 deletions

View File

@@ -0,0 +1,403 @@
# FuelAlert — Legal Pages Spec
## Context for Claude Code
You are generating four legal pages for **FuelAlert**, a UK consumer subscription web/mobile app that shows real-time UK fuel prices and forecasts.
### Reference comparable (structural check, NOT for copying)
A near-identical business already exists and has competent legal pages: **Fuel Finder UK** at https://www.fuel-finder.uk, operated by Scott Benson as a UK sole trader. Same business model, same data sources, same jurisdiction.
**Use it ONLY as a structural sanity check.** Do not copy any text. Their content is copyrighted and their specific processors / retention periods / business decisions are not FuelAlert's.
What Fuel Finder UK does well that this spec already incorporates:
- Explicit sole trader disclosure naming the operator
- Named ICO registration number in the Privacy Policy
- Specific named processors (Stripe, hosting provider, email provider, analytics, mapping services) with what data flows to each
- Strong price-accuracy disclaimer in Terms ("prices may not reflect real-time changes, always verify at the pump")
- "AS IS" / "AS AVAILABLE" basis with liability limitations
- ICO complaint route clearly stated
- Account deletion flow that cancels paid subscription first
What FuelAlert must do BETTER than Fuel Finder UK:
- **Standalone Refund & Cancellation Policy page** — Fuel Finder UK appears to bundle this into Terms. Stripe specifically asks for a refund policy; keep it as a separate page at `/legal/refund`.
- **Express-consent checkbox at subscription checkout** for the 14-day cooling-off period under Consumer Contracts Regulations 2013 (see Refund Policy section below). This is the single most important implementation detail in the entire spec.
If you find yourself generating content that overlaps verbatim with Fuel Finder UK or any other site, stop and rewrite in your own words. Legal pages are copyrighted works.
**Business operator (data controller):**
- Name: Ovidiu Ungureanu
- Trading as: FuelAlert
- Status: Sole trader (not a limited company — do not use "Ltd", "Limited", or any corporate form)
- Location: Peterborough, United Kingdom
- Contact email: [hello@fuelalert.co.uk]
- ICO registration number: [PLACEHOLDER: ZAxxxxxxx — Ovidiu to register at ico.org.uk before launch, fee ~£40/yr]
**Service:**
- UK fuel price comparison and forecasting
- Free tier + two paid subscription tiers: Daily (£0.99/mo) and Smart (£2.49/mo); annual billing also offered
- Data sources: UK government Fuel Finder / Pump Watch open data, ONS Postcode Directory (OGL/Crown Copyright)
- Processes: email address, postcode/location data, account credentials, payment data (via Stripe), usage telemetry
**Jurisdiction:**
- England and Wales
- Consumer law: Consumer Rights Act 2015, Consumer Contracts (Information, Cancellation and Additional Charges) Regulations 2013 ("CCRs")
- Data protection: UK GDPR + Data Protection Act 2018, PECR for cookies/marketing
- Digital markets: Digital Markets, Competition and Consumers Act 2024 ("DMCC")
**Tech stack & integration:**
- Laravel + Blade for server-rendered pages (NOT Vue components — these must render in raw HTML for Stripe reviewers and SEO)
- Route prefix: `/legal/*`
- Layout: extend the existing site layout but with a readable prose container (max-width ~720px, generous line-height)
- Each page has a `last_updated` date in front matter — set to today
- All pages must be linkable from the footer and accessible without JavaScript
### ⚠️ CRITICAL: Server-rendered Blade only, NOT Vue
The rest of this application is a Vue SPA. **That is irrelevant for these pages.** The legal pages must be server-rendered Blade templates.
Rules:
- Files live at `resources/views/legal/{name}.blade.php` — never `.vue`
- Body content must be plain HTML with Blade directives (`@extends`, `@section`, `{{ }}`)
- No Vue components anywhere in the page body — no custom tags like `<legal-privacy-page>`, no `<div id="app">`, no Vue mounting points
- No client-side rendering for the legal content itself — Alpine.js is acceptable only for the cookie banner show/hide behaviour, nothing else
- Pages must work fully with JavaScript disabled
**Verification test (run after generation):**
```bash
curl -s https://[host]/legal/privacy | grep -i "data controller"
curl -s https://[host]/legal/terms | grep -i "subscription"
curl -s https://[host]/legal/refund | grep -i "14-day"
curl -s https://[host]/legal/cookies | grep -i "essential"
```
Each command must return matching lines from the raw HTML response. If any returns empty, the page has been built as a client-rendered component and must be redone as Blade.
If you find yourself reaching for `<script>` tags, Vue single-file components, or any pattern that requires the browser to execute JS before content appears — stop and use plain Blade instead.
---
## Pages to generate
1. `/legal/privacy` — Privacy Policy
2. `/legal/terms` — Terms of Service
3. `/legal/refund` — Refund & Cancellation Policy (can be a section of Terms, but a separate page is cleaner for Stripe review)
4. `/legal/cookies` — Cookie Policy (paired with a real consent banner — see separate section)
---
## 1. Privacy Policy (`/legal/privacy`)
### Required sections, in order:
**1. Who we are**
- Identify Ovidiu Ungureanu as data controller, trading as FuelAlert
- Sole trader status, Peterborough address (use generic "Peterborough, UK" unless a registered business address is provided)
- ICO registration number
- Contact email for privacy queries
**2. What data we collect**
Break into clear subsections:
- *Account data*: email, hashed password, account creation date
- *Location data*: postcodes entered, optional precise GPS if granted, derived location for nearby-station queries
- *Payment data*: handled by Stripe; FuelAlert does NOT store card numbers. Stripe customer ID and subscription metadata only.
- *Usage data*: features used, queries made, price alerts configured (for service delivery and improvement)
- *Technical data*: IP address, browser type, device type (for security and analytics)
- *Marketing preferences*: only if user opts in
**3. Lawful basis for processing**
For each category, state the UK GDPR Article 6 basis:
- Account + service delivery: **contract** (Art. 6(1)(b))
- Payment processing: **contract**
- Security/fraud prevention: **legitimate interests** (Art. 6(1)(f))
- Analytics/product improvement: **legitimate interests**, with opt-out via cookie banner
- Marketing emails: **consent** (Art. 6(1)(a))
**4. How we use your data**
Bullet list, plain English. Tie back to the lawful basis for each use.
**5. Who we share data with (processors)**
Named list — be specific:
- **Stripe** (payment processing) — refer to Stripe's privacy policy
- **[Hosting provider, e.g. Hetzner]** (infrastructure) — EU-based, note location
- **[Email provider, e.g. Fastmail / Postmark]** (transactional email)
- **[Analytics, e.g. Plausible]** — if used; if self-hosted, say so
- State that we do NOT sell data to third parties
**6. International transfers**
- State whether any processors are outside the UK/EEA (Stripe has US operations)
- Reference appropriate safeguards: Standard Contractual Clauses (SCCs) and UK International Data Transfer Addendum
**7. How long we keep data**
- Active account data: while account is active + 12 months after closure
- Payment records: 6 years (HMRC requirement for sole traders)
- Marketing data: until consent is withdrawn
- Logs/analytics: max 24 months
**8. Your rights under UK GDPR**
List all eight rights with one-line explanations:
- Right of access
- Right to rectification
- Right to erasure ("right to be forgotten")
- Right to restrict processing
- Right to data portability
- Right to object
- Rights related to automated decision-making (state we do NOT make solely automated decisions with legal effect)
- Right to withdraw consent
Explain how to exercise rights (email to the contact address, response within one month).
**9. Cookies**
Brief mention with link to the dedicated Cookie Policy.
**10. Security**
Plain-English summary: HTTPS, hashed passwords, encrypted database fields where appropriate, access controls, regular updates. Don't overpromise ("bank-grade" etc. — meaningless and creates liability).
**11. Children**
Service is not directed at under-16s; we do not knowingly collect data from children.
**12. Complaints**
Right to complain to the ICO: ico.org.uk, 0303 123 1113. Encourage contacting FuelAlert first.
**13. Changes to this policy**
We will notify users of material changes by email; non-material changes shown by updated "Last updated" date.
**14. Contact**
Email address for privacy queries.
### Tone & style
- Plain English, Hemingway readability score ~grade 8
- No legalese where avoidable
- Use "we" / "you"
- Short paragraphs, lots of headings, scannable
---
## 2. Terms of Service (`/legal/terms`)
### Required sections, in order:
**1. About these terms**
- Who FuelAlert is (sole trader disclosure)
- These terms form a contract between you and Ovidiu Ungureanu trading as FuelAlert
- By using the service you agree to these terms
- Governing law: England and Wales
**2. The service**
- What FuelAlert does
- Free tier and paid tier descriptions
- We may add, remove, or change features with reasonable notice
**3. Your account**
- Eligibility: 18+, UK resident, accurate information required
- One account per person
- You're responsible for keeping login credentials secure
- We may suspend accounts for breach of these terms
**4. Subscriptions, billing and payment**
This is the section Stripe cares most about. Cover:
- Prices (link to /pricing) — currently £0.99/mo Daily, £2.49/mo Smart, with annual discount
- Billing cycle: monthly or annual, charged in advance
- **Auto-renewal**: subscriptions auto-renew at the end of each period at the then-current price, until cancelled
- Payment method: card via Stripe; you authorise FuelAlert (via Stripe) to charge your method on each renewal
- Failed payments: we'll retry and notify you; persistent failure suspends paid features
- Price changes: 30 days' notice by email before any price increase takes effect on renewal; you may cancel before the increase
- VAT: prices include UK VAT where applicable (sole traders below the VAT threshold do not charge VAT — clarify FuelAlert's current status)
**5. Cancellation and refunds**
Cross-reference the Refund Policy. Summarise:
- You can cancel at any time from your account settings
- Cancellation stops the next renewal; you keep access until the end of the current billing period
- 14-day right to cancel for new subscribers (see Refund Policy for the express-consent mechanism that affects this)
**6. Acceptable use**
You agree not to:
- Scrape, reverse-engineer, or bulk-extract data from the service
- Resell or redistribute price data
- Use the service for any unlawful purpose
- Attempt to compromise security
- Create automated queries beyond normal personal use
**7. Accuracy of price data — IMPORTANT**
This is your liability shield. Say clearly:
- Prices are sourced from official UK government data feeds (Pump Watch / Fuel Finder) and refreshed periodically
- We make reasonable efforts to display accurate prices but **cannot guarantee** prices are correct at the moment you arrive at a station
- Stations may change prices between data feed updates
- FuelAlert is not liable for losses arising from inaccurate price data (wasted journey, fuel cost differences, etc.)
- Always confirm the price at the pump before fuelling
**8. Forecasts and predictions**
- Forecasts are informational only and not financial advice
- Past trends do not guarantee future prices
- We do not warrant the accuracy of any forecast
**9. Intellectual property**
- The FuelAlert name, logo, software, and original content are owned by Ovidiu Ungureanu
- Underlying fuel price data is owned by the respective retailers and published under government open data schemes; ONS Postcode Directory data is © Crown Copyright, used under Open Government Licence v3.0
- You get a limited, non-exclusive, revocable licence to use the service for personal, non-commercial purposes (or commercial fleet use if on a Fleet plan)
**10. Third-party services**
We use Stripe for payments. Your use of Stripe is also subject to Stripe's terms.
**11. Limitation of liability**
- We don't exclude liability for death/personal injury caused by negligence, fraud, or anything that can't be excluded under UK consumer law
- We exclude liability for indirect, consequential, or business losses
- For consumers using the paid service, our total liability in any 12-month period is capped at the amount you paid in subscription fees in that period
- We don't accept liability for issues caused by third-party services we depend on (Stripe outage, data feed outage, etc.)
**12. Termination**
- You can stop using the service and close your account at any time
- We can terminate access for serious breach of these terms, with notice where reasonable
- On termination, paid sections 411 of these terms survive
**13. Changes to these terms**
We may update these terms; material changes notified by email at least 14 days before they take effect.
**14. Disputes**
- Try to resolve directly by contacting us first
- These terms are governed by the laws of England and Wales; courts of England and Wales have non-exclusive jurisdiction
- Consumers retain the right to bring proceedings in their country of residence
- Reference to alternative dispute resolution if applicable
**15. Contact**
Email address.
---
## 3. Refund & Cancellation Policy (`/legal/refund`)
This page is required by Stripe specifically and by UK consumer law. Keep it short and clear.
### Required sections:
**1. Your 14-day right to cancel (cooling-off period)**
- Under the Consumer Contracts Regulations 2013, you have 14 days from the date you subscribe to cancel without giving a reason
- This applies to **new subscribers only**; not subsequent renewals
**2. Express consent to start the service immediately — CRITICAL CLAUSE**
- When you subscribe, you can choose to start using the paid features immediately
- By doing so, you expressly acknowledge that you **lose your right to cancel** once the service has been fully supplied (i.e. once you've used the paid features within the 14-day window)
- If you cancel before using the paid features, you receive a full refund
- If you cancel within 14 days having used some paid features, we may reduce the refund proportionally to reflect usage
**Implementation note for the signup flow:** the express consent must be an unticked checkbox at checkout, with explicit text. Do not pre-tick it. Suggested wording for the checkbox label:
> "I want my subscription to start immediately. I understand that by using paid features within the 14-day cooling-off period, I will lose my right to cancel under the Consumer Contracts Regulations 2013."
**3. How to cancel**
- From your account: Settings → Subscription → Cancel
- Or by emailing the contact address
- Cancellation is effective at the end of the current billing period unless you exercise the 14-day right above
**4. Refunds outside the 14-day period**
- After 14 days, subscription fees are non-refundable for the remainder of the period you paid for
- You keep access until the end of the period
- We may issue discretionary refunds in cases of service failure or where required by law
**5. Annual subscriptions**
- Same 14-day right applies
- After 14 days, annual fees are non-refundable; no pro-rata refund for unused months
**6. Failed payments and involuntary cancellation**
- If your payment fails, we'll retry and notify you
- Paid features suspend after [N] failed retries
- Your account is not deleted; you can resume by updating payment
**7. How long refunds take**
- Refunds are issued to the original payment method within 510 business days of approval
**8. Contact**
Email address for refund requests.
---
## 4. Cookie Policy (`/legal/cookies`)
### Required sections:
**1. What cookies are**
One short paragraph.
**2. Cookies we use**
Table format with columns: Name | Purpose | Duration | Type (Essential / Analytics / Marketing).
Categories to include:
- **Essential**: session, CSRF, authentication, cookie-consent preference itself — these do not require consent under PECR
- **Analytics**: only if you use them; name them specifically (Plausible is cookie-less in many configs, GA4 uses several)
- **Marketing**: only if you run any — likely none at launch
--- ignore for now. need to decide what tool to use for this
**3. Your choices**
- You can accept, decline, or customise non-essential cookies via the consent banner
- You can change your choice at any time via "Cookie Settings" in the footer
- Most browsers also let you block cookies — link to ICO guidance on managing cookies
**4. Changes to this policy**
Standard wording.
**5. Contact**
Email.
---
## Cookie consent banner (separate component)
Implement as a Blade component, server-rendered, that:
- Appears on first visit
- Has three buttons: **Accept all**, **Reject all**, **Customise**
- "Customise" expands to checkboxes per category (Essential — locked on; Analytics; Marketing if applicable)
- Stores choice in a first-party cookie (`fa_cookie_consent`) for 12 months
- **Blocks non-essential scripts from loading until consent is given** — this is the critical bit. Use a tag manager pattern or conditional Blade includes; do NOT load Google Analytics, etc. before consent.
- Persistent "Cookie Settings" link in the footer reopens the customise panel
PECR / ICO require equal prominence for accept and reject. Don't make "Reject" smaller or harder to find.
---
## Implementation notes for Claude Code
1. **Create Laravel routes** in `routes/web.php`:
```php
Route::view('/legal/privacy', 'legal.privacy')->name('legal.privacy');
Route::view('/legal/terms', 'legal.terms')->name('legal.terms');
Route::view('/legal/refund', 'legal.refund')->name('legal.refund');
Route::view('/legal/cookies', 'legal.cookies')->name('legal.cookies');
```
2. **Create Blade layout** `resources/views/layouts/legal.blade.php` extending the main site layout, with a narrow prose container.
3. **Each page** at `resources/views/legal/{name}.blade.php` should:
- Set page title
- Set meta description (short summary, ~150 chars)
- Render the content in semantic HTML (h1, h2, h3, ul, ol, table where appropriate)
- Show "Last updated: [date]" at top
- Use Blade `@section` for content
4. **Update footer** to link to all four legal pages with `route()` helpers. Replace any existing "Cookie Settings" link with a button that opens the consent banner customise panel.
5. **Cookie banner component** at `resources/views/components/cookie-banner.blade.php` — include in main layout, with Alpine.js or vanilla JS for show/hide logic. Must be functional with JavaScript disabled (degrade to "you can change cookie settings in your browser" message).
6. **Do NOT** auto-generate the ICO registration number or contact email. Leave clear `[PLACEHOLDER: ...]` markers throughout for Ovidiu to fill in before deployment.
7. **Do NOT** include claims that aren't true yet — if uncertain, use a `[PLACEHOLDER: verify before launch]` marker rather than guessing.
8. **At the top of each generated file**, add a comment:
```
{{-- DRAFT: Generated [date]. Review by UK-qualified solicitor recommended before launch. --}}
```
---
## Pre-launch checklist for Ovidiu (not for Claude Code)
- [ ] Register with the ICO (ico.org.uk) and obtain registration number — required by law for processing personal data commercially
- [ ] Replace all `[PLACEHOLDER: ...]` markers
- [ ] Confirm hosting provider and processors named in the Privacy Policy are accurate
- [ ] Test the cookie banner: does it block non-essential scripts before consent?
- [ ] Test the signup flow: is the express-consent checkbox unticked by default?
- [ ] Test account cancellation: does it work end-to-end?
- [ ] Have a UK-qualified solicitor review (or use Termly/iubenda as a sanity check)
- [ ] Set `last_updated` dates to the actual go-live date
- [ ] Submit to Stripe with the live URL

View File

@@ -402,9 +402,10 @@
<div class="space-y-4">
<h5 class="font-black text-xs text-zinc-800 tracking-widest">Legal</h5>
<ul class="space-y-2 text-sm text-zinc-500">
<li><a class="hover:text-accent transition-colors" href="#">Privacy Policy</a></li>
<li><a class="hover:text-accent transition-colors" href="#">Terms of Service</a></li>
<li><a class="hover:text-accent transition-colors" href="#">Cookie Settings</a></li>
<li><a class="hover:text-accent transition-colors" href="/legal/privacy">Privacy Policy</a></li>
<li><a class="hover:text-accent transition-colors" href="/legal/terms">Terms of Service</a></li>
<li><a class="hover:text-accent transition-colors" href="/legal/refund">Refund &amp; Cancellation</a></li>
<li><a class="hover:text-accent transition-colors" href="/legal/cookies">Cookie Policy</a></li>
</ul>
</div>
</div>

View File

@@ -0,0 +1,53 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
@include('partials.head', ['title' => $title ?? null])
@isset($metaDescription)
<meta name="description" content="{{ $metaDescription }}">
@endisset
</head>
<body class="min-h-screen bg-[#f5ede5] text-zinc-900 antialiased">
<header class="border-b border-zinc-300 bg-white/70 backdrop-blur">
<div class="mx-auto flex max-w-3xl items-center justify-between px-6 py-4">
<a href="/" class="flex items-center gap-2">
<span class="flex h-8 w-8 items-center justify-center rounded bg-accent text-white">
<iconify-icon icon="lucide:fuel"></iconify-icon>
</span>
<span class="font-display text-xl font-black tracking-tighter text-accent">FuelAlert</span>
</a>
<a href="/" class="text-sm text-zinc-600 hover:text-accent">&larr; Back to site</a>
</div>
</header>
<main class="mx-auto max-w-3xl px-6 py-12">
<article class="space-y-6 leading-relaxed text-zinc-800">
<header class="space-y-3 border-b border-zinc-300 pb-6">
<h1 class="font-display text-3xl font-black tracking-tight text-zinc-900 md:text-4xl">
{{ $heading }}
</h1>
@isset($lastUpdated)
<p class="text-sm text-zinc-500">Last updated: {{ $lastUpdated }}</p>
@endisset
</header>
{{ $slot }}
</article>
</main>
<footer class="mt-16 border-t border-zinc-300 bg-white/50">
<div class="mx-auto max-w-3xl px-6 py-10 text-sm text-zinc-600">
<nav class="flex flex-wrap gap-x-6 gap-y-2">
<a class="hover:text-accent" href="{{ route('legal.privacy') }}">Privacy Policy</a>
<a class="hover:text-accent" href="{{ route('legal.terms') }}">Terms of Service</a>
<a class="hover:text-accent" href="{{ route('legal.refund') }}">Refund &amp; Cancellation</a>
<a class="hover:text-accent" href="{{ route('legal.cookies') }}">Cookie Policy</a>
</nav>
<p class="mt-6 text-xs text-zinc-500">
&copy; {{ date('Y') }} FuelAlert. A trading name of Ovidiu Ungureanu, sole trader, Peterborough, United Kingdom.
</p>
</div>
</footer>
<script src="https://code.iconify.design/iconify-icon/1.0.7/iconify-icon.min.js" defer></script>
</body>
</html>

View File

@@ -0,0 +1,112 @@
{{-- DRAFT: Generated {{ date('Y-m-d') }}. Review by UK-qualified solicitor recommended before launch. --}}
<x-layouts.legal
title="Cookie Policy"
heading="Cookie Policy"
lastUpdated="{{ now()->format('j F Y') }}"
metaDescription="The cookies and similar technologies FuelAlert uses, and how to manage them.">
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">1. What cookies are</h2>
<p>
Cookies are small text files placed on your device by websites you visit. They allow a
site to remember things between visits (for example, that you're signed in) and to
measure how the site is used. This policy explains how FuelAlert uses cookies and
similar technologies, and how you can manage them.
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">2. Cookies we use</h2>
<p>
FuelAlert uses only <strong>essential</strong> cookies &mdash; cookies that are strictly
necessary to deliver the service you've asked for. Under the Privacy and Electronic
Communications Regulations (PECR), these do not require your consent, but we list them
here for transparency.
</p>
<p>
For aggregated usage metrics we run our own self-hosted instance of
<strong>Umami Analytics</strong>, which is <strong>cookieless</strong> &mdash; it does
not set any cookies, does not use device fingerprinting, and does not track you across
sites. Because no personal data is collected, no consent is required.
</p>
<div class="overflow-x-auto">
<table class="w-full border-collapse text-left text-sm">
<thead class="bg-zinc-100">
<tr>
<th class="border border-zinc-300 px-3 py-2 font-semibold">Name</th>
<th class="border border-zinc-300 px-3 py-2 font-semibold">Purpose</th>
<th class="border border-zinc-300 px-3 py-2 font-semibold">Duration</th>
<th class="border border-zinc-300 px-3 py-2 font-semibold">Type</th>
</tr>
</thead>
<tbody>
<tr>
<td class="border border-zinc-300 px-3 py-2 font-mono text-xs">fuel_alert_session</td>
<td class="border border-zinc-300 px-3 py-2">Keeps you signed in and maintains your session state.</td>
<td class="border border-zinc-300 px-3 py-2">Session</td>
<td class="border border-zinc-300 px-3 py-2">Essential</td>
</tr>
<tr>
<td class="border border-zinc-300 px-3 py-2 font-mono text-xs">XSRF-TOKEN</td>
<td class="border border-zinc-300 px-3 py-2">Protects against cross-site request forgery attacks on forms and account actions.</td>
<td class="border border-zinc-300 px-3 py-2">Session</td>
<td class="border border-zinc-300 px-3 py-2">Essential</td>
</tr>
<tr>
<td class="border border-zinc-300 px-3 py-2 font-mono text-xs">remember_web_*</td>
<td class="border border-zinc-300 px-3 py-2">"Remember me" &mdash; keeps you signed in across browser restarts if you tick the box at login.</td>
<td class="border border-zinc-300 px-3 py-2">Up to 5 years</td>
<td class="border border-zinc-300 px-3 py-2">Essential</td>
</tr>
<tr>
<td class="border border-zinc-300 px-3 py-2 font-mono text-xs">fa_location</td>
<td class="border border-zinc-300 px-3 py-2">Stores your most recent postcode search so we can show local prices on return visits without re-querying.</td>
<td class="border border-zinc-300 px-3 py-2">30 days</td>
<td class="border border-zinc-300 px-3 py-2">Essential</td>
</tr>
</tbody>
</table>
</div>
<p class="text-sm text-zinc-600">
<strong>[PLACEHOLDER:</strong> Update the table above if any cookie names change. If a
marketing tool is added in future, a Marketing row will be added here and consent will
be requested before it loads.<strong>]</strong>
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">3. Your choices</h2>
<p>
Because we currently only use essential cookies, there is nothing to opt in or out of
on FuelAlert at this time. If we add non-essential cookies in future (for example,
analytics or marketing), we will present a consent banner and you will be able to
accept, reject, or customise your choice. We will not set non-essential cookies before
you have given consent.
</p>
<p>
All major browsers also let you view, block, or delete cookies. The ICO publishes
guidance on managing cookies in your browser:
<a class="text-accent underline" href="https://ico.org.uk/your-data-matters/online/cookies/" target="_blank" rel="noopener">ico.org.uk &middot; managing cookies</a>.
Note that blocking essential cookies will prevent you from signing in.
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">4. Changes to this policy</h2>
<p>
We may update this policy if we add new cookies, change our providers, or in response to
legal or guidance changes. Material changes will be highlighted by an updated
"Last updated" date at the top of this page.
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">5. Contact</h2>
<p>
Questions about cookies? Email
<a href="mailto:[PLACEHOLDER: hello@fuelalert.co.uk]" class="text-accent underline">[PLACEHOLDER: hello@fuelalert.co.uk]</a>.
</p>
</section>
</x-layouts.legal>

View File

@@ -0,0 +1,205 @@
{{-- DRAFT: Generated {{ date('Y-m-d') }}. Review by UK-qualified solicitor recommended before launch. --}}
<x-layouts.legal
title="Privacy Policy"
heading="Privacy Policy"
lastUpdated="{{ now()->format('j F Y') }}"
metaDescription="How FuelAlert collects, uses and protects your personal data under UK GDPR.">
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">1. Who we are</h2>
<p>
FuelAlert is a trading name of <strong>Ovidiu Ungureanu</strong>, a sole trader based in
Peterborough, United Kingdom. For the purposes of UK data protection law, Ovidiu Ungureanu
is the <strong>data controller</strong> for personal data collected through this service.
</p>
<p>
We are registered with the UK Information Commissioner's Office (ICO).
Our registration number is <strong>[PLACEHOLDER: ICO registration number ZAxxxxxxx]</strong>.
</p>
<p>
If you have any questions about this policy or how we handle your personal data, contact us at
<a href="mailto:[PLACEHOLDER: hello@fuelalert.co.uk]" class="text-accent underline">[PLACEHOLDER: hello@fuelalert.co.uk]</a>.
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">2. What data we collect</h2>
<h3 class="font-semibold text-zinc-900">Account data</h3>
<p>Your email address, a hashed password, and the date you created your account.</p>
<h3 class="font-semibold text-zinc-900">Location data</h3>
<p>
Postcodes or place names you search for. With your permission, your device's precise
location to find nearby stations. Derived approximate location used to query our database.
</p>
<h3 class="font-semibold text-zinc-900">Payment data</h3>
<p>
Payment card details are collected and processed by <strong>Stripe</strong>, our payment
processor. FuelAlert does not see, store, or otherwise have access to your card numbers.
We retain only your Stripe customer ID and subscription metadata (plan, billing cycle,
renewal date).
</p>
<h3 class="font-semibold text-zinc-900">Usage data</h3>
<p>
Features you use, queries you make, and alerts you configure used to deliver the
service and improve it.
</p>
<h3 class="font-semibold text-zinc-900">Technical data</h3>
<p>
IP address, browser type and version, device type, and operating system used for
security, fraud prevention, and basic analytics.
</p>
<h3 class="font-semibold text-zinc-900">Marketing preferences</h3>
<p>Only collected if you opt in to marketing communications.</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">3. Lawful basis for processing</h2>
<p>We process your personal data under the following bases of UK GDPR Article 6:</p>
<ul class="list-disc space-y-1 pl-6">
<li><strong>Account creation and service delivery</strong> &mdash; contract (Art. 6(1)(b)).</li>
<li><strong>Payment processing</strong> &mdash; contract (Art. 6(1)(b)).</li>
<li><strong>Security and fraud prevention</strong> &mdash; legitimate interests (Art. 6(1)(f)).</li>
<li><strong>Analytics and product improvement</strong> &mdash; legitimate interests, with opt-out via our cookie banner where applicable.</li>
<li><strong>Marketing emails</strong> &mdash; consent (Art. 6(1)(a)). You can withdraw consent at any time.</li>
</ul>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">4. How we use your data</h2>
<ul class="list-disc space-y-1 pl-6">
<li>To create and operate your account (contract).</li>
<li>To deliver fuel price information and alerts you have configured (contract).</li>
<li>To process subscription payments via Stripe (contract).</li>
<li>To keep our service secure and prevent abuse (legitimate interests).</li>
<li>To understand which features are used and improve the product (legitimate interests).</li>
<li>To respond to your support enquiries (contract / legitimate interests).</li>
<li>To send marketing emails if you have opted in (consent).</li>
</ul>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">5. Who we share data with</h2>
<p>We use the following processors to deliver the service. We do not sell your data to anyone.</p>
<ul class="list-disc space-y-2 pl-6">
<li>
<strong>Stripe</strong> &mdash; payment processing. Card details, billing address,
and subscription events flow to Stripe. See
<a class="text-accent underline" href="https://stripe.com/privacy" target="_blank" rel="noopener">Stripe's privacy policy</a>.
</li>
<li><strong>[PLACEHOLDER: Hosting provider]</strong> &mdash; infrastructure where our application and database run.</li>
<li><strong>[PLACEHOLDER: Transactional email provider]</strong> &mdash; sends account, billing and alert emails on our behalf.</li>
<li>
<strong>Umami Analytics</strong> &mdash; we run our own self-hosted Umami instance to
collect aggregated, cookieless usage metrics (pages viewed, referrer, country, device
type). No personal data is collected and no analytics data is shared with third
parties.
</li>
<li><strong>[PLACEHOLDER: Notification providers]</strong> &mdash; if you opt in to push, WhatsApp, or SMS alerts, the chosen provider will be named here.</li>
</ul>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">6. International transfers</h2>
<p>
Some of our processors (notably Stripe) operate outside the UK and EEA, including in the
United States. Where personal data is transferred internationally, we rely on appropriate
safeguards under UK GDPR: the UK International Data Transfer Addendum to the EU Standard
Contractual Clauses, or an equivalent mechanism.
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">7. How long we keep data</h2>
<ul class="list-disc space-y-1 pl-6">
<li><strong>Active account data:</strong> for as long as your account is open, plus 12 months after closure.</li>
<li><strong>Payment records:</strong> 6 years, to meet HMRC requirements for self-employed traders.</li>
<li><strong>Marketing data:</strong> until you withdraw consent.</li>
<li><strong>Logs and analytics:</strong> a maximum of 24 months.</li>
</ul>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">8. Your rights under UK GDPR</h2>
<p>You have the following rights in relation to your personal data:</p>
<ul class="list-disc space-y-1 pl-6">
<li><strong>Right of access</strong> &mdash; ask for a copy of the data we hold about you.</li>
<li><strong>Right to rectification</strong> &mdash; ask us to correct inaccurate data.</li>
<li><strong>Right to erasure</strong> ("right to be forgotten") &mdash; ask us to delete your data.</li>
<li><strong>Right to restrict processing</strong> &mdash; ask us to pause processing in certain circumstances.</li>
<li><strong>Right to data portability</strong> &mdash; receive your data in a machine-readable format.</li>
<li><strong>Right to object</strong> &mdash; object to processing based on legitimate interests.</li>
<li><strong>Rights related to automated decision-making</strong> &mdash; we do <strong>not</strong> make solely automated decisions with legal or similarly significant effects on you.</li>
<li><strong>Right to withdraw consent</strong> &mdash; where we rely on consent (e.g. marketing).</li>
</ul>
<p>
To exercise any of these rights, email
<a href="mailto:[PLACEHOLDER: hello@fuelalert.co.uk]" class="text-accent underline">[PLACEHOLDER: hello@fuelalert.co.uk]</a>.
We will respond within one month.
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">9. Cookies</h2>
<p>
We use a small number of cookies to operate the service. Full details &mdash; including
categories and how to change your choices &mdash; are in our
<a class="text-accent underline" href="{{ route('legal.cookies') }}">Cookie Policy</a>.
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">10. Security</h2>
<p>
All traffic between your device and our service is encrypted with HTTPS. Passwords are
stored as one-way hashes &mdash; we never see your plaintext password. Sensitive fields in
our database are protected by access controls, and our infrastructure receives regular
security updates. No system is ever 100% secure; if a breach occurs that affects you, we
will notify you and the ICO as required by law.
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">11. Children</h2>
<p>
FuelAlert is not directed at children. We do not knowingly collect data from anyone under
16. If you believe a child has provided us with personal data, contact us and we will
delete it.
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">12. Complaints</h2>
<p>
We hope you'll contact us first if you have a complaint, so we can try to put it right.
You also have the right to lodge a complaint with the UK Information Commissioner's Office
at any time.
</p>
<p>
ICO website: <a class="text-accent underline" href="https://ico.org.uk" target="_blank" rel="noopener">ico.org.uk</a>
&middot; ICO helpline: 0303 123 1113.
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">13. Changes to this policy</h2>
<p>
We may update this policy from time to time. If we make material changes we will notify
registered users by email. Non-material changes will be shown by an updated "Last updated"
date at the top of this page.
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">14. Contact</h2>
<p>
For any privacy queries, email
<a href="mailto:[PLACEHOLDER: hello@fuelalert.co.uk]" class="text-accent underline">[PLACEHOLDER: hello@fuelalert.co.uk]</a>.
</p>
</section>
</x-layouts.legal>

View File

@@ -0,0 +1,110 @@
{{-- DRAFT: Generated {{ date('Y-m-d') }}. Review by UK-qualified solicitor recommended before launch. --}}
<x-layouts.legal
title="Refund & Cancellation Policy"
heading="Refund &amp; Cancellation Policy"
lastUpdated="{{ now()->format('j F Y') }}"
metaDescription="Your right to cancel a FuelAlert subscription, including the 14-day cooling-off period under UK law.">
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">1. Your 14-day right to cancel</h2>
<p>
Under the <strong>Consumer Contracts (Information, Cancellation and Additional Charges)
Regulations 2013</strong>, you have <strong>14 days</strong> from the date you subscribe
to a paid plan to cancel without giving a reason. This is sometimes called the
"cooling-off period".
</p>
<p>
This 14-day right applies to <strong>new subscribers only</strong>. It does not apply to
subsequent automatic renewals of an existing subscription.
</p>
</section>
<section class="space-y-3 rounded-lg border-l-4 border-accent bg-white/70 p-6">
<h2 class="font-display text-2xl font-bold text-zinc-900">2. Express consent to start the service immediately</h2>
<p>
When you subscribe, we ask you to choose whether the paid features should be available
to you immediately. If you tick the consent box and start using paid features within the
14-day window, you expressly acknowledge that:
</p>
<ul class="list-disc space-y-1 pl-6">
<li>The service is being supplied to you straight away;</li>
<li>
<strong>You will lose your right to cancel under the Consumer Contracts Regulations
2013 once the service has been fully supplied</strong> (i.e. once you have used the
paid features during the cooling-off period).
</li>
</ul>
<p>
If you do <strong>not</strong> tick the express-consent box, your subscription is still
created but paid features remain inactive until the cooling-off period ends, or until
you change your mind and confirm consent.
</p>
<p>
If you cancel within the 14-day window <strong>before</strong> using any paid features,
you receive a <strong>full refund</strong>. If you cancel within the window after using
some paid features, we may reduce the refund proportionally to reflect usage, as
permitted by the Regulations.
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">3. How to cancel</h2>
<p>You can cancel a subscription in either of these ways:</p>
<ul class="list-disc space-y-1 pl-6">
<li>From your account: <strong>Settings &rarr; Subscription &rarr; Cancel</strong>.</li>
<li>By emailing <a href="mailto:[PLACEHOLDER: hello@fuelalert.co.uk]" class="text-accent underline">[PLACEHOLDER: hello@fuelalert.co.uk]</a> from the address on your account.</li>
</ul>
<p>
Unless you are exercising the 14-day right above, cancellation takes effect at the end
of the current billing period. You keep access to paid features until that date.
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">4. Refunds outside the 14-day period</h2>
<p>
Outside the 14-day cooling-off window, subscription fees are <strong>non-refundable</strong>
for the remainder of the period you have paid for. You keep access to paid features
until the end of that period; the subscription simply does not renew.
</p>
<p>
We may issue discretionary refunds where there has been a service failure on our side or
where required by law.
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">5. Annual subscriptions</h2>
<p>
The 14-day cooling-off right applies to annual subscriptions in the same way as monthly
subscriptions. After the 14 days, annual fees are non-refundable; we do not issue
pro-rata refunds for unused months of an annual plan.
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">6. Failed payments and involuntary cancellation</h2>
<p>
If a renewal payment fails, we and Stripe will retry the payment over a short period and
email you. Paid features are suspended after the final unsuccessful retry. Your account
itself is <strong>not deleted</strong>; you can resume by updating your payment method.
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">7. How long refunds take</h2>
<p>
Approved refunds are issued to the original payment method via Stripe and typically
arrive in your account within 5&ndash;10 business days, depending on your bank or card
provider.
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">8. Contact</h2>
<p>
For refund or cancellation queries, email
<a href="mailto:[PLACEHOLDER: hello@fuelalert.co.uk]" class="text-accent underline">[PLACEHOLDER: hello@fuelalert.co.uk]</a>.
</p>
</section>
</x-layouts.legal>

View File

@@ -0,0 +1,221 @@
{{-- DRAFT: Generated {{ date('Y-m-d') }}. Review by UK-qualified solicitor recommended before launch. --}}
<x-layouts.legal
title="Terms of Service"
heading="Terms of Service"
lastUpdated="{{ now()->format('j F Y') }}"
metaDescription="The terms that govern your use of FuelAlert's subscription service.">
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">1. About these terms</h2>
<p>
FuelAlert is a trading name of <strong>Ovidiu Ungureanu</strong>, a sole trader based in
Peterborough, United Kingdom ("we", "us", "our"). These terms form a legally binding
contract between you and Ovidiu Ungureanu trading as FuelAlert.
</p>
<p>
By creating an account or using the service, you confirm that you have read, understood
and accepted these terms. If you do not accept them, please do not use the service.
</p>
<p>These terms are governed by the laws of England and Wales.</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">2. The service</h2>
<p>
FuelAlert provides UK fuel price comparison and forecasting. We aggregate publicly
available UK fuel price data and surface it through a web app, alerts and forecasts.
</p>
<p>
We offer a free tier and one or more paid subscription plans. The current list of plans
and prices is available on our <a class="text-accent underline" href="/#pricing">pricing page</a>.
</p>
<p>
We may add, remove, or change features over time. Where changes materially reduce the
paid service, we will give you reasonable notice and, where appropriate, a way to cancel.
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">3. Your account</h2>
<ul class="list-disc space-y-1 pl-6">
<li>You must be at least 18 years old and resident in the United Kingdom to create an account.</li>
<li>The information you provide must be accurate and kept up to date.</li>
<li>One account per person. You are responsible for keeping your login credentials secure.</li>
<li>You are responsible for activity that takes place under your account.</li>
<li>We may suspend or close accounts where these terms are seriously or repeatedly breached.</li>
</ul>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">4. Subscriptions, billing and payment</h2>
<p>
Paid plans are billed in advance, either monthly or annually. The current price for each
plan is shown on the <a class="text-accent underline" href="/#pricing">pricing page</a> at the time you subscribe.
</p>
<p>
<strong>Auto-renewal.</strong> Subscriptions renew automatically at the end of each
billing period at the then-current price, unless you cancel before the renewal date. By
subscribing you authorise FuelAlert &mdash; through our payment processor Stripe &mdash;
to charge your nominated payment method at each renewal.
</p>
<p>
<strong>Failed payments.</strong> If a payment fails, we and Stripe will retry the
payment over the following days. We will email you when this happens. Persistent failure
will cause your paid features to be suspended; your account itself is not deleted.
</p>
<p>
<strong>Price changes.</strong> If we change the price of your plan, we will give you at
least 30 days' notice by email before the new price takes effect on your next renewal.
You may cancel before the change takes effect.
</p>
<p>
<strong>VAT.</strong> Prices include UK VAT where applicable.
<strong>[PLACEHOLDER: verify before launch &mdash; FuelAlert is currently below the UK VAT registration threshold and therefore does not charge VAT.]</strong>
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">5. Cancellation and refunds</h2>
<p>
You can cancel your subscription at any time from your account settings. Cancellation
stops the next renewal; you keep access to paid features until the end of the current
billing period.
</p>
<p>
New subscribers have a <strong>14-day right to cancel</strong> under the Consumer
Contracts Regulations 2013. Important details &mdash; including the express-consent
mechanism that affects this right &mdash; are set out in our
<a class="text-accent underline" href="{{ route('legal.refund') }}">Refund &amp; Cancellation Policy</a>.
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">6. Acceptable use</h2>
<p>You agree not to:</p>
<ul class="list-disc space-y-1 pl-6">
<li>Scrape, reverse-engineer, or bulk-extract data from the service.</li>
<li>Resell or redistribute fuel price data taken from FuelAlert.</li>
<li>Use the service for any unlawful purpose.</li>
<li>Attempt to circumvent or compromise our security measures.</li>
<li>Use automated tools to make queries beyond what a single human user would reasonably make.</li>
</ul>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">7. Accuracy of price data</h2>
<p>
Fuel prices shown on FuelAlert are sourced from official UK government data feeds
(including the Pump Watch / Fuel Finder transparency schemes) and refreshed
periodically. Stations can change prices at any time, and there is usually a delay
between a forecourt change and the feed update.
</p>
<p>
We make reasonable efforts to display accurate prices but <strong>we cannot guarantee
that the price shown will match the price at the pump</strong> when you arrive.
<strong>Always confirm the price at the pump before fuelling.</strong>
</p>
<p>
We are not liable for any loss arising from inaccurate, delayed, or missing price data,
including the cost of a wasted journey or any difference between the price shown and the
price charged.
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">8. Forecasts and predictions</h2>
<p>
FuelAlert may show forecasts and recommendations (e.g. "fill up now" or "wait"). These
are <strong>informational only</strong>, are not financial advice, and should not be
relied upon as a guarantee of future prices. Past trends do not guarantee future prices.
We do not warrant the accuracy of any forecast.
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">9. Intellectual property</h2>
<p>
The FuelAlert name, logo, software, and original content are owned by Ovidiu Ungureanu.
You receive a limited, non-exclusive, revocable licence to use the service for personal,
non-commercial purposes.
</p>
<p>
Underlying fuel price data is owned by the respective fuel retailers and published under
UK government open data schemes. Postcode and geographic data is sourced from the ONS
Postcode Directory, &copy; Crown Copyright, used under the Open Government Licence v3.0.
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">10. Third-party services</h2>
<p>
We use <strong>Stripe</strong> to process payments. Your use of Stripe is also subject
to Stripe's own terms and privacy policy. We may use other third-party processors to
run the service; these are named in our
<a class="text-accent underline" href="{{ route('legal.privacy') }}">Privacy Policy</a>.
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">11. Limitation of liability</h2>
<p>
Nothing in these terms excludes or limits our liability for death or personal injury
caused by our negligence, fraud or fraudulent misrepresentation, or any other liability
that cannot be excluded under UK consumer law. Your statutory rights as a consumer are
not affected.
</p>
<p>Subject to the paragraph above:</p>
<ul class="list-disc space-y-1 pl-6">
<li>We exclude liability for indirect, consequential, or business losses.</li>
<li>
For paying subscribers, our total liability to you in any 12-month period is capped
at the total amount you paid in subscription fees during that period.
</li>
<li>
We do not accept liability for issues caused by third-party services we rely on,
including but not limited to outages or errors at our payment processor, hosting
provider, or upstream data sources.
</li>
</ul>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">12. Termination</h2>
<p>
You may stop using the service and close your account at any time. We may terminate or
suspend access for serious breach of these terms, with reasonable notice where the
breach is capable of being put right.
</p>
<p>
Sections that by their nature should survive termination (including sections 7 to 11)
will continue to apply after your account is closed.
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">13. Changes to these terms</h2>
<p>
We may update these terms. Material changes will be notified to registered users by
email at least 14 days before they take effect. Continued use of the service after the
change date means you accept the new terms.
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">14. Disputes</h2>
<p>
Please contact us first if you have a complaint &mdash; we will try to resolve it
directly. These terms are governed by the laws of England and Wales, and the courts of
England and Wales have non-exclusive jurisdiction over any dispute. If you live
elsewhere in the United Kingdom, you keep the right to bring proceedings in the courts
of your country of residence.
</p>
</section>
<section class="space-y-3">
<h2 class="font-display text-2xl font-bold text-zinc-900">15. Contact</h2>
<p>
For questions about these terms, email
<a href="mailto:[PLACEHOLDER: hello@fuelalert.co.uk]" class="text-accent underline">[PLACEHOLDER: hello@fuelalert.co.uk]</a>.
</p>
</section>
</x-layouts.legal>

View File

@@ -24,5 +24,13 @@ Route::middleware(['auth'])->prefix('billing')->name('billing.')->group(function
Route::get('/cancel', [BillingController::class, 'cancel'])->name('cancel');
});
// Server-rendered legal pages — must be registered before the SPA catch-all
Route::prefix('legal')->name('legal.')->group(function () {
Route::view('/privacy', 'legal.privacy')->name('privacy');
Route::view('/terms', 'legal.terms')->name('terms');
Route::view('/refund', 'legal.refund')->name('refund');
Route::view('/cookies', 'legal.cookies')->name('cookies');
});
// SPA catch-all — must be last
Route::get('/{any?}', fn () => view('app'))->where('any', '.*')->name('home');

View File

@@ -0,0 +1,54 @@
<?php
it('serves the privacy policy with required content', function (): void {
$response = $this->get('/legal/privacy');
$response->assertStatus(200);
$response->assertSeeText('Privacy Policy');
$response->assertSeeText('data controller');
$response->assertSeeText('UK GDPR');
$response->assertSeeText('Last updated:');
});
it('serves the terms of service with required content', function (): void {
$response = $this->get('/legal/terms');
$response->assertStatus(200);
$response->assertSeeText('Terms of Service');
$response->assertSeeText('subscription');
$response->assertSeeText('England and Wales');
});
it('serves the refund policy with cooling-off content', function (): void {
$response = $this->get('/legal/refund');
$response->assertStatus(200);
$response->assertSeeText('Refund');
$response->assertSeeText('14-day');
$response->assertSeeText('Consumer Contracts');
});
it('serves the cookie policy with essential-cookie content', function (): void {
$response = $this->get('/legal/cookies');
$response->assertStatus(200);
$response->assertSeeText('Cookie Policy');
$response->assertSeeText('essential');
});
it('does not render the SPA mount point on legal pages', function (string $path): void {
$response = $this->get($path);
$response->assertStatus(200);
$response->assertDontSee('<div id="app"></div>', false);
})->with([
'/legal/privacy',
'/legal/terms',
'/legal/refund',
'/legal/cookies',
]);
it('cross-links between legal pages', function (): void {
$this->get('/legal/privacy')->assertSee(route('legal.cookies'), false);
$this->get('/legal/terms')->assertSee(route('legal.refund'), false);
});