/*
 * Nova baseline effects (nova/baseline-polish in the effects catalog): hover micro-interactions +
 * scroll-reveal + scroll-to-top button. Ships with every site and is auto-enqueued via
 * functions.php by default — no per-project generation needed. Unlike the rest of the effects
 * catalog, this one is NOT gated behind a marker attribute: it targets structural selectors present
 * on any real page, so Content's only lever is removing its functions.php enqueue block entirely
 * for a static/minimal/no-animation brief (see ContentContext.ts's Baseline effects section) —
 * never a per-page decision.
 */

/* Global box-sizing reset: a generated custom widget's CSS (e.g. a per-project effect file) that
 * sets width:100% plus its own padding, without ever declaring box-sizing itself, renders wider
 * than its container under the default content-box model — the padding adds on top of the 100%
 * instead of being carved out of it, which has been observed to overflow into and visually cover a
 * neighboring column. border-box makes width:100% + padding behave as most authors actually expect,
 * closing that failure mode at the root instead of requiring every generated CSS file to declare it. */
*,
*::before,
*::after {
    box-sizing: border-box;
}

/* Global responsive-image safety net: WordPress core's own "max-width:100%; height:auto" image
 * reset is scoped to specific selectors (.wp-block-image img, img[class*=wp-image-], etc.) that
 * only ever match a native image block — never a bare <img> a generated custom widget's own
 * markup/JS creates (a product-viewer, a gallery, a card component). Without an explicit
 * width/height/object-fit of its own, such an image renders at full natural resolution and
 * overflows its container by hundreds of pixels, bleeding into neighboring content — confirmed
 * live on a generated shop product-viewer widget. A bare `img` selector has the lowest possible
 * specificity, so any more specific sizing a widget's own CSS declares still wins over this — it
 * only catches images nobody ever sized. */
img {
    max-width: 100%;
    height: auto;
}

/* A submenu caret is a real <button> core never resets, so without this it keeps native OS button
 * chrome (an opaque ButtonFace box around just the caret) under any nav effect's own background. */
.wp-block-navigation-submenu__toggle {
    background: transparent;
    border: none;
    appearance: none;
}

.wp-block-button__link,
.wp-block-group.has-background:not(.alignfull):not(.alignwide) {
    transition:
        transform 0.25s ease,
        box-shadow 0.25s ease,
        filter 0.25s ease;
}

/* Core ships word-break: break-word on button links, which breaks mid-character in narrow
 * containers (e.g. a Classic layout's 30%-width sidebar). Wrap at word boundaries instead;
 * overflow-wrap/hyphens still catch a genuinely too-long single word as a fallback ("Start-ed"
 * reads as intentional, unlike an arbitrary character cut) — but hyphenation alone won't save a
 * button that's cramped enough that even one hyphenated half doesn't fit (most engines refuse a
 * break leaving under ~3 characters on either side, so short words like "Started" don't hyphenate
 * at all). Let the font-size shrink with the container as a last resort instead: core's own button
 * padding is em-based, so it shrinks right along with it. */
.wp-block-buttons {
    container-type: inline-size;
}

/* A `wp:buttons` row's flex items default to shrink-to-min-content (flex-shrink:1, min-width:auto),
 * and because the label itself is allowed to wrap (see the word-break comment below), a multi-word
 * label's min-content is just its single longest word — so once the row can't grow a second line (an
 * explicit flexWrap:"nowrap", or a column narrower than the viewport-based stack breakpoint accounts
 * for), each button shrinks to that word's width before the row itself would ever wrap. flex-shrink:0
 * keeps every button at its natural single-line width instead, so it's the row that wraps (or, in a
 * too-narrow nowrap row, overflows).
 *
 * Scoped to :not(:only-child): a sole button has no sibling to give up space to, so it keeps the
 * narrow-column shrink/wrap/hyphenate behavior the comment below already covers. */
.wp-block-buttons > .wp-block-button:not(:only-child) {
    flex-shrink: 0;
}

/* Default vertical-align:baseline on this inline-block anchor aligns it to a "strut" based on the
 * wrapper's inherited font metrics, not the button's own — a fill button (no border, thicker
 * padding) and an outline button (border, thinner padding) land their text baselines at different
 * offsets from that strut, so their wrapping `.wp-block-button` boxes end up different heights and
 * visibly misaligned in a `wp:buttons` row. top removes the strut dependency entirely. */
.wp-block-button__link {
    word-break: normal;
    overflow-wrap: break-word;
    -webkit-hyphens: auto;
    hyphens: auto;
    font-size: clamp(0.75rem, 4cqw, 1.125rem);
    vertical-align: top;
}

.wp-block-button__link:hover,
.wp-block-group.has-background:not(.alignfull):not(.alignwide):hover {
    transform: translateY(-4px);
    box-shadow: var(--wp--preset--shadow--elevated, 0 8px 24px rgba(0, 0, 0, 0.16));
}

