/* 12 — Cards and grids
   Owns: .rw-grid, the column counts, and the card variants
   Depends on: 01-tokens, 02-base

   Markup contract (read off the live page, not inferred):

     <div class="rw-grid rw-cols-4">
       <div class="rw-grid-item rw-card--image">
         <div class="rw-image"><a href="…"><img …></a></div>
         <div class="rw-card--content"><h3>…</h3><ul><li>…</li></ul></div>
       </div>
     </div>

     <div class="rw-grid rw-cols-3">
       <div class="rw-grid-item rw-card--text">
         <div class="rw-card--content"><h3>…</h3><p>…</p></div>
       </div>
     </div>

   .rw-grid-item carries the surface and shadow; .rw-card--image and
   .rw-card--text are variants on top of it. A third variant should reuse
   .rw-grid-item rather than restating the panel. */

.rw-grid {
    display: grid;
    gap: var(--grid-gap);
}

/* minmax(0, 1fr) rather than a bare 1fr: a grid track's default minimum is
   min-content, so a long unbroken string in one card would widen its column
   and pull the row out of alignment. */
.rw-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.rw-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.rw-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

@media (min-width: 768px) {                                                                                                                                                              
   .rw-grid.rw-cols-2.rw-cols-25-75 {                                                                                                                                                     
      grid-template-columns: 1fr 3fr;                                                                                                                                                      
   }                                                                                                                                                                                      
}

@media (max-width: 1100px) {
   .rw-50-50 .rw-cols-2,
   .rw-50-50 .rw-cols-3,
   .rw-50-50 .rw-cols-4 { grid-template-columns: minmax(0, 1fr); }
}

/* Four across is too tight below the desktop nav breakpoint; three still reads
   at tablet width, so only the 4-col grid folds here. */
@media (max-width: 991px) {
    .rw-cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 768px) {
   .rw-50-50 .rw-cols-2,
   .rw-50-50 .rw-cols-3,
   .rw-50-50 .rw-cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Two across already reads at tablet width, so it only folds at the phone
   breakpoint along with the others. */
@media (max-width: 767px) {
    .rw-cols-2,
    .rw-cols-3,
    .rw-cols-4 { grid-template-columns: minmax(0, 1fr); }
    
    .rw-50-50 .rw-cols-2,
    .rw-50-50 .rw-cols-3,
    .rw-50-50 .rw-cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }

}

@media (max-width: 480px) {
    
    .rw-50-50 .rw-cols-2,
    .rw-50-50 .rw-cols-3,
    .rw-50-50 .rw-cols-4 { grid-template-columns: repeat(1, minmax(0, 1fr)); }

}

/* ── The card panel ─────────────────────────────────────────
   Flex column so cards in a row match height and their content can grow
   without the panel collapsing around it. */
.rw-grid-item {
    display: flex;
    flex-direction: column;
    background-color: var(--surface);
    box-shadow: var(--card-shadow);
    border-radius: var(--rw-small-radius);
    /* Named properties rather than --transition-base, which is `all 0.2s`.
       `all` on a flex container that also holds a transitioning image means
       the browser watches every animatable property on the panel for a change
       it will never see, and it makes any future declaration here animate by
       accident. --header-transition is a bare duration+easing meant to be
       composed like this; 13-carousel.css already borrows it for the play
       button. The name has outgrown the header — it is the site's one
       micro-interaction timing — but a second token holding the same value
       would be worse than a name that reads oddly. */
    transition: transform  var(--header-transition),
                box-shadow var(--header-transition);
}

