/*
Theme Name: O'Side Systems
Theme URI: https://oside.systems
Author: O'Side Systems LLC
Author URI: https://oside.systems
Description: Child theme of Twenty Twenty-Four for oside.systems. Exists so that
  the site's templates and styling live in version control instead of in the
  database. See .harness/standards/architecture/0002-where-content-and-styling-changes-live.md
Template: twentytwentyfour
Version: 2.0.3
Requires at least: 6.4
Requires PHP: 7.0
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: oside-systems
*/

/* Styling belongs in theme.json wherever the block editor can express it —
   that keeps it visible to the Site Editor and to global styles. Use this file
   only for rules theme.json cannot express.

   Everything below is here because it targets markup theme.json has no block
   selector for: the <ul> that core/categories renders, the anchor soup inside
   core/query-pagination, and the form controls WPForms prints. Each rule maps
   to a component in the "Design tokens & components" Figma frame. */

/* --- Category chips -------------------------------------------------------
   Figma: "Chip / Active" and the Writing index category filter.
   core/categories renders a plain <ul>; there is no theme.json path to its
   <li> or its anchors. */
.oss-category-chips {
	display: flex;
	flex-wrap: wrap;
	gap: 10px;
	margin: 0;
	padding: 0;
	list-style: none;
}

.oss-category-chips li {
	margin: 0;
	padding: 0;
	list-style: none;
}

.oss-category-chips a {
	display: inline-block;
	padding: 7px 14px;
	border: 1px solid var(--wp--preset--color--line);
	border-radius: 100px;
	color: var(--wp--preset--color--contrast-2);
	font-size: 0.8125rem;
	font-weight: 500;
	line-height: 1.5;
	text-decoration: none;
	transition: border-color 0.15s ease, color 0.15s ease, background-color 0.15s ease;
}

.oss-category-chips a:hover,
.oss-category-chips a:focus-visible {
	border-color: var(--wp--preset--color--accent);
	color: var(--wp--preset--color--accent);
	text-decoration: none;
}

.oss-category-chips .current-cat > a {
	background-color: var(--wp--preset--color--accent-2);
	border-color: var(--wp--preset--color--accent);
	color: var(--wp--preset--color--accent);
}

/* --- Pagination -----------------------------------------------------------
   Figma: the "Page" chips on the Writing index. core/query-pagination-numbers
   emits bare <a class="page-numbers"> siblings with no block wrapper to target. */
.oss-pagination .wp-block-query-pagination-numbers {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	align-items: center;
}

.oss-pagination .page-numbers {
	display: inline-block;
	padding: 8px 14px;
	border: 1px solid transparent;
	border-radius: 6px;
	color: var(--wp--preset--color--contrast-2);
	font-size: 0.875rem;
	font-weight: 500;
	line-height: 1.5;
	text-decoration: none;
}

.oss-pagination .page-numbers:hover,
.oss-pagination .page-numbers:focus-visible {
	color: var(--wp--preset--color--contrast);
	text-decoration: none;
}

.oss-pagination .page-numbers.current {
	background-color: var(--wp--preset--color--base-2);
	border-color: var(--wp--preset--color--line);
	color: var(--wp--preset--color--contrast);
}

.oss-pagination .wp-block-query-pagination-previous,
.oss-pagination .wp-block-query-pagination-next {
	color: var(--wp--preset--color--accent);
	font-size: 0.875rem;
	font-weight: 500;
	text-decoration: none;
}

/* --- Form controls --------------------------------------------------------
   Figma: the "Input" frames on the Generic page (Contact).

   WPForms renders in "modern" mode here, which drives every colour and metric
   from CSS custom properties it declares on :root. Overriding its rules by
   selector is a specificity fight (its own are of the shape
   `div.wpforms-container-full .wpforms-form input[type=text]`); redefining the
   properties on .wpforms-container wins instead, because custom properties
   resolve from the nearest ancestor that declares them and the container is
   nearer than :root. That also means these values survive a plugin update that
   changes WPForms' selectors. */
