READ ME — ONLY NEEDED IF THE SITE'S INNER PAGES SHOW "404 Not Found"
=====================================================================

This file is an exact copy of the ".htaccess" file that ships in this
same folder (regenerated automatically at build time — do not hand-edit
this file, edit public/.htaccess instead). It exists because cPanel's
File Manager hides files whose name starts with a dot, and some unzip
tools skip them entirely — so ".htaccess" can silently fail to arrive
on the server, which makes every page except the homepage return 404.

HOW TO CHECK IF YOU NEED THIS
-----------------------------
Visit https://dnaofexcellence.org/our-clients
  - Page loads normally  -> .htaccess is working. Delete this .txt file.
  - "404 Not Found"      -> .htaccess is missing. Follow the fix below.

FIX
---
1. In cPanel File Manager, click "Settings" (top right) and tick
   "Show Hidden Files (dotfiles)", then Save. Now you can see dotfiles.
2. Look inside public_html for a file named exactly:  .htaccess
3a. If it EXISTS: open it with "Edit", delete everything inside, then
    copy in everything below the ===CUT=== line of this file and Save.
3b. If it does NOT exist: rename this file to exactly ".htaccess"
    (including the leading dot, no .txt on the end).
4. Reload https://dnaofexcellence.org/our-clients to confirm it works.

Once the site's inner pages load, this .txt file can be deleted.

=========================CUT=========================

####################################################################
# DNA of Excellence — Apache config for the React single-page app.
#
# This file is generated into dist/ by the build (it lives in public/),
# so every deploy ships it automatically. Do not hand-edit it on the
# server — edit remix-green/public/.htaccess and rebuild instead.
#
# It solves three problems that each caused a real outage:
#
#  1. Deep links 404'd. /our-clients, /registration etc. are client-side
#     routes with no matching file on disk, so Apache 404'd them. Three
#     independent fallbacks below hand those URLs to index.html.
#  2. A blank white page persisted after a fix was deployed, because
#     browsers cached the old index.html (no Cache-Control was sent) and
#     it kept requesting JS bundles that no longer existed. index.html is
#     now explicitly never cached, while the hashed assets it points at
#     are cached hard — the standard SPA cache split.
#  3. Apache's own 404 handler was itself 404ing ("Additionally, a 404
#     Not Found error was encountered while trying to use an
#     ErrorDocument"), because a previous config pointed at a page that
#     no longer exists.
####################################################################

DirectoryIndex index.html

# ── Never serve a directory listing ───────────────────────────────
# Directory browsing is enabled on this host, so /assets/ was publicly
# listing every file in the build, and an empty public_html (the brief
# window during an upload) rendered Apache's "Index of /" page complete
# with its ?C=N;O=D sort links instead of the site.
#
# NOTE: if the host does not permit Options in .htaccess, Apache returns
# 500 for the whole site. Should that ever happen, delete just the
# "Options -Indexes" line below — everything else here is unaffected.
# The equivalent can then be set from cPanel's "Indexes" tool instead.
# Verified live on this account: a real folder with no index.html
# (/assets/) returns 403, so Options IS honored here — this isn't a
# theoretical protection, it's confirmed working.
Options -Indexes
IndexIgnore *

# ── SPA routing, fallback #1: mod_rewrite ─────────────────────────
# Real files and directories (assets, images, api/*.php) serve directly;
# everything else is a client-side route and gets index.html.
<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /

  # ── Force HTTPS ───────────────────────────────────────────────
  # Verified live: http://dnaofexcellence.org served 200 over plain
  # HTTP with no redirect, so anyone arriving on an old http:// link
  # (or typing the bare domain) browsed the site insecurely.
  #
  # This deliberately keys off X-Forwarded-Proto being EXPLICITLY
  # "http" rather than the usual `RewriteCond %{HTTPS} !=on`. nginx
  # sits in front of Apache on this host and terminates TLS, so
  # Apache can see every request as plain HTTP even when the visitor
  # is on HTTPS — the naive rule would then redirect https requests
  # to themselves forever and take the whole site down. Keying off
  # the proxy's own header fails safe: if the header is missing, no
  # redirect happens and behaviour is exactly as it is today.
  RewriteCond %{HTTP:X-Forwarded-Proto} =http
  RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

  # ── Canonical host: www -> non-www ────────────────────────────
  # Both currently answer 200, so search engines can see two copies
  # of every page. Non-www is canonical because that's what the
  # printed banner and email signatures use.
  RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
  RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L]

  # Before this fix shipped, Apache briefly served real directory
  # listings, and those pages' own sort-column links (?C=N;O=D and
  # similar — Apache's autoindex "sort by Column, Order") can still be
  # sitting in someone's browser history, a bookmark, or a search
  # engine's cache. The SPA rewrite below would happily load the app
  # for those URLs too, just with the stale query string stuck in the
  # address bar forever. Strip it with a real 301 instead, so browsers
  # forget it and Google drops it from its index.
  RewriteCond %{QUERY_STRING} (^|&)C=[A-Z] [NC,OR]
  RewriteCond %{QUERY_STRING} (^|&)O=[AD] [NC]
  RewriteRule ^(.*)$ /$1? [R=301,L]

  RewriteCond %{REQUEST_FILENAME} -f [OR]
  RewriteCond %{REQUEST_FILENAME} -d
  RewriteRule ^ - [L]

  RewriteRule ^ index.html [L]