/* A gradient button loses the theme's colour response on hover: WordPress emits the gradient
 * preset as `background: var(...) !important`, and the shorthand resets background-color to
 * transparent, so the parent theme's :hover background-color has nothing left to tint (and would
 * paint under the gradient image anyway). filter is touched by nothing in core, Twenty
 * Twenty-Five, or theme.json, so it restores that response with no specificity fight — the lift
 * and shadow above already apply, this only adds the colour shift back. */
.wp-block-button__link[class*='-gradient-background']:hover {
    filter: brightness(1.08) saturate(1.06);
}

.scroll-reveal {
    opacity: 0;
    transform: translateY(24px);
    transition:
        opacity 0.6s ease,
        transform 0.6s ease;
}

.scroll-reveal.is-visible {
    opacity: 1;
    transform: translateY(0);
}

/* Scroll-to-top button: injected by baseline.js (no markup for this exists in any generated page),
 * hidden by default and only shown past a scroll threshold — see baseline.js for why opacity+
 * visibility rather than display is used to hide it (keeps the transition animatable). */
.nova-scroll-to-top {
    position: fixed;
    right: var(--wp--preset--spacing--30, 24px);
    bottom: var(--wp--preset--spacing--30, 24px);
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border: none;
    border-radius: 9999px;
    background: var(--wp--preset--color--contrast, #111);
    color: var(--wp--preset--color--base, #fff);
    cursor: pointer;
    opacity: 0;
    visibility: hidden;
    transform: translateY(8px);
    transition:
        opacity 0.25s ease,
        transform 0.25s ease,
        visibility 0s linear 0.25s;
}

.nova-scroll-to-top.is-visible {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    transition:
        opacity 0.25s ease,
        transform 0.25s ease,
        visibility 0s linear 0s;
}

.nova-scroll-to-top:hover {
    transform: translateY(-4px);
    box-shadow: var(--wp--preset--shadow--elevated, 0 8px 24px rgba(0, 0, 0, 0.16));
}

.nova-scroll-to-top svg {
    width: 20px;
    height: 20px;
    fill: currentColor;
}

.wp-block-cover__inner-container :where(h1, h2, h3, h4, h5, h6, p) {
    text-shadow:
        0 1px 3px rgba(0, 0, 0, 0.45),
        0 1px 12px rgba(0, 0, 0, 0.3);
}

/* Photo covers: diagonal shading for depth, layered on top of the block's own overlayColor/
 * dimRatio via background-image (a separate property, so it never fights the native dim the
 * model sets per block). Native cover attributes only produce a flat color overlay — a gradient
 * shape isn't something dimRatio/overlayColor can express, hence the CSS. */
:where(.wp-block-cover:has(> .wp-block-cover__image-background)) > .wp-block-cover__background {
    background-image: linear-gradient(135deg, rgba(0, 0, 0, 0.35) 0%, rgba(0, 0, 0, 0.1) 100%);
}

/* Card patterns (nova/card-row-centered, nova/info-row-centered): the shared card shape is an
 * accent-5-background group with an image as its first child, an alignItems:stretch layout so the
 * image fills the card's full width instead of shrinking to its own, and the row that holds them
 * stretching every card to the tallest one in its line. Per-generation content re-types this JSON
 * rather than reusing the pattern directly, so a generation can drop/flip alignItems/
 * verticalAlignment — the card still has the right classes/structure, just misaligned. Enforce both
 * structurally (matched on the classes the model reliably keeps — backgroundColor + a leading
 * image — not a bespoke className) so the layout is correct regardless of what the generated JSON
 * says. */
.wp-block-group:has(> .wp-block-group.has-accent-5-background-color:has(> .wp-block-image)) {
    align-items: stretch;
}

.wp-block-group.has-accent-5-background-color:has(> .wp-block-image) {
    align-items: stretch;
}

.wp-block-group.has-accent-5-background-color:has(> .wp-block-image) > .wp-block-image {
    align-self: stretch;
    width: 100%;
}

.wp-block-group.has-accent-5-background-color:has(> .wp-block-image) > .wp-block-image img {
    width: 100%;
}

.is-layout-constrained > .woocommerce {
    max-width: 100%;
}

.woocommerce ul.products,
.woocommerce-page ul.products {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
    gap: var(--wp--preset--spacing--30, 1.5rem);
    align-items: stretch;
    margin: 0;
    padding: 0;
}

.woocommerce ul.products::before,
.woocommerce ul.products::after,
.woocommerce-page ul.products::before,
.woocommerce-page ul.products::after {
    display: none;
    content: none;
}

.woocommerce ul.products li.product,
.woocommerce ul.products[class*='columns-'] li.product,
.woocommerce-page ul.products li.product,
.woocommerce-page ul.products[class*='columns-'] li.product {
    display: flex;
    flex-direction: column;
    width: auto;
    margin: 0;
    padding: var(--wp--preset--spacing--20, 1rem);
    float: none;
    background-color: var(--wp--preset--color--accent-5, transparent);
}

/* Specificity must tie woocommerce.css's own `.button { margin-top:1em }` (0,4,2) via the same
 * selector chain, else that rule silently wins over margin-top:auto. align-self:flex-start keeps
 * the button's original width instead of stretching it full-width. */
.woocommerce ul.products li.product > a.button,
.woocommerce-page ul.products li.product > a.button {
    margin-top: auto;
    align-self: flex-start;
}

/* Both the legacy loop button (add-to-cart.js appends the link after an AJAX add) and the blockified
 * product-button (its own template, toggled via the Interactivity API) print an `a.added_to_cart`
 * "View cart" link next to the button (NOVA-3171). The count now lives in the button itself
 * (functions.php + woo-cart-button.js), so the link is pure layout noise. WooCommerce ships two real
 * competing `display` rules for this class, gated by a runtime condition rather than just specificity:
 * `.woocommerce a.added_to_cart` (woocommerce.css, 0,2,1) fires only when `body` carries `.woocommerce`
 * — shop/category/product-archive/single-product pages, via `is_woocommerce()` — while `a.added_to_cart`
 * (woocommerce-blocktheme.css, 0,1,1) fires everywhere else, including an AI-authored page that embeds
 * `[products]`/`product-button` with no `.woocommerce` on body at all. No single selector beats the
 * (0,2,1) tier on its own pages, and there's no fixed ancestor around this button on every page type to
 * build a higher-specificity selector from (the parent theme isn't vendored here, and the block can
 * land inside arbitrary generated markup) — !important is the only rule that wins both conditions at
 * once. display:none also drops the link from the tab order and a11y tree. */
a.added_to_cart {
    /* biome-ignore lint/complexity/noImportantStyles: must beat .woocommerce a.added_to_cart{display:inline-block} on shop/product pages */
    display: none !important;
}

/* The blockified product-button wraps its `View cart` link in its own `<span hidden
 * data-wp-bind--hidden="!state.displayViewCart">`, toggled by the Interactivity API on a successful
 * add — separately from the `display:none` above on the `<a>` itself. Once that wrapper's `hidden`
 * attribute is removed, the (empty-looking but still-in-flow) span can still claim its own line box
 * next to the button, nudging the grid row taller on the very add that first makes displayViewCart
 * true (NOVA-3171). Collapsing the wrapper itself removes any doubt about that box independently of
 * whatever the hidden `<a>` inside it does. No vendor CSS targets this wrapper, so a plain display:none
 * already wins outright — this isn't a specificity fight, just locking the box down regardless of the
 * Interactivity API's runtime `hidden` toggle. */
span:has(> a.added_to_cart) {
    display: none;
}

.wc-block-grid__products {
    justify-content: center;
}

/* nova/media-split's two flex children ask for the ~50/50 share via selfStretch:"fill", but core's
 * block-supports/layout.php turns that into `flex-grow:1` alone — no flex-basis, no min-width. With
 * basis staying `auto`, the browser decides wrapping from each item's own max-content width: the
 * image contributes its source resolution (Unsplash serves w=1080) and the text column the width of
 * its longest paragraph on one unbroken line. Either already overflows any real viewport, so the
 * row wrapped unconditionally and the section rendered as one stacked column instead of two side by
 * side. Supply the missing basis: equal for both children (so grow splits the row ~50/50),
 * min()-capped so the row still stacks below ~710px of container width instead of squeezing.
 *
 * Matched on this marker className, not core's own generated wp-container-content-* class: that
 * class fires for ANY child layout attribute, selfStretch:"fixed" + an explicit flexSize included
 * (confirmed against wp-includes/block-supports/layout.php), so matching it broadly also clobbered
 * the explicit flex-basis of a legitimately fixed-width two-column row — e.g. a fetched Twenty
 * Twenty-Five pattern, which wpButtonWidthRules.ts's fixed-width ban does not cover (buttons only).
 * The className costs coverage of a hand-composed fill/fill row the model writes from scratch
 * without media-split's markup, but that idiom isn't something the content prompt ever asks a model
 * to invent, so the trade-off favors not breaking an unrelated fixed-width pattern. */
.nova-media-split-col {
    flex: 1 1 min(100%, 20rem);
    min-width: 0;
}

@media (max-width: 600px) {
    .wp-block-group.is-layout-flex:not(.is-nowrap):has(> .wp-block-group) {
        flex-direction: column;
        align-items: stretch;
    }

    .wp-block-group.is-layout-grid {
        grid-template-columns: 1fr;
    }
}

/* A `wp:navigation-submenu` group parent carries no `url` — a dropdown group is a container, not a
 * page — and core's navigation-submenu.php only emits `href` when one is set, so it renders as an
 * href-less `<a>`. Per the HTML spec that is not a hyperlink, so it never picks up the pointer
 * cursor: over the group's own label the browser falls back to the I-beam, as if the name were
 * selectable text, while the chevron right beside it does show a pointer (core styles
 * `.wp-block-navigation-submenu__toggle` directly). Hovering the label is what opens the dropdown
 * — `pointerenter` sits on the wrapping `<li>` — so the pointer matches the behaviour there.
 * `:not([href])` scopes this to group labels alone; real links already get the right cursor from
 * their own href. */
.wp-block-navigation-submenu > .wp-block-navigation-item__content:not([href]) {
    cursor: pointer;
}
