/* ==========================================================================
   BNSCA design tokens — one place every page takes its colours, type and
   spacing from.

   Why this file exists
   --------------------
   The palette and the two typefaces come from the club's earlier GitHub
   Pages portal, which is the reference for *identity* only: Cinzel headings,
   Montserrat body, and the navy/gold/white scheme. Nothing else was taken
   from it — not its markup, not its layout, not its data, and none of the
   Google-hosted services it depended on.

   Before this, four stylesheets each declared their own `:root` with slightly
   different navies and golds, and a colour changed in one place drifted from
   the other three. Every one of those variable names still works exactly as
   it did; they are now *aliases* pointing here, so 2,200 lines of existing
   rules render in the reference palette without being rewritten. That is the
   whole trick — the identity changes, the layout does not.

   Loaded before every other stylesheet, on every page, public and private.
   ========================================================================== */

:root {
  /* ---------- Reference palette, named exactly as the club states it -------
     These eleven values are the identity. Nothing below invents a colour that
     is not derived from one of them, and any new rule should reach for one of
     these rather than typing a hex code. */
  --c-navy:          #0A192F;   /* primary navy — page background */
  --c-navy-deep:     #001F3F;   /* deep navy — panels, header */
  --c-navy-card:     #002852;   /* card navy — raised surfaces */
  --c-gold:          #D4AF37;   /* primary gold — headings, borders, buttons */
  --c-gold-light:    #FCF6BA;   /* light gold — highlight, gradient top */
  --c-white:         #FFFFFF;
  --c-ink-dark:      #1E293B;   /* body text on a light surface */
  --c-ink-dark-mute: #475569;   /* secondary text on a light surface */
  --c-ink-light-mute:#CBD5E1;   /* secondary text on a dark surface */
  --c-success:       #22C55E;
  --c-error:         #EF4444;
  --c-warning:       #D97706;

  /* ---------- Derived shades ----------------------------------------------
     Two tones the palette does not name but every interface needs: a gold dark
     enough to sit as a 1px border without shouting, and one step darker again
     for a pressed state. Both are the primary gold with its lightness pulled
     down, so they stay the same hue. */
  --c-gold-deep:     #B08D2A;
  --c-gold-deeper:   #8C6F21;

  /* A translucent gold for hairlines and dividers. Sitting on whatever navy is
     behind it means one value works on all three surface tones. */
  --c-gold-veil:     rgba(212, 175, 55, 0.28);
  --c-gold-veil-soft:rgba(212, 175, 55, 0.14);

  /* ---------- Banner headings --------------------------------------------
     The four stops of the metallic gradient poured through the big page
     titles — the home hero, SOCIAL ACTIVITIES, NOTICE BOARD, CONTACT US, and
     every section and panel title that shares the effect.

     They are variables because the gradient used to end at #b6892f and #8f6a22,
     which are dark browns. On a tall hero with a bright photograph behind it
     that reads as engraved metal, which is the intent; on a short compact
     banner over dark navy, most of a capital letter's height falls in those
     last two stops and the whole title reads as black. That is what made
     CONTACT US unreadable.

     The range is now bright the whole way down: cream at the top to a gold
     that is still clearly gold at the bottom, with nothing below it. The
     deepest stop is #D9B24C rather than #8F6A22 — a lift of about 55% in
     luminance — so the letters keep their top-lit shading without any part of
     them going dark. */
  --banner-heading-top:  #FFF7E0;
  --banner-heading-high: #FFE29A;
  --banner-heading-mid:  #F8D878;
  /* The darkest the gradient is ever allowed to get. #D9B24C was tried first
     and still read as bronze on the compact banners: the sweep animation slides
     a 300%-wide gradient across the glyphs, so for part of every cycle the
     deepest stop is what covers most of a capital letter. Whatever the phase,
     the title now sits between #EFCB6A and cream — never below the gold the
     club asked for. */
  --banner-heading-deep: #EFCB6A;

  /* The solid colour the big page titles are painted in.

     The titles used to be transparent glyphs with a metallic gradient clipped
     through them and a highlight sweeping across. On the tall home hero that
     reads as engraved metal. On the short banners — CONTACT US, NOTICE BOARD,
     SOCIAL ACTIVITIES — it did not: measured on the rendered page, the swept
     gradient left the letters reading as dark bronze against dark navy, and
     replacing the same element's fill with this one colour made it legible
     immediately. So the banner title is a solid gold with a shadow under it,
     and the gradient is kept for the section and panel titles further down the
     page, where it renders as intended. */
  --banner-heading-color: #F8D878;

  /* How much room the two fixed side tabs — Join Our Community on the left,
     Donate Us on the right — need beside the content so they never sit over
     it. Zero on a wide screen, where the 1200px column already leaves more
     than enough margin. */
  --fab-room: 0px;

  /* ---------- Typography --------------------------------------------------
     Cinzel is a titling face: beautiful in a heading, unreadable in a
     paragraph, and it has no italic. It is therefore restricted to headings
     and the brand mark. Everything a visitor actually reads or types — body
     copy, labels, form fields, buttons, tables — is Montserrat.

     The fallbacks matter more than usual here. `font-display: swap` means the
     first paint uses them, so they are chosen to be close in width: Georgia
     for Cinzel (both are serif and wide), the system UI stack for Montserrat.
     A badly matched fallback is what makes a page visibly jump when the real
     face arrives. */
  --font-heading: 'Cinzel', Georgia, 'Times New Roman', serif;
  --font-body: 'Montserrat', 'Segoe UI', system-ui, -apple-system, Roboto, Arial, sans-serif;
  --font-mono: 'Consolas', 'SF Mono', 'Courier New', monospace;

  /* Heading weights, from the reference: 700 for most, 900 for the page title
     and anything meant to carry the page. */
  --weight-heading: 700;
  --weight-heading-strong: 900;

  /* ---------- Responsive type scale ---------------------------------------
     Every size is a clamp(), so there is no breakpoint at which text jumps.
     The minimum is what a 320px phone gets, the maximum what a 1920px desktop
     gets, and the middle term scales with the viewport in between.

     The 0.5rem floor inside each middle term is what keeps this accessible: a
     clamp whose middle term is *only* vw units ignores the reader's browser
     font-size setting entirely, so someone who has set text to "large" gets
     nothing. Mixing rem into the middle term keeps their preference in play.

     Body settles at 17px rather than the 19px this site used, because 19px
     Montserrat sets noticeably larger than 19px Segoe UI — the measured
     x-height is about 6% taller — and paragraphs were running wider than the
     comfortable line length at desktop widths. */
  --fs-hero:    clamp(1.75rem, 0.9rem + 3.6vw, 3.25rem);
  --fs-h1:      clamp(1.55rem, 0.95rem + 2.6vw, 2.6rem);
  --fs-h2:      clamp(1.3rem,  0.95rem + 1.5vw, 1.95rem);
  --fs-h3:      clamp(1.12rem, 0.95rem + 0.75vw, 1.4rem);
  --fs-h4:      clamp(1rem,    0.92rem + 0.4vw,  1.15rem);
  --fs-body:    clamp(0.95rem, 0.9rem + 0.25vw,  1.0625rem);
  --fs-small:   clamp(0.8rem,  0.77rem + 0.16vw, 0.875rem);
  --fs-tiny:    clamp(0.72rem, 0.7rem + 0.1vw,   0.78rem);

  --lh-heading: 1.22;
  --lh-body: 1.7;

  /* The comfortable measure for running text: roughly 70 characters at the
     body size above. Applied to paragraphs rather than to containers, so a
     grid of cards is not squeezed by it. */
  --measure: 68ch;

  /* ---------- Layout ------------------------------------------------------ */
  --container: 1200px;
  --container-wide: 1360px;

  /* The gutter is a clamp for one specific reason: 20px on a 320px screen
     leaves 280px of content, which is too little for two member cards side by
     side, and 20px on a 1920px screen is not a gutter at all. */
  --gutter: clamp(0.875rem, 0.6rem + 1.2vw, 2rem);

  --radius: 12px;
  --radius-sm: 8px;
  --radius-lg: 18px;
  --radius-pill: 999px;

  /* Three depths, all built on themed inks.

     --shadow was already themed (style.css rebuilds it from --surface-shadow);
     these two were not, so a modal and the emoji panel were casting
     rgba(0,0,0,0.5) onto a #F5F7FB page. Black at half strength under a white
     card does not read as elevation, it reads as dirt. The light theme's inks
     are navy-tinted and much weaker, which is what a shadow on paper looks
     like.

     The dark values below are exactly what they were. */
  --surface-shadow-soft:   rgba(0, 0, 0, 0.30);
  --surface-shadow-strong: rgba(0, 0, 0, 0.50);

  --shadow: 0 4px 18px rgba(0, 0, 0, 0.45);
  --shadow-sm: 0 2px 8px var(--surface-shadow-soft);
  --shadow-lg: 0 18px 44px var(--surface-shadow-strong);

  /* ---------- Motion ------------------------------------------------------
     One duration and one easing, so transitions across the site feel like one
     product. Both collapse to zero under prefers-reduced-motion, at the bottom
     of this file. */
  --t-fast: 120ms;
  --t: 200ms;
  --t-slow: 380ms;
  --ease: cubic-bezier(0.4, 0, 0.2, 1);

  /* ---------- Touch -------------------------------------------------------
     44px is the smallest target the WCAG 2.2 pointer-target rule accepts, and
     it is the figure every control on this site is sized against. */
  --tap: 44px;

  /* ---------- Surfaces, as a theme ---------------------------------------
     The four values below are what the light theme swaps. Everything on the
     site is built out of the aliases in style.css — `--navy` for the page,
     `--panel` for a card, `--ink` for text — so redefining them is the whole
     of the theme change; not one layout rule knows which one is in force.

     In the dark theme they are exactly what they always were. */
  --surface-page:  var(--c-navy);
  --surface-panel: var(--c-navy-deep);
  --surface-card:  var(--c-navy-card);
  --surface-text:  var(--c-white);
  --surface-text-muted: var(--c-ink-light-mute);
  --surface-line:  var(--c-gold-deep);
  --surface-input: #0d2846;
  --surface-veil:  rgba(255, 255, 255, 0.08);
  --surface-shadow: rgba(0, 0, 0, 0.45);
  /* A tint of the page colour, for anything that needs to sit *slightly* off
     the background — a hovered row, a striped table. */
  --surface-tint:  rgba(255, 255, 255, 0.06);

  /* ---------- Animation inks ---------------------------------------------
     The colours the decorative animations paint in. They are tokens, and not
     literals inside the animation stylesheets, for one reason: an effect
     designed against navy is not merely "too faint" on a light page, it is
     invisible. White stars measured 1.07:1 against the light theme's page —
     there is no opacity at which that becomes a design.

     So each effect gets two deliberate palettes rather than one palette and a
     compromise. These are the dark ones, unchanged: exactly the values that
     were written inline before.

     Star field. Soft white for the small distant ones, warm white for the
     middle layer, the faintest gold for the few large ones. */
  --stars-ink-1: rgba(255, 255, 255, 0.72);
  --stars-ink-2: rgba(255, 250, 240, 0.85);
  --stars-ink-3: rgba(252, 246, 186, 0.90);

  /* ShineBorder's travelling gradient, and the Limelight lamp over the nav.

     Every value here is the literal the effect used before it was tokenised —
     #FCF6BA is --c-gold-light, #3E6FB8 was --shine-blue, 0.35 and 0.28 were the
     limelight's glow and cone. Restated rather than re-derived so the dark
     theme cannot drift when the light one is tuned; that is the whole point of
     splitting them, and it is easy to lose by writing a "close enough" value
     here instead of the real one. */
  --shine-ink-gold: #D4AF37;
  --shine-ink-gold-soft: #FCF6BA;
  --shine-ink-blue: #3E6FB8;
  --limelight-ink: #D4AF37;
  --limelight-glow: rgba(212, 175, 55, 0.35);
  --limelight-cone: rgba(212, 175, 55, 0.28);
}