.wpforms-container {
	--wpforms-field-background-color: var(--wp--preset--color--on-accent);
	--wpforms-field-border-color: var(--wp--preset--color--line);
	--wpforms-field-border-color-spare: var(--wp--preset--color--line);
	--wpforms-field-text-color: var(--wp--preset--color--contrast);
	--wpforms-field-menu-color: var(--wp--preset--color--base-2);
	--wpforms-field-border-radius: 8px;
	--wpforms-field-size-font-size: 15px;
	--wpforms-field-size-input-height: 48px;
	--wpforms-field-size-padding-h: 14px;

	--wpforms-label-color: var(--wp--preset--color--contrast-2);
	--wpforms-label-sublabel-color: var(--wp--preset--color--contrast-3);
	--wpforms-label-size-font-size: 14px;
	--wpforms-label-size-sublabel-font-size: 13px;

	--wpforms-button-background-color: var(--wp--preset--color--accent);
	--wpforms-button-border-color: var(--wp--preset--color--accent);
	--wpforms-button-text-color: var(--wp--preset--color--on-accent);
	--wpforms-button-border-radius: 6px;
	--wpforms-button-size-font-size: 15px;
	--wpforms-button-size-height: 50px;
	--wpforms-button-size-padding-h: 28px;
}

.wpforms-container .wpforms-field-label,
.wpforms-container .wpforms-field-sublabel {
	font-family: var(--wp--preset--font-family--body);
	font-weight: 500;
}

.wpforms-container ::placeholder {
	color: var(--wp--preset--color--contrast-3);
	opacity: 1;
}

/* The Figma runs every control the full width of the form column. WPForms sizes
   fields by class instead — "medium" is 60% — via

     div.wpforms-container .wpforms-form .wpforms-field input.wpforms-field-medium

   which is four classes and two elements. `.wpforms-container` is repeated below
   to reach five classes, so these win on specificity rather than on the load
   order of two stylesheets neither of us controls. */
.wpforms-container.wpforms-container .wpforms-form .wpforms-field input.wpforms-field-small,
.wpforms-container.wpforms-container .wpforms-form .wpforms-field input.wpforms-field-medium,
.wpforms-container.wpforms-container .wpforms-form .wpforms-field input.wpforms-field-large,
.wpforms-container.wpforms-container .wpforms-form .wpforms-field select.wpforms-field-small,
.wpforms-container.wpforms-container .wpforms-form .wpforms-field select.wpforms-field-medium,
.wpforms-container.wpforms-container .wpforms-form .wpforms-field select.wpforms-field-large,
.wpforms-container.wpforms-container .wpforms-form .wpforms-field textarea.wpforms-field-small,
.wpforms-container.wpforms-container .wpforms-form .wpforms-field textarea.wpforms-field-medium,
.wpforms-container.wpforms-container .wpforms-form .wpforms-field textarea.wpforms-field-large,
.wpforms-container.wpforms-container .wpforms-form .wpforms-field .wpforms-field-row.wpforms-field-small,
.wpforms-container.wpforms-container .wpforms-form .wpforms-field .wpforms-field-row.wpforms-field-medium,
.wpforms-container.wpforms-container .wpforms-form .wpforms-field .wpforms-field-row.wpforms-field-large {
	max-width: 100%;
}

.wpforms-container textarea {
	min-height: 140px;
	resize: vertical;
}

.wpforms-container .wpforms-field {
	padding: 0;
}

.wpforms-container .wpforms-field-container > .wpforms-field + .wpforms-field {
	margin-top: 18px;
}

.wpforms-container .wpforms-submit-container {
	margin-top: 22px;
	padding: 0;
}

/* Put the captcha and the submit button on one row, with the button flush to
   the right edge of the fields above it.

   WPForms emits `.wpforms-recaptcha-container` and `.wpforms-submit-container`
   as adjacent siblings of the form, immediately after `.wpforms-field-container`,
   so making the form itself a wrapping flex row is enough. Everything defaults
   to its own full-width row, which keeps any element WPForms adds later — the
   noscript notice, a honeypot, a future field group — laid out as it is today;
   only these two opt into sharing a row. */
.oss-contact-form .wpforms-form {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 22px;
}

.oss-contact-form .wpforms-form > * {
	flex: 0 0 100%;
}

.oss-contact-form .wpforms-form > .wpforms-recaptcha-container,
.oss-contact-form .wpforms-form > .wpforms-submit-container {
	flex: 0 1 auto;
	margin-top: 0;
}

.oss-contact-form .wpforms-form > .wpforms-submit-container {
	margin-left: auto;
}