/* The content box fills the panel, and a trailing image is pinned to its
   bottom edge.

   THE PROBLEM. .rw-grid is a grid, so sibling cards are already stretched to
   equal heights — that part always worked. But .rw-card--content was a block
   box sized by its own content, so it stopped wherever the copy ran out. Two
   cards whose descriptions differ by one line put their images at different
   heights: measured 27px apart on the "Proudly Based in South Florida" row,
   where one card's paragraph wrapped to three lines and the other's to two.
   The images are the thing the eye lines up on, so the row read as broken
   even though every box was the right size.

   THE FIX, in two halves. `flex: 1 1 auto` makes the content box grow into
   the height the grid has already given the panel — without it there is no
   spare space for the second half to distribute. Then `margin-top: auto` on a
   trailing image absorbs all of it, pushing that element to the bottom.
   Copy still flows from the top, so the cards differ where they should (the
   text) and agree where it matters (the logo).

   :has() rather than a class typed into the markup, because the condition IS
   "this card happens to end with an image" — that is a fact about the content,
   not a decision someone should have to remember to declare. Supported
   everywhere current (Chrome 105+, Safari 15.4+, Firefox 121+), and it fails
   safe: a browser without it drops the selector as invalid and the image
   simply sits where it used to.

   `margin-bottom: 0` because the trailing <p> keeps the base paragraph margin
   otherwise, which would hold the image up off the padding edge by a line's
   worth of space and undo the alignment it was just given.

   Verified across all nine cards on the home page: every element keeps its
   existing position and height under this rule. The only thing that changes is
   the content box's own height, which has no background and is invisible. */
.rw-card--content {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    padding: var(--card-padding);
}

.rw-card--content > :last-child:has(img) {
    margin-top: auto;
    margin-bottom: 0;
}

/* Card titles and list items are links, and 02-base.css colours every anchor
   --brand. The h3 and li colours set below were being applied correctly and
   then overridden inside by the anchor — the card looked entirely brand blue
   despite the container colours being right. Inheriting keeps the colour
   decision with the card, so a title stays near-black and a list item stays
   accent, and it means a new card variant does not have to remember to
   re-state its link colours.

   :not(.btn) BECAUSE A BUTTON IS NOT A LINK THAT NEEDS THE CARD'S COLOUR — it
   is a component that already knows its own, and these two rules were quietly
   overruling it. `.rw-card--content a` is 0-2-1 against .btn-primary's 0-1-0,
   so the "Explore …" buttons on /services/ inherited the card's near-black
   --text instead of the white their own rule sets, giving black-on-red. The
   hover pair was worse: at 0-3-1 it beat .btn-primary:hover too, so pointing
   at a button turned its label dark red AND underlined it.

   This is the same principle 10-sections.css states for dark bands — a
   component that declares its own colours should win over the context it sits
   in. The exclusion is on .btn rather than on a card-specific class so it
   holds for any button dropped into any card, including ones not built yet. */
.rw-card--content a:not(.btn) {
    color: inherit;
    text-decoration: none;
}

.rw-card--content a:not(.btn):hover,
.rw-card--content a:not(.btn):focus-visible {
    color: var(--accent-dark);
    text-decoration: underline;
}

/* ── Image card ─────────────────────────────────────────────
   overflow: hidden so the photo cannot poke past the panel's corners. */
.rw-card--image { overflow: hidden; }

/* Fixed ratio so a row of cards keeps its photos aligned regardless of what
   is uploaded. 3:2 matches the current assets (800x534) — change the token
   here rather than re-cropping images. */
.rw-image {
    aspect-ratio: 3 / 2;
    overflow: hidden;
}

.rw-image a {
    display: block;
    height: 100%;
}

.rw-image img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* The global h3 is --brand-light; on this card the title is near-black so the
   accent list below it carries the colour. */
.rw-card--image .rw-card--content h3 {
    margin-bottom: var(--spacing-sm);
    color: var(--accent);
}

.rw-card--content ul {
    margin: 0;
    padding-left: 1.2em;
}

/* The bullet takes its colour from the list item, so setting `color` here
   handles the marker too — no ::marker rule and no faked pseudo-element. */
.rw-card--content li {
    color: var(--accent);
    margin-bottom: 0.35em;
}

.rw-card--content li:last-child { margin-bottom: 0; }

/* ── Text card ──────────────────────────────────────────────
   No image; an accent rule along the top edge instead. */
