# Genes Generate a dependency-free PHP project with one public `index.php` entry point from this specification. ## Rules - Always include the BASE CSS below exactly in the generated `index.php`. - Select one layout preset according to the requested page. - Include only the selected layout preset CSS. - The layout preset defines structure, not visual identity. - Always add a compact visual design for the requested site: typography, colors, spacing, navigation, buttons, forms, cards, and content sections. - Keep fixed `rem` canvas and fixed-width columns. - Do not use `minmax()`, fluid columns, frameworks, CDNs, or build tools. - The layout presets are examples, not mandatory page types. - Choose the number, meaning, and width of columns according to the page. - CSS may be inline in `` or stored in an external project stylesheet. - Keep PHP, HTML, CSS, JSON, SQL, Markdown, and `.htaccess` files UTF-8 without BOM. ## BASE CSS — copy exactly ```css * { box-sizing: border-box; padding: 0; margin: 0; line-height: 1em } html { -webkit-text-size-adjust: 100% } html, body { overflow-x: clip } @media (max-width: 639px) { html { font-size: 3.125vw } } @media (min-width: 640px) and (max-width: 1279px) { html { font-size: 1.5625vw } } @media (min-width: 1280px) { html { font-size: .78125vw } } img, svg, video, canvas { display: block; max-width: 100% } button, input, textarea, select { font: inherit; border: 0 } ul, ol { list-style: none } table { border-collapse: collapse } * { scrollbar-width: thin; scrollbar-color: #999 transparent } *::-webkit-scrollbar { width: .6rem; height: .6rem } *::-webkit-scrollbar-track { background: transparent } *::-webkit-scrollbar-thumb { background: #999; border-radius: 1rem } *::-webkit-scrollbar-thumb:hover { background: #777 } ``` Canvas sizes: ```text Mobile: 32rem Tablet: 64rem Desktop: 128rem ``` ## Layout presets ### Dashboard Desktop columns: `20rem 36rem 72rem`. Tablet columns: `20rem 44rem`. Mobile: one `32rem` column. ```html
``` ```css .g-dashboard { display: grid; grid-template-columns: 20rem 36rem 72rem; width: 128rem; height: 100vh } .g-nav { background: #ddd; padding: 1rem } .g-main { background: #eee; padding: 1rem } .g-side { background: #ccc; padding: 1rem } @media (min-width: 640px) and (max-width: 1279px) { .g-dashboard { grid-template-columns: 20rem 44rem; width: 64rem } .g-side { display: none } } @media (max-width: 639px) { .g-dashboard { grid-template-columns: 32rem; width: 32rem } .g-nav, .g-side { display: none } } ``` ### Blog Desktop columns: `80rem 48rem`. Tablet: one `64rem` column. Mobile: one `32rem` column. ```html
``` ```css .g-blog { display: grid; grid-template-columns: 80rem 48rem; width: 128rem; min-height: 100vh } .g-article { background: #eee; padding: 1rem } .g-related { background: #ccc; padding: 1rem } @media (min-width: 640px) and (max-width: 1279px) { .g-blog { grid-template-columns: 64rem; width: 64rem } .g-related { display: none } } @media (max-width: 639px) { .g-blog { grid-template-columns: 32rem; width: 32rem } .g-related { display: none } } ``` ### Landing Desktop: one `128rem` column. Tablet: one `64rem` column. Mobile: one `32rem` column. ```html
``` ```css .g-landing { width: 128rem; min-height: 100vh; background: #eee; padding: 2rem } @media (min-width: 640px) and (max-width: 1279px) { .g-landing { width: 64rem } } @media (max-width: 639px) { .g-landing { width: 32rem } } ``` ## Generation For `dashboard`, `blog`, or `landing`, copy the BASE CSS, add only the selected preset CSS, and generate the matching HTML structure. Add the user's content inside the regions. Do not add unused layouts or speculative components. ## Generated project structure The default static site should be multi-page, even when all content is static. If the user explicitly requests a single-page site, use `home` as the only page. Minimum static site: ```text index.php templates/ layout.php home.php 404.php data/config.json data/content.json .htaccess ``` The default multi-page static site must contain at least three pages: `/`, `/about`, and `/contact`. Use one `index.php` entry point and route requests from `REQUEST_URI`. Do not use query-string page routing such as `?page=about` unless explicitly requested. ### Example project recipe: multilingual static landing For a three-language, three-page landing site, generate: ```text index.php templates/ layout.php home.php about.php contact.php 404.php data/config.json data/content.json .htaccess ``` Use locale-aware paths: ```text /en/ /en/about /en/contact /tr/ /tr/about /tr/contact /de/ /de/about /de/contact ``` Store all page translations in `data/content.json`. Keep templates shared between languages and use the landing preset for all three pages unless requested otherwise. ## URL rewriting Generate this root `.htaccess` for Apache unless the user requests another server: ```apache RewriteEngine On RewriteRule ^data/ - [F,L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^ index.php [L] ``` This routes extensionless paths to `index.php`, preserves existing files and directories, and denies direct web access to `/data/`. ## Request routing Parse the request path from `$_SERVER['REQUEST_URI']`: ```text / -> locale: default, page: home /about -> locale: default, page: about /en/ -> locale: en, page: home /en/about -> locale: en, page: about ``` Remove the query string and leading/trailing slashes before parsing. If the first segment matches a configured locale, use it as the locale; otherwise use the default locale and treat the first segment as the page. Return a 404 response for unknown pages or unsupported locales. Do not use query-string routing for pages. ## Configuration Always generate `data/config.json` for site-level configuration. Keep configuration separate from templates and content. `config.json` must never contain page content or translations. ```json { "site": { "name": "Site name", "url": "https://example.com", "default_locale": "en", "locales": ["en", "tr"] }, "theme": { "title": "Site title", "description": "Site description" } } ``` Use `config.json` only for system and site settings such as URLs, locale settings, feature flags, environment-independent options, and theme metadata. Do not put page content, posts, passwords, secrets, or translations in `config.json`. Use `content.json`, `content.sqlite`, or both as required; `config.json` does not select one provider. ## Localization Store static and page content in `data/content.json`. Store queryable or editable records in `data/content.sqlite` when needed; both may be used by the same page. The JSON content root always uses page keys. A single-page site uses `home` as its only page key. A multi-page site uses keys such as `home`, `about`, and `contact`. Do not duplicate templates per language. ```json { "home": { "title": { "en": "English Title", "tr": "Türkçe title" }, "text": { "en": "English text", "tr": "Türkçe metin" } } } ``` The `home` object may contain any sections required by the page; it is not limited to `hero`. Use keys such as `header`, `intro`, `features`, `pricing`, `faq`, `article`, `related`, and `footer` only when needed. Every user-visible string must come from the content layer, including navigation labels, buttons, links, headings, labels, placeholders, and messages. Use a `common` object for shared strings such as site navigation; use page objects for page-specific strings. Never hardcode user-visible text in the template. The content tree may be shallow or deeply nested. Pages may use sections, subsections, or direct values. Do not require every page to use the same structure. The only required rule is that a translatable leaf is a locale object. For multiple pages, add sibling page keys: ```json { "home": { "header": { "title": { "en": "English title", "tr": "Turkish title", "de": "German title" } } }, "about": { "title": { "en": "About us", "tr": "Hakkımızda", "de": "Über uns" } } } ``` Select locale from the URL path, for example `/en/about` and `/tr/about`, unless the user requests another method. Use `site.default_locale` as fallback. Resolve a missing translation to the requested locale, then the default locale, then an empty string. Escape translated plain text with `htmlspecialchars()`. Resolve the page key before rendering its matching template; unknown page keys use the 404 template. Language links must preserve the current page, for example `/en/about` links to `/tr/about` and `/de/about`. Only locales listed in `config.json` are valid. Plain text must always be escaped before output. Do not output content as raw HTML. Rich HTML content is allowed only when explicitly requested and clearly marked as trusted content in the data model. ## Data sources - System settings: use `data/config.json`. - Static, custom, shared, and localized page content: use `data/content.json`. - Queryable, editable, filterable, paginated, or relational records: use `data/content.sqlite`. - A page may use JSON, SQLite, or both. - CRUD/CMS: add only when the user requests editable content or an admin interface. Normalize all required data into one `$view` package. A page may use either source or both; templates must not depend on where a field came from. Allowed root-level content keys are page keys plus the reserved `common` object. Never expose database files, configuration files, or raw JSON directly. Do not add SQLite, CRUD, authentication, or an admin panel to a simple static site. ## Canonical runtime logic Do not invent a new routing, localization, or rendering architecture. Use this pipeline in every generated project: ```text request → route → locale → page → data sources → view package → template ``` The runtime must: 1. Load `data/config.json`. 2. Load the required data sources: `content.json`, `content.sqlite`, or both. 3. Parse `REQUEST_URI` after removing the project base path and query string. 4. Resolve locale from the first URL segment or use `site.default_locale`. 5. Resolve page from the next URL segment or use `home`. 6. Reject unsupported locales and unknown pages with HTTP 404. 7. Resolve localized values using the requested locale, then the default locale. 8. Build one `$view` package containing `site`, `theme`, `locale`, `page`, `common`, and localized page `content`. 9. Escape plain text with `htmlspecialchars()` before output. 10. Render the shared template with the `$view` package. Templates must not parse routes, load JSON/SQLite, resolve locales, or call a text lookup function for every field. They only render `$site`, `$theme`, `$locale`, `$common`, and `$content`. Use native PHP templates; do not invent a template DSL. Use this compact runtime shape. Normalize JSON and SQLite results into the same `$view` package so templates do not depend on where a field came from: ```php function localize($value, $locale, $default) { if (!is_array($value)) return $value; if (array_key_exists($locale, $value)) return $value[$locale]; if (array_key_exists($default, $value)) return $value[$default]; foreach ($value as $key => $item) $value[$key] = localize($item, $locale, $default); return $value; } function make_view($config, $content, $locale, $page, $records = []) { $pageData = $content[$page] ?? null; if (!$pageData) { http_response_code(404); $page = '404'; $pageData = $content[$page] ?? []; } $default = $config['site']['default_locale'] ?? 'en'; return [ 'site' => $config['site'] ?? [], 'theme' => $config['theme'] ?? [], 'locale' => $locale, 'page' => $page, 'common' => localize($content['common'] ?? [], $locale, $default), 'content' => localize($pageData, $locale, $default), 'data' => $records ]; } function render($view) { extract($view, EXTR_SKIP); require __DIR__ . "/templates/{$page}.php"; } ``` Keep routing, data loading, view preparation, and rendering as separate small steps. Data may come from JSON, SQLite, or both, but the data source must not create a new routing, localization, or template architecture. ## Assets Assets are shared by all page types: ```text assets/ ├── media/ ├── icons/ └── fonts/ ``` - Store images, icons, and fonts under `assets/`. - Store only relative asset paths in content data, never binary blobs or base64. - Use stable, lowercase, URL-safe filenames. - Store uploaded media under a content-type directory, for example `assets/items//` or `assets/persons//`. - A CMS may provide a small media manager for listing, uploading, selecting, and removing assets. - Page images must use only `jpg`, `jpeg`, `png`, `webp`, or `gif`. - Page images must be no larger than `1920x1920` pixels. - Page images must not exceed `1 MB` per file. - Validate the real MIME type and dimensions server-side; never trust the filename extension or client-provided MIME type. - Reject images that fail validation before writing them to `assets/`. - Do not overwrite an existing asset without an explicit request. - Keep `data/` private and `assets/` public. - Do not allow PHP execution inside upload/media directories. - Generated `.htaccess` rules must protect `data/` without blocking public assets. - Asset paths must work from every localized page URL. ## SQLite mode Use SQLite for queryable, editable, filterable, paginated, or relational data. Use JSON for static, custom, shared, or page-specific data. A page may use either or both; data sources have no semantic role such as blog or landing. Store the database at: ```text data/content.sqlite ``` The main content tables are: ```text persons items labels events ``` Every main table has an internal integer `id` and a public stable `hash`. Fast filter fields such as `type` and `status` are regular TEXT columns containing URL-safe values, not hashes. They may be used on `persons`, `items`, and `events` when relevant. Their definitions are stored in `labels` using `key` and `value`. Content ownership fields use person hashes: ```text created_by = persons.hash updated_by = persons.hash owner = persons.hash ``` Use `created_by` and `updated_by` for audit history. Use `owner` when users must only access or edit their own content. Admin users may access all records according to their role. Editors may edit only records they own or are assigned. Apply ownership and role checks in the admin layer. Example only; use values required by the requested project: ```text items.hash = "itm_abc123" items.type = "post" items.status = "published" ``` The `labels` table contains definitions and localized display text: ```text labels.hash labels.key labels.value labels.type labels.status labels.text labels.data ``` Use a unique constraint on `(key, value)`. Every label also has a `type` and `status` field. Examples of label types are `tag`, `category`, `role`, `type`, and `status`. Label status may be `active` or `archived`. Free tags are stored in the `labels` table as `key/value` definitions, and selected values are stored in the entity `labels` JSON array. A tag cloud queries labels with `type = 'tag'` and `status = 'active'`. Keep `type` and `status` on entities as indexed columns. Use `data` JSON for type-specific fields. Localized content fields use locale objects: ```json { "en": "English title", "fi": "Finnish title", "tr": "Turkish title" } ``` Use this format for fields such as `items.title`, `items.summary`, `items.text`, and localized label display text. Use SQLite JSON functions for locale extraction and fallback. Collection list queries must select only `hash`, `slug`, localized `title`, and localized `summary`; do not load the full body for a list. For `items`, index `type`, `status`, and `created_at`. Add equivalent indexes to `persons` or `events` when those fields are used for filtering. Use SQLite FTS5 as an optional search index for title, summary, and body when full-text search is needed. FTS5 is an index over content, not a replacement for `items`. ## CMS admin mode Add the admin only when the user requests manual editing. The admin writes directly to the live SQLite database; it does not require a public write API. Use the dashboard preset as the admin workspace: ```text 20rem left column = table and navigation selector 36rem center column = filtered record list 72rem right column = selected record editor ``` Example structure: ```html
``` The first admin version supports only: ```text login list search filter create edit publish/unpublish delete locale selection ``` Admin scope: ```text items = primary content editor persons = user and ownership management labels = type, status, category, role, and tag definitions events = read-only audit and submission viewer ``` Require sessions, password hashing, CSRF protection, prepared statements, input validation, and login rate limiting. Do not add a visual page builder, drag-and-drop editor, generic table editor, or large JavaScript admin framework. ## SQLite migrations Migration support is a later optional layer. Do not add it to a simple SQLite site unless requested. When enabled, add this directory: ```text migrations/ ``` Keep migration files in a non-public `migrations/` directory. If that directory must be inside the web root, deny it in `.htaccess`. When enabled, ordered SQL migration files are applied by a site-side consumer and recorded in `schema_migrations`. Migrations may insert, update, or delete records, but must be incremental and must not rebuild or replace content tables or overwrite manual admin changes.