/*
Theme Name:  Certek Framework
Theme URI:   https://highrise.digital
Author:      Highrise Digital
Author URI:  https://highrise.digital
Description: The Certek parent theme. A minimal FSE block theme for Certek group sites, extended per brand by a child theme. Semantic colour tokens, Utopia fluid type and space, native section styles.
Requires at least: 6.6
Tested up to: 6.8
Requires PHP: 8.0
Version:     1.0.0
License:     GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: certek-framework
Tags: full-site-editing, block-patterns, custom-colors, editor-style
*/


/* ── Base resets ──────────────────────────────────────────────────────── */

*,
*::before,
*::after {
    box-sizing: border-box;
}

img,
video {
    max-width: 100%;
    height: auto;
    display: block;
}

body {
    overflow-x: clip;
}

b,
strong {
    font-weight: 700;
}

/* The editor offers a highlight colour on mark. Drop the browser default
   yellow so unstyled highlights inherit the surrounding text colours. */
mark {
    background-color: transparent;
    color: inherit;
}

:is(h1, h2, h3, h4, h5, h6, blockquote) {
    text-wrap: balance;
}

:is(p, li, dt, dd, figcaption, caption) {
    text-wrap: pretty;
}

/* Fluid line-height: derived from each element's own font size, so one rule
   covers the whole type scale. Small text resolves to ~1.6, body text
   to ~1.5, 20px to ~1.34, display sizes clamp at 1.1. Replaces per-size
   ladders; block-level line-height settings in the editor still win via
   inline styles, and block buttons keep line-height 1 from theme.json
   (its class selector outweighs the type selector here). */
h1, h2, h3, h4, h5, h6, p, li, dt, dd,
blockquote, figcaption, caption, th, td, button {
    line-height: clamp(1.1em, calc(0.7em + 0.8rem), 1.7em);
}

/* Global transitions for interactive elements */
a,
button,
.wp-block-button__link,
input[type="button"],
input[type="submit"],
input[type="reset"] {
    transition: color 0.2s ease, background-color 0.2s ease, border-color 0.2s ease;
}

/* Skip link (WP outputs this for block themes) */
.skip-link {
    position: absolute;
    top: -100%;
    left: 1rem;
    padding: 0.5rem 1rem;
    background: var(--wp--preset--color--brand);
    color: var(--wp--preset--color--on-brand);
    font-weight: 600;
    z-index: 9999;
    text-decoration: none;
}

.skip-link:focus {
    top: 1rem;
}


/* ── Eyebrow ──────────────────────────────────────────────────────────────
   The small kicker label that sits above a heading, on paragraphs and
   headings alike. Sentence case, small, in the section's eyebrow colour.
   The glyph in front of it is a brand flourish and belongs to the child
   theme, not here. */

:is(.wp-block-heading, p).is-style-eyebrow {
    color: var(--hd-eyebrow);
    font-size: var(--wp--preset--font-size--2-xs);
    font-weight: 600;
    letter-spacing: 0;
    text-transform: none;
    margin-block-end: 0;
}

:is(.wp-block-heading, p).is-style-eyebrow + :is(h1, h2, h3, h4, h5, h6) {
    margin-top: var(--wp--preset--spacing--2-xs);
}


/* ── Term pills ───────────────────────────────────────────────────────────
   Every post terms block renders its terms as chips. The fill and label are
   tokens so a section can swap them where the default surface chip would
   disappear into the band. */

.wp-block-post-terms {
    display: flex;
    flex-wrap: wrap;
    gap: var(--wp--preset--spacing--3-xs);
}

.wp-block-post-terms .wp-block-post-terms__separator {
    display: none;
}

.wp-block-post-terms a {
    display: inline-block;
    line-height: 1.3;
    padding: 0.2em 0.6em;
    border-radius: var(--wp--custom--radius--m);
    background: var(--hd-pill-fill, var(--wp--preset--color--surface));
    color: var(--hd-pill-label, var(--wp--preset--color--ink));
    text-decoration: none;
}

.wp-block-post-terms a:hover,
.wp-block-post-terms a:focus {
    background: var(--wp--preset--color--brand);
    color: var(--wp--preset--color--on-brand);
    text-decoration: none;
}


/* ── Link underline weight ────────────────────────────────────────────────
   Links are underlined everywhere (theme.json). On Brand and Alt no palette
   colour clears 4.5:1 as a distinct hover, so there the underline thickens
   from 1px to 2px and the colour holds; elsewhere the colour changes and
   the weight holds. */