/* ==========================================================================
   LIGHT THEME

   Switched on by `data-theme="light"` on <html>, written by theme.js before
   the first paint. Only surfaces and text change; the gold, the type, the
   spacing and every layout rule are shared — this is the same site with the
   lights on, not a second design.

   The golds are deepened rather than kept as they are. #D4AF37 on white is
   about 2.2:1, which is not readable text — the deeper #8C6F21 clears 4.5:1
   while still reading as the club's gold. The navy becomes the text colour,
   which is the same pairing the identity already uses, simply inverted.
   ========================================================================== */

:root[data-theme="light"] {
  --surface-page:  #F5F7FB;
  --surface-panel: #FFFFFF;
  --surface-card:  #FFFFFF;
  --surface-text:  var(--c-ink-dark);
  --surface-text-muted: var(--c-ink-dark-mute);
  --surface-line:  #D8DEE9;
  --surface-input: #FFFFFF;
  --surface-veil:  rgba(10, 25, 47, 0.06);
  --surface-shadow: rgba(15, 30, 55, 0.14);
  --surface-tint:  rgba(10, 25, 47, 0.04);

  /* Navy-tinted and weak. A shadow on a bright surface is a hint of depth, not
     a dark band — the strong one is still only a quarter the opacity of its
     dark-theme counterpart, spread over the same distance. */
  --surface-shadow-soft:   rgba(15, 30, 55, 0.10);
  --surface-shadow-strong: rgba(15, 30, 55, 0.22);

  /* Gold, dark enough to read as text and as a border on white. The bright
     --c-gold is kept for fills — a gold button with navy text on it — where it
     is a background rather than a foreground.

     #9A7A1E measured 4.06:1 on white and 3.78:1 on the page, so it failed AA
     on both — and because it is the token it failed everywhere at once: the
     Join Us button, the review stars, member initials, the skip link.

     The replacement had to clear 4.5:1 on every light surface the site
     actually paints, not just on white. Measured against all four —
     #FFFFFF, the #F5F7FB page, the #EEF2F9 tint the notice board uses, and the
     #FFFDF7 card — #8A6D18 still failed on the tint at 4.37:1. #846714 is the
     first value with real margin everywhere (worst case 4.76:1) and is still
     unmistakably the club's gold rather than a brown. */
  --c-gold: #846714;
  --c-gold-light: #7A5F13;
  --c-gold-deep: #8C6F21;
  --c-gold-veil: rgba(132, 103, 20, 0.32);
  --c-gold-veil-soft: rgba(132, 103, 20, 0.15);

  /* ---------- Animation inks, light ---------------------------------------
     Not the dark palette at a different opacity — different colours.

     Stars become deep navy and muted royal blue: ink on paper rather than
     light in a sky. The largest and rarest keep a gold cast so the field still
     reads as this club's, not as grey dust. The opacities are deliberately low
     — the eye should catch movement in the background, never particles
     competing with a paragraph.

     Measured against the #F5F7FB page: the 1px layer lands near 1.6:1, which
     is present but quiet; the 3px layer near 2.4:1, noticeable but soft. */
  --stars-ink-1: rgba(23, 49, 84, 0.34);
  --stars-ink-2: rgba(31, 68, 118, 0.42);
  --stars-ink-3: rgba(132, 103, 20, 0.40);

  /* ShineBorder in light: navy into royal blue into gold. On white, gold alone
     is nearly the page colour at the pale end of its sweep, so the navy is what
     actually draws the edge and the gold is the warmth in it. */
  --shine-ink-gold: #846714;
  --shine-ink-gold-soft: rgba(132, 103, 20, 0.45);
  --shine-ink-blue: rgba(23, 49, 84, 0.70);

  /* The Limelight lamp is a DARK accent here, by request: a bright lamp on a
     bright bar is nothing at all. Deep navy at the source, fading through a
     soft royal blue cone. */
  --limelight-ink: #0F2748;
  --limelight-glow: rgba(15, 39, 72, 0.45);
  --limelight-cone: rgba(27, 74, 122, 0.18);

  /* ---------- Status colours, light ---------------------------------------
     The three semantic colours are tuned for a dark page: on white they
     measured 3.76:1 (error), 2.28:1 (success) and 3.19:1 (warning) — all under
     AA, and all used as *text* as well as as badge fills. "Sunday: Closed" and
     every rejected/pending/paid chip was in that group.

     Deepened only as far as readability needs, so a red still reads as a red
     and a green as a green — the meaning is the point of these, and a set of
     muddy near-blacks would be a worse answer than the contrast problem.
     Measured worst case across white, the page and the tint: 5.01, 5.30, 5.35.

     Where they are used as a fill behind white text, deepening only helps. */
  --c-error:   #C62828;
  --c-success: #137333;
  --c-warning: #A14A08;

  /* Tells the browser this page is light, so form controls, scrollbars and
     autofill are painted to match rather than inheriting a dark scheme. */
  color-scheme: light;
}

