/* ==========================================================================
   GigNGo brand layer — the canonical tokens and primitives.

   This is the source of truth for the look. It is deliberately PORTABLE and
   PURELY ADDITIVE: dropping it into any page defines tokens and `gng-*`
   classes but restyles nothing on its own. Nothing here re-points a legacy
   variable, so it cannot shift the colours of a page that hasn't opted in.

   To adopt on a page:
     1. <link rel="stylesheet" href="/css/brand.css">
     2. Paste the marks snippet (see BRANDING.md) if you use .gng-wavy
     3. Apply `gng-*` classes, or map the page's own classes onto these rules

   The rules the look is built on — read BRANDING.md before changing any of
   these numbers:
     · Colour comes from the logo only: slate, green, white, and tints of them
     · Every corner is rounded, no two by the same amount (mirrors the logo)
     · Edges are the same weight but never perfectly straight
     · Display face is Rubik; body is DM Sans
     · No outlines and no hard shadows — shape does the separating
   ========================================================================== */

:root {
    /* --- Colour. Straight off the logo; only tints are derived. ---------- */
    --gng-ink: #2E3A41;          /* logo slate */
    --gng-brand: #46963B;        /* logo green */
    --gng-ink-2: #5A6A72;        /* slate, lightened for body copy */
    --gng-brand-deep: #3A7F30;   /* pressed / text-on-wash */
    --gng-brand-bright: #5BA649; /* gradient partner */
    --gng-paper: #FFFFFF;        /* the logo's own field */
    --gng-paper-2: #F1F5F0;      /* white with a breath of the green in it */
    --gng-wash: #E4EFE1;         /* green at ~12%, for chips and hovers */
    /* A card must contrast with the surface under it. On a tinted section a
       card is white; on a WHITE section it takes this tone instead, otherwise
       it's white-on-white and only a shadow separates the two. */
    --gng-surface-on-paper: #EBF2E9;
    --gng-hairline: #E2E8E1;

    /* --- Corners. The logo's trick: rounded everywhere, uneven amounts.
           Never introduce a square corner; never make all four match. ----- */
    --gng-r-slab: 76px 46px 72px 50px / 50px 72px 46px 76px;
    --gng-r-card: 42px 26px 40px 29px / 29px 40px 26px 42px;
    --gng-r-card-alt: 29px 42px 27px 40px / 40px 27px 42px 29px;
    --gng-r-soft: 28px 18px 26px 20px / 20px 26px 18px 28px;
    --gng-r-blob: 58% 42% 46% 54% / 48% 54% 46% 52%;

    /* --- Shape cycle -----------------------------------------------------
       The rule isn't only "no two corners alike" — it's "no two SHAPES alike".
       A grid where every card is the same hand-drawn outline reads as one
       shape stamped out repeatedly, which is the thing the look is arguing
       against. These are the same family (nothing square, nothing uniform,
       everything in the 26–44px band) but no card is a copy of its neighbour.
       Apply with .gng-vary on the grid; see the cycle rules further down. */
    --gng-r-card-1: 42px 26px 40px 29px / 29px 40px 26px 42px;
    --gng-r-card-2: 30px 44px 27px 38px / 41px 28px 43px 30px;
    --gng-r-card-3: 38px 29px 44px 26px / 27px 42px 30px 40px;
    --gng-r-card-4: 44px 28px 32px 41px / 30px 39px 43px 27px;
    --gng-r-card-5: 27px 41px 43px 30px / 44px 29px 26px 38px;
    --gng-r-card-6: 40px 43px 29px 27px / 38px 26px 41px 44px;

    /* Small chips and nav hit-areas — same idea at a smaller scale. */
    --gng-r-chip-1: 10px 6px 9px 7px / 7px 9px 6px 10px;
    --gng-r-chip-2: 7px 10px 6px 9px / 9px 6px 10px 7px;
    --gng-r-chip-3: 9px 7px 10px 6px / 6px 10px 7px 9px;
    --gng-r-chip-4: 6px 9px 7px 10px / 10px 7px 9px 6px;

    --gng-r-soft-1: 28px 18px 26px 20px / 20px 26px 18px 28px;
    --gng-r-soft-2: 20px 29px 17px 26px / 27px 19px 28px 21px;
    --gng-r-soft-3: 26px 21px 29px 17px / 18px 28px 20px 27px;
    --gng-r-soft-4: 29px 19px 22px 27px / 21px 26px 29px 18px;

    --gng-r-blob-1: 58% 42% 46% 54% / 48% 54% 46% 52%;
    --gng-r-blob-2: 46% 54% 58% 42% / 54% 46% 52% 48%;
    --gng-r-blob-3: 52% 48% 42% 58% / 46% 52% 54% 46%;
    --gng-r-blob-4: 44% 56% 51% 49% / 53% 47% 49% 51%;
    --gng-r-pill: 999px;
    /* Irregular pills. The browser clamps radii that overflow the box, but it
       scales them PROPORTIONALLY — so oversized values with ~20% variation
       between corners survive the clamp as a pill whose ends aren't quite
       matched. Works at any button height without per-element numbers. */
    --gng-r-pill-1: 999px 820px 940px 870px;
    --gng-r-pill-2: 850px 999px 880px 960px;
    --gng-r-pill-3: 960px 890px 999px 830px;
    --gng-r-pill-4: 880px 950px 860px 999px;

    /* --- Elevation. Soft and low-contrast; never a hard offset. --------- */
    --gng-shadow: 0 10px 26px rgba(34, 48, 46, .09);
    --gng-shadow-hover: 0 20px 42px rgba(34, 48, 46, .15);

    /* --- Type ------------------------------------------------------------ */
    --gng-display: Rubik, system-ui, sans-serif;
    --gng-body: 'DM Sans', system-ui, sans-serif;

    --gng-ease: cubic-bezier(.2, .8, .3, 1);
}