a:where(:not(.wp-element-button)) {
    text-decoration-thickness: var(--hd-link-thickness);
}

a:where(:not(.wp-element-button)):hover,
a:where(:not(.wp-element-button)):focus {
    text-decoration-thickness: var(--hd-link-thickness-hover);
}


/* ── Section rhythm ───────────────────────────────────────────────────────
   Padding and vertical margin for group "sections". The three tiers come
   from tokens.css (--hd-section-space, --wide, --full).

   Full-width groups are treated as section bands purely by alignment: any
   .alignfull group gets the band padding and margins, background or not.
   No render filter or editor script needed, and background images and
   section styles are covered for free. Everything uses :where() (zero
   specificity), so padding set on the block in the editor always wins.

   Inline and wide groups only read as "boxed" when they have a background,
   which means core's has-background class (colours/gradients) or one of
   the theme's section styles, matched here by their shared
   is-style-section- prefix. */

/* Custom properties inherit, so every group and cover starts on the base
   tier rather than picking up the tier of the band it sits inside. The
   tier rules below outrank this. */
:where(.wp-block-group, .wp-block-cover) {
    --hd-band-pad: var(--hd-section-space);
}

:where(.wp-block-group):is(.has-background, [class*="is-style-section-"]) {
    --hd-band-pad: var(--hd-section-space);
    padding-top: var(--hd-section-space);
    padding-bottom: var(--hd-band-pad);
    padding-left: var(--hd-section-space);
    padding-right: var(--hd-section-space);
    margin-top: var(--hd-section-space);
    margin-bottom: var(--hd-section-space);
    border-radius: var(--wp--custom--radius--m);
}

.wp-block-cover.alignwide,
:where(.wp-block-group).alignwide {
    margin-top: var(--hd-section-space-wide);
    margin-bottom: var(--hd-section-space-wide);
}

.wp-block-cover,
:where(.alignwide):is(.has-background, [class*="is-style-section-"]) {
    --hd-band-pad: var(--hd-section-space-wide);
    padding: var(--hd-section-space-wide);
}

/* Every full-width group and cover is a section band */
.wp-block-cover.alignfull,
:where(.wp-block-group).alignfull {
    --hd-band-pad: var(--hd-section-space-full);
    padding-top: var(--hd-section-space-full);
    padding-bottom: var(--hd-band-pad);
}

:where(.wp-block-group, .wp-block-cover).alignfull {
    margin-top: var(--hd-section-space-full);
    margin-bottom: var(--hd-section-space-full);
}

/* A full-width band is never rounded. Two things make a band full width:
   the alignfull class, and sitting at the root of a template, where a
   block spans the viewport with no align class at all -- a direct child of
   .wp-site-blocks, or of a root-level template part. The classes are
   repeated so the rule outweighs anything else that rounds a group at
   class specificity: the boxed-group rule above, the card surface, a
   section style, or a radius set on the Group block in global styles. A
   radius set on the block itself is an inline style and still wins. */
.wp-block-group.alignfull.alignfull,
.wp-block-cover.alignfull.alignfull,
.wp-site-blocks > .wp-block-group.wp-block-group,
.wp-site-blocks > .wp-block-cover.wp-block-cover,
.wp-site-blocks > .wp-block-template-part > .wp-block-group.wp-block-group,
.wp-site-blocks > .wp-block-template-part > .wp-block-cover.wp-block-cover {
    border-radius: 0;
}

/* Adjacent full-width bands sit flush */
:where(.wp-block-group, .wp-block-cover).alignfull + :where(.wp-block-group, .wp-block-cover).alignfull {
    margin-top: calc(-1 * var(--hd-section-space-full));
}

/* The Overlap up block style pulls a section into the one above it by
   --hd-overlap, and gives that section the same amount of extra bottom
   padding so its content is never covered. One token drives both halves,
   and it is fluid, so the overlap tracks the viewport without a media
   query. The class is repeated on purpose: it has to outweigh the flush-bands
   rule above, and core's layout styles, which set margin-block-start on
   `> * + *` at two classes and print after this stylesheet. Position and
   z-index are needed because backgrounds paint before inline content, so
   without them the section above prints its text over the overlapping
   section's background. */
.wp-block-group.is-style-overlap-up.is-style-overlap-up,
.wp-block-cover.is-style-overlap-up.is-style-overlap-up {
    position: relative;
    z-index: 1;
    margin-block-start: calc(-1 * var(--hd-overlap));
}