:root[data-theme="dark"] { color-scheme: dark; }

/* ==========================================================================
   DARK ISLANDS

   A few surfaces are dark in *both* themes because that is what they are for:
   the gallery lightbox is a viewer, and a viewer is dark so the photograph is
   the only bright thing in it.

   That creates a trap the light theme walks straight into. The light palette
   deepens the gold so it can be read on white — and then that deepened gold is
   painted onto a surface that is still navy, where it measured 3.10:1 on the
   lightbox counter. The theme did its job; the surface simply is not the one
   the theme assumed.

   So these islands take the dark palette back. Scoped to the surfaces rather
   than fixed per element, because the next control added inside the lightbox
   should inherit the right answer without anybody remembering this.

   The scope is `.gshow-stage`, NOT `.gshow`. The lightbox root also contains
   the caption, and that caption sits on a white panel — scoping to the root
   handed it the dark palette and put pale grey text on white, which is the
   same bug pointing the other way. Only the stage is actually dark.

   It costs nothing in dark mode: the values are already these.
   ========================================================================== */
:root[data-theme="light"] .gshow-stage,
:root[data-theme="light"] .hero-overlay {
  --c-gold: #D4AF37;
  --c-gold-light: #FCF6BA;
  --c-gold-deep: #B08D2A;
  --c-gold-veil: rgba(212, 175, 55, 0.28);
  --gold: #D4AF37;
  --gold-dark: #B08D2A;
  --ink: #FFFFFF;
  --muted: #CBD5E1;
  --surface-text: #FFFFFF;
  --surface-text-muted: #CBD5E1;
}