/* Once the pair no longer fits side by side the button wraps onto its own line,
   where pinning it right would leave it stranded opposite nothing. */
@media (max-width: 600px) {
	.oss-contact-form .wpforms-form > .wpforms-submit-container {
		margin-left: 0;
	}
}

/* Classic (non-modern) render mode ignores the properties above, and the search
   and comment fields are core blocks WPForms never touches. */
.wpforms-container.wpforms-render-classic input[type="text"],
.wpforms-container.wpforms-render-classic input[type="email"],
.wpforms-container.wpforms-render-classic input[type="tel"],
.wpforms-container.wpforms-render-classic input[type="url"],
.wpforms-container.wpforms-render-classic select,
.wpforms-container.wpforms-render-classic textarea,
.wp-block-search__input,
.wp-block-post-comments-form input[type="text"],
.wp-block-post-comments-form input[type="email"],
.wp-block-post-comments-form textarea {
	background-color: var(--wp--preset--color--on-accent);
	border: 1px solid var(--wp--preset--color--line);
	border-radius: 8px;
	color: var(--wp--preset--color--contrast);
	font-family: var(--wp--preset--font-family--body);
	font-size: 0.9375rem;
	padding: 12px 14px;
}

.wpforms-container.wpforms-render-classic input:focus,
.wpforms-container.wpforms-render-classic select:focus,
.wpforms-container.wpforms-render-classic textarea:focus,
.wp-block-search__input:focus {
	border-color: var(--wp--preset--color--accent);
	outline: none;
}

/* hCaptcha renders in a cross-origin iframe, so its widget cannot be themed
   from here. Give it a little breathing room rather than leaving it flush. */
.wpforms-container .wpforms-recaptcha-container {
	margin-block: 8px;
}

/* --- Preformatted ---------------------------------------------------------
   The Contact page sets its mailing address as core/preformatted, which
   theme.json styles separately from core/code. Match the code block so the two
   monospace treatments do not diverge. */
.wp-block-preformatted {
	background-color: var(--wp--preset--color--base-2);
	border: 1px solid var(--wp--preset--color--line);
	border-radius: 8px;
	color: var(--wp--preset--color--contrast-2);
	font-size: 0.9375rem;
	line-height: 1.6;
	padding: 18px 22px;
}

/* --- Headshot -------------------------------------------------------------
   The Services bio crops a portrait into a 140px circle. WordPress's own
   150x150 thumbnail is a hard centre crop, and on this portrait it cuts the top
   of the head off, so the template points at the uncropped 683x1024 size
   instead. `cover` then only ever crops vertically, and biasing that crop well
   above centre keeps the whole head in frame while sitting the face at roughly
   40% of the circle, which is where a portrait wants to sit. */
.oss-headshot img {
	object-position: center 12%;
}

/* --- Mobile navigation overlay --------------------------------------------
   Figma: the Menu overlay. Four separate things pulled this overlay away from
   the rest of the site. The colours are fixed on the block in
   `parts/header.html` (`overlayBackgroundColor` / `overlayTextColor`, which the
   Site Editor can also see) and the gutter in `theme.json`; the alignment and
   the type scale are fixed here.

   Colours are NOT set here, and the rules that used to be are gone. Core ships

     .wp-block-navigation:not(.has-background) …is-menu-open…  { background-color: #fff }
     .wp-block-navigation:not(.has-text-color) …is-menu-open…  { color: #000 }

   at five classes. A plain `.wp-block-navigation__responsive-container.is-menu-open`
   is two, so the override that lived here never applied and the overlay rendered
   as black text on white — the one screen on the site that ignored the palette.
   Setting the block's overlay colour attributes instead emits
   `.has-base-background-color` / `.has-contrast-2-color`, which core declares
   `!important`, so they win on their own terms rather than by out-specifying a
   moving target.

   1. Alignment. The header nav is justified right, which is correct in the bar
   and wrong in the overlay: core reads the same
   `--navigation-layout-justification-setting` for both, so the full-screen menu
   inherited `flex-end` and hugged the right edge while every other block on the
   site aligns left to the content column. Core reads that property from three
   different selectors, so redefining the property is one rule where overriding
   `align-items` would be three. It has to be redeclared on the `<ul>` as well as
   on the container: core puts `items-justified-right` on both elements, so the
   list re-declares the property for its own children and a container-only
   override would leave the items ragged inside a left-aligned list. */
