@import url("tokens.css");

/* ==========================================================================
   K-BIAN — Reset, base typography and core utilities

   Every colour below comes from tokens.css. There are no hex values in this
   file and there must never be.
   ========================================================================== */

/* ==========================================================================
   1. Fonts

   Self-hosted: the Content-Security-Policy has font-src 'self', and pulling
   from Google would send every visitor's IP to a third country before they
   consent, which the Kenya Data Protection Act 2019 and the GDPR both care
   about.

   Static per-weight latin-subset cuts, not variable fonts — only the exact
   weights the design uses (Montserrat 600/700, Work Sans 400/500/600), so
   there is nothing in the file the site never renders.
   ========================================================================== */

@font-face {
    font-family: 'Montserrat';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url("../fonts/montserrat-latin-600.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                   U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
                   U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
                   U+FFFD;
}

@font-face {
    font-family: 'Montserrat';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url("../fonts/montserrat-latin-700.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                   U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
                   U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
                   U+FFFD;
}

@font-face {
    font-family: 'Work Sans';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("../fonts/worksans-latin-400.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                   U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
                   U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
                   U+FFFD;
}

@font-face {
    font-family: 'Work Sans';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url("../fonts/worksans-latin-500.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                   U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
                   U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
                   U+FFFD;
}

@font-face {
    font-family: 'Work Sans';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url("../fonts/worksans-latin-600.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                   U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
                   U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
                   U+FFFD;
}

/* ==========================================================================
   2. Reset
   ========================================================================== */

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

* {
    margin: 0;
}

html {
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
    tab-size: 4;

    /* Reserves the vertical scrollbar's width unconditionally, whether or
       not a scrollbar is actually showing, so toggling `overflow: hidden`
       (the gallery lightbox's scroll lock — see gallery.css) never shifts
       page content sideways. */
    scrollbar-gutter: stable;
}

body {
    min-height: 100vh;
    min-height: 100svh;
}

/* Media are block-level and never overflow their container. Always pair with
   explicit width and height attributes in the markup to reserve the space and
   avoid layout shift. */
img,
picture,
video,
canvas,
svg,
iframe {
    display: block;
    max-width: 100%;
}

img,
video {
    height: auto;
}

/* Form controls do not inherit typography by default */
input,
button,
textarea,
select {
    font: inherit;
    color: inherit;
}

button {
    background: none;
    border: none;
}

textarea {
    resize: vertical;
}

/* Stop long URLs and unbroken strings tearing layouts open */
p,
h1,
h2,
h3,
h4,
h5,
h6,
li,
dd,
dt,
figcaption {
    overflow-wrap: break-word;
}

/* Let the browser own list semantics; strip them per-component instead */
:where(ul, ol)[role="list"] {
    list-style: none;
    padding-left: 0;
}

/* Anchored headings should not hide under the sticky header */
:target {
    scroll-margin-block-start: var(--kb-space-2xl);
}

/* ==========================================================================
   3. Base typography
   ========================================================================== */

body {
    font-family: var(--kb-font-body);
    font-size: var(--fs-body);
    font-weight: var(--kb-weight-regular);
    line-height: var(--lh-body);
    color: var(--kb-text);
    background-color: var(--kb-bg);
}

/* Headings share family and colour; size, weight, line-height and tracking
   are set per level below because the content type scale pairs a specific
   weight with each step (hero/h2 bold, h3 semibold) rather than one shared
   weight for all of them — see the Design language section in CLAUDE.md. */
h1,
h2,
h3,
h4 {
    font-family: var(--kb-font-heading);
    font-weight: var(--kb-weight-semibold);
    color: var(--kb-text-heading);
    text-wrap: balance;
}

/* The hero H1 must read as a statement, not a label: at least 3x body size. */
h1 {
    font-size: var(--fs-hero);
    font-weight: var(--kb-weight-bold);
    line-height: var(--lh-hero);
    letter-spacing: var(--kb-tracking-tight);
}

/* A section H2 must be at least 2x body size. */
h2 {
    font-size: var(--fs-h2);
    font-weight: var(--kb-weight-bold);
    line-height: var(--lh-h2);
    letter-spacing: var(--kb-tracking-h2);
}

h3 {
    font-size: var(--fs-h3);
    line-height: var(--lh-h3);
}

h4 {
    font-size: var(--fs-h3);
    line-height: var(--lh-h3);
}

/* h5 and h6 are rare; styled as small-caps labels rather than tiny headings */
h5,
h6 {
    font-family: var(--kb-font-body);
    font-size: var(--fs-small);
    font-weight: var(--kb-weight-semibold);
    line-height: var(--kb-leading-normal);
    letter-spacing: var(--kb-tracking-caps);
    text-transform: uppercase;
    color: var(--kb-text-muted);
}

p {
    text-wrap: pretty;
}

/* Vertical rhythm between flowing content */
:where(p, ul, ol, dl, blockquote, figure, table, pre) + :where(p, ul, ol, dl, blockquote, figure, table, pre) {
    margin-block-start: var(--kb-space-md);
}

:where(p, ul, ol, blockquote, figure) + :where(h2, h3, h4) {
    margin-block-start: var(--kb-space-xl);
}

:where(h1, h2, h3, h4, h5, h6) + * {
    margin-block-start: var(--kb-space-sm);
}

a {
    color: var(--kb-link);
    text-decoration: underline;
    text-decoration-thickness: 0.08em;
    text-underline-offset: 0.18em;
    transition: color var(--kb-duration-fast) var(--kb-ease-out);
}

a:hover {
    color: var(--kb-link-hover);
    text-decoration-thickness: 0.14em;
}

strong,
b {
    font-weight: var(--kb-weight-bold);
}

small {
    font-size: var(--fs-small);
}

blockquote {
    border-inline-start: var(--kb-border-thick) solid var(--kb-rule);
    padding-inline-start: var(--kb-space-md);
    font-size: var(--fs-lead);
    color: var(--kb-text-heading);
}

hr {
    border: none;
    border-block-start: var(--kb-border-thin) solid var(--kb-border-subtle);
    margin-block: var(--kb-space-xl);
}

code,
kbd,
samp,
pre {
    font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
    font-size: 0.9em;
}

::selection {
    background-color: var(--kb-accent-soft);
    color: var(--kb-ink);
}

/* ==========================================================================
   4. Layout
   ========================================================================== */

.container {
    width: 100%;
    max-width: var(--kb-container);
    margin-inline: auto;
    padding-inline: var(--kb-gutter);
}

.container--narrow {
    --kb-container: var(--kb-container-sm);
}

/* A 68ch prose column must never sit alone in a wide container — either the
   container narrows to fit it, or the remaining space holds something else
   (a sidebar, an image). This is the narrowing half of that rule: genuinely
   text-only sections use this instead of .container, so the measure fills
   its container and there is no dead space to the right. See CLAUDE.md. */
.container--reading {
    --kb-container: var(--kb-container-reading);
}

.container--prose {
    --kb-container: var(--kb-container-xs);
}

.container--medium {
    --kb-container: var(--kb-container-md);
}

.container--wide {
    --kb-container: var(--kb-container-xl);
}

/* Cap prose line length without constraining its container */
.prose {
    max-width: var(--kb-measure);
}

/* ==========================================================================
   5. Buttons

   One base class controls every dimension — padding, height, radius,
   font-size, weight, letter-spacing. Variants below change colour ONLY,
   never size, so a row of mixed variants (Get Involved, the newsletter form)
   always lines up on one baseline. min-height 44px meets the WCAG 2.5.5
   target size guidance. The transparent border on every variant keeps them
   visible in Windows High Contrast Mode, where background colours are
   discarded.
   ========================================================================== */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--kb-space-2xs);
    min-height: 2.75rem;
    padding: var(--kb-space-xs) var(--kb-space-md);
    border: var(--kb-border-medium) solid transparent;
    border-radius: var(--kb-radius-md);
    font-family: var(--kb-font-body);
    font-size: var(--fs-small);
    font-weight: var(--kb-weight-semibold);
    line-height: var(--kb-leading-normal);
    letter-spacing: var(--kb-tracking-wide);
    text-align: center;
    text-decoration: none;
    cursor: pointer;
    transition:
        background-color var(--kb-duration-base) var(--kb-ease-out),
        border-color var(--kb-duration-base) var(--kb-ease-out),
        color var(--kb-duration-base) var(--kb-ease-out);
}

.btn:hover {
    text-decoration: none;
}

.btn[aria-disabled="true"],
.btn:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

/* Primary — brand green, white text (5.8:1) */
.btn--primary {
    background-color: var(--kb-primary);
    border-color: var(--kb-primary);
    color: var(--kb-text-on-brand);
}

.btn--primary:hover {
    background-color: var(--kb-primary-hover);
    border-color: var(--kb-primary-hover);
    color: var(--kb-text-on-brand);
}

/* Donate — gold, white text (5.7:1).
   Gold is reserved for donate actions. Do not use this class for anything
   else, and do not introduce another gold button. */
.btn--donate {
    background-color: var(--kb-donate);
    border-color: var(--kb-donate);
    color: var(--kb-text-on-brand);
}

.btn--donate:hover {
    background-color: var(--kb-donate-hover);
    border-color: var(--kb-donate-hover);
    color: var(--kb-text-on-brand);
}

/* Outline — for secondary actions beside a primary or donate button.
   The border is a non-text boundary so it keeps the sampled brand green; the
   label is text, so it takes the text-safe green and stays legible when this
   sits on a --kb-green-100 band. */
.btn--outline {
    background-color: transparent;
    border-color: var(--kb-primary);
    color: var(--kb-primary-text);
}

.btn--outline:hover {
    background-color: var(--kb-bg-band);
    border-color: var(--kb-primary-hover);
    color: var(--kb-primary-hover);
}

/* Outline, light — the same role sitting on a dark section or a photographic
   hero, where the green border/text pairing above would not be legible. */
.btn--outline-light {
    border-color: var(--kb-text-on-deep);
    color: var(--kb-text-on-deep);
}

.btn--outline-light:hover {
    background-color: var(--kb-text-on-deep);
    color: var(--kb-green-900);
}

/* ==========================================================================
   6. Utilities
   ========================================================================== */

/* Hidden from sight, still announced by screen readers.
   The :focus exclusions mean a skip-to-content link written with this class
   reveals itself when tabbed to, with no extra CSS. */
.visually-hidden:not(:focus):not(:focus-within) {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* Skip-to-content link. Pair with class="skip-link visually-hidden". */
.skip-link:focus {
    position: fixed;
    inset-block-start: var(--kb-space-sm);
    inset-inline-start: var(--kb-space-sm);
    z-index: var(--kb-z-skip);
    padding: var(--kb-space-xs) var(--kb-space-md);
    background-color: var(--kb-surface);
    color: var(--kb-link-hover);
    border-radius: var(--kb-radius-md);
    box-shadow: var(--kb-shadow-lg);
    text-decoration: none;
}

/* Honeypot container.
   Config\Honeypot renders <div id="hpc">, without the framework's stock
   inline style="display:none" — style-src 'self' in our CSP would drop that
   inline style and expose the trap field to real visitors. Deleting this rule
   puts a stray "Fill This Field" input on every form on the site. */
#hpc {
    display: none;
}

/* ==========================================================================
   7. Focus

   A visible focus indicator is not optional (WCAG 2.4.7). Never set
   outline: none without putting an equally visible replacement in its place.
   ========================================================================== */

/* Deliberately no border-radius here: an outline already follows the
   element's own corner radius, and setting one would reshape rounded
   elements at the moment they receive focus. */
:focus-visible {
    outline: var(--kb-focus-width) solid var(--kb-focus);
    outline-offset: var(--kb-focus-offset);
}

/* Suppress the ring for pointer users only, in browsers that support
   :focus-visible — the rule above still fires for keyboard users. */
:focus:not(:focus-visible) {
    outline: none;
}

/* On dark sections the teal ring loses contrast against the deep green, so
   flip it to cream. */
.on-dark :focus-visible {
    outline-color: var(--kb-text-on-deep);
}

/* ==========================================================================
   8. Motion

   Smooth scrolling is opt-in for people who have not asked for less motion,
   and the reduce block below is the site-wide backstop: any animation or
   transition added anywhere, in any file, is switched off by it. Nothing on
   this site may depend on motion to convey meaning.
   ========================================================================== */

@media (prefers-reduced-motion: no-preference) {
    html {
        scroll-behavior: smooth;
    }
}

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-delay: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        transition-delay: 0s !important;
        scroll-behavior: auto !important;
    }

    html {
        scroll-behavior: auto !important;
    }
}

/* Scroll-triggered fade-and-rise, driven by assets/js/animate.js.
   Scoped under html.has-animations, which only that script ever adds — so
   with no JS, reduced motion, or no IntersectionObserver, [data-animate]
   sections are simply visible from the start rather than stuck at opacity
   0. See the comment in animate.js. */
.has-animations [data-animate] {
    opacity: 0;
    transform: translateY(20px);
    transition: opacity 500ms var(--kb-ease-out), transform 500ms var(--kb-ease-out);
}

.has-animations [data-animate].is-visible {
    opacity: 1;
    transform: translateY(0);
}