/* Until theme.js runs, `data-theme` is absent. That only happens with
   JavaScript off, and the site is dark by default — so nothing is needed here
   beyond the dark values already in :root. */

/* ==========================================================================
   Base — the handful of rules that must be true on every page before any
   page-specific stylesheet has an opinion.
   ========================================================================== */

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

html {
  /* Anchored navigation — a notice deep link, a policy table-of-contents jump,
     an FAQ permalink — otherwise lands with the target under the sticky
     header. This is the whole fix, and it applies to keyboard focus too. */
  scroll-padding-top: 96px;
  -webkit-text-size-adjust: 100%;
}

body {
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  /* Belt and braces against horizontal scroll. Individual overflowing children
     are still fixed at source — this only stops one escaping into a
     page-wide sideways scrollbar on a phone while it is being found. */
  overflow-x: hidden;
}

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-heading);
  font-weight: var(--weight-heading);
  line-height: var(--lh-heading);
  /* A long unbroken word — a pasted URL, a compound Bengali transliteration —
     used to push the whole heading past the viewport edge on a 320px screen.
     `balance` is progressive: browsers that have it even out the ragged edge
     of a two-line heading, and the rest ignore it. */
  overflow-wrap: break-word;
  text-wrap: balance;
}

/* Running text gets the same protection, and `pretty` keeps a single word from
   being left alone on the last line. */
