-- Projects — the source of truth for /projects and /projects/{slug}.
--
-- Two shapes live in one row: the short form the index needs (title, period,
-- summary, categories) and the long form a case-study page needs (lede, body,
-- metrics, links). Keeping them together avoids a join on every page render
-- and keeps editing in one place.
--
-- JSON is used for the small repeating lists — categories, stack, metrics,
-- links. At this scale that is the right trade: a normalised category table
-- would add two tables and a join to support a filter over a handful of rows.
-- MariaDB can still query inside them via JSON_CONTAINS if that is ever needed.
-- If projects grow past a few dozen, or categories need their own descriptions
-- and pages, promote `categories` to cja_project_categories + a map table.

CREATE TABLE IF NOT EXISTS `cja_projects` (
  `project_id`     int(10) unsigned NOT NULL AUTO_INCREMENT,

  -- identity
  `slug`           varchar(120)  NOT NULL,
  `title`          varchar(160)  NOT NULL,
  `kind`           varchar(60)   NOT NULL DEFAULT '',   -- "SaaS", "SaaS / Media"

  -- timeline. period_label is what renders; the dates are what sorts, so an
  -- ongoing project ("2020 — present") still orders correctly against a dated one.
  `period_label`   varchar(60)   NOT NULL DEFAULT '',
  `started_on`     date          DEFAULT NULL,
  `ended_on`       date          DEFAULT NULL,          -- NULL = ongoing
  `status`         enum('live','building','archived','concept')
                                 NOT NULL DEFAULT 'live',

  -- copy
  `summary`        varchar(500)  NOT NULL DEFAULT '',   -- index row
  `lede`           text          DEFAULT NULL,          -- detail page opener
  `body`           mediumtext    DEFAULT NULL,          -- markdown
  `role`           varchar(200)  NOT NULL DEFAULT '',   -- "Solo — design, build, ops"

  -- structured lists
  `categories`     longtext      DEFAULT NULL CHECK (json_valid(`categories`)),
  `stack`          longtext      DEFAULT NULL CHECK (json_valid(`stack`)),
  `metrics`        longtext      DEFAULT NULL CHECK (json_valid(`metrics`)),
  `links`          longtext      DEFAULT NULL CHECK (json_valid(`links`)),

  -- media
  `cover_image`    varchar(255)  NOT NULL DEFAULT '',
  `cover_alt`      varchar(255)  NOT NULL DEFAULT '',

  -- seo. Nullable so the page can fall back to title/summary rather than
  -- forcing an editor to duplicate them.
  `seo_title`       varchar(180) DEFAULT NULL,
  `seo_description` varchar(320) DEFAULT NULL,

  -- publishing
  `featured`       tinyint(1)    NOT NULL DEFAULT 0,
  `sort_order`     int(11)       NOT NULL DEFAULT 0,
  `published`      tinyint(1)    NOT NULL DEFAULT 0,
  `published_at`   datetime      DEFAULT NULL,

  `created_at`     datetime      NOT NULL DEFAULT current_timestamp(),
  `updated_at`     datetime      NOT NULL DEFAULT current_timestamp()
                                 ON UPDATE current_timestamp(),

  PRIMARY KEY (`project_id`),
  UNIQUE KEY `uq_cja_projects_slug` (`slug`),
  KEY `idx_cja_projects_listing` (`published`, `sort_order`, `started_on`),
  KEY `idx_cja_projects_status` (`status`),
  KEY `idx_cja_projects_featured` (`featured`, `sort_order`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
