# Proof: a Page Lemon website template

> An app template for a wedding and portrait photographer: each couple signs in, opens the proof gallery the photographer assigned, picks their album photos by frame number against their allowance before picks close, follows each pick through retouching, downloads the finished files and orders prints and albums. A public page, six client pages under /app and a privacy notice with blanks.

You are reading this because the user wants their own website made from the Proof template and published with Page Lemon (PageLemon.com). They may have pasted only "Use template pagelemon.com/tp/Proof": that is the whole request, so start now. In short: ask them a few questions about their business, get the template's files (with the Page Lemon connector, `deploy` with `template: "proof"` copies them for you), make the site theirs (their name, words, details and pictures; no sample content left), then publish it with Page Lemon and give them the link. The steps below say exactly how. The full publishing guide is at https://pagelemon.com/agents.

## About this template

- **Look:** Contact sheet: darkroom near-black, safelight red accents, paper white, film-strip frames and grease-pencil circles (dark, bold, modern)
- **Pages:** Home (`index.html`): The public page: a contact sheet of frames beside the headline, one large phone screen of the picks with three steps, four steps from the day to the album as a film strip, a red band of facts, the three shoot types with from prices and what is included, prints and albums, the studio details and client sign-in; Home (`app/index.html`): Signed out: a welcome with sign-in and who to email if not invited. Signed in: a greeting, the current gallery's meter (distinct photos chosen against the allowance, days to send), Add a pick and Open gallery, a lock note when choosing is over, the latest choices, a send card, where you are (gallery, choose, send, retouching, finals, from the member's own rows), downloads with their closing dates, and the rules from the gallery's package; Gallery (`app/gallery.html`): The current proof gallery: shoot date, frame range, album photos, send-by date, how long it stays open (Keep it another year), Open your gallery in a new tab, the frames of the sample gallery with the picks circled, the three steps, a compact count strip, and every gallery with open, closed, sent or delivered and links to it and its choices; Picks (`app/picks.html`): A sticky count with Review and send; the choices newest first, each with its thumbnail (or frame number), finish, crop, note, status and reply, and Change (fills the form) and Remove (saves a removal row); Add a photo (the open gallery, a big frame-number field checked against the frame range, finish, crop, note), first when nothing is chosen yet and after the choices once there are some; Send your selection, which saves the exact frames and count once, then locks the page with a note to email Ines; Finals (`app/finals.html`): The delivered finals, each with the number of photos, a download button and the date its link closes (closed links become Ask Ines for a new link), and where you are; Extras (`app/extras.html`): Prints, canvases, parent albums, thank-you cards and extra retouched photos and another year of the gallery, with prices; a request form (the photographer invoices by email); the client's orders with their status and reply; Account (`app/account.html`): The package of the current gallery from the photographer's own data (name, what it includes, gallery open until, Keep it another year), how to reach the studio, then sign-in, devices and leaving last; Sample gallery (`sample/gallery.html`): Sample only: stands in for the photographer's own gallery service, which the gallery link opens on a real site (the sample rows link to example.com, and app.js sends those to this page). Delete it when the real links are in.; Sample download (`sample/download.html`): Sample only: stands in for a real delivery link, with one sample file to download. Delete it when the real links are in.; Privacy (`privacy.html`): The privacy notice for clients, with blanks the Site Owner fills in
- **Fonts:** Playfair Display, Space Mono (Google Fonts)
- **Live demo:** https://try-proof.pagelemon.dev/ (a working copy with sample data; people opening https://pagelemon.com/tp/Proof in a browser land there). The template's own files: https://pagelemon.com/templates/proof/; the page about it: https://pagelemon.com/tp/Proof/use
- **Manifest:** https://pagelemon.com/templates/proof/template.json (machine-readable: files, customise, facts, interview)

## Files

With the Page Lemon connector you don't need to fetch these: `deploy` with `template: "proof"` copies them all (step 2). Otherwise fetch each file from https://pagelemon.com/templates/proof/ and save it with the same relative path (including `art/`). Fetch nothing else: templates have no external images or scripts.