/* The style is often set on a full-width wrapper whose child is the box
   that actually carries the colour. That wrapper paints nothing but still
   takes the band's top padding, which would hold the visible box down and
   swallow the pull-up. A group that paints nothing has no use for that
   padding, so it goes, and the overlap is measured from the content.
   A group that is itself the coloured band keeps its padding. */
.wp-block-group.is-style-overlap-up.is-style-overlap-up:not(.has-background):not([class*="is-style-section-"]) {
    padding-block-start: 0;
}

/* The section above makes room. No second editor step: :has() finds it.
   --hd-band-pad is whichever padding tier that section landed on; the
   fallback covers a group that is not a band at all.

   Its bottom margin has to go, or it collapses with the negative top
   margin and all the overlap buys is a smaller gap. With the margin gone
   the pull-up is the whole of --hd-overlap. */
.wp-block-group.wp-block-group:has(+ .is-style-overlap-up),
.wp-block-cover.wp-block-cover:has(+ .is-style-overlap-up) {
    padding-bottom: calc(var(--hd-band-pad, var(--hd-section-space)) + var(--hd-overlap));
    margin-block-end: 0;
}

/* First/last section hugs the template edges */
.entry-content > :where(.wp-block-group, .wp-block-cover):first-child {
    margin-top: 0;
}

.entry-content > :where(.wp-block-group, .wp-block-cover):last-child {
    margin-bottom: 0;
}

:where(.wp-block-group):is(.has-background, [class*="is-style-section-"], .alignfull) > *:first-child {
    margin-top: 0;
}

footer.wp-block-template-part {
    margin-top: 0;
}

.wp-site-blocks > .wp-block-template-part {
    margin-block-start: 0;
}

/* Header/footer chrome groups don't take the section band margins */
header.wp-block-template-part :where(.wp-block-group):is(.alignwide, .alignfull),
footer.wp-block-template-part :where(.wp-block-group):is(.alignwide, .alignfull) {
    margin-top: 0;
    margin-bottom: 0;
}

/* A dark header borrows the section-ink variation for its colour tokens, but
   a header is not a section band: it takes none of the padding, margin or
   radius that the rule above gives a boxed group. Top and bottom padding are
   left alone, because those come from the part's own inline style. */
header.wp-block-template-part > .wp-block-group[class*="is-style-section-"] {
    padding-left: 0;
    padding-right: 0;
    margin-top: 0;
    margin-bottom: 0;
    border-radius: 0;
}

/* ...but a header that was already taking the root padding keeps it, so the
   logo and navigation stay on the same gutter as the rest of the page. */
header.wp-block-template-part > .wp-block-group[class*="is-style-section-"].has-global-padding {
    padding-left: var(--wp--style--root--padding-left);
    padding-right: var(--wp--style--root--padding-right);
}

/* The header always overlays the first section rather than sitting above it.
   It comes out of flow so the first section starts at the top of the page and
   shows through behind the logo and navigation. On a dark page the section
   style still supplies the light-on-dark tokens; its background is dropped so
   the section behind shows through. */
.wp-site-blocks {
    position: relative;
}

.wp-site-blocks > header.wp-block-template-part {
    position: absolute;
    inset-block-start: 0;
    inset-inline: 0;
    z-index: 9999;
}

/* The header no longer occupies flow space, so whatever follows it starts the
   page and takes no top margin. Templates that already zero this inline are
   unaffected. */
.wp-site-blocks > header.wp-block-template-part + * {
    margin-block-start: 0;
}

/* Because the header overlays it, the first section of the page needs enough
   top padding to clear the logo and navigation. The padding goes on the
   section itself, never on the main or post-content wrappers above it, so a
   full-bleed background still reaches the top of the page and runs behind
   the header.

   Which element that is depends on the template, so the wrappers are stepped
   through: the section can sit straight after the header, inside the main
   element, or inside the post content within it. The post content is only
   stepped into when it is itself the first thing in the main element, as on a
   page; on a single post the entry content sits below the title and meta, so
   its first block is not the first block on the page and must not be padded.
   That keeps the three to one match on any given page. A padding set on the
   block in the editor is written inline and so overrides this without a
   specificity fight. */
.wp-site-blocks {
    --hd-header-clearance: var(--wp--preset--spacing--3-xl);
}

