/*
Theme Name: GP Child Factory
Theme URI: https://contentfactory.ashuthefire.com/
Description: The fleet's single GeneratePress child theme. One package, byte-identical on every site: the entire look is driven by the `factory_design_tokens` option, so a site is re-designed by writing DATA, never by writing PHP or CSS on the box. Covers header, navigation, typography, spacing, colour surfaces and the homepage/archive loop cards — product boxes belong to Site Kit.
Author: Fleet Ops
Template: generatepress
Version: 1.1.0
Requires at least: 6.0
Tested up to: 6.8
License: GNU General Public License v2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: gp-child-factory
*/

/* =========================================================================
   1. TOKENS — the whole design surface, and the theme's shipped defaults.

   Edition "PRINT2WEB": editorial monochrome, square corners, hairline rules.
   Deliberately the SAME vocabulary and the SAME values the provisioning lane
   seeds `factory_design_tokens` with, so a site with no option set and a site
   seeded with the defaults look identical.

   Every name here is also in the allow-list in functions.php. A token that is
   not in that list is ignored when the option is read — the list below IS the
   contract, and adding to it is a theme release, not a data change.
   ========================================================================= */
:root {
  /* colour surfaces */
  --fx-ink: #0f172a;
  --fx-bg: #ffffff;
  --fx-panel: #f8fafc;
  --fx-line: #e2e8f0;
  --fx-muted: #475569;
  --fx-faint: #94a3b8;
  --fx-accent: #0f172a;
  --fx-accent-ink: #ffffff;
  --fx-link: #0f172a;
  --fx-link-hover: #475569;

  /* header + navigation */
  --fx-header-bg: #ffffff;
  --fx-header-ink: #0f172a;
  --fx-header-border: #e2e8f0;
  --fx-header-pad: 1.75rem;
  --fx-nav-bg: #ffffff;
  --fx-nav-ink: #0f172a;
  --fx-nav-ink-hover: #475569;
  --fx-nav-size: 0.75rem;
  --fx-nav-tracking: 0.12em;
  --fx-title-size: 1.5rem;
  --fx-title-tracking: -0.01em;

  /* typography */
  --fx-font-body: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --fx-font-heading: var(--fx-font-body);
  --fx-font-size: 1.0625rem;
  --fx-line-height: 1.7;
  --fx-heading-weight: 700;
  --fx-heading-line-height: 1.25;
  --fx-heading-tracking: -0.01em;

  /* spacing + structure */
  --fx-space: 1.5rem;
  --fx-space-lg: 3rem;
  --fx-content-width: 48rem;
  --fx-radius: 0px;
  --fx-border: 1px;
}

/* =========================================================================
   2. SURFACES — every rule below reads a token and nothing else.

   Kept intentionally short. GeneratePress already ships a complete, accessible
   base; this file only re-points its visible surfaces at the tokens. Anything
   that is not header, navigation, typography, spacing or colour does not
   belong here — product boxes are Site Kit's (skin/skin.css).
   ========================================================================= */

body {
  font-family: var(--fx-font-body);
  font-size: var(--fx-font-size);
  line-height: var(--fx-line-height);
  color: var(--fx-ink);
  background-color: var(--fx-bg);
}

h1, h2, h3, h4, h5, h6,
.entry-title {
  font-family: var(--fx-font-heading);
  font-weight: var(--fx-heading-weight);
  line-height: var(--fx-heading-line-height);
  letter-spacing: var(--fx-heading-tracking);
  color: var(--fx-ink);
}

a {
  color: var(--fx-link);
}

a:hover,
a:focus {
  color: var(--fx-link-hover);
}

/* ── header ── */
.site-header {
  background-color: var(--fx-header-bg);
  color: var(--fx-header-ink);
  border-bottom: var(--fx-border) solid var(--fx-header-border);
}

.site-header .inside-header {
  padding-top: var(--fx-header-pad);
  padding-bottom: var(--fx-header-pad);
}

.main-title,
.main-title a {
  font-family: var(--fx-font-heading);
  font-size: var(--fx-title-size);
  font-weight: var(--fx-heading-weight);
  letter-spacing: var(--fx-title-tracking);
  color: var(--fx-header-ink);
  text-decoration: none;
}

.site-description {
  color: var(--fx-muted);
}

/* ── navigation ── */
.main-navigation,
.main-navigation ul ul {
  background-color: var(--fx-nav-bg);
}