/* ==========================================================================
   Type
   ========================================================================== */

.gng-display,
.gng-h1, .gng-h2, .gng-h3 {
    font-family: var(--gng-display);
    letter-spacing: -.022em;
}

/* Rubik carries a true 900, so weight comes from the cut, not from tracking. */
.gng-h1 { font-size: clamp(2.6rem, 5.2vw, 5.1rem); line-height: 1.14; font-weight: 900; letter-spacing: -.03em; }
.gng-h2 { font-size: clamp(1.8rem, 3.2vw, 2.7rem); line-height: 1.08; font-weight: 800; }
.gng-h3 { font-size: clamp(1.1rem, 1.6vw, 1.35rem); font-weight: 700; }
.gng-lede { font-size: 1.1rem; color: var(--gng-ink-2); line-height: 1.65; }

/* ==========================================================================
   Hand-drawn marks

   Constant stroke weight, never a straight line. The green swipe under an
   emphasised word, and a short drawn rule for under a section heading.
   ========================================================================== */

.gng-mark {
    background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='60' height='12' viewBox='0 0 60 12'%3E%3Cpath d='M0 7 q 7.5 -5 15 0 t 15 0 t 15 0 t 15 0' fill='none' stroke='%2346963B' stroke-width='4' stroke-linecap='round'/%3E%3C/svg%3E") repeat-x 0 100%;
    background-size: 60px 12px;
    padding-bottom: 12px;
    color: var(--gng-brand);
}

/* On dark or photographic ground the swipe goes white to stay legible. */
.gng-mark-light {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='60' height='12' viewBox='0 0 60 12'%3E%3Cpath d='M0 7 q 7.5 -5 15 0 t 15 0 t 15 0 t 15 0' fill='none' stroke='%23FFFFFF' stroke-width='4.5' stroke-linecap='round'/%3E%3C/svg%3E");
    color: inherit;
}

.gng-rule {
    display: block;
    width: 132px;
    height: 14px;
    margin: 18px auto 0;
    opacity: .45;
    background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='132' height='14' viewBox='0 0 132 14'%3E%3Cpath d='M3 10 C 24 3, 40 12, 62 7 S 100 3, 129 8' fill='none' stroke='%2346963B' stroke-width='3.2' stroke-linecap='round'/%3E%3C/svg%3E") no-repeat;
}