.wp-site-blocks > header.wp-block-template-part + *:not(main):not(.wp-block-post-content),
.wp-site-blocks > header.wp-block-template-part + :is(main, .wp-block-post-content) > :first-child:not(.wp-block-post-content),
.wp-site-blocks > header.wp-block-template-part + :is(main, .wp-block-post-content) > .wp-block-post-content:first-child > :first-child {
    padding-top: var(--hd-header-clearance);
}

header.wp-block-template-part > .wp-block-group[class*="is-style-section-"] {
    background-color: transparent;
}

/* Editor re-assertions. The editor's layout styles carry higher specificity
   than our :where() rules, so the same values are re-stated here. */
.editor-styles-wrapper :where(.wp-block-group):is(.has-background, [class*="is-style-section-"]) {
    padding: var(--hd-section-space);
    margin-block-start: var(--hd-section-space);
    margin-block-end: var(--hd-section-space);
}

.editor-styles-wrapper :where(.wp-block-group).alignwide {
    margin-block-start: var(--hd-section-space-wide);
    margin-block-end: var(--hd-section-space-wide);
}

.editor-styles-wrapper :where(.wp-block-group).alignfull {
    padding-top: var(--hd-section-space-full);
    padding-bottom: var(--hd-section-space-full);
}

.editor-styles-wrapper :where(.wp-block-group, .wp-block-cover).alignfull {
    margin-block-start: var(--hd-section-space-full);
    margin-block-end: var(--hd-section-space-full);
}

.editor-styles-wrapper .wp-block-group.alignfull.alignfull,
.editor-styles-wrapper .wp-block-cover.alignfull.alignfull,
.editor-styles-wrapper .is-root-container > .wp-block-group.wp-block-group,
.editor-styles-wrapper .is-root-container > .wp-block-cover.wp-block-cover,
.editor-styles-wrapper .is-root-container > .wp-block-template-part > .wp-block-group.wp-block-group,
.editor-styles-wrapper .is-root-container > .wp-block-template-part > .wp-block-cover.wp-block-cover {
    border-radius: 0;
}

.editor-styles-wrapper :where(.wp-block-group, .wp-block-cover).alignfull + :where(.wp-block-group, .wp-block-cover).alignfull {
    margin-block-start: calc(-1 * var(--hd-section-space-full));
}

.editor-styles-wrapper .wp-block-group.is-style-overlap-up.is-style-overlap-up,
.editor-styles-wrapper .wp-block-cover.is-style-overlap-up.is-style-overlap-up {
    margin-block-start: calc(-1 * var(--hd-overlap));
}

.editor-styles-wrapper .wp-block-group.is-style-overlap-up.is-style-overlap-up:not(.has-background):not([class*="is-style-section-"]) {
    padding-block-start: 0;
}

.editor-styles-wrapper .wp-block-group.wp-block-group:has(+ .is-style-overlap-up),
.editor-styles-wrapper .wp-block-cover.wp-block-cover:has(+ .is-style-overlap-up) {
    padding-bottom: calc(var(--hd-band-pad, var(--hd-section-space)) + var(--hd-overlap));
    margin-block-end: 0;
}

.editor-styles-wrapper :where(.wp-block-group):is(.has-background, [class*="is-style-section-"], .alignfull) > *:first-child {
    margin-block-start: 0;
}


/* ── Grid layouts ─────────────────────────────────────────────────────── */

/* Grid gap lives in theme.json styles.css: core's own gap rule is
   :root :where(.is-layout-grid) inside global styles, which outranks
   anything a theme stylesheet can say at :where() specificity. */

.wp-block-group.is-layout-grid > * {
    margin-top: 0;
    margin-bottom: 0;
}

/* Manual grid placement stays fixed at every viewport, which cramps small
   screens. Below 768px force a single column so items stack. !important is
   required to beat the inline styles WP writes on the blocks. */
@media (max-width: 767px) {
    .wp-block-group.is-layout-grid {
        grid-template-columns: 1fr !important;
        grid-template-rows: auto !important;
    }

    .wp-block-group.is-layout-grid > * {
        grid-column: auto !important;
        grid-row: auto !important;
    }
}


/* ── Cards ────────────────────────────────────────────────────────────────
   Shared card surface used by the certek card patterns. The surface only;
   whether the whole card is clickable is the Linked card block style's job,
   in assets/styles/core-group.css. */