p, li, dd, blockquote { overflow-wrap: break-word; text-wrap: pretty; }

/* Anything that can carry an address or an unspaced identifier breaks
   anywhere rather than overflowing — a reference id, a UPI string, an email in
   a table cell. */
code, kbd, samp, pre, .u-break { overflow-wrap: anywhere; word-break: break-word; }

img, svg, video, canvas { max-width: 100%; }
img, video { height: auto; }

/* Form controls do not inherit the page font by default on any browser. */
input, select, textarea, button { font-family: inherit; font-size: inherit; }

/* A grid or flex child refuses to shrink below its content's intrinsic width
   unless told otherwise, which is the single most common cause of a layout
   overflowing on a phone. */
.u-min0 { min-width: 0; }

/* ---------- Focus --------------------------------------------------------
   One visible ring, on every focusable thing, in gold so it reads on navy and
   in navy-on-gold where the surface is light. :focus-visible rather than
   :focus, so a mouse click does not leave a ring behind but a Tab key does. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 3px solid var(--c-gold);
  outline-offset: 2px;
  border-radius: 3px;
}

/* ---------- Reduced motion ------------------------------------------------
   Honoured as a single rule rather than per-animation, so a marquee, a hero
   cross-fade or a hover lift added later is covered without anyone
   remembering to. The durations are collapsed rather than set to `none`:
   `animation: none` can leave an element at its start frame, mid-slide, while
   a 1ms duration runs it to its final state immediately. */
@media (prefers-reduced-motion: reduce) {
  :root { --t-fast: 0ms; --t: 0ms; --t-slow: 0ms; }
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    scroll-behavior: auto !important;
  }
}