.rw-card--text {
    border-top: 3px solid var(--accent);
    text-align: center;
}

.rw-card--text .rw-card--content h3 {
    margin-bottom: var(--spacing-lg);
    color: var(--accent);
    font-size: 1.125rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.rw-card--text .rw-card--content p {
    margin-bottom: 0;
    color: var(--text-muted);
}


/* ── Micro-interactions ─────────────────────────────────────
   Two movements, on the PANEL and on the PHOTO, and both are driven from the
   panel so a card answers as one object: pointing at the title lifts the card
   AND pushes the photo, rather than the two halves reacting separately.

   ── Why the hover rules are inside @media (hover: hover) ─────
   Because on a touch screen they would not be micro-interactions, they would
   be bugs. A tap sets :hover and leaves it set until the next tap elsewhere,
   so a phone user taps a card, follows the link, comes back, and finds one
   card in the grid permanently raised and zoomed with no way to release it.
   The query is the standard way to say "only where a pointer can leave again".

   :focus-within is deliberately OUTSIDE that query, and gets the same
   treatment. These cards are not one link — .rw-card--image contains two, on
   the photo and on the title — so there is nothing to give a :focus style to
   that covers the card. :focus-within covers it whichever of them the keyboard
   lands on, and a keyboard user should see the same affordance a mouse user
   does whether or not the device also has a pointer.

   The a:focus-visible ring from 02-base.css still draws on top of this; the
   lift is additional, not a replacement, so focus is never signalled by motion
   alone.

   ── Why :has(a) ─────────────────────────────────────────────
   A card that rises under the pointer is promising it does something. Not
   every card is a link: all nine .rw-card--text cards on /video-production/
   contain no anchor at all — they are labelled panels of copy — and lifting
   those would offer a click that does not exist. :has(a) makes the movement a
   consequence of the card being interactive rather than of it being a card,
   so a panel that gains a link later starts responding on its own.

   The :focus-within rules need no such guard: there is nothing inside a
   linkless card that can take focus, so they can never fire on one. */
@media (hover: hover) {
    .rw-grid-item:has(a):hover {
        transform: translateY(calc(var(--card-lift) * -1));
        box-shadow: var(--card-shadow-hover);
    }
}

.rw-grid-item:focus-within {
    transform: translateY(calc(var(--card-lift) * -1));
    box-shadow: var(--card-shadow-hover);
}

/* The photo push. .rw-image already carries overflow: hidden for its
   aspect-ratio crop, so the scaled image is clipped by the frame it is
   already in — no extra containment, and no chance of it escaping the panel's
   corners.

   transform, not width/height: scale is composited, so this costs nothing per
   frame, and it grows from the centre, which keeps the subject of the photo
   where it was. Sizing the element instead would reflow the card on every
   frame of the transition.

   The transition lives on the img at rest so it eases BOTH ways. Declared only
   here rather than in the .rw-image img rule above, because that rule is about
   what the photo is; this is about what it does. */
.rw-card--image .rw-image img {
    transition: transform var(--header-transition);
}

@media (hover: hover) {
    .rw-card--image:has(a):hover .rw-image img {
        transform: scale(var(--card-image-zoom));
    }
}

.rw-card--image:focus-within .rw-image img {
    transform: scale(var(--card-image-zoom));
}

/* Reduced motion. The STATES are kept and only the tweening is dropped: a card
   still darkens its shadow under the pointer, it just arrives there rather
   than travelling. Removing the hover state entirely would take away an
   affordance from the people most likely to be navigating deliberately.

   The lift and the zoom do go, because both ARE motion — there is no version
   of "move 4px instantly" that is not the thing being opted out of. */
@media (prefers-reduced-motion: reduce) {
    .rw-grid-item,
    .rw-card--image .rw-image img {
        transition: none;
    }

    .rw-grid-item:has(a):hover,
    .rw-grid-item:focus-within,
    .rw-card--image:has(a):hover .rw-image img,
    .rw-card--image:focus-within .rw-image img {
        transform: none;
    }
}