- https://pagelemon.com/templates/proof/index.html
- https://pagelemon.com/templates/proof/privacy.html
- https://pagelemon.com/templates/proof/app/index.html
- https://pagelemon.com/templates/proof/app/gallery.html
- https://pagelemon.com/templates/proof/app/picks.html
- https://pagelemon.com/templates/proof/app/finals.html
- https://pagelemon.com/templates/proof/app/extras.html
- https://pagelemon.com/templates/proof/app/account.html
- https://pagelemon.com/templates/proof/sample/download.html
- https://pagelemon.com/templates/proof/sample/gallery.html
- https://pagelemon.com/templates/proof/kit.css
- https://pagelemon.com/templates/proof/kit.js
- https://pagelemon.com/templates/proof/kit-icons.svg
- https://pagelemon.com/templates/proof/styles.css
- https://pagelemon.com/templates/proof/app.js
- https://pagelemon.com/templates/proof/site.webmanifest
- https://pagelemon.com/templates/proof/favicon.svg
- https://pagelemon.com/templates/proof/art/icons.svg
- https://pagelemon.com/templates/proof/art/photos/rings.webp
- https://pagelemon.com/templates/proof/art/photos/rings-t.webp
- https://pagelemon.com/templates/proof/art/photos/bouquet.webp
- https://pagelemon.com/templates/proof/art/photos/bouquet-t.webp
- https://pagelemon.com/templates/proof/art/photos/arch.webp
- https://pagelemon.com/templates/proof/art/photos/arch-t.webp
- https://pagelemon.com/templates/proof/art/photos/confetti.webp
- https://pagelemon.com/templates/proof/art/photos/confetti-t.webp
- https://pagelemon.com/templates/proof/art/photos/first-dance.webp
- https://pagelemon.com/templates/proof/art/photos/first-dance-t.webp
- https://pagelemon.com/templates/proof/art/photos/sparklers.webp
- https://pagelemon.com/templates/proof/art/photos/sparklers-t.webp
- https://pagelemon.com/templates/proof/art/shoot-portrait.svg
- https://pagelemon.com/templates/proof/art/shoot-headshot.svg
- https://pagelemon.com/templates/proof/art/extra-retouch.svg
- https://pagelemon.com/templates/proof/art/extra-print.svg
- https://pagelemon.com/templates/proof/art/extra-canvas.svg
- https://pagelemon.com/templates/proof/art/extra-album.svg
- https://pagelemon.com/templates/proof/art/extra-cards.svg
- https://pagelemon.com/templates/proof/art/extra-gallery.svg
- https://pagelemon.com/templates/proof/art/find-number.svg
- https://pagelemon.com/templates/proof/og.png
- https://pagelemon.com/templates/proof/apple-touch-icon.png
- https://pagelemon.com/templates/proof/icon-192.png
- https://pagelemon.com/templates/proof/icon-512.png
- https://pagelemon.com/templates/proof/icon-512-maskable.png

## Notes for Proof (its `customise` rules)

