{
  "name": "Proof",
  "slug": "proof",
  "blocks": ["Pages", "Lists", "Sign-in: users"],
  "persona": "freelancers",
  "keywords": [
    "photographer",
    "client proofing",
    "proofing gallery",
    "client gallery",
    "pick favourites",
    "client portal",
    "photo selection",
    "wedding photographer",
    "photo delivery",
    "client login",
    "favourite photos",
    "client photo gallery",
    "photo proofing app",
    "photography clients"
  ],
  "description": "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.",
  "audience": [
    "wedding photographers",
    "portrait and headshot photographers",
    "photographers with clients who choose their own photos"
  ],
  "category": "Client portal",
  "job": "member-app",
  "categoryId": "client-portal",
  "alsoFits": [
    "photo selection",
    "client gallery",
    "photo delivery",
    "photographer client area",
    "proofing gallery"
  ],
  "direction": "Contact sheet: darkroom near-black, safelight red accents, paper white, film-strip frames and grease-pencil circles",
  "mood": [
    "dark",
    "bold",
    "modern"
  ],
  "fonts": [
    "Playfair Display",
    "Space Mono"
  ],
  "memberApp": {
    "path": "/app",
    "setup": {
      "signup": "invite",
      "profile_fields": [
        {
          "name": "name",
          "type": "text",
          "label": "First name"
        }
      ],
      "display_field": "name"
    },
    "lists": [
      {
        "list": "galleries",
        "label": "Client galleries",
        "by": "owner",
        "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"
          }
        ]
      },
      {
        "list": "picks",
        "label": "Album picks",
        "by": "member",
        "status_options": [
          "Picked",
          "Retouching",
          "In album"
        ],
        "reply": true,
        "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"
          }
        ],
        "plan_rules": {
          "limit": {
            "*": 120
          },
          "per": "month"
        }
      },
      {
        "list": "submissions",
        "label": "Picks sent",
        "by": "member",
        "status_options": [
          "Received",
          "Retouching",
          "Delivered",
          "Reopened"
        ],
        "reply": true,
        "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"
          }
        ],
        "plan_rules": {
          "limit": {
            "*": 3
          },
          "per": "month"
        }
      },
      {
        "list": "deliveries",
        "label": "Finals",
        "by": "owner",
        "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"
          }
        ]
      },
      {
        "list": "extras",
        "label": "Prints and extras",
        "by": "member",
        "status_options": [
          "Requested",
          "Invoice sent",
          "In production",
          "Delivered"
        ],
        "reply": true,
        "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"
          }
        ]
      }
    ],
    "demo": {
      "member": {
        "name": "Ada Whitfield",
        "email": "ada.whitfield@example.com",
        "profile": {
          "name": "Ada"
        }
      },
      "plans": [],
      "rows": {
        "galleries": [
          {
            "title": "Ada and Tom, wedding day",
            "shoot_date": "-5w:sat",
            "gallery_link": "https://gallery.ineshalloway.example.com/ada-and-tom",
            "frames": "1–518",
            "picks_allowed": 30,
            "deadline": "+9d",
            "_added": "-12d",
            "about": "All 518 frames, from getting ready at Hollin House to the sparklers at 22:40, in the order of the day.",
            "open_until": "+47w:sat",
            "package": "Wedding Collection",
            "package_details": "Full day, from getting ready to the first dance\nEvery frame in your private gallery for 12 months\n25 sneak peeks within a week\n30 album photos, retouched by hand\nA 30-page linen album, delivered by courier",
            "thumb_url": "https://gallery.ineshalloway.example.com/ada-and-tom/thumbs/{frame}.jpg"
          },
          {
            "title": "Whitfield family mini session",
            "shoot_date": "-4w:sun",
            "gallery_link": "https://gallery.ineshalloway.example.com/whitfield-mini",
            "frames": "1–64",
            "picks_allowed": 6,
            "deadline": "-1w:sun",
            "open_until": "+48w:sun",
            "about": "Half an hour in the garden for Grandma Ruth's eightieth: 64 frames of the whole family.",
            "package": "Family mini session",
            "package_details": "30 minutes at home or outdoors\n6 photos, retouched by hand\nYour gallery open for 12 months",
            "_added": "-3w:tue"
          },
          {
            "title": "Ada and Tom, engagement shoot",
            "shoot_date": "-80d",
            "gallery_link": "https://gallery.ineshalloway.example.com/ada-tom-engagement",
            "frames": "1–96",
            "picks_allowed": 10,
            "deadline": "-66d",
            "_added": "-78d",
            "about": "Ninety-six frames from the harbour wall at golden hour.",
            "open_until": "+285d",
            "package": "Engagement shoot",
            "package_details": "Two hours at golden hour\n10 photos, retouched by hand"
          }
        ],
        "picks": [
          {
            "label": "Frame 488 · Ada and Tom, wedding day",
            "gallery": "Ada and Tom, wedding day",
            "frame": 488,
            "finish": "Colour",
            "crop": "Landscape 3:2",
            "memo": "The sparklers. Keep it dark and moody.",
            "_added": "-1d",
            "_status": "Picked",
            "change": "Picked"
          },
          {
            "label": "Frame 112 · Ada and Tom, wedding day",
            "gallery": "Ada and Tom, wedding day",
            "frame": 112,
            "finish": "Black and white",
            "crop": "As shot",
            "memo": "First dance. Please keep the fairy lights.",
            "_added": "-2d",
            "_status": "Picked",
            "change": "Picked"
          },
          {
            "label": "Frame 204 · Ada and Tom, wedding day",
            "gallery": "Ada and Tom, wedding day",
            "frame": 204,
            "finish": "Colour",
            "crop": "Portrait 4:5",
            "memo": "The bouquet. Keep the ribbon in, please.",
            "_added": "-4d",
            "_status": "Picked",
            "change": "Picked"
          },
          {
            "label": "Frame 88 · Ada and Tom, wedding day",
            "gallery": "Ada and Tom, wedding day",
            "frame": 88,
            "finish": "Colour",
            "crop": "As shot",
            "memo": "Confetti on the path, our favourite of the day. Can you take the exit sign out of the corner?",
            "_added": "-8d",
            "_status": "Picked",
            "_reply": "Yes, the sign comes out when I retouch it.",
            "change": "Picked"
          },
          {
            "label": "Frame 63 · Ada and Tom, wedding day",
            "gallery": "Ada and Tom, wedding day",
            "frame": 63,
            "finish": "Colour",
            "crop": "Landscape 3:2",
            "memo": "The arch at dusk. Keep this one for the album cover.",
            "_added": "-9d",
            "_status": "Picked",
            "_reply": "Lovely choice for the cover. I'll keep the dusk colour.",
            "change": "Picked"
          },
          {
            "label": "Frame 14 · Ada and Tom, wedding day",
            "gallery": "Ada and Tom, wedding day",
            "frame": 14,
            "finish": "Colour",
            "crop": "Square",
            "memo": "The rings, for the opening page.",
            "_added": "-11d",
            "_status": "Picked",
            "_reply": "Perfect for the opening page.",
            "change": "Picked"
          },
          {
            "label": "Frame 42 · Whitfield family mini session",
            "frame": 42,
            "change": "Picked",
            "finish": "Colour",
            "crop": "Portrait 4:5",
            "gallery": "Whitfield family mini session",
            "memo": "Grandma Ruth laughing with the cake.",
            "_added": "-2w:tue",
            "_status": "In album",
            "_reply": "The cake candles came up beautifully."
          },
          {
            "label": "Frame 37 · Whitfield family mini session",
            "frame": 37,
            "change": "Picked",
            "finish": "Black and white",
            "crop": "As shot",
            "gallery": "Whitfield family mini session",
            "memo": "The whole family on the bench.",
            "_added": "-2w:tue",
            "_status": "In album"
          },
          {
            "label": "Frame 23 · Whitfield family mini session",
            "frame": 23,
            "change": "Picked",
            "finish": "Colour",
            "crop": "Square",
            "gallery": "Whitfield family mini session",
            "memo": "Everyone under the apple tree.",
            "_added": "-2w:tue",
            "_status": "In album"
          },
          {
            "label": "Frame 19 · Whitfield family mini session",
            "frame": 19,
            "change": "Picked",
            "finish": "Colour",
            "crop": "Landscape 3:2",
            "gallery": "Whitfield family mini session",
            "memo": "Dad and Grandma on the swing seat.",
            "_added": "-2w:tue",
            "_status": "In album"
          },
          {
            "label": "Frame 11 · Whitfield family mini session",
            "frame": 11,
            "change": "Picked",
            "finish": "Either",
            "crop": "Portrait 4:5",
            "gallery": "Whitfield family mini session",
            "memo": "The hands on the blanket.",
            "_added": "-2w:tue",
            "_status": "Retouching",
            "_reply": "Working on this one now: softening the shadow across the hands."
          },
          {
            "label": "Frame 4 · Whitfield family mini session",
            "frame": 4,
            "change": "Picked",
            "finish": "Colour",
            "crop": "As shot",
            "gallery": "Whitfield family mini session",
            "memo": "The garden gate as everyone arrived.",
            "_added": "-2w:tue",
            "_status": "Picked"
          }
        ],
        "deliveries": [
          {
            "title": "Wedding sneak peeks",
            "download_link": "https://delivery.ineshalloway.example.com/ada-tom-sneak-peeks",
            "files": 25,
            "expires": "+30d",
            "_added": "-5w:sun",
            "details": "Twenty-five favourites from the day, ready to share with family."
          },
          {
            "title": "Engagement finals",
            "download_link": "https://delivery.ineshalloway.example.com/ada-tom-engagement",
            "files": 10,
            "expires": "+20d",
            "_added": "-40d",
            "details": "Your ten engagement picks, retouched, at full size and web size. The link is open for 60 days."
          }
        ],
        "extras": [
          {
            "quantity": 2,
            "frames": "14, 63, 204",
            "_added": "-6d",
            "_status": "Invoice sent",
            "_reply": "Invoice for £480 is in your email. The albums go to print once your picks close.",
            "details": "One for each set of parents, the same linen as ours.",
            "product": "Parent album",
            "gallery": "Ada and Tom, wedding day"
          },
          {
            "quantity": 1,
            "frames": "63",
            "_added": "-3w:mon",
            "_status": "Delivered",
            "_reply": "Posted first class on Tuesday. Enjoy writing them!",
            "product": "Thank-you cards, pack of 25",
            "gallery": "Ada and Tom, wedding day"
          }
        ],
        "submissions": [
          {
            "gallery": "Whitfield family mini session",
            "picks": "4 Colour As shot; 11 Either Portrait 4:5; 19 Colour Landscape 3:2; 23 Colour Square; 37 Black and white As shot; 42 Colour Portrait 4:5",
            "count": 6,
            "message": "One big print of 37 for Grandma Ruth, please.",
            "_added": "-2w:tue",
            "_status": "Retouching",
            "_reply": "Four of six are finished. Frame 11 is in progress; frame 4 is next."
          },
          {
            "gallery": "Ada and Tom, engagement shoot",
            "picks": "9, 22, 31, 38, 41, 47, 58, 63, 77, 90",
            "count": 10,
            "message": "All ten for the frame wall, please.",
            "_added": "-68d",
            "_status": "Delivered",
            "_reply": "All ten retouched and waiting for you on Finals."
          }
        ]
      }
    }
  },
  "files": [
    "index.html",
    "privacy.html",
    "app/index.html",
    "app/gallery.html",
    "app/picks.html",
    "app/finals.html",
    "app/extras.html",
    "app/account.html",
    "sample/download.html",
    "sample/gallery.html",
    "kit.css",
    "kit.js",
    "kit-icons.svg",
    "styles.css",
    "app.js",
    "site.webmanifest",
    "favicon.svg",
    "art/icons.svg",
    "art/photos/rings.webp",
    "art/photos/rings-t.webp",
    "art/photos/bouquet.webp",
    "art/photos/bouquet-t.webp",
    "art/photos/arch.webp",
    "art/photos/arch-t.webp",
    "art/photos/confetti.webp",
    "art/photos/confetti-t.webp",
    "art/photos/first-dance.webp",
    "art/photos/first-dance-t.webp",
    "art/photos/sparklers.webp",
    "art/photos/sparklers-t.webp",
    "art/shoot-portrait.svg",
    "art/shoot-headshot.svg",
    "art/extra-retouch.svg",
    "art/extra-print.svg",
    "art/extra-canvas.svg",
    "art/extra-album.svg",
    "art/extra-cards.svg",
    "art/extra-gallery.svg",
    "art/find-number.svg",
    "og.png",
    "apple-touch-icon.png",
    "icon-192.png",
    "icon-512.png",
    "icon-512-maskable.png"
  ],
  "pages": [
    {
      "path": "index.html",
      "label": "Home",
      "purpose": "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"
    },
    {
      "path": "app/index.html",
      "label": "Home",
      "purpose": "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"
    },
    {
      "path": "app/gallery.html",
      "label": "Gallery",
      "purpose": "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"
    },
    {
      "path": "app/picks.html",
      "label": "Picks",
      "purpose": "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"
    },
    {
      "path": "app/finals.html",
      "label": "Finals",
      "purpose": "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"
    },
    {
      "path": "app/extras.html",
      "label": "Extras",
      "purpose": "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"
    },
    {
      "path": "app/account.html",
      "label": "Account",
      "purpose": "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"
    },
    {
      "path": "sample/gallery.html",
      "label": "Sample gallery",
      "purpose": "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."
    },
    {
      "path": "sample/download.html",
      "label": "Sample download",
      "purpose": "Sample only: stands in for a real delivery link, with one sample file to download. Delete it when the real links are in."
    },
    {
      "path": "privacy.html",
      "label": "Privacy",
      "purpose": "The privacy notice for clients, with blanks the Site Owner fills in"
    }
  ],
  "facts": [
    "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"
  ],
  "interview": [
    "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?"
  ],
  "customise": {
    "strings": [
      "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."
  },
  "schemaVersion": 2,
  "updated": "2026-10-21"
}