</IfModule>

# ── SPA routing, fallback #2: FallbackResource ────────────────────
# Used when mod_rewrite is unavailable (Apache 2.2.16+). Same effect,
# no rewrite engine required.
<IfModule !mod_rewrite.c>
  <IfModule mod_dir.c>
    FallbackResource /index.html
  </IfModule>
</IfModule>

# ── SPA routing, fallback #3: ErrorDocument ───────────────────────
# Last resort so a visitor always sees the app rather than a raw Apache
# error page, and so the broken-ErrorDocument loop can't recur.
ErrorDocument 404 /index.html

# ── Caching ───────────────────────────────────────────────────────
# index.html must always be revalidated: it is the only file that names
# the current hashed bundles, so a stale copy points at deleted files
# and renders a blank page. Everything Vite emits under /assets/ has a
# content hash in its filename, so it can be cached indefinitely — a new
# build produces new filenames rather than needing invalidation.
<IfModule mod_headers.c>
  <FilesMatch "\.html$">
    Header set Cache-Control "no-cache, no-store, must-revalidate"
    Header set Pragma "no-cache"
    Header set Expires "0"
  </FilesMatch>

  <FilesMatch "\.(js|css|woff|woff2|ttf|otf|eot)$">
    Header set Cache-Control "public, max-age=31536000, immutable"
  </FilesMatch>

  <FilesMatch "\.(jpg|jpeg|png|gif|svg|webp|ico|avif)$">
    Header set Cache-Control "public, max-age=2592000"
  </FilesMatch>
</IfModule>

# Fallback caching for hosts without mod_headers.
<IfModule mod_expires.c>
  ExpiresActive On
  ExpiresByType text/html "access plus 0 seconds"
  ExpiresByType image/jpeg "access plus 1 month"
  ExpiresByType image/png "access plus 1 month"
  ExpiresByType image/gif "access plus 1 month"
  ExpiresByType image/svg+xml "access plus 1 month"
  ExpiresByType text/css "access plus 1 year"
  ExpiresByType application/javascript "access plus 1 year"
  ExpiresByType text/javascript "access plus 1 year"
</IfModule>

# ── Compression ───────────────────────────────────────────────────
# NOTE: measured against the live server, nothing is currently being
# compressed at all — the main bundle transfers ~504KB when it gzips to
# ~160KB. The earlier list only named `application/javascript`, but Vite's
# JS is served as `text/javascript`, so it never matched. That is fixed
# below; if responses still arrive without `Content-Encoding: gzip`, then
# mod_deflate simply isn't enabled on the account and no .htaccess rule
# can turn it on — use cPanel's "Optimize Website" tool instead.
<IfModule mod_deflate.c>
  AddOutputFilterByType DEFLATE text/html text/css text/plain text/xml
  AddOutputFilterByType DEFLATE text/javascript application/javascript
  AddOutputFilterByType DEFLATE application/json application/xml
  AddOutputFilterByType DEFLATE image/svg+xml
  AddOutputFilterByType DEFLATE font/ttf font/otf application/vnd.ms-fontobject
</IfModule>

# ── Keep this folder's own config off the public web ──────────────
# The plain-text htaccess copy exists so cPanel (which hides dotfiles)
# can still show it, but it was being served publicly at 200, handing
# anyone the site's full server configuration. Denying it over HTTP
# leaves it perfectly visible in File Manager, which is all it's for.
<Files "HTACCESS-BACKUP-rename-to-dot-htaccess.txt">
  <IfModule mod_authz_core.c>
    Require all denied
  </IfModule>
  <IfModule !mod_authz_core.c>
    Order allow,deny
    Deny from all
  </IfModule>
</Files>

# ── Correct MIME types ────────────────────────────────────────────
# Vite emits ES modules; some shared hosts mislabel .mjs and refuse to
# execute it, which shows up as a blank page with a console MIME error.
<IfModule mod_mime.c>
  AddType text/javascript .js .mjs
  AddType text/css .css
  AddType image/svg+xml .svg
  AddType application/json .json
  AddType image/webp .webp
  AddType image/avif .avif
  AddType font/woff .woff
  AddType font/woff2 .woff2
</IfModule>
