Safari caches static assets far more aggressively than Chrome/Firefox
and largely ignores heuristic revalidation, so a new deploy could keep
showing the old CSS/JS indefinitely even on a hard refresh — no
Cache-Control headers were set anywhere to guide it otherwise.
inc/assets.php adds asset_version($path), which appends a ?v=<mtime>
query string derived from the file's last-modified time. Every page
that links styles.css or main.js (index.php, menu.php, and all five
admin pages) now uses it, so the URL changes whenever the file's
contents change and browsers always fetch the new version.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
npm run dist called "npm run build", but that script had been renamed
to "build:sass" without updating the reference, so dist errored out
before ever running scripts/build-dist.js.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Hero "Say Hello" button now opens the Facebook page instead of a
mailto: link, matching the other social CTAs on the page.
- Removed the "Contact" nav link since "Hours & Visit" already scrolls
to the same section (which includes the Find Us / contact card).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- components/_cards.scss: .stat-card gets margin-top: 2rem; rebuilt
styles.css to match.
- index.php: strip trailing whitespace on a blank line in the music
section.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Breaks the single 2,167-line styles.scss into abstracts/base/components/
layout/pages partials, forwarded from styles.scss (now a manifest only)
via @use. Organized by what each rule is/does rather than by page, since
a lot of it is intentionally shared — the item/announcement/user admin
list grids share one CSS Grid system across three admin pages, and
.knot/.rule-row/.hero-ctas/the announcement banner are used on both
index.php and menu.php. Splitting by page would have fought that.
abstracts/tokens — :root custom properties
base/reset, utilities — bare-element defaults, small reusable utility classes
components/* — buttons, cards/panels, forms, status-badge,
admin data-grid, admin modal, announcement
banner, dividers, social-row
layout/* — header/nav, footer, admin nav+dashboard shell
pages/* — home, public menu, admin login/menu/hours
One real content move: .category-panel .panel-head's mobile override
now sits next to its desktop rule in pages/admin-menu instead of inside
the shared admin-data-grid mobile block — keeping it there would have
broken a same-specificity cascade order dependency once split across
files.
Verified behavior-preserving: fresh build's declaration count, class-
token set, and declaration multiset all match the pre-split styles.css
exactly; homepage, menu page, and admin login spot-checked visually and
via computed styles.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Replace inc/facebook-stats.php with inc/google-reviews.php: fetches
average star rating + review count from the Places API (New) instead
of Facebook's Graph API, same cache-to-JSON/fallback-on-failure shape
as before.
- index.php: stat card now shows the native star rating ("4.8★ average
rating") instead of a recommend %, sourced from Google. Drops the
Facebook-followers line entirely — Google's API has no equivalent and
showing a stale/static number wasn't worth keeping. "Follow on
Facebook" links elsewhere on the site are untouched.
- config.example.php: swap FB_PAGE_ID/FB_PAGE_ACCESS_TOKEN/
FB_STATS_CACHE_TTL/FB_FALLBACK_* for GOOGLE_PLACE_ID/
GOOGLE_PLACES_API_KEY/GOOGLE_STATS_CACHE_TTL/GOOGLE_FALLBACK_RATING/
GOOGLE_FALLBACK_REVIEW_COUNT (set to the pub's actual 4.8★/71 reviews),
with setup instructions in comments. config.php (gitignored) updated
to match.
- Remove the now-dead .stat-row CSS that only existed for the dropped
followers line; rebuild styles.css.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Move hand-authored CSS into src/scss/styles.scss, compiled to the
tracked styles.css via `npm run build` (sass, no source maps).
- Add package.json/package-lock.json with dev tooling: sass, and a
`dev` script running php + sass --watch + browser-sync together.
- Add scripts/build-dist.js (npm run dist): assembles an allowlisted,
production-only copy of the site into dist/ (gitignored) for pushing
to hosting — excludes node_modules, src/, package files, and the
live db/fitz.sqlite while including config.php.
- .gitignore: ignore node_modules/, styles.css.map, and dist/.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Admin menu page: mobile item cards now put Actions in the top-right
corner next to Status, matching the desktop row layout instead of
stacking at the bottom; same treatment applied to announcements and
users for consistency across all three admin lists
- Center button label text everywhere via .btn { justify-content: center }
instead of one-off overrides
- Add a Disable/Enable Menu toggle to the admin menu toolbar; the public
menu page shows its existing "menu being updated" message while disabled
- Add an Enabled/Disabled status to user accounts, with its own toggle
badge, guardrails against disabling your own account or the last
enabled account, and an immediate session kick for anyone disabled
mid-session
- Fix the announcement banner / hero tagline "·" separators to hide only
when their neighboring text actually wraps onto a different line,
instead of a fixed breakpoint or always-on dot
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Add a Hours admin panel (admin/hours.php, admin/hours.js) for editing
the weekly open/close schedule and an optional per-day kitchen closing
time, with a live preview of how it'll read on the public site
- Store hours in a new pub_hours table (inc/db.php), seeded with the
site's existing Thu-Sat/9pm-kitchen schedule; inc/hours.php formats
and groups it for display (day ranges, kitchen-close note, footer
summary)
- Wire the public Visit card and both site footers (index.php, menu.php)
to read hours from that data instead of hardcoded copy
- Rebuild the Announcements and Users admin tables onto the same CSS
Grid list pattern already used for the Menu (admin-table-actions ->
row-actions), and make the Announcements status toggle an in-place
AJAX action like the menu item's (admin/index.js)
- Add the Hours tab to admin nav across all four admin pages
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Replace the <table>-based item and category-header rows with CSS Grid
containers using named grid-template-areas, so desktop and mobile each
get their own independent arrangement of the same fields instead of
mobile being a display:block hack forced onto table markup
- Make the status badge itself the availability toggle (no separate
"Mark Sold Out" button), fixed-width so toggling never shifts the
Status column, and shorten its label to "Sold Out"
- Merge the item Tag into the name line as an inline badge instead of
giving it its own column
- Group the move/edit/delete controls into two stacked button columns
(was 4-5 buttons wide) and right-align them on mobile
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Public site: replace the always-hidden mobile nav with a hamburger
toggle (index.php, menu.php, main.js, styles.css)
- Admin: render the Menu/Announcements/Users tables as labeled cards
below ~760px instead of squeezing descriptions into unreadable
single-word-per-line columns
- Admin menu: replace the manual "Order" number (which allowed
duplicates and never reflected in real time) with ↑/↓ buttons that
reorder items and categories immediately and renumber sort_order
sequentially on every move, self-healing any stale duplicates
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Implement admin user management interface (, , ) with async CRUD and password updating
- Add Users tab link to admin navigation across and
- Use in to lower specificity for native button resets, allowing modifier classes like to take precedence
Replaces the text-only Live Music section with a two-column layout
(photo alongside copy) featuring resident musician Jesse Hart, with
a responsive single-column fallback below 50em. Updates the section
copy to name a resident musician plus guest performers rather than
citing a single past artist by name.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
CSS redundancy cleanup on the online menu management feature:
- .menu-category was a byte-for-byte duplicate of the existing .panel
class (background/border/border-radius/padding); folds it into
.panel's shared selector group instead, keeping only what's not
shared (no margin-bottom, since these sit in a grid whose own gap
already spaces them)
- Several inline style="..." attributes were hand-copied verbatim
between admin/menu.php's PHP template and admin/menu.js's client-
side row/panel templates (price/sort/description cell formatting,
category heading spacing, the empty-state row). Replaced with real
CSS classes (.item-price-cell, .item-sort-cell, .item-desc-cell,
.cat-name-heading, .cat-desc-text, .no-items-row td) so the styling
lives in one place instead of two
- class="font-mono" was referenced four times but never defined
anywhere in CSS (silently doing nothing); it's now a real one-line
utility, which also fixes two previously-dead uses of it already
sitting in admin/index.php
- .form-actions already existed as a class with its own margin-top,
but every usage (two new modals here, plus a pre-existing one in
admin/index.php) redundantly re-specified layout via inline style
with a slightly different margin-top value each time; extended the
shared class to own the whole pattern and dropped all three inline
overrides
- Removed a no-op `justify-content: flex-start` (the default value)
from the new .admin-toolbar
Caught and fixed a regression from the above before it shipped:
removing the inline margin-bottom override on the category heading
exposed that .panel h2's margin-bottom (from the existing panel
styles) has higher specificity than a bare .cat-name-heading class,
so the heading would have rendered with 5x the intended spacing.
Rescoped as .category-panel .cat-name-heading to reliably win.
Also restores the "Mark Sold Out" / "Mark Available" button wording
(both directions of the toggle, in both the PHP-rendered rows and
the two JS render paths) after it had been shortened to just "Sold
Out" / "Available".
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a public menu page (menu.php) and an admin CRUD interface
(admin/menu.php + admin/menu.js) backed by new menu_categories and
menu_items tables, replacing the "menu lives on Facebook" placeholder
on the homepage.
The admin JS/PHP came out of an AI-assisted pass that used outdated
JS (var, .then() chains, string-built HTML) and had some rough edges
security-wise, so this also:
- Rewrites the admin JS to modern ES (const/let, async/await,
template literals, dataset API) and splits it out of admin/menu.php
into admin/menu.js for proper separation of concerns
- Adds declare(strict_types=1) and PHP 8 match-based action dispatch
in place of an if-chain
- Validates category_id/item_id/category_id server-side before use,
so a bad ID returns a clean JSON error instead of an uncaught
PDOException or a null item breaking the client
- Rejects non-string POST fields instead of crashing trim() on them
- Replaces the category-save handler's full-page location.reload()
with in-place DOM updates (panel insert/update, resort by
sort_order, keep the item modal's category dropdown in sync), so
every admin action is now AJAX with no reload
- Sets data-name/data-desc via dataset assignment rather than string-
interpolating them into an HTML attribute, closing a stored-XSS
path through the category name/description fields
Removes seed-menu.php, a leftover stub with no working code left in
it after the AI's one-time seeding run.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Add --danger-bg/--danger-text tokens and point .error-msg,
.alert-error, and .btn-danger at them instead of repeating the
same rgba/hex literals
- Drop .login-btn's cursor: pointer (already covered by button.btn)
- Drop .btn-outline's background: transparent (already covered by
button.btn for buttons; default background for anchors)
No visual changes intended.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Update inc/announcements.php to support listing, fetching by ID, toggling, and deleting multiple announcements
- Upgrade admin/index.php to an announcement management view with data table, status indicators, and create/edit sub-views
- Add admin table and action styles to styles.css without using inline style blocks
- Update index.php to render all currently active banner and hours announcements dynamically
- Add --font-serif/--font-mono/--font-sans tokens to :root and point
all ~24 hardcoded font-family declarations at them
- Drop an exact-duplicate .footer-brand .knot rule
- Merge the shared background/border of .stat-card, .card,
.login-card, and .panel into one grouped selector
- Combine .btn-outline:hover and .nav-cta:hover into one rule
- Move remaining inline style="" attributes in index.php and
admin/index.php into named classes/rules (.about-grid h2,
footer address/.social-row spacing, .panel-head,
.checkbox-label-lg, .field-hint, .form-actions)
No visual changes intended — values carried over as-is.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Move all admin dashboard and login rules into styles.css
- Remove embedded <style> blocks from admin/login.php and admin/index.php
- Clean up redundant preview action button
- Add SQLite database initialization and management in inc/db.php
- Implement session authentication and CSRF security helpers in inc/auth.php
- Implement announcement querying and saving in inc/announcements.php
- Add management dashboard, login, and logout in admin/
- Protect db/ directory with Apache access controls
- Refactor hardcoded px values to rem and em across layout, components, and media queries
- Improve WCAG color contrast by updating --gold-dim token
- Convert hours table to a semantic description list (<dl>)
- Wrap hero logo in h1 for proper heading hierarchy
- Add database ignore rules to .gitignore
Static-turned-PHP site for a pub in Marion, Indiana, with:
- Hero section with tiled brick-texture background
- Live-capable stat card (Facebook followers/reviews via Graph API,
with graceful fallback to config.php constants when no token is set)
- Open Graph / Twitter Card meta tags for social sharing
- robots.txt
config.php is gitignored (holds a Facebook Page Access Token once
generated); config.sample.php is the tracked template — copy it to
config.php and fill in values to run this locally or in deployment.