/* ==========================================================================
   Surfaces
   ========================================================================== */

.gng-card {
    background: var(--gng-paper);
    border: none;
    border-radius: var(--gng-r-card);
    box-shadow: var(--gng-shadow);
    transition: transform .28s var(--gng-ease), box-shadow .28s ease;
}

.gng-card-alt { border-radius: var(--gng-r-card-alt); }
.gng-slab { border-radius: var(--gng-r-slab); }

/* The half-degree of human error. Hovering straightens it up. */
.gng-tilt-a { transform: rotate(-.5deg); }
.gng-tilt-b { transform: rotate(.38deg); }
.gng-tilt-c { transform: rotate(-.24deg); }

.gng-card:hover {
    transform: rotate(0deg) translateY(-6px);
    box-shadow: var(--gng-shadow-hover);
}

/* --- Slightly wavy edges -------------------------------------------------
   The fill moves onto a ::before layer and only that layer is displaced, so
   the silhouette drifts a couple of pixels off true while the type on top
   stays pin sharp. The shadow rides inside the filter chain so it follows
   the wavy shape instead of sitting under a rectangle.

   Requires the marks snippet (the #edgeWobble filter) in the page. Do NOT
   use this on a card whose photo runs to the edge — displacing a photo's own
   frame reads as a printing fault rather than a drawn line.
   ------------------------------------------------------------------------ */

.gng-wavy {
    background: transparent;
    box-shadow: none;
    position: relative;
    isolation: isolate;
}

.gng-wavy::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    background: var(--gng-paper);
    border-radius: inherit;
    filter: url(#edgeWobble) drop-shadow(0 8px 16px rgba(46, 58, 65, .10));
}

