{
  "name": "Clubhouse",
  "slug": "clubhouse",
  "blocks": ["Pages", "Lists", "Sign-in: users", "Actions: pay, scan"],
  "persona": "community",
  "keywords": [
    "camera club app",
    "photography club",
    "club members app",
    "request to join",
    "approve members",
    "outings",
    "RSVP",
    "club night check-in",
    "monthly competition",
    "photo competition",
    "judge's comments",
    "club subscription",
    "noticeboard",
    "hobby club",
    "members area"
  ],
  "description": "An app template for a camera club that approves its members: people ask to join on the sign-in page and the committee reviews each request in the Page Lemon Dashboard; members reply to outings with guests and lifts, enter the monthly theme with a photo and get the judge's score and note back, and check in at club night with a code (every 10th night a free print). A public page, six member pages under /app and a privacy notice with blanks.",
  "audience": [
    "camera and photography clubs",
    "hobby clubs and societies that vote new members in"
  ],
  "category": "Membership",
  "job": "member-app",
  "categoryId": "membership",
  "alsoFits": [
    "walking club",
    "birdwatching group",
    "sketching club",
    "allotment society",
    "book club"
  ],
  "direction": "Light box of square slide mounts in Kodachrome yellow and red, with members' prints, a navy band and a film-box card",
  "mood": [
    "light",
    "bold",
    "classic"
  ],
  "fonts": [
    "League Spartan",
    "Lexend"
  ],
  "memberApp": {
    "path": "/app",
    "setup": {
      "signup": "request",
      "request_note": "Ottoline, our secretary, reads every request. The committee answers at the next club night.",
      "profile_fields": [
        {
          "name": "name",
          "type": "text",
          "label": "First name",
          "required": true
        },
        {
          "name": "last_name",
          "type": "text",
          "label": "Last name",
          "required": true
        },
        {
          "name": "camera",
          "type": "text",
          "label": "Which camera do you use?"
        },
        {
          "name": "about",
          "type": "long_text",
          "label": "A line about yourself"
        }
      ],
      "display_field": "name"
    },
    "lists": [
      {
        "list": "rsvps",
        "label": "Outing replies",
        "by": "member",
        "fields": [
          {
            "name": "outing",
            "type": "choice",
            "label": "Outing",
            "required": true,
            "options": [
              "Sat 17 Oct · Hollin Pier at golden hour",
              "Sun 8 Nov · Sandling Marsh at sunrise",
              "Fri 20 Nov · Ludwell Quay after dark",
              "Mon 7 Dec · Danbrook Woods in the frost"
            ]
          },
          {
            "name": "coming",
            "type": "choice",
            "label": "Are you coming?",
            "required": true,
            "options": [
              "Yes",
              "Maybe",
              "No"
            ]
          },
          {
            "name": "guests",
            "type": "number",
            "label": "Guests you're bringing"
          },
          {
            "name": "lift",
            "type": "choice",
            "label": "Lifts",
            "options": [
              "I can give a lift",
              "I need a lift",
              "No lift needed"
            ]
          },
          {
            "name": "memo",
            "type": "long_text",
            "label": "Note for the leader"
          },
          {
            "name": "outing_date",
            "type": "date",
            "label": "Outing date"
          }
        ]
      },
      {
        "list": "entries",
        "label": "Theme entries",
        "by": "member",
        "status_options": [
          "Received",
          "Judging",
          "Judged",
          "Replaced"
        ],
        "reply": true,
        "fields": [
          {
            "name": "theme",
            "type": "choice",
            "label": "Theme",
            "required": true,
            "options": [
              "Street",
              "Golden hour",
              "Reflections",
              "Lights after dark",
              "Close up"
            ]
          },
          {
            "name": "title",
            "type": "text",
            "label": "Title",
            "required": true
          },
          {
            "name": "photo",
            "type": "photo",
            "label": "Your photo",
            "required": true
          },
          {
            "name": "memo",
            "type": "long_text",
            "label": "About the photo"
          }
        ]
      },
      {
        "list": "nights",
        "label": "Club nights",
        "by": "owner",
        "fields": [
          {
            "name": "item",
            "type": "text",
            "label": "For",
            "required": true
          }
        ]
      }
    ],
    "demo": {
      "member": {
        "name": "Ruth Ashdown",
        "email": "ruth.ashdown@example.com",
        "profile": {
          "name": "Ruth",
          "last_name": "Ashdown",
          "camera": "Olympus OM-D E-M10 with the 12-40mm",
          "about": "Retired teacher. Mostly water and weather, usually in wellies."
        }
      },
      "plans": [
        "member"
      ],
      "rows": {
        "rsvps": [
          {
            "outing": "Sun 8 Nov · Sandling Marsh at sunrise",
            "coming": "Yes",
            "guests": 1,
            "memo": "Bringing my neighbour Pat; two seats free from Kettlebridge station.",
            "_added": "-1d",
            "lift": "I can give a lift",
            "outing_date": "2026-11-08"
          },
          {
            "outing": "Sat 17 Oct · Hollin Pier at golden hour",
            "coming": "Yes",
            "memo": "Meeting you there.",
            "_added": "-2w:wed",
            "lift": "No lift needed",
            "outing_date": "2026-10-17"
          }
        ],
        "entries": [
          {
            "theme": "Reflections",
            "title": "Puddle outside the Harbour Tea Room",
            "photo": "art/photos/puddle.webp",
            "memo": "Ludwell Quay on a wet Thursday, shot from knee height at 1/60.",
            "_added": "-2d",
            "_status": "Received"
          },
          {
            "theme": "Reflections",
            "title": "Wet sand at low tide",
            "photo": "art/photos/groyne.webp",
            "memo": "First try, on the beach below the quay; I went back on Thursday for the puddle.",
            "_added": "-6d",
            "_status": "Replaced"
          },
          {
            "theme": "Golden hour",
            "title": "Heron at the sluice gate",
            "photo": "art/photos/heron.webp",
            "memo": "Sandling Marsh, ten minutes after sunrise on the club outing.",
            "_added": "-4w:sun",
            "_status": "Judged",
            "_reply": "Score 18/20 · Commended. The heron stands out against the warm light. Crop the left edge to remove the reeds that draw attention away from it. Maggie Thorne"
          },
          {
            "theme": "Street",
            "title": "Chip shop window, Ludwell",
            "photo": "art/photos/shop-window.webp",
            "memo": "Handheld at 1/30, so forgive the softness.",
            "_added": "-8w:sun",
            "_status": "Judged",
            "_reply": "Score 16/20 · Wonderful warm colour and a strong sign. It needs a person or a bike to tell a story; wait for one next time. Maggie Thorne"
          }
        ],
        "nights": [
          {
            "item": "Club night",
            "_added": "-1w:tue"
          },
          {
            "item": "Club night",
            "_added": "-2w:tue"
          },
          {
            "item": "Club night",
            "_added": "-3w:tue"
          },
          {
            "item": "Club night",
            "_added": "-4w:tue"
          },
          {
            "item": "Club night",
            "_added": "-5w:tue"
          },
          {
            "item": "Club night",
            "_added": "-6w:tue"
          },
          {
            "item": "Club night",
            "_added": "-7w:tue"
          }
        ]
      },
      "staff": {
        "members": [
          {
            "name": "Ruth",
            "counts": {
              "Club night": 7
            },
            "plan": "member",
            "since": "2024-09-10"
          },
          {
            "name": "Wyn",
            "counts": {
              "Club night": 1
            },
            "plan": "member",
            "since": "2011-09-13"
          },
          {
            "name": "Hal",
            "counts": {
              "Club night": 9
            },
            "plan": "member",
            "since": "2016-10-04"
          },
          {
            "name": "Priya",
            "counts": {
              "Club night": 3
            },
            "plan": "concession",
            "since": "2026-09-08"
          },
          {
            "name": "Tom",
            "counts": {
              "Club night": 6
            },
            "plan": "member",
            "since": "2022-09-06"
          },
          {
            "name": "Ottoline",
            "counts": {
              "Club night": 8
            },
            "plan": "member",
            "since": "1998-09-15"
          }
        ],
        "log": [
          {
            "name": "Wyn",
            "action": "Free print",
            "at": "19:41",
            "by": "Hal"
          },
          {
            "name": "Priya",
            "action": "Club night",
            "at": "19:28",
            "by": "Ottoline"
          },
          {
            "name": "Hal",
            "action": "Club night",
            "at": "19:22",
            "by": "Ottoline"
          },
          {
            "name": "Tom",
            "action": "Club night",
            "at": "19:19",
            "by": "Ottoline"
          },
          {
            "name": "Wyn",
            "action": "Club night",
            "at": "19:12",
            "by": "Ottoline"
          }
        ],
        "logDay": "-1w:tue",
        "views": [
          "rsvps",
          "entries"
        ],
        "tally": {
          "rsvps": "coming",
          "entries": "theme"
        },
        "entries": {
          "rsvps": [
            {
              "name": "Wyn",
              "values": {
                "outing": "Sun 8 Nov · Sandling Marsh at sunrise",
                "outing_date": "2026-11-08",
                "coming": "Yes",
                "lift": "I can give a lift",
                "memo": "Leading. Three seats from the station at 6.15."
              },
              "added": "-6d"
            },
            {
              "name": "Hal",
              "values": {
                "outing": "Sun 8 Nov · Sandling Marsh at sunrise",
                "outing_date": "2026-11-08",
                "coming": "Yes",
                "guests": 1,
                "lift": "No lift needed"
              },
              "added": "-5d"
            },
            {
              "name": "Priya",
              "values": {
                "outing": "Sun 8 Nov · Sandling Marsh at sunrise",
                "outing_date": "2026-11-08",
                "coming": "Maybe",
                "lift": "I need a lift",
                "memo": "If someone can collect me from the station."
              },
              "added": "-2d"
            },
            {
              "name": "Tom",
              "values": {
                "outing": "Sun 8 Nov · Sandling Marsh at sunrise",
                "outing_date": "2026-11-08",
                "coming": "No",
                "memo": "Working that weekend, sorry."
              },
              "added": "-1d"
            },
            {
              "name": "Ottoline",
              "values": {
                "outing": "Sun 8 Nov · Sandling Marsh at sunrise",
                "outing_date": "2026-11-08",
                "coming": "Yes",
                "lift": "No lift needed"
              },
              "added": "-4d"
            }
          ],
          "entries": [
            {
              "name": "Wyn",
              "values": {
                "theme": "Golden hour",
                "title": "Last ferry, Ludwell",
                "memo": "The 6.40 ferry leaving the quay at dusk, lights on the still water. Tripod on the harbour wall, 4 seconds."
              },
              "status": "Judged",
              "reply": "Score 19/20 · Print of the month. Beautifully still water and the ferry placed just right. Maggie Thorne",
              "added": "-4w:sun"
            },
            {
              "name": "Hal",
              "values": {
                "theme": "Golden hour",
                "title": "Frost on the birches",
                "memo": "Frosted bracken between birch trunks in Danbrook Woods, low sun from the left."
              },
              "status": "Judged",
              "reply": "Score 18/20. Lovely light; a lower viewpoint would lift the bracken against the trunks. Maggie Thorne",
              "added": "-4w:sun"
            }
          ]
        },
        "queue": [
          {
            "name": "Wyn",
            "values": {
              "theme": "Reflections",
              "title": "Low tide, Ludwell",
              "memo": "Mud flats at dusk with St Mary’s tower upside down in a tidal pool. 24mm, f/11."
            },
            "status": "Judging"
          },
          {
            "name": "Priya",
            "values": {
              "theme": "Reflections",
              "title": "Shop window on Mill Lane",
              "memo": "The baker’s window at night with the street lamps doubled in the glass. My first entry, shot on my phone."
            },
            "status": "Received"
          },
          {
            "name": "Hal",
            "values": {
              "theme": "Reflections",
              "title": "Spoon",
              "memo": "A soup spoon on the kitchen table, the window and garden curved in its bowl. Close up, window light."
            },
            "status": "Received"
          }
        ],
        "copy": {
          "show": "Show this code to Ottoline at the door, or to Hal when you collect your free print.",
          "scanTitle": "Door phone",
          "scanHint": "Point the camera at the code on the member's phone.",
          "noun": "check-in",
          "nouns": "check-ins",
          "notes": {
            "Free print": "One A3 print on the club printer, from any photo the member brings on a card or phone."
          },
          "queueNote": "Reflections closes at 8pm on the Sunday before judging night. Hal marks each entry Judging when Maggie Thorne has it; Maggie writes her reply as \"Score __/20 · your note\", then marks it Judged, and the member sees both at once. When a member sends a newer entry for the same theme, mark the older one Replaced. Outing replies count each member’s latest reply: for Sandling Marsh that is 4 going with 2 guests, 1 maybe and 1 can’t go; 2 can give a lift and 1 needs one.",
          "daily": "one per club night",
          "next": "next club night"
        }
      }
    },
    "scan": [
      {
        "list": "nights",
        "actions": [
          {
            "label": "Club night",
            "values": {
              "item": "Club night"
            },
            "goal": 10,
            "daily_limit": 1
          },
          {
            "label": "Free print",
            "redeem": 10,
            "from": "Club night"
          }
        ]
      }
    ],
    "plans": [
      {
        "plan": "member",
        "name": "Full member",
        "paid": true,
        "price": {
          "amount": 4200,
          "currency": "gbp",
          "interval": "year"
        }
      },
      {
        "plan": "concession",
        "name": "Concession",
        "paid": true,
        "price": {
          "amount": 2400,
          "currency": "gbp",
          "interval": "year"
        }
      }
    ]
  },
  "files": [
    "index.html",
    "app/index.html",
    "app/outings.html",
    "app/enter.html",
    "app/entries.html",
    "app/card.html",
    "app/account.html",
    "privacy.html",
    "styles.css",
    "app.js",
    "kit.css",
    "kit.js",
    "kit-icons.svg",
    "art/icons.svg",
    "art/hall-prints.svg",
    "art/photos/heron.webp",
    "art/photos/harbour.webp",
    "art/photos/harbour-wide.webp",
    "art/photos/birch.webp",
    "art/photos/puddle.webp",
    "art/photos/shop-window.webp",
    "art/photos/groyne.webp",
    "art/macro-leaf.svg",
    "art/noon-street.svg",
    "art/portrait-silhouette.svg",
    "site.webmanifest",
    "favicon.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: what the club does, when it meets, the subscription, what members get in the app, and how to ask to join (the button opens the sign-in page, which takes requests)"
    },
    {
      "path": "app/index.html",
      "label": "Clubhouse",
      "purpose": "Before anyone signs in, a welcome with Sign in and Ask to join; once signed in, the member card with club nights toward a free print, the next outing with the member's reply, this month's entry with its status, the Tuesday programme and the noticeboard"
    },
    {
      "path": "app/outings.html",
      "label": "Outings",
      "purpose": "Each outing with its cover, meeting point and leader, the member's current reply and a reply form that starts from it (Going, Maybe, Can't go, guests, lifts, a note), plus the member's replies"
    },
    {
      "path": "app/enter.html",
      "label": "Enter the theme",
      "purpose": "This month's theme and how it's judged, and the entry form with a photo"
    },
    {
      "path": "app/entries.html",
      "label": "My entries",
      "purpose": "The member's entries newest first with the status (Received, Judging, Judged) and the judge's score and note, plus last month's results"
    },
    {
      "path": "app/card.html",
      "label": "Card",
      "purpose": "The member card: the code to show at the door on club night, club nights toward a free A3 print, and the nights checked in"
    },
    {
      "path": "app/account.html",
      "label": "Account",
      "purpose": "The member's membership (Full member or Concession, or the two plan cards to pay the year), their club details, card and the secretary, then Page Lemon's sign-in and billing block with profile, devices, renewal and leaving"
    },
    {
      "path": "privacy.html",
      "label": "Privacy",
      "purpose": "The privacy notice, with blanks the Site Owner fills in"
    }
  ],
  "facts": [
    "club name",
    "where and when the club meets",
    "secretary's name and email",
    "the yearly subscription and concession",
    "the next outings, meeting points and leaders",
    "this month's theme and judge",
    "the check-in reward",
    "privacy contact and address"
  ],
  "interview": [
    "What is the club called, and where and when do you meet?",
    "Who reads the requests to join, and which email should people write to?",
    "This app is for adults aged 18 or older. What non-sensitive questions should people answer when they ask to join (start: first name, last name, camera, a line about themselves)?",
    "Which outings are coming up: date, place, meeting point and who leads each?",
    "What are this year's monthly themes, who judges, and how is each entry scored?",
    "What does a member get for coming to club nights (every 10th night a free A3 print, or something else)?",
    "What is the yearly subscription, and is there a concession?",
    "What address and privacy contact should the privacy notice show?"
  ],
  "customise": {
    "strings": [
      "Fenwold Camera Club in the header, tab rail, footer and titles; replace every data-pl-fill fact with the club's own: Thursby Hall in Kettlebridge, Tuesdays 7.30pm, the secretary Ottoline Pennick, outings lead Wyn Carrick, competition secretary Hal Brennock and the judge Maggie Thorne. Replace site.webmanifest.name too.",
      "Fill in every highlighted blank on privacy.html with the Site Owner before users use the site (not legal advice: https://pagelemon.com/agents/member-app-legal)",
      "Outings: the rsvps list's outing options. Change them with save_app_list, and the outing cards on app/outings.html (each card's data-c-date, hidden outing and outing_date inputs and data-pl-where) and the Next outing card on app/index.html together, using exactly the same text and date. Replies split into Upcoming and Past by outing_date, and a card hides itself once its day has passed. Add new outings before removing past ones so old replies keep their label.",
      "Themes: the entries list's theme options. Change them with save_app_list, the select on app/enter.html, the theme heading on app/enter.html, the data-pl-where on app/index.html and the red band line at the top of index.html together. Statuses are Received, Judging, Judged and Replaced (for an older entry when a user sends a newer one for the same theme); the owner sets them in the Dashboard (Apps, the entries list) and writes the score and note as the reply.",
      "Check-in: save_app_list for nights with scan exactly as memberApp.scan says (Club night, goal 10, one a day; Free print redeems 10). Change the goal and reward words on app/index.html and app/card.html together. Whoever stands at the door needs the Scan users permission.",
      "Subscriptions: save_plan for member (Full member, £42 a year) and concession (Concession, £24 a year); paid memberships need Starter or Scale and a connected Stripe account. Without them, remove the two plan cards on app/account.html and say how users pay instead (for example to the secretary); the app keeps no record of cash payments.",
      "The Tuesday programme and the noticeboard on app/index.html, the outing rules on app/outings.html and last month's results on app/entries.html are plain HTML the owner edits.",
      "Club business only: never add health or medical questions, and never ask for card, bank or ID numbers in the join questions or the lists. Never create accounts for children or collect their personal data, including through adults' accounts.",
      "Club nights are Tuesdays at 7.30pm; app.js shows the coming Tuesdays' dates in the programme and on the welcome screen (data-c-tue). If the club meets on another day, change the weekday in app.js section 4 and the words Tuesday on the pages.",
      "Staff see each user's first name (display_field name) when they scan; memberApp.demo.staff uses the same first names."
    ],
    "colours": "Edit the colour tokens in the art direction 2026-10-25 block at the end of styles.css (the --k-brand, --k-hero and --k-gold tokens, with the navy band and the red band in their own rules); the dark scheme follows the device. --mustard is a background colour for tags and notice pins; keep text on it in --mustard-ink.",
    "fonts": "League Spartan and Lexend, loaded from Google Fonts on every page; your AI can self-host them on request.",
    "sections": "App pages live under /app and use the data-pl-view markup. Joining is request-to-join: setup_app with memberApp.setup (signup \"request\", request_note and profile_fields; no template), then save_app_list for each list in memberApp.lists. Ask the owner what the join questions should be (profile_fields, at most 10, no photo or file; start from first name, last name, camera and a line about themselves) and write request_note in their words (at most 300 characters). The sign-in page then reads \"Ask to join\" with the request note, asks the questions after the email code, and the request waits in the Dashboard (Apps, Requests) for the owner to approve or decline; the person gets an email either way. Through the AI, invite_users naming a waiting address approves it and remove_user declines it (both ask for the owner's yes first). If setup_app refuses signup \"request\" (request-to-join is not on for this account yet), use signup \"invite\" without request_note and profile_fields, and the secretary invites the people the club says yes to with invite_users. Then rewrite what promises request-to-join: on index.html and app/index.html the Ask to join buttons become a mailto link to the secretary's email, its paragraphs say to email the secretary with the same details and that the club replies by email and sends an invitation if it says yes, and How users get in says users sign in with the address their invitation went to; on privacy.html, \"When you ask to join\" becomes what people email the secretary, the email-address line says it is used to invite them and sign them in, and the request sentences under How long we keep it become a blank for how long the club keeps emailed requests (agree it with the owner). app.js shows each reply (Yes, Maybe, No) as Going, Maybe or Can't go with its colour, keeps only the latest reply per outing in Your replies, fills each outing's form from the user's current reply, shows the next Tuesday's date on the signed-out welcome and the chosen photo's name on the entry form.",
    "images": "favicon.svg is the enamel badge; the PNG icons and manifest are made from it. The outing covers and the sample prints (art/photos/*.webp) are free stock photos from Unsplash and Pexels (PHOTOS.md): replace them with the club's own photos of its outings and members' prints, with the photographers' permission. art/hall-prints.svg is the felt-board drawing and art/icons.svg holds three icons the app kit doesn't have (image, trophy, flag)."
  },
  "schemaVersion": 2
}