.wp-block-navigation__responsive-container.is-menu-open,
.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__container {
	--navigation-layout-justification-setting: flex-start;
}

/* 2. Gutter. Core pads the open overlay with

     padding: clamp(1rem, var(--wp--style--root--padding-top), 20rem) … ;

   which collapsed to `0` on all four sides, running the links off the right edge
   of the screen. The cause was in our own theme.json, not in core: root padding
   was declared `"top": "0"`, and a unitless zero is a <number>, not a <length>.
   Inside `clamp()` that is a type error, and because the value arrives through
   `var()` the whole shorthand — including the two sides that were valid — is
   invalid at computed-value time and falls back to `0`. It is fixed at source:
   theme.json now says `0px`, so the overlay picks up the page's own gutter and
   nothing is needed here. Recorded because the failure is invisible: the
   declaration is well-formed, DevTools shows it struck through with no warning,
   and every other consumer of those two properties was unaffected.

   3. Scale. The bar sets 0.875rem, which is right for a horizontal strip beside
   the site title and far too small for four links alone on a full screen. Step
   the overlay up to the shared `large` preset rather than inventing a size, and
   let the block's own 32px gap do the spacing. */
.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation-item__content {
	font-size: var(--wp--preset--font-size--large);
	line-height: 1.3;
}

/* --- Navigation link hover (DEFERRALS B10) --------------------------------
   The palette's own hover, everywhere the navigation block renders — the
   desktop bar and the overlay both. theme.json sets `core/navigation`
   `elements.link:hover` to contrast, but global styles emit element rules as
   `:root :where(…)` at one class, and core's
   `.wp-block-navigation .wp-block-navigation-item__content.wp-block-navigation-item__content
   { color: inherit }` at three classes wins at all times, so the hover and
   focus states declared there never land. Core doubles the class to beat
   itself; doubling it once more under :hover/:focus-visible is four classes,
   which wins regardless of load order. This rule supersedes the overlay-scoped
   hover that used to live here — the overlay's links match it too, since the
   responsive container sits inside the nav element. */
.wp-block-navigation .wp-block-navigation-item__content.wp-block-navigation-item__content:hover,
.wp-block-navigation .wp-block-navigation-item__content.wp-block-navigation-item__content:focus-visible {
	color: var(--wp--preset--color--contrast);
}

/* --- Focus visibility -----------------------------------------------------
   The dark palette makes the browser default focus ring nearly invisible. */
:where(a, button, input, select, textarea, summary):focus-visible {
	outline: 2px solid var(--wp--preset--color--accent);
	outline-offset: 2px;
}

/* --- Measure -------------------------------------------------------------
   Figma constrains several ledes and headlines to a fixed measure (660, 700,
   940 …) while keeping them flush with the left edge of the content column.

   WordPress's constrained layout gives every unaligned child
   `max-width: <content-size>; margin-left: auto !important; margin-right: auto
   !important`, so a narrower child ends up centred in the column rather than
   aligned to it. Core sets those with !important, so !important here is
   answering !important, not reaching for a shortcut.

   The subtlety that matters: `margin-left: 0` would align the element to the
   *container's* left edge, but its full-width siblings align to the left edge of
   the centred column inside that container. Those two are the same place only
   when the container is exactly the column plus its gutters — which is 1440px
   here, the Figma frame width. Anywhere wider, a `margin-left: 0` element juts
   out to the left of everything else on the page. So the left margin is the
   offset to the column instead: half the leftover space, floored at zero for
   viewports narrower than the column. */
:root {
	--oss-content-column: 1248px;
}

.oss-measure-640,
.oss-measure-660,
.oss-measure-700,
.oss-measure-720,
.oss-measure-760,
.oss-measure-820,
.oss-measure-880,
.oss-measure-940 {
	margin-left: max(0px, (100% - var(--oss-content-column)) / 2) !important;
	margin-right: auto !important;
}

.oss-measure-640 { max-width: 640px; }
.oss-measure-660 { max-width: 660px; }
.oss-measure-700 { max-width: 700px; }
.oss-measure-720 { max-width: 720px; }
.oss-measure-760 { max-width: 760px; }
.oss-measure-820 { max-width: 820px; }
.oss-measure-880 { max-width: 880px; }
.oss-measure-940 { max-width: 940px; }