.main-navigation .main-nav ul li a,
.main-navigation .menu-toggle {
  font-family: var(--fx-font-body);
  font-size: var(--fx-nav-size);
  letter-spacing: var(--fx-nav-tracking);
  text-transform: uppercase;
  color: var(--fx-nav-ink);
}

.main-navigation .main-nav ul li:hover > a,
.main-navigation .main-nav ul li:focus > a,
.main-navigation .main-nav ul li.current-menu-item > a {
  color: var(--fx-nav-ink-hover);
  background-color: transparent;
}

/* The current page is marked with a rule rather than a fill: the same signal
   at every viewport, and it survives a token set that makes nav and page
   backgrounds the same colour. */
.main-navigation .main-nav ul li.current-menu-item > a {
  box-shadow: inset 0 -2px 0 0 var(--fx-accent);
}

/* ── content surfaces ── */
.separate-containers .inside-article,
.separate-containers .comments-area,
.separate-containers .page-header,
.separate-containers .paging-navigation,
.one-container .container {
  background-color: var(--fx-bg);
  border-radius: var(--fx-radius);
}

.entry-content > * + * {
  margin-top: var(--fx-space);
}

.entry-content > h2,
.entry-content > h3 {
  margin-top: var(--fx-space-lg);
}

.entry-content blockquote {
  border-left: var(--fx-border) solid var(--fx-accent);
  padding-left: var(--fx-space);
  color: var(--fx-muted);
  font-style: normal;
}

.entry-content table th {
  background-color: var(--fx-panel);
}

.entry-content table td,
.entry-content table th {
  border-color: var(--fx-line);
}

.entry-meta,
.entry-meta a {
  color: var(--fx-faint);
}

/* ── buttons ── */
button,
html input[type="button"],
input[type="reset"],
input[type="submit"],
.button,
.wp-block-button__link {
  background-color: var(--fx-accent);
  color: var(--fx-accent-ink);
  border-radius: var(--fx-radius);
}

button:hover,
html input[type="button"]:hover,
input[type="reset"]:hover,
input[type="submit"]:hover,
.button:hover,
.wp-block-button__link:hover {
  background-color: var(--fx-ink);
  color: var(--fx-accent-ink);
}

/* ── footer ── */
.site-info {
  background-color: var(--fx-panel);
  color: var(--fx-muted);
  border-top: var(--fx-border) solid var(--fx-line);
}

/* =========================================================================
   3. CARDS — the loop surface, both ways a site's "/" can render it.

   A fleet front page is either the core latest-posts block (static front
   page) or GeneratePress's own archive loop (posts index). The same twelve
   fx-card-* / fx-grid-* tokens drive both DOMs, so one design decision lands
   identically whichever kind of homepage a site was provisioned with.

   Unlike sections 1–2, these tokens carry no :root default: every var()
   below ships its fallback inline, and the colour fallbacks chain to the
   section-1 surface they shadow (card bg → page bg, title ink → ink), so a
   design that sets no card token still renders its cards in its own palette.
   ========================================================================= */

/* ── the grid ── */

/* GP prints page-header, pagination and (with its columns feature) a wrapper
   as siblings of the articles, so everything that is not an article spans the
   full row rather than becoming an accidental card. */
.blog .site-main,
.archive .site-main {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--fx-grid-min, 280px), 1fr));
  gap: var(--fx-grid-gap, var(--fx-space, 1.5rem));
}

.blog .site-main > *:not(article),
.archive .site-main > *:not(article) {
  grid-column: 1 / -1;
}

/* Core spaces the block with per-item margins; those are zeroed on the card
   below so the one gap token spaces both surfaces.

   GRID, NOT core's flex. Core's flex layout sizes columns with width calcs
   that ignore the gap, so at the block's columns:3 the third card wraps on
   desktop (verified against core's blocks/latest-posts/style.css math). A
   real grid also makes --fx-grid-min govern BOTH loop surfaces — without it
   the token the designer is promised would visibly do nothing on the static
   front page every provision now builds. `!important` beats core's own
   is-grid flex declaration regardless of cascade order. */
.wp-block-latest-posts__list.is-grid {
  display: grid !important;
  grid-template-columns: repeat(auto-fill, minmax(var(--fx-grid-min, 280px), 1fr));
  gap: var(--fx-grid-gap, var(--fx-space, 1.5rem));
  padding: 0;
  list-style: none;
}