/* Shorter elements get less displacement so the filter region can contain it. */
.gng-wavy-sm::before {
    filter: url(#edgeWobbleSoft) drop-shadow(0 6px 12px rgba(46, 58, 65, .09));
}

.gng-wavy:hover::before {
    filter: url(#edgeWobble) drop-shadow(0 16px 26px rgba(46, 58, 65, .16));
}

.gng-wavy-green::before {
    background: linear-gradient(135deg, var(--gng-brand-bright), var(--gng-brand));
}


/* ==========================================================================
   Shape cycle — put .gng-vary on a grid and its children stop being copies.
   ========================================================================== */

.gng-vary > *:nth-child(6n+1) { border-radius: var(--gng-r-card-1); }
.gng-vary > *:nth-child(6n+2) { border-radius: var(--gng-r-card-2); }
.gng-vary > *:nth-child(6n+3) { border-radius: var(--gng-r-card-3); }
.gng-vary > *:nth-child(6n+4) { border-radius: var(--gng-r-card-4); }
.gng-vary > *:nth-child(6n+5) { border-radius: var(--gng-r-card-5); }
.gng-vary > *:nth-child(6n) { border-radius: var(--gng-r-card-6); }

.gng-vary-soft > *:nth-child(4n+1) { border-radius: var(--gng-r-soft-1); }
.gng-vary-soft > *:nth-child(4n+2) { border-radius: var(--gng-r-soft-2); }
.gng-vary-soft > *:nth-child(4n+3) { border-radius: var(--gng-r-soft-3); }
.gng-vary-soft > *:nth-child(4n) { border-radius: var(--gng-r-soft-4); }

.gng-vary-blob > *:nth-child(4n+1) .gng-blob { border-radius: var(--gng-r-blob-1); }
.gng-vary-blob > *:nth-child(4n+2) .gng-blob { border-radius: var(--gng-r-blob-2); }
.gng-vary-blob > *:nth-child(4n+3) .gng-blob { border-radius: var(--gng-r-blob-3); }
.gng-vary-blob > *:nth-child(4n) .gng-blob { border-radius: var(--gng-r-blob-4); }

/* ==========================================================================
   Controls
   ========================================================================== */

.gng-btn {
    display: inline-block;
    font-family: var(--gng-display);
    font-weight: 700;
    font-size: 1.05rem;
    line-height: 1;
    padding: 17px 34px;
    border: none;
    border-radius: var(--gng-r-pill);
    background: var(--gng-brand);
    color: #fff;
    text-decoration: none;
    cursor: pointer;
    transition: background .2s ease, transform .2s var(--gng-ease);
}

.gng-btn:hover { background: var(--gng-brand-bright); transform: translateY(-1px); }

.gng-btn-ghost {
    background: var(--gng-paper);
    color: var(--gng-ink);
    box-shadow: var(--gng-shadow);
    font-weight: 600;
}

.gng-btn-ghost:hover { background: var(--gng-wash); color: var(--gng-brand-deep); }

.gng-chip {
    display: inline-block;
    padding: 6px 14px;
    border-radius: var(--gng-r-pill);
    background: var(--gng-wash);
    color: var(--gng-brand-deep);
    font-family: var(--gng-display);
    font-weight: 700;
    font-size: .8rem;
}

/* Icon chip: a green blob with the glyph knocked out in white, the way the
   logo knocks its check out of the green. */
.gng-blob {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 58px;
    height: 58px;
    border-radius: var(--gng-r-blob);
    background: var(--gng-brand);
    color: #fff;
    font-size: 1.4rem;
}

/* ==========================================================================
   Section flow

   Sections don't butt up against each other — one flows into the next on a
   long curve. Put .gng-flow on the LOWER section and fill it with the tone of
   the section ABOVE it. `top:-1px` because at 0 a subpixel seam of the
   section underneath shows as a hairline where the tones differ most.
   ========================================================================== */

.gng-flow { position: relative; padding-top: 132px; }

.gng-flow::before {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    top: -1px;
    height: 93px;
    z-index: 0;
    pointer-events: none;
    background-repeat: no-repeat;
    background-size: 100% 100%;
}

/* Coming down out of a white section. */
.gng-flow-paper::before {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1440' height='92' viewBox='0 0 1440 92' preserveAspectRatio='none'%3E%3Cpath d='M0,0 L1440,0 L1440,40 C1210,86 1030,14 790,42 C566,68 356,18 0,58 Z' fill='%23FFFFFF'/%3E%3C/svg%3E");
}

/* Coming down out of a tinted section. */
.gng-flow-tint::before {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1440' height='92' viewBox='0 0 1440 92' preserveAspectRatio='none'%3E%3Cpath d='M0,0 L1440,0 L1440,44 C1180,10 1010,80 770,50 C548,22 348,74 0,38 Z' fill='%23F1F5F0'/%3E%3C/svg%3E");
}

/* Content must sit above the curve. */
.gng-flow > * { position: relative; z-index: 1; }

/* ==========================================================================
   Promise ribbon

   Slate band, white words, green stars — the logo's own arrangement. The
   wrapper is the clipper: without it the over-wide band widens the whole
   document and shoves every section sideways on phones.
   ========================================================================== */

/* The clipper is a STRAIGHT rectangle but the band inside it is rotated, so
   the band's two ends rise and fall outside that rectangle and get sliced —
   which chops the tops and bottoms off the words at each end.

   The padding gives the rotated ends somewhere to go (overflow clips at the
   padding box, so padding enlarges the visible area). It has to cover the
   worst case, which grows with viewport width:

     excursion at each end = (viewport x 1.10) / 2 x sin(1.2deg)
     1440px -> 17px   1920px -> 22px   2560px -> 30px   3456px -> 40px

   48px covers past 4000px wide. The negative margins on the page pull the
   extra padding back out so the layout doesn't move. */
/* z-index has to beat whatever this band overlaps. The band leans, so its
   lower end hangs into the section below; if that section is positioned with
   a higher z-index and an opaque background it paints over the ribbon and the
   low end reads as cut off. */
.gng-ribbon-wrap {
    position: relative;
    z-index: 20;
    overflow: hidden;
    padding: 48px 0;
}

.gng-ribbon {
    width: 110%;
    margin-left: -5%;
    padding: 24px 0;
    background: var(--gng-ink);
    color: #fff;
    transform: rotate(-1.2deg);   /* never scaleX() — it stretches the type */
    overflow: hidden;
    font-family: var(--gng-display);
    font-weight: 800;
    font-size: 1.3rem;
    line-height: 1.45;
    letter-spacing: -.01em;
    white-space: nowrap;
    -webkit-user-select: none;
    user-select: none;
}

/* The track must be an EVEN number of identical sequences (so -50% lands on
   the second half) AND one half must be at least as wide as the band, or the
   end of each cycle shows empty band. Spacing lives on the sequence, including
   a trailing gap, so the seam spaces exactly like everything else. */
.gng-ribbon-track {
    display: flex;
    align-items: center;
    width: max-content;
    animation: gngRibbon 100s linear infinite;
}

.gng-ribbon-seq {
    display: flex;
    align-items: center;
    gap: 26px;
    padding-right: 26px;
    flex: 0 0 auto;
}

.gng-ribbon-track > * { flex: 0 0 auto; }
.gng-ribbon-star { color: var(--gng-brand); font-size: 1.1rem; }

@keyframes gngRibbon {
    from { transform: translateX(0); }
    to { transform: translateX(-50%); }
}

/* ==========================================================================
   Motion
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
    .gng-card,
    .gng-btn,
    .gng-ribbon-track { transition: none; animation: none; }
}

@media (max-width: 768px) {
    /* Tilt reads as a mistake at phone widths where cards stack full-bleed. */
    .gng-tilt-a, .gng-tilt-b, .gng-tilt-c { transform: none; }
    .gng-flow { padding-top: 96px; }
    .gng-flow::before { height: 56px; }
    .gng-ribbon { font-size: 1.02rem; padding: 15px 0; }
    .gng-blob { width: 50px; height: 50px; font-size: 1.2rem; }
}

/* The shared header is position:fixed and reserves no space, so a page that
   previously had a static or sticky header needs this in its place or the
   content slides up underneath. 12px + 64px + 12px = 88px.
   Pages with a full-bleed hero designed to sit UNDER the header (the landing
   page) don't use it. */
.gng-hdr-spacer { height: 88px; }

/* ==========================================================================
   Shared header + mobile menu

   The header is injected by shared-header.js AFTER the stylesheets, so these
   need `body` in front to out-specify it rather than !important. It lives here
   rather than in a page's own CSS because the header is shared — any page that
   links brand.css should get the branded chrome for free.
   ========================================================================== */

/* The outer bar is transparent and pointer-events:none, but its blur smears
   whatever sits behind the top of the page. The frosted look that matters is
   on the pill itself, which keeps its own blur. */
body .site-hdr { backdrop-filter: none; -webkit-backdrop-filter: none; }

body .site-hdr-inner {
    background: rgba(255, 255, 255, .95);
    border-radius: var(--gng-r-pill-1);
}
body .site-hdr.site-scrolled .site-hdr-inner { background: rgba(255, 255, 255, .98); }

body .site-hdr-post { border-radius: var(--gng-r-pill-2); font-weight: 700; }
body .site-hdr-login { border-radius: var(--gng-r-pill-4); }

body .site-hdr-menu,
body .site-hdr-dropdown {
    background: var(--gng-paper);
    border-radius: var(--gng-r-soft);
}

body .site-hdr-nav a:nth-child(odd),
body .site-hdr-nav li:nth-child(odd) > .site-hdr-menu-btn { border-radius: var(--gng-r-chip-1); }
body .site-hdr-nav a:nth-child(even),
body .site-hdr-nav li:nth-child(even) > .site-hdr-menu-btn { border-radius: var(--gng-r-chip-3); }
body .site-hdr-nav li:nth-child(3n) > a { border-radius: var(--gng-r-chip-2); }

body .site-mob-cta-post { border-radius: var(--gng-r-pill-2); }
body .site-mob-cta-login { border-radius: var(--gng-r-pill-4); }
body .site-mob-panel { border-radius: var(--gng-r-slab) 0 0 var(--gng-r-slab); }