- Ines Halloway-Brandt Photography in the header, footer and titles, with the studio address, phone 01632 960 845 and studio@ineshalloway.example.com: replace every data-pl-fill fact with the photographer's own, and the shoot names, from prices and package lists on index.html and app/account.html.
- Fill in every highlighted blank on privacy.html with the Site Owner before clients use the site (not legal advice: https://pagelemon.com/agents/member-app-legal).
- setup_app with memberApp.setup (invite only: the photographer invites each couple after the shoot), then save_app_list for galleries, picks, submissions, deliveries and extras exactly as memberApp.lists says. Change fields there, in the add forms and in the rows together.
- Galleries (by owner): one row per shoot, assigned to the client in the Dashboard, with the https link to the proof gallery on the photographer's own gallery service, the frame range, the album picks included and the date picks close. The newest gallery drives the picks meter on Home, Gallery and Picks. open_until is how long the gallery and its downloads stay open; Keep my gallery another year is sold on Extras.
- Finals (by owner): one row per delivery with the https download link, the number of photos and the date the link closes.
- Extras: change the seven items and prices on app/extras.html and in the extras list options together. Requests are not payments: the photographer confirms, sends an invoice by email and moves the status from Requested to Invoice sent, In production and Delivered.
- The wedding photos in art/photos/ are free stock (credits in PHOTOS.md): six photos, each with a -t thumbnail, used for the sample frames, the welcome strip, the gallery cover and the wedding shoot card. The rest of art/ is drawn: shoot-portrait.svg and shoot-headshot.svg (1200x1500), extra-*.svg (600x600) and the icons. Replace them with the photographer's own work as WebP under 300 KB each at the same shapes; never put a client's photos on the public page without permission.
- The selection rules are the page's (app.js), not the server's: one choice per frame per gallery (the newest row wins, a Remove row (Keep or remove = Removed) takes it out), counted only for the current gallery; the add and send forms lock when the gallery is missing, closed or sent; Send your selection saves the exact frames, finishes, crops and count in Picks sent. To reopen a sent selection, set the latest Picks sent row to Reopened. Day to day the photographer opens Apps, Entries in the Dashboard: Picks sent shows each selection to work from, Album picks each photo, and statuses and replies show on the client's pages.
- Galleries carry the package (package, package_details) so Account and Home show what each client actually booked. The sample/ pages are only for the sample rows' example.com links; delete them, and drop rule 0 in app.js, once real links are in.
- thumb_url (optional, galleries): if your gallery service has a thumbnail address per frame, put it with {frame} where the number goes (for example https://your-gallery/thumbs/{frame}.jpg) and each choice shows its photo; otherwise the frame number shows. The sample rows use art/photos/ for the seeded frames.
- What Page Lemon enforces and what the page does: the lists carry plan_rules (picks: at most 120 rows a month per client, which covers 30 photos and their changes; Picks sent: at most 3 a month), enforced on every save. The send-by date, one send per gallery and the frame range are checked by app.js in the browser only, so check each Picks sent row before you start retouching and tell the client if something is late or out of range. Remove saves a row with Keep or remove set to Removed. Keep the newest row per user, gallery and frame, then discard Removed.
- Photo slots: every public image with data-pl-photo (the twelve contact-sheet frames and the three shoot cards) is a slot for the photographer's own work, and its value says the shape to use. Ask for real photos at setup and replace the stock photos before the site goes live; the stock frames in art/photos/ only stand in for the sample rows.
- **Colours:** Edit the darkroom tokens in the art direction block at the end of styles.css: --k-bg and --k-hero (ink), --k-brand (safelight red, for the main action and the grease circles only), --k-gold (fixer amber for numbers and chips) and --k-muted (grain). The light scheme stays dark. The app's components come from kit.css and are never edited here.
- **Fonts:** Playfair Display and Space Mono, loaded from Google Fonts on every page; your AI can self-host them on request.
- **Sections:** Client pages live under /app on the shared app kit (kit.css, kit.js, kit-icons.svg; art/icons.svg for Proof's own icons) and use the data-pl-view markup. app/index.html: welcome when signed out, then the picks meter, latest picks and finals. app/gallery.html: the current gallery and all galleries. app/picks.html: the picks add form and the picks with status and reply. app/finals.html: deliveries. app/extras.html: the extras add form and orders. app/account.html: the account block and the package. app.js fills the picks meter, the link buttons and the link-closing badges from what Page Lemon renders.
- **Images:** Art in art/ (see the string about the wedding photos); favicon.svg is the frame mark, and the PNG icons and manifest are made from it.

## Set up the app

This is an app template: the home page and privacy notice are public, and the pages under `/app` are for signed-in users, each seeing only their own entries and the entries the Site Owner assigns to them. Users sign in with a code sent to their email. The full guide is https://pagelemon.com/agents/member-app; read it before you set this up, and its legal notes at https://pagelemon.com/agents/member-app-legal.

- **Needs:** a kept site on App or above, and the Site Owner signed in to Page Lemon with two-step sign-in (they turn the app on themselves, at the approve link `setup_app` returns).
- **Never:** health, mood or therapy records, accounts for children, or fields for card, bank or ID numbers (Acceptable Use: https://pagelemon.com/acceptable-use#member-app). Users pay through Stripe.
- **Then:** `setup_app` with these settings, the join questions and note in the user's own words, then `save_app_list` once for each list below, exactly as written, so the field names match the pages.

  ```json
  {"site":"<site>","signup":"invite","profile_fields":[{"name":"name","type":"text","label":"First name"}],"display_field":"name","path":"/app"}
  ```

- **Client galleries** (`galleries`): the Site Owner adds these rows in the Page Lemon Dashboard (Apps tab) and assigns each to one user, who only reads it.

  ```json
  {"site":"<site>","list":"galleries","label":"Client galleries","fields":[{"name":"title","type":"text","label":"Gallery","required":true},{"name":"shoot_date","type":"date","label":"Shoot date","required":true},{"name":"gallery_link","type":"text","label":"Gallery link","required":true},{"name":"thumb_url","type":"text","label":"Thumbnail address per frame"},{"name":"frames","type":"text","label":"Frame numbers"},{"name":"picks_allowed","type":"number","label":"Album picks included"},{"name":"deadline","type":"date","label":"Picks close"},{"name":"open_until","type":"date","label":"Gallery open until"},{"name":"about","type":"long_text","label":"Note for the client"},{"name":"package","type":"text","label":"Package"},{"name":"package_details","type":"long_text","label":"What the package includes"}]}
  ```

- **Album picks** (`picks`): users add these rows themselves on the pages.

  ```json
  {"site":"<site>","list":"picks","label":"Album picks","fields":[{"name":"label","type":"text","label":"Photo"},{"name":"change","type":"choice","label":"Keep or remove","required":true,"options":["Picked","Removed"]},{"name":"finish","type":"choice","label":"Finish","required":false,"options":["Colour","Black and white","Either"]},{"name":"crop","type":"choice","label":"Crop","required":false,"options":["As shot","Square","Portrait 4:5","Landscape 3:2"]},{"name":"frame","type":"number","label":"Frame number","required":true},{"name":"gallery","type":"text","label":"Gallery","required":true},{"name":"memo","type":"long_text","label":"A note for Ines"}],"status_options":["Picked","Retouching","In album"],"reply":true}
  ```

- **Picks sent** (`submissions`): users add these rows themselves on the pages.

  ```json
  {"site":"<site>","list":"submissions","label":"Picks sent","fields":[{"name":"gallery","type":"text","label":"Gallery","required":true},{"name":"picks","type":"long_text","label":"Frames sent"},{"name":"count","type":"number","label":"Photos sent"},{"name":"message","type":"long_text","label":"A note with your picks"}],"status_options":["Received","Retouching","Delivered","Reopened"],"reply":true}
  ```

- **Finals** (`deliveries`): the Site Owner adds these rows in the Page Lemon Dashboard (Apps tab) and assigns each to one user, who only reads it.

  ```json
  {"site":"<site>","list":"deliveries","label":"Finals","fields":[{"name":"title","type":"text","label":"Delivery","required":true},{"name":"download_link","type":"text","label":"Download link","required":true},{"name":"files","type":"number","label":"Photos"},{"name":"expires","type":"date","label":"Link closes"},{"name":"details","type":"long_text","label":"Note"}]}
  ```

- **Prints and extras** (`extras`): users add these rows themselves on the pages.

  ```json
  {"site":"<site>","list":"extras","label":"Prints and extras","fields":[{"name":"product","type":"choice","label":"What would you like?","required":true,"options":["Extra retouched photo","Fine-art print 8×10","Fine-art print 12×16","Canvas 20×30","Parent album","Thank-you cards, pack of 25","Keep my gallery another year"]},{"name":"quantity","type":"number","label":"How many","required":true},{"name":"gallery","type":"text","label":"Gallery"},{"name":"frames","type":"text","label":"Frame numbers"},{"name":"details","type":"long_text","label":"Anything else"}],"status_options":["Requested","Invoice sent","In production","Delivered"],"reply":true}
  ```

## Build the site, step by step

1. **Interview first.** Ask the user these questions, a few at a time, in plain words, and collect every fact the site needs (studio name, address, phone, studio email, shoot types and from prices, package contents, album picks included, extras and prices, studio hours, privacy contact and address, gallery service used for the links). If they can't answer one, remove that element rather than inventing it.
   - What is your studio called, and what do you shoot most (weddings, portraits, headshots)?
   - Where is the studio, and what phone number and email should clients use?
   - What do you charge from for each type of shoot, and what is in each package?
   - How many album photos are included, and how long do clients have to pick?
   - Which online gallery do you use for the proof links, and how long do download links stay open?
   - Which prints, albums and extras do you sell, and at what prices?
   - What address and privacy contact should the privacy notice show?
2. **Get the files.** If you have the Page Lemon tool (the MCP connector with a `deploy` tool; https://pagelemon.com/docs/connect sets it up), call `deploy` with `template: "proof"` (no files): Page Lemon copies every file above on its side and returns `site`, `revision` and, without an account, `site_token`. Read each page you will change with `get_site` and `path`, make the changes below, then publish only the changed files with `deploy`, `site`, `base_revision` (that revision) and `patch: true`; show the link only after that. Without the tool, fetch the files listed above. If you cannot fetch files either, ask the user for what you need and rebuild the pages from the live demo as closely as you can.
3. **Make it the user's site.** Follow `customise` in `template.json`:
   - Replace every element marked `data-pl-fill` (names, phone, email, address, hours, prices, people, dates, links, stats) on every page, then remove the attributes. **Never ship a `data-pl-fill` default.** The sample town, street and people's names also appear in ordinary sentences, `aria-label`s and image `alt` text: replace them there too. Name, address and phone must match exactly in the footer, the visit or contact page and the JSON-LD.
   - Sample reviews (`data-pl-fill="review"`) are fictional: delete them unless the user gives you real ones. Delete each whole review, with its name or caption, and the section and its nav link when none is left. Never invent reviews, numbers or awards.
   - Update each page's `<title>`, meta description, canonical, `og:url` and `og:image` to the user's real address, and the JSON-LD block on the home page (type, name, address, `geo` coordinates (delete them unless you have the real ones), hours, phone, `url`).
   - Photos: each `<img data-pl-photo="...">` says what photo belongs there and its shape. Swap in the user's photos (compressed, WebP or JPEG under about 300 KB, same aspect ratio, real `alt`), or keep the illustration. Ask for photos; never hot-link.
   - Make a new `og.png` (1200×630) of the new home page if you can, with its `og:image` tags; otherwise delete the sample one and its `og:image` tags (a template deploy has already left both out). Change colours only through the `:root` tokens in `styles.css`, fonts through the font tokens and the `<link>` that loads them. Google Fonts is fine, but it sends each visitor's IP address to Google, so the privacy-friendly choice is to self-host the font files (woff2) in the site folder with `@font-face`, or use system fonts.
   - Delete pages or sections the user doesn't need (and their nav links) rather than leaving them generic. Keep the layout, type scale and signature interaction: they are what makes it look designed.
   - Remove the `<!-- Page Lemon template ... -->` comment.
4. **Forms and bookings.** Templates have no fake forms. Each `data-pl-slot` shows a fallback that works today (call, email, or the Site Owner's own booking or payment link: ask for it). The designed form sits next to it in an inert `<template data-pl-slot-form="<id>">`; `template.json.slots` says which page has which. When the user wants the real thing (Forms and Booking: any paid plan; staff and rooms only on some, and each plan has a limit of online bookings a day), set the form up as a list (`save_list`) or the service (`save_service`), put the matching markup in the slot (reuse the template's form classes so it matches), delete the fallback and the `<template>`, and copy the paragraph from `<template data-pl-privacy="forms">` into `privacy.html`.
5. **Check before publishing:**
   - Search every file for `data-pl-fill`, the template's fictional business name and its sample phone, email and street, in links too (`tel:` and `mailto:` hrefs, and map links, where the street is written like `18+Linden+Walk`): none left.
   - All links between pages work; nav and footer are the same on every page.
   - Body text contrast at least 4.5:1, large text and buttons 3:1, after any colour change.
   - No horizontal scroll at 320 px; headlines still fit on a phone after copy changes.
   - One `h1` per page, a unique `<title>` and meta description per page, alt text on meaningful images.
   - Copy sounds like the user, not like an AI (see https://pagelemon.com/guides/website-copy-that-doesnt-sound-like-ai).
6. **Publish with Page Lemon** (below; with the tool, the patch deploy from step 2 is the publish), then tell the user which template you used and that they can keep changing the site, or add bookings and forms, by asking.
7. **Turn on the app** as "Set up the app" above says: `setup_app`, then `save_app_list` for each list, the approve link to the Site Owner, `invite_users`, a test with `preview_as_user` and `get_app` with `check: true` before you say it's ready.

## Publish with Page Lemon

Pick the first route you can use:

1. **You can run shell commands:** build if needed, then upload only the folder with `index.html` (`dist/`, `build/`, `out/`…). Never upload source code, `.env` files or keys: everything you upload becomes public.
   (at most 100 files and 1 MB in all, `index.html` at the root):
   ```bash
   node -e 'const fs=require("fs"),p=require("path"),d=process.argv[1],files=[];(function w(x){for(const e of fs.readdirSync(x,{withFileTypes:true})){const f=p.join(x,e.name);e.isDirectory()?w(f):files.push({path:p.relative(d,f).split(p.sep).join("/"),content_base64:fs.readFileSync(f).toString("base64")})}})(d);console.log(JSON.stringify({files}))' <dir> \
     | curl -sS --fail-with-body -H "Content-Type: application/json" --data-binary @- https://api.pagelemon.com/v2/anonymous-sites
   ```
   If the user gave you an API key (`plak_…`) or `~/.config/pagelemon/credentials` exists, publish into their account instead (see "Updating a site" in https://pagelemon.com/agents). If `.pagelemon.json` exists, the site is already published: see "Updating a site" in https://pagelemon.com/agents.
2. **You have the Page Lemon MCP connector** (`https://mcp.pagelemon.com/mcp`): call its `publish_site` tool with every file. See "No shell: the MCP connector" in https://pagelemon.com/agents.
3. **Neither (a chat app without the Page Lemon connector):** say once that they can connect Page Lemon to this AI app (steps at https://pagelemon.com/docs/connect) so you can publish and change the site for them. In the Gemini app or Grok (free plan included) it takes about a minute: Gemini at gemini.google.com/customize/connectors (**Custom**), Grok at grok.com/connectors (**New Connector**, **Custom**), with the link `https://mcp.pagelemon.com/mcp`; then they sign in and click **Allow**. Give those steps in plain words. If they can't or would rather not (for example ChatGPT's free plan), don't ask them to save or upload files. Put the whole site in ONE complete page: CSS and JavaScript inline, images as https links or small data URIs, from `<!DOCTYPE html>` to `</html>`. Show it as one code block, then tell them: "Tap Copy on the code, open pagelemon.com/publish, tap Paste and publish." Only if you can really give them a downloadable .zip (a file they tap to download, not code to save): "Download this zip, open pagelemon.com/publish, tap Drop or choose a file." They get a live link with no account, online for 72 hours unless they tap **Keep it free**. Don't say you can't help.

Then:

- **Pass on `agent_message` from the response in full**, success or failure, in the user's language and your own words. It has the link, the expiry time, the private keep link and the terms line: copy every link, code, number, date and domain exactly, and never drop a link, deadline or the terms line (keep both its links). If it asks a question, ask it and wait. Times in it are UTC; `*_at` fields have them in ISO, so you can give the user's local time.
- **On success, save `.pagelemon.json`** in the project folder and add it to `.gitignore` (see https://pagelemon.com/agents).
- **Offer to help keep the site** (see "Keeping the site" in https://pagelemon.com/agents). Sites published without an account go offline after 72 hours unless the user keeps them with a free account.

By publishing, the user agrees to the Terms (https://pagelemon.com/terms) and Privacy Policy (https://pagelemon.com/privacy). The `agent_message` includes this line; keep it.

More: https://pagelemon.com/agents (full guide: updating a site, keeping it, custom domains, plugins). All templates: https://pagelemon.com/templates/index.json.