.certek-card {
    position: relative;
    display: flex;
    flex-direction: column;
    height: 100%;
    /* Cards are always a base surface, whatever section they sit in, so the
       whole role set is restated rather than inherited from the section. */
    background: var(--wp--preset--color--base);
    --hd-heading: var(--wp--preset--color--ink);
    --hd-text: var(--wp--preset--color--body);
    --hd-eyebrow: var(--hd-interactive);
    --hd-link: var(--hd-interactive);
    --hd-link-hover: var(--wp--custom--state--interactive-hover);
    --hd-link-thickness: 1px;
    --hd-link-thickness-hover: 1px;
    --hd-button-fill: var(--wp--preset--color--brand);
    --hd-button-label: var(--wp--preset--color--on-brand);
    --hd-outline: var(--hd-interactive);
    --hd-control-border: var(--wp--preset--color--body);
    --hd-hairline: var(--wp--preset--color--line);
    --hd-pill-fill: var(--wp--preset--color--surface);
    --hd-pill-label: var(--wp--preset--color--ink);
    color: var(--hd-text);
    border: 1px solid var(--hd-hairline);
    border-radius: var(--wp--custom--radius--m);
    overflow: hidden;
    transition: transform 0.25s ease, box-shadow 0.25s ease;
}

/* Headings and links inside a card follow the card, not the surrounding
   section. The repeated class outweighs the section styles' generated
   element rules; the :not() guards leave buttons and per-block link
   colours (e.g. brand category links) and term pills intact. */
.wp-block-group.certek-card.certek-card :is(h1, h2, h3, h4, h5, h6) {
    color: var(--hd-heading);
}

.wp-block-group.certek-card.certek-card a:not(.wp-element-button):not(.has-link-color a):not(.wp-block-post-terms a) {
    color: var(--hd-heading);
}

.wp-block-group.certek-card.certek-card a:not(.wp-element-button):not(.has-link-color a):not(.wp-block-post-terms a):hover,
.wp-block-group.certek-card.certek-card a:not(.wp-element-button):not(.has-link-color a):not(.wp-block-post-terms a):focus {
    color: var(--hd-link);
}

.certek-card:hover,
.certek-card:focus-within {
    transform: translateY(-2px);
    box-shadow: var(--wp--preset--shadow--card);
}

.certek-card__media {
    margin: 0;
    overflow: hidden;
}

.certek-card__media img {
    width: 100%;
    object-fit: cover;
    transition: transform 0.4s ease;
}

.certek-card:hover .certek-card__media img,
.certek-card:focus-within .certek-card__media img {
    transform: scale(1.05);
}

.certek-card__body {
    display: flex;
    flex-direction: column;
    flex-grow: 1;
    margin-block-start: 0;
}

/* Article card: the excerpt grows to fill the body, so a row of cards lines
   up regardless of excerpt length. */
.certek-card .wp-block-post-excerpt {
    display: flex;
    flex-direction: column;
    flex-grow: 1;
}

.certek-card .wp-block-post-excerpt__excerpt {
    font-size: var(--wp--preset--font-size--xs);
}

.certek-card .wp-block-post-title {
    letter-spacing: 0;
}

.certek-card .wp-block-post-title a {
    text-decoration: none;
}


/* ── Scroll reveal ────────────────────────────────────────────────────────
   JS (assets/js/animations.js) only toggles .is-inview on .animate elements.
   CSS owns the look; add new animation types here, not in the JS. */

.has-js .animate {
    opacity: 0;
    transition: opacity 0.6s ease, transform 0.6s ease;
    will-change: opacity, transform;
}

.has-js .animate.is-inview {
    opacity: 1;
    transform: none;
}

.has-js .animate.fade-up    { transform: translateY(1.5rem); }
.has-js .animate.fade-down  { transform: translateY(-1.5rem); }
.has-js .animate.fade-left  { transform: translateX(1.5rem); }
.has-js .animate.fade-right { transform: translateX(-1.5rem); }
.has-js .animate.fade-in    { transform: none; }
.has-js .animate.scale-in   { transform: scale(0.96); }

/* Optional stagger: add animate-stagger to a parent of animated children */
.has-js .animate-stagger > .animate {
    transition-delay: calc(var(--anim-step, 80ms) * var(--anim-i, 0));
}

@media (prefers-reduced-motion: reduce) {
    .has-js .animate,
    .has-js .animate.is-inview {
        opacity: 1 !important;
        transform: none !important;
        transition: none !important;
    }
}