/* Core sets per-column widths on the items; a grid track owns sizing now. */
.wp-block-latest-posts__list.is-grid li {
  width: auto !important;
  max-width: none;
}

/* ── the card ── */

/* One anatomy, two DOMs. `overflow: hidden` is load-bearing: it is what
   clips the image zoom and the accent bar to the card's radius. */
.wp-block-latest-posts__list.is-grid li,
.blog .site-main article,
.archive .site-main article {
  position: relative;
  overflow: hidden;
  margin: 0;
  padding: 0;
  background-color: var(--fx-card-bg, var(--fx-bg, #ffffff));
  border: var(--fx-border, 1px) solid var(--fx-card-border, var(--fx-line, #e2e8f0));
  border-radius: var(--fx-card-radius, var(--fx-radius, 0px));
  box-shadow: var(--fx-card-shadow, 0 2px 8px rgba(15, 23, 42, 0.04), 0 1px 3px rgba(15, 23, 42, 0.03));
  transition: box-shadow 0.4s cubic-bezier(0.22, 1, 0.36, 1),
    transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}

.wp-block-latest-posts__list.is-grid li:hover,
.blog .site-main article:hover,
.archive .site-main article:hover {
  box-shadow: var(--fx-card-shadow-hover, 0 16px 48px rgba(15, 23, 42, 0.1), 0 4px 14px rgba(15, 23, 42, 0.05));
  transform: translateY(calc(-1 * var(--fx-card-lift, 4px)));
}

/* The accent arrives as a 3px bar with no layout of its own, so revealing it
   on hover cannot shift the card's content. */
.wp-block-latest-posts__list.is-grid li::after,
.blog .site-main article::after,
.archive .site-main article::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: var(--fx-card-accent, var(--fx-accent, #0f172a));
  opacity: 0;
  transition: opacity 0.3s ease;
  z-index: 2;
  pointer-events: none;
}

.wp-block-latest-posts__list.is-grid li:hover::after,
.blog .site-main article:hover::after,
.archive .site-main article:hover::after {
  opacity: 1;
}

/* GP's .inside-article paints its own surface (section 2 points it at
   --fx-bg), so inside a card it must read the card token — otherwise a
   design's card colour would show only as a sliver at the border. */
.blog .site-main article .inside-article,
.archive .site-main article .inside-article {
  background-color: var(--fx-card-bg, var(--fx-bg, #ffffff));
  padding: var(--fx-card-pad, 1.25rem);
}

/* ── featured image ── */

/* latest-posts: the image is the card's first child, so it runs flush to the
   edges and the title link below carries the padding. */
.wp-block-latest-posts__list .wp-block-latest-posts__featured-image {
  margin: 0;
  overflow: hidden;
  line-height: 0;
}

/* GP loop: the image sits inside .inside-article's padding and GP may place
   it above OR below the title, so it stays framed — flush would need to know
   which — and takes half the card radius so the frame reads nested, not cut. */
.blog .site-main article .post-image,
.archive .site-main article .post-image {
  margin: 0 0 var(--fx-card-pad, 1.25rem);
  overflow: hidden;
  line-height: 0;
  border-radius: calc(var(--fx-card-radius, var(--fx-radius, 0px)) / 2);
}

/* A fixed ratio rather than a fixed height: rows stay level at every column
   count auto-fill produces, with no breakpoint table to maintain. */
.wp-block-latest-posts__list .wp-block-latest-posts__featured-image img,
.blog .site-main article .post-image img,
.archive .site-main article .post-image img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  transition: transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}

.wp-block-latest-posts__list.is-grid li:hover .wp-block-latest-posts__featured-image img,
.blog .site-main article:hover .post-image img,
.archive .site-main article:hover .post-image img {
  transform: scale(1.04);
}

/* ── title + meta ── */

.wp-block-latest-posts__list a.wp-block-latest-posts__post-title {
  display: block;
  padding: var(--fx-card-pad, 1.25rem);
  font-family: var(--fx-font-heading, inherit);
  font-size: var(--fx-card-title-size, 1rem);
  font-weight: var(--fx-heading-weight, 700);
  line-height: var(--fx-heading-line-height, 1.25);
  letter-spacing: var(--fx-heading-tracking, -0.01em);
  color: var(--fx-card-title-ink, var(--fx-ink, #0f172a));
  text-decoration: none;
}

.blog .site-main article .entry-title,
.archive .site-main article .entry-title {
  font-size: var(--fx-card-title-size, 1rem);
}

.blog .site-main article .entry-title a,
.archive .site-main article .entry-title a {
  color: var(--fx-card-title-ink, var(--fx-ink, #0f172a));
  text-decoration: none;
}

/* Titles answer the accent bar on hover. With no tokens set, accent and
   title ink share a default, so the neutral card stays quiet. */
.wp-block-latest-posts__list a.wp-block-latest-posts__post-title:hover,
.wp-block-latest-posts__list a.wp-block-latest-posts__post-title:focus,
.blog .site-main article .entry-title a:hover,
.blog .site-main article .entry-title a:focus,
.archive .site-main article .entry-title a:hover,
.archive .site-main article .entry-title a:focus {
  color: var(--fx-card-accent, var(--fx-accent, #0f172a));
}

/* The block's optional date/author/excerpt lines: the title above already
   carries a full pad on every side, so these pad sides + bottom only. */
.wp-block-latest-posts__list .wp-block-latest-posts__post-date,
.wp-block-latest-posts__list .wp-block-latest-posts__post-author,
.wp-block-latest-posts__list .wp-block-latest-posts__post-excerpt {
  margin: 0;
  padding: 0 var(--fx-card-pad, 1.25rem) var(--fx-card-pad, 1.25rem);
  color: var(--fx-muted, #475569);
}

/* =========================================================================
   4. HERO — the identity block the provisioning lane opens the static front
   page with (a wp:group carrying .fx-hero: the tagline as a heading, one
   sentence of copy, the latest-posts grid below). Added 2026-08-05, when a
   zero-post homepage still rendered as a bare page title over whitespace.

   Same contract as the cards: the page content carries the CLASS and nothing
   else, this file carries the look. Deliberately NO new token names — the
   hero dresses itself entirely from the section-1 vocabulary (panel, line,
   ink, muted, spacing, type), so every design release restyles it for free
   and the allow-list in functions.php stays untouched.
   ========================================================================= */

.fx-hero {
  padding: calc(var(--fx-space-lg, 3rem) * 1.5) var(--fx-space, 1.5rem);
  margin-bottom: var(--fx-grid-gap, var(--fx-space, 1.5rem));
  text-align: center;
  background-color: var(--fx-panel, #f8fafc);
  border: var(--fx-border, 1px) solid var(--fx-line, #e2e8f0);
  border-radius: var(--fx-radius, 0px);
}

/* An h2 by semantics (the page keeps its one h1), the page's headline by
   size — scaled from the site-title token so a design that grows the brand
   grows the hero with it. */
.fx-hero .wp-block-heading {
  margin: 0;
  font-size: calc(var(--fx-title-size, 1.5rem) * 1.75);
  line-height: var(--fx-heading-line-height, 1.25);
  letter-spacing: var(--fx-heading-tracking, -0.01em);
  color: var(--fx-ink, #0f172a);
}

/* One sentence, measure-capped so it reads as a line of copy, not a block. */
.fx-hero p {
  max-width: 34em;
  margin: var(--fx-space, 1.5rem) auto 0;
  color: var(--fx-muted, #475569);
}

/* GP prints the page title above the content, and on the provisioned front
   page that title is the SITE NAME — printed a second time right under the
   header brand, which is the default-install look the hero replaces. Hidden
   only when the content actually OPENS with the hero, so a hand-edited home
   page keeps its title; a browser without :has() shows the title again,
   which is the pre-hero rendering, not a break. (2026-08-05) */
.home .inside-article:has(> .entry-content > .fx-hero:first-child) > .entry-header {
  display: none;
}

/* Motion is the one thing this section adds that the theme never had, so it
   is the one thing a reduced-motion user must be able to decline. */
@media (prefers-reduced-motion: reduce) {
  .wp-block-latest-posts__list.is-grid li,
  .wp-block-latest-posts__list.is-grid li:hover,
  .wp-block-latest-posts__list.is-grid li:hover .wp-block-latest-posts__featured-image img,
  .blog .site-main article,
  .blog .site-main article:hover,
  .blog .site-main article:hover .post-image img,
  .archive .site-main article,
  .archive .site-main article:hover,
  .archive .site-main article:hover .post-image img {
    transition: none;
    transform: none;
  }
}
