# 2026-08-19 security fix (pre-launch audit finding M-2): every real page
# under public/ is meant to be reached ONLY through index.php's router
# (which sets up $auth/$pdo/$db, the session, CSRF verification, and
# security headers before require()'ing the target file) — never requested
# directly. Before this file existed, the root .htaccess's
# `RewriteCond %{REQUEST_FILENAME} !-f` let any real file bypass the front
# controller entirely, since that condition only rewrites requests for
# paths that AREN'T a real file. Confirmed live during the audit: a direct
# GET to public/dashboard.php (and every other public/*.php file tried)
# returned 200 with a raw, unhandled PHP fatal error (undefined $auth —
# every page assumes the router already constructed it) — which, under a
# development-mode config, discloses full filesystem paths in the response
# body. Every real caller reaches these files via index.php's own
# require($base . '/public/....php'), a filesystem-level include that is
# completely unaffected by Apache-level access control on the URL — so
# this closes the direct-access gap with zero effect on real routing.
# Confirmed via a full grep of this app's own client-facing code (every
# <a href>, <form action>, and fetch() call in public/, api/, src/, and the
# two root-level static HTML files) that nothing anywhere links directly to
# a public/*.php path — every real link uses the clean /route style URLs
# index.php's router expects.
#
# Scoped to .php only — public/uploads/ (image files, already separately
# .htaccess-protected at its own directory level) and any other non-PHP
# static asset under public/ are unaffected.
<FilesMatch "\.php$">
    <IfModule mod_authz_core.c>
        Require all denied
    </IfModule>
    <IfModule !mod_authz_core.c>
        Order deny,allow
        Deny from all
    </IfModule>
</FilesMatch>
