/* --- tb-fonts --- */
/*
 * Totoong Balita — the two typefaces, served from this domain.
 *
 * Generated from the Google Fonts stylesheet and then deduplicated:
 * the files Google returns for weights 400/500/600/700 are byte for
 * byte identical, because these are variable fonts and the weight is
 * an axis inside one file rather than four separate cuts. Sixteen
 * @font-face rules therefore point at six files.
 *
 * latin-ext is kept, and it is not optional. The peso sign, U+20B1,
 * is in latin-ext and in no other subset we load - drop it and every
 * headline with a price in it renders the currency symbol in a
 * fallback face while the digits beside it stay in Newsreader.
 *
 * Dropped: cyrillic, cyrillic-ext, greek, greek-ext and vietnamese.
 * Nothing this newsroom publishes needs them, and unicode-range means
 * a browser only ever fetches the subsets a page actually uses.
 */

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('https://totoongbalita.com/wp-content/themes/totoong-balita/assets/fonts/inter-normal-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('https://totoongbalita.com/wp-content/themes/totoong-balita/assets/fonts/inter-normal-latin.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: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('https://totoongbalita.com/wp-content/themes/totoong-balita/assets/fonts/inter-normal-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('https://totoongbalita.com/wp-content/themes/totoong-balita/assets/fonts/inter-normal-latin.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: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('https://totoongbalita.com/wp-content/themes/totoong-balita/assets/fonts/inter-normal-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('https://totoongbalita.com/wp-content/themes/totoong-balita/assets/fonts/inter-normal-latin.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: 'Inter';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('https://totoongbalita.com/wp-content/themes/totoong-balita/assets/fonts/inter-normal-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('https://totoongbalita.com/wp-content/themes/totoong-balita/assets/fonts/inter-normal-latin.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: 'Newsreader';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('https://totoongbalita.com/wp-content/themes/totoong-balita/assets/fonts/newsreader-italic-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Newsreader';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('https://totoongbalita.com/wp-content/themes/totoong-balita/assets/fonts/newsreader-italic-latin.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: 'Newsreader';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('https://totoongbalita.com/wp-content/themes/totoong-balita/assets/fonts/newsreader-normal-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Newsreader';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('https://totoongbalita.com/wp-content/themes/totoong-balita/assets/fonts/newsreader-normal-latin.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: 'Newsreader';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('https://totoongbalita.com/wp-content/themes/totoong-balita/assets/fonts/newsreader-normal-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Newsreader';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('https://totoongbalita.com/wp-content/themes/totoong-balita/assets/fonts/newsreader-normal-latin.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: 'Newsreader';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('https://totoongbalita.com/wp-content/themes/totoong-balita/assets/fonts/newsreader-normal-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Newsreader';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('https://totoongbalita.com/wp-content/themes/totoong-balita/assets/fonts/newsreader-normal-latin.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;
}

/* ---------------------------------------------------------------
 * Metric-matched fallbacks
 *
 * Until a webfont arrives the browser draws the next family in the
 * stack, and when it swaps the text is re-measured. If the two faces
 * are different widths the line breaks move, and everything below the
 * paragraph moves with them — which is what a reader sees as the page
 * jumping, and what Lighthouse reported as CLS 0.173 on desktop.
 *
 * Measured on the live page rather than guessed:
 *
 *   Inter      vs -apple-system   17.8% narrower
 *   Inter      vs Arial            5.7% narrower
 *   Newsreader vs Times New Roman  4.3% narrower
 *
 * -apple-system was the first fallback in the body stack, so every
 * paragraph on a Mac was being laid out 18% too narrow and then
 * reflowed. size-adjust scales the substitute so it occupies the same
 * width as the real face, and the swap becomes invisible.
 *
 * local() only, so nothing is downloaded. Where these faces do not
 * exist — most Android devices — the rule simply fails to match and
 * the stack falls through to the plain families below, exactly as it
 * did before.
 * ------------------------------------------------------------- */

@font-face {
  font-family: 'Inter Fallback';
  font-weight: 400;
  src: local('Arial'), local('Helvetica Neue'), local('Liberation Sans');
  size-adjust: 105.7%;
  ascent-override: 90%;
  descent-override: 22%;
  line-gap-override: 0%;
}

/* Inter widens with weight as well, just less than Newsreader: the
   single rule above left 600 at -1.8% and 700 at -2.8%. Small numbers,
   but they apply to every button and eyebrow on the page. */
@font-face {
  font-family: 'Inter Fallback';
  font-weight: 500 600;
  src: local('Arial'), local('Helvetica Neue'), local('Liberation Sans');
  size-adjust: 107.6%;
  ascent-override: 90%;
  descent-override: 22%;
  line-gap-override: 0%;
}

@font-face {
  font-family: 'Inter Fallback';
  font-weight: 700;
  src: local('Arial'), local('Helvetica Neue'), local('Liberation Sans');
  size-adjust: 108.7%;
  ascent-override: 90%;
  descent-override: 22%;
  line-gap-override: 0%;
}

/* One rule per weight, because a single substitute cannot follow a
 * variable font.
 *
 * Newsreader widens as it gets heavier — 732px, 771px, 791px for the
 * same headline at 400, 600 and 700. Times New Roman, loaded through
 * local(), is one static face: it measured 738px at every weight, so a
 * single size-adjust matched 400 nicely (+0.8%) and left 700 at -6.8%,
 * which is worse than the plain fallback it replaced. Headlines are
 * mostly 700, so that is the weight that matters.
 *
 * Each size-adjust below is the measured ratio for its own weight. */

@font-face {
  font-family: 'Newsreader Fallback';
  font-weight: 400;
  src: local('Times New Roman'), local('Liberation Serif'), local('Times');
  size-adjust: 103.5%;
  ascent-override: 92%;
  descent-override: 24%;
  line-gap-override: 0%;
}

@font-face {
  font-family: 'Newsreader Fallback';
  font-weight: 600;
  src: local('Times New Roman'), local('Liberation Serif'), local('Times');
  size-adjust: 109%;
  ascent-override: 92%;
  descent-override: 24%;
  line-gap-override: 0%;
}

@font-face {
  font-family: 'Newsreader Fallback';
  font-weight: 700;
  src: local('Times New Roman'), local('Liberation Serif'), local('Times');
  size-adjust: 111.8%;
  ascent-override: 92%;
  descent-override: 24%;
  line-gap-override: 0%;
}

@font-face {
  font-family: 'Newsreader Fallback';
  font-style: italic;
  font-weight: 400;
  src: local('Times New Roman Italic'), local('Liberation Serif Italic'), local('Times Italic');
  /* Italic needed its own number and in the other direction: at 103.5%
     it came out 8.3% too wide. The masthead's "News Online" is set in
     it, so this one is visible on every page. */
  size-adjust: 95.6%;
  ascent-override: 92%;
  descent-override: 24%;
  line-gap-override: 0%;
}

/* --- tb-tokens --- */
/**
 * Totoong Balita — Design Tokens
 * "Editorial Clarity" design system. Single source of truth for every
 * color, type scale, space step and structural rule on the site.
 *
 * Nothing in this file styles an element. It only declares values.
 * Component stylesheets consume these variables and never hardcode a hex,
 * a px font-size, or a rem margin.
 *
 * COLOR — SETTLED 2026-09-25.
 * DESIGN.md states its palette twice and the two disagree. We use the prose
 * sections' values, not the YAML frontmatter's Material ramp, because:
 *   1. every component spec in DESIGN.md cites the prose values by hex
 *      ("red vertical bar #c91d24", "hairline #e2e5e9", "solid #111418");
 *   2. the frontmatter's outline-variant (#e5bdb9) is pink — a generator
 *      artifact from seeding the neutral ramp off the red, and at odds with
 *      the document's own call for crisp neutral hairlines;
 *   3. the frontmatter's surface (#f8f9ff) is blue-tinted, where the prose
 *      asks for "#ffffff as the pure paper default".
 * The frontmatter ramp is kept at the bottom as --tb-m3-* for reference only.
 * Reviewed against both renderings and confirmed; don't silently reopen this.
 */

:root {
	/* ---------------------------------------------------------------
	 * BRAND
	 * ------------------------------------------------------------- */
	--tb-primary:              #c91d24;  /* Editorial Red — breaking, category pips, section rules */
	--tb-primary-dark:         #a20013;  /* Pressed / hover state for red surfaces */
	--tb-on-primary:           #ffffff;
	--tb-primary-tint:         #fdeced;  /* Faint red wash — alert row backgrounds */

	--tb-secondary:            #0f3b82;  /* Deep Broadcast Navy — civic, institutional, links */
	--tb-secondary-dark:       #0a2a5e;
	--tb-on-secondary:         #ffffff;
	--tb-secondary-tint:       #eaf0fa;

	--tb-tertiary:             #1b74e4;  /* Electric Cobalt — interactive states, active tabs */
	--tb-tertiary-dark:        #145ab4;
	--tb-on-tertiary:          #ffffff;

	/* ---------------------------------------------------------------
	 * INK — text colors, densest to lightest
	 * ------------------------------------------------------------- */
	--tb-ink:                  #111418;  /* Deep Newsroom Charcoal — headlines, body */
	--tb-ink-strong:           #000000;
	--tb-ink-muted:            #464c54;  /* Decks, excerpts, secondary copy */
	--tb-ink-subtle:           #616770;  /* Timestamps, bylines, captions */
	--tb-ink-faint:            #8b9199;  /* Disabled, placeholder */
	--tb-ink-inverse:          #ffffff;  /* Text on dark/red bands */
	--tb-ink-inverse-muted:    rgba(255, 255, 255, 0.72);

	/* ---------------------------------------------------------------
	 * SURFACE — backgrounds, densest to lightest
	 * ------------------------------------------------------------- */
	--tb-surface:              #ffffff;  /* Pure paper — the default canvas */
	--tb-surface-sunken:       #f8f9fa;  /* Secondary story wells, utility trays, tables */
	--tb-surface-muted:        #f1f3f5;  /* Hover fills, input backgrounds */
	--tb-surface-inverse:      #111418;  /* Dark header bands, footer */
	--tb-surface-inverse-alt:  #1c2026;  /* Raised block inside a dark band */

	/* ---------------------------------------------------------------
	 * STRUCTURE — the hairline grid. No shadows anywhere.
	 * ------------------------------------------------------------- */
	--tb-border:               #e2e5e9;  /* Soft boundary — the default rule */
	--tb-border-strong:        #cbd0d6;  /* Category dividers, input perimeters */
	--tb-border-inverse:       rgba(255, 255, 255, 0.16);
	--tb-rule:                 1px solid var(--tb-border);
	--tb-rule-strong:          1px solid var(--tb-border-strong);
	--tb-rule-inverse:         1px solid var(--tb-border-inverse);

	/* Status */
	--tb-error:                #ba1a1a;
	--tb-error-tint:           #ffdad6;
	--tb-success:              #0f7b3f;
	--tb-success-tint:         #e3f5ea;
	--tb-warning:              #a35a00;
	--tb-warning-tint:         #fdf0e0;

	/* ---------------------------------------------------------------
	 * CATEGORY ACCENTS
	 * Per DESIGN.md: red for news, navy for business, neutral for
	 * lifestyle/culture. Sections pick one via the --tb-accent hook.
	 * ------------------------------------------------------------- */
	--tb-accent:               var(--tb-primary);
	--tb-accent-news:          #c91d24;
	--tb-accent-politics:      #0f3b82;
	--tb-accent-business:      #0f3b82;
	--tb-accent-infra:         #1b74e4;
	--tb-accent-environment:   #0f7b3f;
	--tb-accent-sports:        #a35a00;
	--tb-accent-lifestyle:     #464c54;
	--tb-accent-culture:       #464c54;

	/* ---------------------------------------------------------------
	 * TYPEFACES
	 * Newsreader = narrative authority. Inter = speed and UI utility.
	 * ------------------------------------------------------------- */
	/* The Fallback families are metric-matched stand-ins defined in
	   tb-fonts.css. They sit second so the text is the right width
	   before the webfont lands, which is what stops the page jumping
	   when it does. The plain families stay behind them for platforms
	   where the local() source does not exist. */
	--tb-font-headline: "Newsreader", "Newsreader Fallback", "Times New Roman", Georgia, serif;
	--tb-font-body: "Inter", "Inter Fallback", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
	--tb-font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;

	/* ---------------------------------------------------------------
	 * TYPE SCALE
	 * Each role ships size / line-height / weight / tracking as four
	 * variables so a component can apply a role in one place. The
	 * `tb-t-*` utility classes in tb-base.css bundle them.
	 * ------------------------------------------------------------- */

	/* display-hero — the single lead story on a front page */
	--tb-display-hero-size: 3rem;          /* 48px */
	--tb-display-hero-line: 3.375rem;      /* 54px */
	--tb-display-hero-weight: 700;
	--tb-display-hero-track: -0.02em;

	/* headline-lg — section leads, single-post titles */
	--tb-headline-lg-size: 2rem;           /* 32px */
	--tb-headline-lg-line: 2.375rem;       /* 38px */
	--tb-headline-lg-weight: 700;
	--tb-headline-lg-track: -0.015em;

	/* headline-md — secondary features */
	--tb-headline-md-size: 1.375rem;       /* 22px */
	--tb-headline-md-line: 1.75rem;        /* 28px */
	--tb-headline-md-weight: 600;
	--tb-headline-md-track: -0.005em;

	/* headline-sm — grid cards, compact rows */
	--tb-headline-sm-size: 1.125rem;       /* 18px */
	--tb-headline-sm-line: 1.5rem;         /* 24px */
	--tb-headline-sm-weight: 600;
	--tb-headline-sm-track: 0;

	/* subhead — decks under a hero headline */
	--tb-subhead-size: 1rem;               /* 16px */
	--tb-subhead-line: 1.5rem;             /* 24px */
	--tb-subhead-weight: 500;

	/* body-lg — article running text */
	--tb-body-lg-size: 1.125rem;           /* 18px */
	--tb-body-lg-line: 1.875rem;           /* 30px */
	--tb-body-lg-weight: 400;

	/* body-md — card excerpts, default UI copy */
	--tb-body-md-size: 0.9375rem;          /* 15px */
	--tb-body-md-line: 1.4375rem;          /* 23px */
	--tb-body-md-weight: 400;

	/* body-sm — dense listings, secondary excerpts */
	--tb-body-sm-size: 0.8125rem;          /* 13px */
	--tb-body-sm-line: 1.1875rem;          /* 19px */
	--tb-body-sm-weight: 400;

	/* label-category — uppercase section and category markers */
	--tb-label-size: 0.6875rem;            /* 11px */
	--tb-label-line: 0.875rem;             /* 14px */
	--tb-label-weight: 700;
	--tb-label-track: 0.08em;

	/* timestamp — relative times, bylines */
	--tb-timestamp-size: 0.75rem;          /* 12px */
	--tb-timestamp-line: 1rem;             /* 16px */
	--tb-timestamp-weight: 400;

	/* caption — photo credits, fine print */
	--tb-caption-size: 0.75rem;            /* 12px */
	--tb-caption-line: 1.0625rem;          /* 17px */
	--tb-caption-weight: 400;

	/* ---------------------------------------------------------------
	 * SPACING — 4px base rhythm
	 * ------------------------------------------------------------- */
	--tb-space-xxs: 0.25rem;   /*  4px */
	--tb-space-xs:  0.5rem;    /*  8px */
	--tb-space-sm:  0.75rem;   /* 12px */
	--tb-space-md:  1rem;      /* 16px */
	--tb-space-lg:  1.5rem;    /* 24px */
	--tb-space-xl:  2rem;      /* 32px */
	--tb-space-2xl: 3rem;      /* 48px */
	--tb-space-3xl: 4.5rem;    /* 72px */

	/* ---------------------------------------------------------------
	 * LAYOUT
	 * ------------------------------------------------------------- */
	--tb-container-max: 1280px;
	--tb-gutter: 1rem;              /* mobile page margin; bumped at >=1024px */
	--tb-grid-gap: 1rem;            /* card-to-card gap; bumped at >=1024px */
	--tb-section-gap: 3rem;         /* vertical space between homepage sections */
	--tb-measure: 68ch;             /* max line length for article body copy */

	/* ---------------------------------------------------------------
	 * SHAPE — strict sharp geometry. Circles are the only exception,
	 * reserved for live pips and author avatars.
	 * ------------------------------------------------------------- */
	--tb-radius: 0;
	--tb-radius-pill: 9999px;
	--tb-radius-circle: 50%;

	/* Media aspect ratios */
	--tb-ratio-hero: 16 / 9;
	--tb-ratio-card: 16 / 10;
	--tb-ratio-square: 1 / 1;
	--tb-ratio-portrait: 3 / 4;

	/* ---------------------------------------------------------------
	 * MOTION — restrained. Images scale, headlines recolor, nothing lifts.
	 * ------------------------------------------------------------- */
	--tb-ease: cubic-bezier(0.2, 0, 0.2, 1);
	--tb-duration-fast: 150ms;
	--tb-duration: 300ms;
	--tb-duration-slow: 500ms;
	--tb-media-hover-scale: 1.04;
	--tb-media-hover-scale-lead: 1.02;

	/* Overlay gradient for text sitting on photography (WCAG AAA) */
	--tb-media-scrim: linear-gradient(180deg, rgba(0, 0, 0, 0) 40%, rgba(17, 20, 24, 0.85) 100%);

	/* Focus ring — visible, squared, no outer glow.
	 * currentColor, not a fixed hue: the ring takes the colour of whatever it
	 * surrounds, so it is ink on paper and light on the dark footer and drawer
	 * without a single per-component override. It was --tb-tertiary (Electric
	 * Cobalt) — the one blue on an otherwise red-and-ink site, which read as a
	 * stray browser artifact rather than as part of the design.
	 *
	 * 1px at 1px offset, not 2px at 2px. Tabbed through BBC, NPR, Rappler,
	 * Inquirer, Philstar and GMA: every one of them that draws a ring draws it
	 * at 1px, and the four running the browser default sit at 1px offset 1px.
	 * A 2px ring held 2px off the element reads as a detached box floating
	 * around things — that, not the colour alone, was what looked wrong.
	 * This traces the element instead of boxing it.
	 * Do not replace this with `none`: see tb-base.css for why. */
	--tb-focus-color: currentColor;
	--tb-focus-ring: 1px solid currentColor;
	--tb-focus-offset: 1px;

	/* Stacking order */
	--tb-z-base: 1;
	--tb-z-sticky: 100;
	--tb-z-header: 200;
	--tb-z-drawer: 300;
	--tb-z-modal: 400;
	--tb-z-toast: 500;

	/* Measured at runtime by tb-header.js so sections can offset for the
	   fixed masthead without hardcoding its height. */
	--tb-header-height: 0px;   /* full masthead — set by tb-ui.js */
	--tb-header-sticky: 0px;   /* condensed scroll bar — set by tb-ui.js */
}

/* Desktop: wider gutters and grid gaps per DESIGN.md's 12-col spec. */
@media (min-width: 1024px) {
	:root {
		--tb-gutter: 1.5rem;
		--tb-grid-gap: 1.5rem;
		--tb-section-gap: 4rem;
	}
}

/* Mobile type: hero and large headlines step down one rung. */
@media (max-width: 767px) {
	:root {
		--tb-display-hero-size: 1.875rem;   /* 30px */
		--tb-display-hero-line: 2.25rem;    /* 36px */
		--tb-display-hero-track: -0.015em;

		--tb-headline-lg-size: 1.5rem;      /* 24px */
		--tb-headline-lg-line: 1.875rem;    /* 30px */
		--tb-headline-lg-track: -0.01em;

		--tb-headline-md-size: 1.25rem;     /* 20px */
		--tb-headline-md-line: 1.625rem;    /* 26px */

		--tb-body-lg-size: 1.0625rem;       /* 17px */
		--tb-body-lg-line: 1.75rem;         /* 28px */
	}
}

@media (prefers-reduced-motion: reduce) {
	:root {
		--tb-duration-fast: 0ms;
		--tb-duration: 0ms;
		--tb-duration-slow: 0ms;
		--tb-media-hover-scale: 1;
		--tb-media-hover-scale-lead: 1;
	}
}

/* -------------------------------------------------------------------
 * ALTERNATE PALETTE — DESIGN.md YAML frontmatter (Material ramp).
 * This is what code.html compiles to. Unused by default; to adopt it,
 * remap the brand/ink/surface tokens above to these values.
 * ----------------------------------------------------------------- */
:root {
	--tb-m3-primary:            #a20013;
	--tb-m3-primary-container:  #c91d24;
	--tb-m3-secondary:          #395ca4;
	--tb-m3-tertiary:           #004c9f;
	--tb-m3-surface:            #f8f9ff;
	--tb-m3-surface-container:  #eceef3;
	--tb-m3-surface-highest:    #e1e2e8;
	--tb-m3-on-surface:         #191c20;
	--tb-m3-on-surface-variant: #5c403d;
	--tb-m3-outline:            #906f6c;
	--tb-m3-outline-variant:    #e5bdb9;
}

/* --- tb-base --- */
/**
 * Totoong Balita — Base Layer
 * Reset, document defaults, typography roles, layout primitives.
 * Consumes tb-tokens.css. Declares no color or size of its own.
 */

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

html {
	-webkit-text-size-adjust: 100%;
	scroll-behavior: smooth;
	/* Anchored jumps clear the fixed masthead */
	/* The condensed bar, not the full masthead: by the time an anchor
	   jump lands you are scrolled, so the condensed band is what covers
	   the target. Falls back to the full height before tb-ui.js runs. */
	scroll-padding-top: calc(var(--tb-header-sticky, var(--tb-header-height)) + var(--tb-space-md));
}

body {
	margin: 0;
	background: var(--tb-surface);
	color: var(--tb-ink);
	font-family: var(--tb-font-body);
	font-size: var(--tb-body-md-size);
	line-height: var(--tb-body-md-line);
	font-weight: var(--tb-body-md-weight);
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	text-rendering: optimizeLegibility;
}

h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd { margin: 0; }
ul, ol { margin: 0; padding: 0; }
ul[class], ol[class] { list-style: none; }

img, picture, video, canvas, svg {
	display: block;
	max-width: 100%;
}
img { height: auto; border-style: none; }

input, button, textarea, select {
	font: inherit;
	color: inherit;
}
button { background: none; border: none; padding: 0; cursor: pointer; }

a { color: inherit; text-decoration: none; }

table { border-collapse: collapse; width: 100%; }

hr {
	border: none;
	border-top: var(--tb-rule);
	margin: var(--tb-space-lg) 0;
}

/* Keyboard-only by design. :focus-visible means a mouse click never draws a
 * ring — only Tab, arrow keys and other keyboard entry do — and the second
 * rule clears the ring for anything that still reports :focus after a click.
 * The ring itself is currentColor (see tb-tokens.css), not cobalt.
 * This is not decoration: without it nobody navigating by keyboard can tell
 * where they are, which includes newsroom staff working through the editor
 * and the post list. Retone it, move it, inset it — but leave it visible. */
:focus-visible {
	outline: var(--tb-focus-ring);
	outline-offset: var(--tb-focus-offset);
}
:focus:not(:focus-visible) { outline: none; }

::selection {
	background: var(--tb-primary);
	color: var(--tb-on-primary);
}

/* -------------------------------------------------------------------
 * TYPOGRAPHY ROLES
 * Apply a role with one class. These are the only places the type
 * scale is assembled; components reference the class or @extend the
 * same four variables.
 * ----------------------------------------------------------------- */
.tb-t-display {
	font-family: var(--tb-font-headline);
	font-size: var(--tb-display-hero-size);
	line-height: var(--tb-display-hero-line);
	font-weight: var(--tb-display-hero-weight);
	letter-spacing: var(--tb-display-hero-track);
}

.tb-t-headline-lg {
	font-family: var(--tb-font-headline);
	font-size: var(--tb-headline-lg-size);
	line-height: var(--tb-headline-lg-line);
	font-weight: var(--tb-headline-lg-weight);
	letter-spacing: var(--tb-headline-lg-track);
}

.tb-t-headline-md {
	font-family: var(--tb-font-headline);
	font-size: var(--tb-headline-md-size);
	line-height: var(--tb-headline-md-line);
	font-weight: var(--tb-headline-md-weight);
	letter-spacing: var(--tb-headline-md-track);
}

.tb-t-headline-sm {
	font-family: var(--tb-font-headline);
	font-size: var(--tb-headline-sm-size);
	line-height: var(--tb-headline-sm-line);
	font-weight: var(--tb-headline-sm-weight);
}

.tb-t-subhead {
	font-family: var(--tb-font-body);
	font-size: var(--tb-subhead-size);
	line-height: var(--tb-subhead-line);
	font-weight: var(--tb-subhead-weight);
}

.tb-t-body-lg {
	font-family: var(--tb-font-body);
	font-size: var(--tb-body-lg-size);
	line-height: var(--tb-body-lg-line);
}

.tb-t-body {
	font-family: var(--tb-font-body);
	font-size: var(--tb-body-md-size);
	line-height: var(--tb-body-md-line);
}

.tb-t-body-sm {
	font-family: var(--tb-font-body);
	font-size: var(--tb-body-sm-size);
	line-height: var(--tb-body-sm-line);
}

.tb-t-label {
	font-family: var(--tb-font-body);
	font-size: var(--tb-label-size);
	line-height: var(--tb-label-line);
	font-weight: var(--tb-label-weight);
	letter-spacing: var(--tb-label-track);
	text-transform: uppercase;
}

.tb-t-timestamp {
	font-family: var(--tb-font-body);
	font-size: var(--tb-timestamp-size);
	line-height: var(--tb-timestamp-line);
	font-weight: var(--tb-timestamp-weight);
	color: var(--tb-ink-subtle);
}

.tb-t-caption {
	font-family: var(--tb-font-body);
	font-size: var(--tb-caption-size);
	line-height: var(--tb-caption-line);
	color: var(--tb-ink-subtle);
}

/* Editorial distinction — analysis, opinion, exclusives */
.tb-t-italic { font-style: italic; font-weight: 400; }

/* -------------------------------------------------------------------
 * LAYOUT PRIMITIVES
 * ----------------------------------------------------------------- */

/* The page container. Every full-width band puts one of these inside. */
.tb-container {
	width: 100%;
	max-width: var(--tb-container-max);
	margin-inline: auto;
	padding-inline: var(--tb-gutter);
}

/* A full-bleed band that still aligns its contents to the container. */
.tb-band { width: 100%; }
.tb-band--sunken { background: var(--tb-surface-sunken); }
.tb-band--inverse { background: var(--tb-surface-inverse); color: var(--tb-ink-inverse); }
.tb-band--ruled { border-block: var(--tb-rule); }

/* Vertical rhythm between homepage sections. */
.tb-section { margin-bottom: var(--tb-section-gap); }
.tb-section:last-child { margin-bottom: 0; }

/* Article measure — caps line length for sustained reading. */
.tb-measure { max-width: var(--tb-measure); }

/* --- Grid ---------------------------------------------------------
 * 12 columns on desktop, 8 on tablet, 4 on mobile, matching DESIGN.md.
 * Children opt into spans with .tb-col-{n} / .tb-col-md-{n}.
 * ----------------------------------------------------------------- */
.tb-grid {
	display: grid;
	gap: var(--tb-grid-gap);
	grid-template-columns: repeat(4, minmax(0, 1fr));
}

@media (min-width: 768px) {
	.tb-grid { grid-template-columns: repeat(8, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
	.tb-grid { grid-template-columns: repeat(12, minmax(0, 1fr)); }
}

/* Mobile spans (4-col base) */
.tb-col-1 { grid-column: span 1; }
.tb-col-2 { grid-column: span 2; }
.tb-col-3 { grid-column: span 3; }
.tb-col-4 { grid-column: span 4; }

/* Tablet spans (8-col) */
@media (min-width: 768px) {
	.tb-col-md-2 { grid-column: span 2; }
	.tb-col-md-3 { grid-column: span 3; }
	.tb-col-md-4 { grid-column: span 4; }
	.tb-col-md-5 { grid-column: span 5; }
	.tb-col-md-6 { grid-column: span 6; }
	.tb-col-md-8 { grid-column: span 8; }
}

/* Desktop spans (12-col) */
@media (min-width: 1024px) {
	.tb-col-lg-2  { grid-column: span 2; }
	.tb-col-lg-3  { grid-column: span 3; }
	.tb-col-lg-4  { grid-column: span 4; }
	.tb-col-lg-5  { grid-column: span 5; }
	.tb-col-lg-6  { grid-column: span 6; }
	.tb-col-lg-7  { grid-column: span 7; }
	.tb-col-lg-8  { grid-column: span 8; }
	.tb-col-lg-9  { grid-column: span 9; }
	.tb-col-lg-12 { grid-column: span 12; }
}

/* --- Equal-width card matrices ------------------------------------
 * For the common "N cards across" case where 12-col spans are overkill.
 * ----------------------------------------------------------------- */
.tb-cards {
	display: grid;
	gap: var(--tb-grid-gap);
	grid-template-columns: 1fr;
}
@media (min-width: 640px) {
	.tb-cards--2 > *, .tb-cards--3 > *, .tb-cards--4 > * { min-width: 0; }
	.tb-cards--2, .tb-cards--3, .tb-cards--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
	.tb-cards--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
	.tb-cards--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* --- Stack: vertical flow with a consistent gap -------------------- */
.tb-stack { display: flex; flex-direction: column; gap: var(--tb-space-md); }
.tb-stack--xs { gap: var(--tb-space-xs); }
.tb-stack--sm { gap: var(--tb-space-sm); }
.tb-stack--lg { gap: var(--tb-space-lg); }

/* --- Cluster: horizontal group that wraps -------------------------- */
.tb-cluster {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--tb-space-xs);
}
.tb-cluster--sm { gap: var(--tb-space-sm); }
.tb-cluster--md { gap: var(--tb-space-md); }
.tb-cluster--between { justify-content: space-between; flex-wrap: nowrap; }

/* Vertical reading lanes — the optional 1px column rule from DESIGN.md */
.tb-lane + .tb-lane { border-left: var(--tb-rule); padding-left: var(--tb-grid-gap); }
@media (max-width: 1023px) {
	.tb-lane + .tb-lane { border-left: none; padding-left: 0; }
}

/* -------------------------------------------------------------------
 * UTILITIES
 * ----------------------------------------------------------------- */

/* Truncate to N lines. The workhorse for headlines and excerpts. */
.tb-clamp-1, .tb-clamp-2, .tb-clamp-3, .tb-clamp-4 {
	display: -webkit-box;
	-webkit-box-orient: vertical;
	overflow: hidden;
}
/* The [hidden] attribute must actually hide.
 *
 * Any display: declaration outranks the UA stylesheet's
 * [hidden] { display: none }, so an element styled as flex or grid stays
 * on screen no matter how many times a script sets .hidden = true. The
 * newsroom's upload progress bar sat visible at 0% for exactly this
 * reason. One !important here is worth the alternative, which is
 * remembering to write a :not([hidden]) guard on every flex component
 * anyone ever adds. */
[hidden] { display: none !important; }

.tb-clamp-1 { -webkit-line-clamp: 1; }
.tb-clamp-2 { -webkit-line-clamp: 2; }
.tb-clamp-3 { -webkit-line-clamp: 3; }
.tb-clamp-4 { -webkit-line-clamp: 4; }

.tb-muted  { color: var(--tb-ink-muted); }
.tb-subtle { color: var(--tb-ink-subtle); }

.tb-hide { display: none !important; }
@media (max-width: 639px)  { .tb-hide-sm  { display: none !important; } }
@media (max-width: 767px)  { .tb-hide-mob { display: none !important; } }
@media (max-width: 1023px) { .tb-hide-tab { display: none !important; } }
@media (min-width: 1024px) { .tb-hide-desk { display: none !important; } }

/* Screen-reader only, with a visible skip-link variant. */
.tb-sr-only {
	position: absolute;
	width: 1px; height: 1px;
	padding: 0; margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}
.tb-skip-link {
	position: absolute;
	top: 0; left: 0;
	z-index: var(--tb-z-toast);
	transform: translateY(-120%);
	background: var(--tb-surface-inverse);
	color: var(--tb-ink-inverse);
	padding: var(--tb-space-xs) var(--tb-space-md);
	font-size: var(--tb-body-sm-size);
	font-weight: 600;
	transition: transform var(--tb-duration-fast) var(--tb-ease);
}
.tb-skip-link:focus { transform: translateY(0); }

/* --- tb-components --- */
/**
 * Totoong Balita — Component Library
 * Every reusable element on the site: section headers, story cards,
 * badges, buttons, inputs, banners, pagination.
 *
 * Rule of the house: a new page composes these classes. It does not
 * write new card or button CSS. If a page needs a variant, the variant
 * is added here as a modifier so every page gets it.
 */

/* ===================================================================
 * 1. SECTION HEADER  —  .tb-section-head
 * Red vertical bar + uppercase title, closed by a full-width rule.
 * Recolor per section with --tb-accent.
 * ================================================================= */
.tb-section-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--tb-space-sm) var(--tb-space-md);
	flex-wrap: wrap;
	padding-bottom: var(--tb-space-xs);
	margin-bottom: var(--tb-space-md);
	border-bottom: var(--tb-rule);
}

/* The live/"see all" affordance keeps to one line and never wraps
   mid-phrase next to the title. */
.tb-section-head > .tb-cluster,
.tb-section-head__link { flex-shrink: 0; white-space: nowrap; }

.tb-section-head__group {
	display: flex;
	align-items: center;
	gap: var(--tb-space-xs);
	min-width: 0;
}

/* The 3px accent bar that anchors every section title. */
.tb-section-head__bar {
	flex-shrink: 0;
	width: 3px;
	height: 1.25rem;
	background: var(--tb-accent);
}

.tb-section-head__title {
	font-family: var(--tb-font-body);
	font-size: var(--tb-label-size);
	line-height: var(--tb-label-line);
	font-weight: var(--tb-label-weight);
	letter-spacing: var(--tb-label-track);
	text-transform: uppercase;
	color: var(--tb-ink);
}

.tb-section-head__note {
	font-size: var(--tb-caption-size);
	line-height: var(--tb-caption-line);
	color: var(--tb-ink-subtle);
	padding-left: var(--tb-space-xs);
}

.tb-section-head__link {
	display: inline-flex;
	align-items: center;
	gap: var(--tb-space-xxs);
	flex-shrink: 0;
	font-size: var(--tb-caption-size);
	font-weight: 600;
	color: var(--tb-primary);
	transition: color var(--tb-duration-fast) var(--tb-ease);
}
.tb-section-head__link:hover { color: var(--tb-primary-dark); text-decoration: underline; }
.tb-section-head__link svg { width: 14px; height: 14px; }

/* Section accent presets — set on the section, inherited by the bar. */
.tb-accent-news       { --tb-accent: var(--tb-accent-news); }
.tb-accent-politics   { --tb-accent: var(--tb-accent-politics); }
.tb-accent-business   { --tb-accent: var(--tb-accent-business); }
.tb-accent-infra      { --tb-accent: var(--tb-accent-infra); }
.tb-accent-environment{ --tb-accent: var(--tb-accent-environment); }
.tb-accent-sports     { --tb-accent: var(--tb-accent-sports); }
.tb-accent-lifestyle  { --tb-accent: var(--tb-accent-lifestyle); }
/* Showbiz has carried the "culture" accent since it was seeded and the token
   has always existed, but this rule did not — the class landed on the markup
   and resolved to nothing. Featured uses it too. */
.tb-accent-culture    { --tb-accent: var(--tb-accent-culture); }

/* ===================================================================
 * 2. LIVE PIP  —  .tb-pip
 * The pulsing dot. One of the two sanctioned circles in the system.
 * ================================================================= */
.tb-pip {
	display: inline-block;
	flex-shrink: 0;
	width: 8px;
	height: 8px;
	border-radius: var(--tb-radius-circle);
	background: var(--tb-primary);
	animation: tb-pulse 2s var(--tb-ease) infinite;
}
.tb-pip--sm { width: 6px; height: 6px; }
.tb-pip--light { background: var(--tb-ink-inverse); }

@keyframes tb-pulse {
	0%, 100% { opacity: 1; }
	50%      { opacity: 0.35; }
}
@media (prefers-reduced-motion: reduce) {
	.tb-pip { animation: none; }
}

/* ===================================================================
 * 3. BADGES & EYEBROWS  —  .tb-badge / .tb-eyebrow
 * Eyebrow = unfilled category marker above a headline (the default).
 * Badge   = filled marker, for overlays and breaking stories.
 * ================================================================= */
.tb-eyebrow {
	display: inline-block;
	font-family: var(--tb-font-body);
	font-size: var(--tb-label-size);
	line-height: var(--tb-label-line);
	font-weight: var(--tb-label-weight);
	letter-spacing: var(--tb-label-track);
	text-transform: uppercase;
	color: var(--tb-accent);
	transition: color var(--tb-duration-fast) var(--tb-ease);
}
a.tb-eyebrow:hover { color: var(--tb-primary-dark); }

.tb-badge {
	display: inline-flex;
	align-items: center;
	gap: var(--tb-space-xxs);
	padding: 0.1875rem var(--tb-space-xs);
	background: var(--tb-accent);
	color: var(--tb-on-primary);
	border-radius: var(--tb-radius);
	font-family: var(--tb-font-body);
	font-size: var(--tb-label-size);
	line-height: var(--tb-label-line);
	font-weight: var(--tb-label-weight);
	letter-spacing: var(--tb-label-track);
	text-transform: uppercase;
	white-space: nowrap;
}
.tb-badge--navy    { background: var(--tb-secondary); }
.tb-badge--cobalt  { background: var(--tb-tertiary); }
.tb-badge--neutral { background: var(--tb-ink); }
.tb-badge--outline {
	background: transparent;
	color: var(--tb-accent);
	border: 1px solid currentColor;
}

/* -------------------------------------------------------------------
 * BREAKING / LIVE FLAG  —  .tb-flag
 * Sits in .tb-card__kicker beside the category eyebrow. Always red,
 * never the section accent: a sports story that is breaking is
 * breaking, and inheriting --tb-accent would print it in bronze.
 * ----------------------------------------------------------------- */
.tb-card__kicker {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--tb-space-xs);
	min-width: 0;
}

.tb-flag {
	display: inline-flex;
	align-items: center;
	gap: var(--tb-space-xxs);
	flex-shrink: 0;
	font-family: var(--tb-font-body);
	font-size: var(--tb-label-size);
	line-height: var(--tb-label-line);
	font-weight: var(--tb-label-weight);
	letter-spacing: var(--tb-label-track);
	text-transform: uppercase;
	white-space: nowrap;
}

/* Solid, for the lead and the article header. */
.tb-flag--pill {
	padding: 0.1875rem var(--tb-space-xs);
	background: var(--tb-primary);
	color: var(--tb-on-primary);
	border-radius: var(--tb-radius);
}

/* Quieter, for stacked lists. Five solid pills down the side of the
   lead would out-shout the lead they sit beside. */
.tb-flag--rule {
	padding-left: var(--tb-space-xs);
	border-left: 2px solid var(--tb-primary);
	color: var(--tb-primary);
}

/* The LIVE pip is white for the solid pill. On the rule form there is
   no red behind it, so it takes the text colour instead of vanishing. */
.tb-flag--rule .tb-pip { background: currentColor; }

/* ===================================================================
 * 4. META ROW  —  .tb-meta
 * Byline • timestamp • bureau, separated by interpuncts.
 * ================================================================= */
.tb-meta {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--tb-space-xs);
	font-size: var(--tb-timestamp-size);
	line-height: var(--tb-timestamp-line);
	color: var(--tb-ink-subtle);
}
.tb-meta__author { font-weight: 500; color: var(--tb-ink); }
.tb-meta__bureau { color: var(--tb-tertiary); }
.tb-meta__sep { color: var(--tb-border-strong); }

/* ===================================================================
 * 5. MEDIA FRAME  —  .tb-media
 * Overflow-hidden box that crops to a ratio and scales its image on
 * hover. Depth comes from the crop, never from a shadow.
 * ================================================================= */
.tb-media {
	position: relative;
	display: block;
	overflow: hidden;
	background: var(--tb-surface-muted);
	aspect-ratio: var(--tb-ratio-card);
}
.tb-media--hero    { aspect-ratio: var(--tb-ratio-hero); }
.tb-media--square  { aspect-ratio: var(--tb-ratio-square); }
.tb-media--portrait{ aspect-ratio: var(--tb-ratio-portrait); }

.tb-media img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform var(--tb-duration) var(--tb-ease);
}

/* Scrim for headlines laid over photography. */
.tb-media__scrim {
	position: absolute;
	inset: 0;
	background: var(--tb-media-scrim);
	pointer-events: none;
}

/* Badge parked on top of the image. */
.tb-media__badge {
	position: absolute;
	top: var(--tb-space-xs);
	left: var(--tb-space-xs);
	z-index: var(--tb-z-base);
}

/* Duration chip for video thumbnails. */
.tb-media__duration {
	position: absolute;
	right: var(--tb-space-xs);
	bottom: var(--tb-space-xs);
	padding: 0.125rem var(--tb-space-xxs);
	background: rgba(17, 20, 24, 0.85);
	color: var(--tb-ink-inverse);
	font-size: var(--tb-caption-size);
	font-variant-numeric: tabular-nums;
}

/* ===================================================================
 * 6. STORY CARDS
 * Three shapes, one hover contract: image scales, headline turns navy,
 * nothing moves. All share the .tb-card base.
 * ================================================================= */
.tb-card {
	display: flex;
	flex-direction: column;
	min-width: 0;
	background: transparent;   /* the white canvas is the card body */
	border-radius: var(--tb-radius);
}

/* Shared hover contract */
.tb-card:hover .tb-media img { transform: scale(var(--tb-media-hover-scale)); }
.tb-card:hover .tb-card__title > a,
.tb-card:hover .tb-card__title { color: var(--tb-secondary); }

.tb-card__body {
	display: flex;
	flex-direction: column;
	gap: var(--tb-space-xxs);
	padding-top: var(--tb-space-sm);
	min-width: 0;
}

.tb-card__title {
	font-family: var(--tb-font-headline);
	font-size: var(--tb-headline-sm-size);
	line-height: var(--tb-headline-sm-line);
	font-weight: var(--tb-headline-sm-weight);
	color: var(--tb-ink);
	transition: color var(--tb-duration-fast) var(--tb-ease);
}

.tb-card__excerpt {
	font-size: var(--tb-body-sm-size);
	line-height: var(--tb-body-sm-line);
	color: var(--tb-ink-muted);
}

.tb-card__meta {
	margin-top: var(--tb-space-xxs);
	font-size: var(--tb-timestamp-size);
	line-height: var(--tb-timestamp-line);
	color: var(--tb-ink-subtle);
}

/* --- 6a. LEAD CARD — the 8-column anchor ------------------------- */
.tb-card--lead .tb-media { aspect-ratio: var(--tb-ratio-hero); }
.tb-card--lead:hover .tb-media img { transform: scale(var(--tb-media-hover-scale-lead)); }

.tb-card--lead .tb-card__title {
	font-size: var(--tb-headline-lg-size);
	line-height: var(--tb-headline-lg-line);
	font-weight: var(--tb-headline-lg-weight);
	letter-spacing: var(--tb-headline-lg-track);
}

.tb-card--lead .tb-card__excerpt {
	font-size: var(--tb-body-md-size);
	line-height: var(--tb-body-md-line);
	margin-top: var(--tb-space-xxs);
}

/* Hero variant — headline sits over the photograph. */
.tb-card--overlay { position: relative; }
.tb-card--overlay .tb-media { aspect-ratio: var(--tb-ratio-hero); }
.tb-card--overlay .tb-card__body {
	position: absolute;
	inset-inline: 0;
	bottom: 0;
	padding: var(--tb-space-lg);
	color: var(--tb-ink-inverse);
}
.tb-card--overlay .tb-card__title { color: var(--tb-ink-inverse); }
.tb-card--overlay:hover .tb-card__title { color: var(--tb-ink-inverse); }
.tb-card--overlay .tb-card__excerpt,
.tb-card--overlay .tb-card__meta { color: var(--tb-ink-inverse-muted); }

/* --- 6b. ROW CARD — compact horizontal list item ----------------- */
.tb-card--row {
	flex-direction: row;
	gap: var(--tb-space-sm);
	align-items: flex-start;
}
.tb-card--row .tb-media {
	flex-shrink: 0;
	width: 8rem;             /* 128px */
	aspect-ratio: var(--tb-ratio-card);
}
.tb-card--row .tb-card__body {
	padding-top: 0;
	justify-content: space-between;
	flex: 1;
}
.tb-card--row .tb-card__title {
	font-size: var(--tb-body-md-size);
	line-height: 1.3;
	font-weight: 600;
}

@media (min-width: 640px) {
	.tb-card--row .tb-media { width: 9rem; }
	.tb-card--row .tb-card__title {
		font-size: var(--tb-headline-sm-size);
		line-height: var(--tb-headline-sm-line);
	}
}

/* Thumbnail on the right instead — for sidebar lists. */
.tb-card--row-reverse { flex-direction: row-reverse; }

/* --- 6c. TEXT ROW — headline only, no image ---------------------- */
.tb-card--text .tb-card__body { padding-top: 0; }
.tb-card--text .tb-card__title {
	font-size: var(--tb-body-md-size);
	line-height: 1.35;
	font-weight: 600;
}

/* --- 6d. NUMBERED ROW — trending / most-read lists --------------- */
.tb-card--numbered {
	flex-direction: row;
	gap: var(--tb-space-sm);
	align-items: baseline;
}
.tb-card--numbered .tb-card__rank {
	flex-shrink: 0;
	width: 1.5rem;
	font-family: var(--tb-font-headline);
	font-size: var(--tb-headline-md-size);
	font-weight: 700;
	color: var(--tb-primary);
	font-variant-numeric: tabular-nums;
	line-height: 1;
}

/* --- 6e. LISTS — hairline-separated stacks ----------------------- */
.tb-list { display: flex; flex-direction: column; }
.tb-list > * { padding-block: var(--tb-space-sm); border-bottom: var(--tb-rule); }
.tb-list > *:first-child { padding-top: 0; }
.tb-list > *:last-child { padding-bottom: 0; border-bottom: none; }

/* ===================================================================
 * 7. BREAKING / TICKER BANNER  —  .tb-ticker
 * Solid red strip, white uppercase type, pulsing pip.
 * ================================================================= */
.tb-ticker {
	display: flex;
	align-items: center;
	gap: var(--tb-space-sm);
	min-height: 2rem;
}

.tb-ticker__tag {
	display: inline-flex;
	align-items: center;
	gap: var(--tb-space-xxs);
	flex-shrink: 0;
	padding: 0.125rem var(--tb-space-xs);
	background: var(--tb-primary);
	color: var(--tb-on-primary);
	font-size: var(--tb-label-size);
	line-height: var(--tb-label-line);
	font-weight: var(--tb-label-weight);
	letter-spacing: var(--tb-label-track);
	text-transform: uppercase;
}

/* --- The moving part -----------------------------------------------
 * A viewport that clips, a track that translates. The track holds the
 * headlines twice; moving it by exactly -50% lands the copy where the
 * original began, so the loop is seamless rather than snapping back.
 *
 * Duration is set per-instance by tb-ui.js from the measured track
 * width, so the speed stays constant whether there are three headlines
 * or twelve.
 * ------------------------------------------------------------------ */
.tb-ticker__viewport {
	flex: 1;
	min-width: 0;
	overflow: hidden;
	/* Fade the leading and trailing edges so headlines enter and leave
	   instead of being guillotined by the container edge. */
	-webkit-mask-image: linear-gradient(90deg, transparent, #000 2rem, #000 calc(100% - 2rem), transparent);
	        mask-image: linear-gradient(90deg, transparent, #000 2rem, #000 calc(100% - 2rem), transparent);
}

.tb-ticker__track {
	display: flex;
	width: max-content;
	animation: tb-ticker-scroll var(--tb-ticker-duration, 40s) linear infinite;
}

.tb-ticker__group {
	display: flex;
	align-items: center;
	gap: var(--tb-space-lg);
	padding-right: var(--tb-space-lg);
	flex-shrink: 0;
}

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

/* Stop while someone is reading or tabbing through, and when they've
   asked it to stop. */
.tb-ticker:hover .tb-ticker__track,
.tb-ticker:focus-within .tb-ticker__track,
.tb-ticker.is-paused .tb-ticker__track {
	animation-play-state: paused;
}

.tb-ticker__item {
	flex-shrink: 0;
	white-space: nowrap;
	font-size: var(--tb-body-sm-size);
	color: var(--tb-ink);
	transition: color var(--tb-duration-fast) var(--tb-ease);
}
.tb-ticker__item:hover,
.tb-ticker__item:focus-visible { color: var(--tb-primary); }

/* Separator between headlines — a dot the item carries with it, so the
   spacing stays even as the track loops. */
.tb-ticker__item + .tb-ticker__item::before {
	content: "";
	display: inline-block;
	width: 4px;
	height: 4px;
	margin-right: var(--tb-space-lg);
	border-radius: var(--tb-radius-circle);
	background: var(--tb-border-strong);
	vertical-align: middle;
}

/* --- Pause control -------------------------------------------------- */
.tb-ticker__toggle {
	flex-shrink: 0;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 1.5rem;
	height: 1.5rem;
	margin-left: var(--tb-space-xs);
	color: var(--tb-ink-subtle);
	transition: color var(--tb-duration-fast) var(--tb-ease);
}
.tb-ticker__toggle:hover { color: var(--tb-primary); }

/* Two bars for pause; a triangle for play. Drawn in CSS so the control
   costs nothing extra to load. */
.tb-ticker__toggle-icon {
	width: 8px;
	height: 9px;
	border-left: 3px solid currentColor;
	border-right: 3px solid currentColor;
}
.tb-ticker.is-paused .tb-ticker__toggle-icon {
	width: 0;
	height: 0;
	border-left: 8px solid currentColor;
	border-right: 0;
	border-top: 5px solid transparent;
	border-bottom: 5px solid transparent;
}

/* Reduced motion: no marquee at all. The track becomes a normal
   horizontally scrollable strip, and the duplicate is hidden so the
   headlines aren't listed twice. */
@media (prefers-reduced-motion: reduce) {
	.tb-ticker__viewport {
		overflow-x: auto;
		scrollbar-width: none;
		-webkit-mask-image: none;
		        mask-image: none;
	}
	.tb-ticker__viewport::-webkit-scrollbar { display: none; }
	.tb-ticker__track { animation: none; }
	.tb-ticker__group[aria-hidden="true"] { display: none; }
	.tb-ticker__toggle { display: none; }
}

/* Full-bleed breaking bar — the loudest thing on the page. */
.tb-breaking {
	background: var(--tb-primary);
	color: var(--tb-on-primary);
}
.tb-breaking .tb-ticker__tag {
	background: var(--tb-on-primary);
	color: var(--tb-primary);
}
.tb-breaking .tb-ticker__item { color: var(--tb-on-primary); font-weight: 500; }
.tb-breaking .tb-ticker__item:hover { color: var(--tb-on-primary); text-decoration: underline; }

/* ===================================================================
 * 8. BUTTONS  —  .tb-btn
 * Squared, unshadowed, two weights.
 * ================================================================= */
.tb-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--tb-space-xxs);
	padding: var(--tb-space-xs) var(--tb-space-sm);
	border-radius: var(--tb-radius);
	border: 1px solid transparent;
	font-family: var(--tb-font-body);
	font-size: var(--tb-body-sm-size);
	font-weight: 700;
	line-height: 1.25rem;
	text-align: center;
	cursor: pointer;
	transition: background-color var(--tb-duration-fast) var(--tb-ease),
	            color var(--tb-duration-fast) var(--tb-ease),
	            border-color var(--tb-duration-fast) var(--tb-ease);
}

.tb-btn--primary {
	background: var(--tb-ink);
	color: var(--tb-ink-inverse);
}
.tb-btn--primary:hover { background: #000; }

.tb-btn--red {
	background: var(--tb-primary);
	color: var(--tb-on-primary);
}
.tb-btn--red:hover { background: var(--tb-primary-dark); }

.tb-btn--navy {
	background: var(--tb-secondary);
	color: var(--tb-on-secondary);
}
.tb-btn--navy:hover { background: var(--tb-secondary-dark); }

/* Secondary / Load More — outlined, centered. */
.tb-btn--outline {
	background: var(--tb-surface);
	color: var(--tb-ink);
	border-color: var(--tb-border-strong);
}
.tb-btn--outline:hover { background: var(--tb-surface-sunken); }

.tb-btn--ghost { background: transparent; color: var(--tb-ink); }
.tb-btn--ghost:hover { background: var(--tb-surface-muted); }

.tb-btn--lg { padding: var(--tb-space-sm) var(--tb-space-lg); font-size: var(--tb-body-md-size); }
.tb-btn--sm { padding: var(--tb-space-xxs) var(--tb-space-xs); font-size: var(--tb-caption-size); }
.tb-btn--block { display: flex; width: 100%; }

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

.tb-btn svg { width: 1em; height: 1em; }

/* ===================================================================
 * 9. FORM CONTROLS  —  .tb-input / .tb-search
 * 1px perimeter, sharp corners, navy focus with no outer glow.
 * ================================================================= */
.tb-input,
.tb-textarea,
.tb-select {
	width: 100%;
	padding: var(--tb-space-xs) var(--tb-space-sm);
	background: var(--tb-surface);
	color: var(--tb-ink);
	border: 1px solid var(--tb-border-strong);
	border-radius: var(--tb-radius);
	font-size: var(--tb-body-md-size);
	line-height: 1.5rem;
	transition: border-color var(--tb-duration-fast) var(--tb-ease);
}
.tb-input::placeholder,
.tb-textarea::placeholder { color: var(--tb-ink-faint); }

.tb-input:focus,
.tb-textarea:focus,
.tb-select:focus {
	outline: none;
	border-color: var(--tb-secondary);
}

.tb-textarea { min-height: 8rem; resize: vertical; }

/* Search field with an inline glass icon. */
.tb-search { position: relative; display: flex; align-items: center; }
.tb-search .tb-input { padding-right: 2rem; }
.tb-search__icon {
	position: absolute;
	right: var(--tb-space-xs);
	width: 16px;
	height: 16px;
	color: var(--tb-ink-subtle);
	pointer-events: none;
}
.tb-search--compact .tb-input {
	padding-block: 0.25rem;
	font-size: var(--tb-body-sm-size);
	background: var(--tb-surface-sunken);
	border-color: var(--tb-border);
}

.tb-field { display: flex; flex-direction: column; gap: var(--tb-space-xxs); }
.tb-field__label {
	font-size: var(--tb-body-sm-size);
	font-weight: 600;
	color: var(--tb-ink);
}
.tb-field__hint { font-size: var(--tb-caption-size); color: var(--tb-ink-subtle); }
.tb-field__error { font-size: var(--tb-caption-size); color: var(--tb-error); }

/* ===================================================================
 * 10. TAGS  —  .tb-tag
 * Outlined pills for topic lists under an article.
 * ================================================================= */
.tb-tag {
	display: inline-flex;
	align-items: center;
	padding: var(--tb-space-xxs) var(--tb-space-xs);
	border: var(--tb-rule-strong);
	border-radius: var(--tb-radius);
	font-size: var(--tb-body-sm-size);
	color: var(--tb-ink-muted);
	transition: border-color var(--tb-duration-fast) var(--tb-ease),
	            color var(--tb-duration-fast) var(--tb-ease);
}
.tb-tag:hover { border-color: var(--tb-secondary); color: var(--tb-secondary); }

/* ===================================================================
 * 11. PAGINATION  —  .tb-pagination
 * ================================================================= */
.tb-pagination {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	align-items: center;
	gap: var(--tb-space-xxs);
	margin-top: var(--tb-space-xl);
	padding-top: var(--tb-space-lg);
	border-top: var(--tb-rule);
}
.tb-pagination .page-numbers {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 2.25rem;
	height: 2.25rem;
	padding-inline: var(--tb-space-xs);
	border: var(--tb-rule-strong);
	font-size: var(--tb-body-sm-size);
	font-weight: 600;
	color: var(--tb-ink);
	transition: background-color var(--tb-duration-fast) var(--tb-ease),
	            color var(--tb-duration-fast) var(--tb-ease);
}
.tb-pagination .page-numbers:hover { background: var(--tb-surface-sunken); }
.tb-pagination .page-numbers.current {
	background: var(--tb-ink);
	border-color: var(--tb-ink);
	color: var(--tb-ink-inverse);
}
.tb-pagination .page-numbers.dots { border-color: transparent; }

/* ===================================================================
 * 12. NOTICES  —  .tb-notice
 * ================================================================= */
.tb-notice {
	padding: var(--tb-space-sm) var(--tb-space-md);
	border-left: 3px solid var(--tb-accent);
	background: var(--tb-surface-sunken);
	font-size: var(--tb-body-md-size);
}
.tb-notice--error   { border-left-color: var(--tb-error);   background: var(--tb-error-tint); }
.tb-notice--success { border-left-color: var(--tb-success); background: var(--tb-success-tint); }
.tb-notice--warning { border-left-color: var(--tb-warning); background: var(--tb-warning-tint); }

/* ===================================================================
 * 13. EMPTY STATE  —  .tb-empty
 * ================================================================= */
.tb-empty {
	padding: var(--tb-space-2xl) var(--tb-space-md);
	text-align: center;
	border: var(--tb-rule);
	background: var(--tb-surface-sunken);
}
.tb-empty__title {
	font-family: var(--tb-font-headline);
	font-size: var(--tb-headline-md-size);
	font-weight: 600;
	margin-bottom: var(--tb-space-xs);
}
.tb-empty__text { color: var(--tb-ink-muted); font-size: var(--tb-body-md-size); }

/* ===================================================================
 * 14. PAGE LAYOUT & SIDEBAR MODULES
 * These live here rather than in tb-homepage.css because articles,
 * archives and search results all use the same two-column shell and
 * the same sidebar modules. Keeping them in the homepage sheet meant
 * a single post rendered its sidebar with no layout at all.
 * ================================================================= */
.tb-layout {
	display: grid;
	gap: var(--tb-space-xl);
	grid-template-columns: 1fr;
}
@media (min-width: 1024px) {
	.tb-layout { grid-template-columns: minmax(0, 1fr) 20rem; gap: var(--tb-space-2xl); }
}

.tb-sidebar {
	display: flex;
	flex-direction: column;
	gap: var(--tb-space-xl);
	min-width: 0;
}
@media (min-width: 1024px) {
	.tb-sidebar--sticky {
		position: sticky;
		top: calc(var(--tb-header-sticky, var(--tb-header-height)) + var(--tb-space-md));
		align-self: start;
	}
}

.tb-module { display: flex; flex-direction: column; }

.tb-module--boxed {
	padding: var(--tb-space-md);
	background: var(--tb-surface-sunken);
	border: var(--tb-rule);
}

/* Newsletter signup */
.tb-newsletter { display: flex; flex-direction: column; gap: var(--tb-space-sm); }
.tb-newsletter__title {
	font-family: var(--tb-font-headline);
	font-size: var(--tb-headline-md-size);
	line-height: var(--tb-headline-md-line);
	font-weight: 600;
}
.tb-newsletter__text { font-size: var(--tb-body-sm-size); color: var(--tb-ink-muted); }

/* Weather panel */
.tb-weather-list { display: flex; flex-direction: column; }
.tb-weather-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--tb-space-sm);
	padding-block: var(--tb-space-xs);
	border-bottom: var(--tb-rule);
	font-size: var(--tb-body-sm-size);
}
.tb-weather-row:last-child { border-bottom: none; }
.tb-weather-row__town { color: var(--tb-ink); font-weight: 500; }
.tb-weather-row__temp { font-variant-numeric: tabular-nums; color: var(--tb-ink-muted); }

/* Ad slot — reserved space, so no layout shift when a creative loads. */
.tb-adslot {
	display: flex;
	align-items: center;
	justify-content: center;
	background: var(--tb-surface-sunken);
	border: var(--tb-rule);
	color: var(--tb-ink-faint);
	font-size: var(--tb-caption-size);
	text-transform: uppercase;
	letter-spacing: var(--tb-label-track);
	min-height: 250px;
}
.tb-adslot--leaderboard { min-height: 90px; }
.tb-adslot--billboard { min-height: 250px; }

/* --- tb-header-footer --- */
/**
 * Totoong Balita — Masthead & Footer
 * The fixed three-tier header (utility bar / wordmark / nav), the mobile
 * drawer, and the dark footer.
 */

/* ===================================================================
 * MASTHEAD
 * Three stacked rows inside one fixed band:
 *   1. utility  — date, edition, search, account
 *   2. brand    — wordmark and tagline
 *   3. nav      — primary sections + weather chip
 * ================================================================= */
.tb-header {
	position: fixed;
	top: 0;
	left: 0;
	width: 100%;
	z-index: var(--tb-z-header);
	background: var(--tb-surface);
	border-bottom: var(--tb-rule);
}

.tb-header__inner {
	display: flex;
	flex-direction: column;
}

/* --- Scroll behaviour (headroom) ----------------------------------
   Scrolling down past the masthead slides the whole band out of view.
   Scrolling up brings it straight back, but condensed: the utility
   strip and the ticker stay collapsed and the wordmark row shrinks, so
   the returning bar costs ~92px instead of ~176px. Reaching the top of
   the page restores the full masthead.

   The collapse uses grid 1fr -> 0fr rather than animating a height,
   because the ticker row is content-sized and has no height to animate.

   IMPORTANT: --tb-header-height must NOT track the live header. It
   reserves main's top padding; if it shrank as the header condensed,
   the page would lurch upward, which changes scrollY, which re-fires
   the scroll handler — a feedback loop. tb-ui.js measures both heights
   once, with transitions suppressed, and publishes them as constants. */
.tb-header {
	transition: transform var(--tb-duration-fast) var(--tb-ease);
	will-change: transform;
}

.tb-header.is-hidden { transform: translateY(-100%); }

.tb-header__collapse {
	display: grid;
	grid-template-rows: 1fr;
	transition: grid-template-rows var(--tb-duration-fast) var(--tb-ease);
}
.tb-header__collapse-inner {
	overflow: hidden;
	min-height: 0;
}

.tb-header.is-condensed .tb-header__collapse { grid-template-rows: 0fr; }

/* Clipped content stays focusable, so a keyboard user could tab into a
   search field they cannot see. visibility removes it from the tab
   order — delayed on the way out so it does not blink away before the
   row has finished collapsing, immediate on the way back in. */
.tb-header.is-condensed .tb-header__collapse-inner {
	visibility: hidden;
	transition: visibility 0s linear var(--tb-duration-fast);
}

/* Nothing to animate off-screen. */
.tb-header.is-condensed .tb-ticker__track { animation-play-state: paused; }

/* The wordmark row shrinks rather than disappearing — the masthead
   should still say whose site this is. */
.tb-header .tb-header__brand {
	transition: height var(--tb-duration-fast) var(--tb-ease);
}
.tb-header .tb-wordmark {
	transition: font-size var(--tb-duration-fast) var(--tb-ease);
}
.tb-header.is-condensed .tb-header__brand { height: 3rem; }
.tb-header.is-condensed .tb-wordmark { font-size: var(--tb-headline-lg-size); }
.tb-header.is-condensed .tb-wordmark__suffix { font-size: var(--tb-body-size); }

/* Measurement pass: tb-ui.js toggles the real state classes to read
   both heights and must not see interpolated values. */
.tb-header.is-measuring,
.tb-header.is-measuring * { transition: none !important; }

/* Reduced motion needs no rules here: tb-tokens.css already sets
   --tb-duration-fast to 0ms under that query, so every transition above
   — including the visibility delay — collapses to an instant state
   change. The header still condenses and hides; that is screen space,
   not decoration. It just stops sliding. */

/* --- Row 1: utility ---------------------------------------------- */
.tb-header__utility {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--tb-space-md);
	height: 2.25rem;
	border-bottom: var(--tb-rule);
}

.tb-header__date {
	font-size: var(--tb-timestamp-size);
	color: var(--tb-ink-subtle);
	text-transform: uppercase;
	letter-spacing: 0.04em;
	white-space: nowrap;
}

.tb-header__divider {
	width: 1px;
	height: 0.75rem;
	background: var(--tb-border);
	flex-shrink: 0;
}

.tb-header__edition {
	font-size: var(--tb-label-size);
	font-weight: var(--tb-label-weight);
	letter-spacing: var(--tb-label-track);
	text-transform: uppercase;
	color: var(--tb-tertiary);
	white-space: nowrap;
}

.tb-header__utility-actions {
	display: flex;
	align-items: center;
	gap: var(--tb-space-sm);
	flex-shrink: 0;
}

.tb-header__search { width: 10rem; min-width: 0; }
@media (min-width: 640px) { .tb-header__search { width: 14rem; } }

/* --- Utility strip: shedding detail instead of wrapping ------------
   The strip is a fixed-height 2.25rem bar. `.tb-cluster` wraps by
   default, so at mid widths the edition dropped to a second line, the
   cluster grew to 62px, and — being vertically centred in a 36px bar —
   bled out of it at BOTH ends: the date clipped above the header, the
   edition spilling down over the masthead. Measured at 660px.

   So this cluster never wraps. It sheds detail instead, least
   load-bearing first, and the strip stays exactly one line tall. */
.tb-header__meta {
	flex-wrap: nowrap;
	min-width: 0;
}

.tb-header__date-short { display: none; }

/* 1. The word "Edisyon:" goes first — the region names say it alone. */
@media (max-width: 1023px) {
	.tb-header__edition-label { display: none; }
}

/* 2. Then the weekday and the spelled-out month: "25 Set 2026". */
@media (max-width: 879px) {
	.tb-header__date-long  { display: none; }
	.tb-header__date-short { display: inline; }
}

/* 3. Then the edition itself. Row 2's standfirst and the footer both
      still name the region, so nothing is actually lost here. */
@media (max-width: 559px) {
	.tb-header__divider,
	.tb-header__edition { display: none; }
}

/* 4. Finally the search field, which collapses to its own icon below
      768px (see "Mobile search"). The date stays: with the field out
      of the flow there is room for it again, and a strip holding one
      icon and nothing else is a wasted rule. */

/* --- Mobile search: a glass that expands over the date -------------
   A permanent full-width field was taking the whole strip on a phone
   and pushing the masthead down the page. Below 768px it becomes a
   magnifying glass at the right-hand end, and opens leftward across
   the row, over the date.

   Taken out of the flow rather than grown from zero width: animating
   the form's width squeezes the input inside it, so the placeholder
   compresses as it opens. Laid out at full width and revealed with
   clip-path, the text never moves — it is uncovered, right to left.

   Open state is a class on the header, not the `hidden` attribute:
   `hidden` would have to fight a media query over who gets to say
   `display`, and that argument has no good ending. */
.tb-header__searchbtn { display: none; }

@media (max-width: 767px) {
	.tb-header__utility { position: relative; }

	.tb-header__searchbtn {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		width: 2rem;
		height: 2rem;
		flex-shrink: 0;
		margin-right: calc(var(--tb-space-xxs) * -1);   /* optical edge */
		padding: 0;
		border: 0;
		background: none;
		color: var(--tb-ink-subtle);
		cursor: pointer;
		transition: color var(--tb-duration-fast) var(--tb-ease);
	}
	.tb-header__searchbtn:hover,
	.tb-header.is-searching .tb-header__searchbtn { color: var(--tb-ink); }
	.tb-header__searchbtn svg { width: 18px; height: 18px; }

	.tb-header__search {
		position: absolute;
		top: 50%;
		left: 0;
		right: calc(2rem + var(--tb-space-xs));
		width: auto;
		transform: translateY(-50%);
		opacity: 0;
		visibility: hidden;
		clip-path: inset(0 0 0 100%);
		/* visibility is delayed out so the reveal can finish playing
		   backwards before the field leaves the tab order. */
		transition: opacity var(--tb-duration-fast) var(--tb-ease),
		            clip-path var(--tb-duration-fast) var(--tb-ease),
		            visibility 0s linear var(--tb-duration-fast);
	}

	.tb-header.is-searching .tb-header__search {
		opacity: 1;
		visibility: visible;
		clip-path: inset(0 0 0 0);
		transition-delay: 0s;
	}

	/* The field carries its own inline glass, which sat right beside
	   the button that opened it — two identical icons an inch apart,
	   one of them inert. The button is the glass here, so the field's
	   own is dropped and the padding it reserved comes back. */
	.tb-header__search .tb-search__icon { display: none; }
	.tb-header__search .tb-input { padding-right: var(--tb-space-xs); }

	/* The date sits underneath. visibility rather than display so the
	   strip keeps its height and nothing jumps. */
	.tb-header.is-searching .tb-header__meta { visibility: hidden; }
}

.tb-header__account {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2rem;
	height: 2rem;
	flex-shrink: 0;
	border-radius: var(--tb-radius-circle);   /* avatar — sanctioned circle */
	background: var(--tb-primary);
	color: var(--tb-on-primary);
	transition: background-color var(--tb-duration-fast) var(--tb-ease);
}
.tb-header__account:hover { background: var(--tb-primary-dark); }

/* Latest draw, beside the weather. Lives here rather than in tb-lotto.css
   because the masthead renders on every page and that sheet does not. */
.tb-header__lotto {
	display: inline-flex;
	align-items: center;
	gap: var(--tb-space-xxs);
	font-size: var(--tb-caption-size);
	color: var(--tb-ink-subtle);
	font-variant-numeric: tabular-nums;
	white-space: nowrap;
}
.tb-header__lotto:hover { color: var(--tb-primary); }
.tb-header__lotto svg { width: 14px; height: 14px; flex-shrink: 0; }

/* The navrow's right-hand tray. Two chips now, so they need a container
   of their own rather than each shrinking against the nav independently. */
.tb-header__chips {
	display: flex;
	align-items: center;
	gap: var(--tb-space-md);
	flex-shrink: 0;
}
.tb-header__account svg { width: 18px; height: 18px; }

/* --- Row 2: brand ------------------------------------------------- */
.tb-header__brand {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--tb-space-md);
	height: 4rem;
}

/* Room to breathe around the lockup in the full desktop header.
 *
 * The row was 4rem (64px) while the brandmark — 48px wordmark plus the
 * strapline beneath it — measures 67px. It was not merely tight, it
 * overflowed by 3px and sat hard against the rule above and below.
 *
 * Expressed as a taller row rather than padding on purpose: tb-ui.js
 * animates this element between its full and condensed heights, and
 * `height: auto` with padding cannot be transitioned — the masthead
 * would snap instead of collapsing. 5.5rem leaves ~10px of clearance
 * top and bottom and keeps the animation intact.
 *
 * Mobile keeps 4rem: the wordmark steps down to 30px there, so the
 * lockup is around 46px and already clears comfortably. */
@media (min-width: 768px) {
	.tb-header:not(.is-condensed) .tb-header__brand { height: 5.5rem; }
}

.tb-header__lockup {
	display: flex;
	align-items: baseline;
	gap: var(--tb-space-sm);
	min-width: 0;
}

.tb-wordmark {
	display: inline-flex;
	align-items: baseline;
	gap: var(--tb-space-xxs);
	font-family: var(--tb-font-headline);
	font-size: var(--tb-display-hero-size);
	line-height: 1;
	font-weight: 700;
	letter-spacing: var(--tb-display-hero-track);
	color: var(--tb-primary);
	white-space: nowrap;
}
.tb-wordmark__suffix {
	font-size: var(--tb-headline-lg-size);
	font-weight: 400;
	font-style: italic;
	letter-spacing: normal;
	color: var(--tb-ink);
}
.tb-wordmark img { max-height: 3rem; width: auto; }

.tb-header__tagline {
	font-size: var(--tb-caption-size);
	color: var(--tb-ink-subtle);
	text-transform: uppercase;
	letter-spacing: 0.04em;
	padding-left: var(--tb-space-xs);
}

.tb-header__standfirst {
	display: flex;
	flex-direction: column;
	text-align: right;
	flex-shrink: 0;
}
.tb-header__standfirst-lead {
	/* The slogan is set in the masthead's serif, not the interface
	   sans. It belongs to the nameplate beside it rather than to the
	   navigation around it, and Newsreader 700 is already loaded for
	   headlines, so this costs no extra font request. */
	font-family: var(--tb-font-headline);
	/* Twice the label size: this is the masthead's slogan now, not a
	   descriptive line, so it carries the weight rather than sitting as
	   a caption beside the wordmark. */
	font-size: calc(var(--tb-label-size) * 2);
	font-weight: var(--tb-label-weight);
	/* Tracking set for small uppercase labels opens gaps at this size. */
	letter-spacing: 0;
	/* Rendered as written. At twice the size the uppercase transform this
	   rule used to carry would shout, and the line has its own
	   exclamation mark to do that job. */
	text-transform: none;
	line-height: 1.1;
	color: var(--tb-primary);
}
.tb-header__standfirst-sub {
	font-size: var(--tb-caption-size);
	color: var(--tb-ink-subtle);
}

/* --- Row 3: primary navigation ------------------------------------ */
.tb-header__navrow {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--tb-space-md);
	height: 2.75rem;
	border-top: var(--tb-rule);
}

.tb-nav {
	display: flex;
	align-items: center;
	gap: var(--tb-space-md);
	height: 100%;
	overflow-x: auto;
	white-space: nowrap;
	scrollbar-width: none;
}
.tb-nav::-webkit-scrollbar { display: none; }

@media (min-width: 640px) { .tb-nav { gap: var(--tb-space-lg); } }

.tb-nav__link {
	display: flex;
	align-items: center;
	height: 100%;
	font-size: var(--tb-label-size);
	line-height: var(--tb-label-line);
	font-weight: var(--tb-label-weight);
	letter-spacing: var(--tb-label-track);
	text-transform: uppercase;
	color: var(--tb-ink-muted);
	border-bottom: 2px solid transparent;
	transition: color var(--tb-duration-fast) var(--tb-ease),
	            border-color var(--tb-duration-fast) var(--tb-ease);
}
.tb-nav__link:hover { color: var(--tb-ink); }

/* Active section — red underline, no layout shift (border is always there) */
.tb-nav__link.is-active,
.tb-nav__item.current-menu-item > .tb-nav__link {
	color: var(--tb-primary);
	border-bottom-color: var(--tb-primary);
}

.tb-header__weather {
	display: inline-flex;
	align-items: center;
	gap: var(--tb-space-xxs);
	flex-shrink: 0;
	padding-left: var(--tb-space-md);
	font-size: var(--tb-caption-size);
	color: var(--tb-ink-subtle);
}
.tb-header__weather svg { width: 14px; height: 14px; color: var(--tb-tertiary); }

/* --- Weather picker ------------------------------------------------
   One town, and the reader chooses which. The chip used to show the
   first two in the list, which was two cities neither of them was
   necessarily near. Every town's reading is already in the page, so
   switching costs no request. */
.tb-wxpick { position: relative; }

.tb-wxpick__btn {
	display: inline-flex;
	align-items: center;
	gap: var(--tb-space-xxs);
	padding: var(--tb-space-xxs) 0;
	border: 0;
	background: none;
	font: inherit;
	color: inherit;
	cursor: pointer;
	transition: color var(--tb-duration-fast) var(--tb-ease);
}
.tb-wxpick__btn:hover { color: var(--tb-ink); }
.tb-wxpick__caret {
	color: var(--tb-ink-faint) !important;   /* beats .tb-header__weather svg */
	transition: transform var(--tb-duration-fast) var(--tb-ease);
}
.tb-wxpick__btn[aria-expanded="true"] .tb-wxpick__caret { transform: rotate(180deg); }

.tb-wxpick__menu {
	position: absolute;
	top: calc(100% + var(--tb-space-xxs));
	right: 0;
	/* Inside the header's own stacking context, so it only has to clear
	   its siblings — the scale has no dropdown step and this does not
	   warrant inventing one. */
	z-index: var(--tb-z-sticky);
	width: 15rem;
	max-height: 60vh;
	overflow-y: auto;
	background: var(--tb-surface);
	border: var(--tb-rule-strong);
	/* No shadow tokens in this system — the strong rule is the lift. */
}

.tb-wxpick__list { list-style: none; margin: 0; padding: 0; }

.tb-wxpick__opt {
	display: flex;
	align-items: baseline;
	gap: var(--tb-space-xs);
	width: 100%;
	padding: var(--tb-space-xs) var(--tb-space-sm);
	border: 0;
	background: none;
	font-family: var(--tb-font-body);
	font-size: var(--tb-body-sm-size);
	text-align: left;
	color: var(--tb-ink);
	cursor: pointer;
	transition: background-color var(--tb-duration-fast) var(--tb-ease);
}
.tb-wxpick__opt:hover { background: var(--tb-surface-sunken); }
.tb-wxpick__opt.is-current { font-weight: 700; }

.tb-wxpick__town { flex-shrink: 0; }
.tb-wxpick__prov {
	flex: 1;
	min-width: 0;
	font-size: var(--tb-caption-size);
	color: var(--tb-ink-faint);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}
.tb-wxpick__temp {
	flex-shrink: 0;
	font-variant-numeric: tabular-nums;
	color: var(--tb-ink-muted);
}

.tb-wxpick__opt--locate {
	align-items: center;
	gap: var(--tb-space-xs);
	border-bottom: var(--tb-rule);
	color: var(--tb-secondary);
	font-weight: 600;
}
.tb-wxpick__opt--locate svg { color: currentColor !important; }
.tb-wxpick__opt--locate[disabled] { opacity: 0.6; cursor: default; }

/* Says which town the coordinates landed on, or why they did not. The
   site covers nine towns; a reader outside them gets the nearest one
   named rather than presented as theirs. */
.tb-wxpick__note {
	margin: 0;
	padding: var(--tb-space-xs) var(--tb-space-sm);
	border-bottom: var(--tb-rule);
	background: var(--tb-surface-sunken);
	font-size: var(--tb-caption-size);
	color: var(--tb-ink-muted);
}

/* --- Row 4: regional ticker --------------------------------------- */
.tb-header__ticker {
	background: var(--tb-surface-sunken);
	border-top: var(--tb-rule);
}

/* --- Mobile menu toggle -------------------------------------------- */
.tb-header__burger {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2.25rem;
	height: 2.25rem;
	margin-left: calc(var(--tb-space-xs) * -1);
	color: var(--tb-ink);
	flex-shrink: 0;
}
.tb-header__burger svg { width: 24px; height: 24px; }

/* The drawer duplicates the section bar exactly, so only one of them
   shows at a time: drawer on phones, the scrolling bar from 768 up. */
@media (min-width: 768px) { .tb-header__burger { display: none; } }
@media (max-width: 767px) { .tb-header__navrow { display: none; } }

/* ===================================================================
 * MOBILE DRAWER
 * ================================================================= */
.tb-drawer {
	position: fixed;
	inset: 0;
	z-index: var(--tb-z-drawer);
	visibility: hidden;
	pointer-events: none;
}
.tb-drawer.is-open { visibility: visible; pointer-events: auto; }

.tb-drawer__scrim {
	position: absolute;
	inset: 0;
	background: rgba(17, 20, 24, 0.5);
	opacity: 0;
	transition: opacity var(--tb-duration) var(--tb-ease);
}
.tb-drawer.is-open .tb-drawer__scrim { opacity: 1; }

.tb-drawer__panel {
	position: absolute;
	top: 0;
	left: 0;
	width: min(20rem, 85vw);
	height: 100%;
	display: flex;
	flex-direction: column;
	background: var(--tb-surface-inverse);
	color: var(--tb-ink-inverse);
	transform: translateX(-100%);
	transition: transform var(--tb-duration) var(--tb-ease);
	overflow-y: auto;
}
.tb-drawer.is-open .tb-drawer__panel { transform: translateX(0); }

.tb-drawer__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--tb-space-md);
	padding: var(--tb-space-md);
	border-bottom: var(--tb-rule-inverse);
}
/* The wordmark's "News" suffix inherits --tb-ink, which is invisible on
   the drawer's dark panel. Re-point it, and drop the lockup to headline
   scale so it doesn't dominate the panel head. */
.tb-drawer .tb-wordmark { font-size: var(--tb-headline-lg-size); }
.tb-drawer .tb-wordmark__suffix {
	color: var(--tb-ink-inverse);
	font-size: var(--tb-headline-md-size);
}

.tb-drawer__close {
	display: inline-flex;
	padding: var(--tb-space-xxs);
	color: var(--tb-ink-inverse);
}
.tb-drawer__close svg { width: 24px; height: 24px; }

.tb-drawer__nav { display: flex; flex-direction: column; padding: var(--tb-space-xs) 0; }
.tb-drawer__link {
	padding: var(--tb-space-sm) var(--tb-space-md);
	font-size: var(--tb-subhead-size);
	font-weight: 600;
	color: var(--tb-ink-inverse);
	border-bottom: var(--tb-rule-inverse);
	transition: background-color var(--tb-duration-fast) var(--tb-ease);
}
.tb-drawer__link:hover { background: var(--tb-surface-inverse-alt); }
.tb-drawer__link.is-active { color: var(--tb-primary-tint); border-left: 3px solid var(--tb-primary); }

/* Sign-in, pinned to the foot of the panel.
   The masthead's account icon is hidden below 768px and the utility
   strip has no room for it — by 419px it is one full-width search
   field. It lives here instead, below the sections, because it is the
   least-used thing in the drawer and should read that way.
   margin-top:auto pins it to the bottom on a tall phone; with enough
   sections to fill the panel it simply follows the last link. */
.tb-drawer__foot {
	margin-top: auto;
	padding: var(--tb-space-md);
	border-top: var(--tb-rule-inverse);
}

.tb-drawer__account {
	display: flex;
	align-items: center;
	gap: var(--tb-space-sm);
	font-size: var(--tb-body-sm-size);
	font-weight: 600;
	color: var(--tb-ink-inverse-muted);
	transition: color var(--tb-duration-fast) var(--tb-ease);
}
.tb-drawer__account:hover { color: var(--tb-ink-inverse); }

/* The same red disc as the masthead's account chip, so the two read as
   one control that moved rather than two different things. */
.tb-drawer__account-icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2rem;
	height: 2rem;
	flex-shrink: 0;
	border-radius: var(--tb-radius-circle);   /* avatar — sanctioned circle */
	background: var(--tb-primary);
	color: var(--tb-on-primary);
	transition: background-color var(--tb-duration-fast) var(--tb-ease);
}
.tb-drawer__account:hover .tb-drawer__account-icon { background: var(--tb-primary-dark); }
.tb-drawer__account-icon svg { width: 18px; height: 18px; }

body.tb-drawer-open { overflow: hidden; }

/* ===================================================================
 * MAIN OFFSET
 * The fixed masthead is measured at runtime; main is padded to match.
 * ================================================================= */
.tb-main {
	padding-top: var(--tb-header-height);
	min-height: 60vh;
}
.tb-main__inner { padding-block: var(--tb-space-lg); }

/* ===================================================================
 * FOOTER
 * ================================================================= */
.tb-footer {
	background: var(--tb-surface-inverse);
	color: var(--tb-ink-inverse);
	margin-top: var(--tb-space-2xl);
}

.tb-footer__top {
	display: grid;
	gap: var(--tb-space-xl);
	padding-block: var(--tb-space-2xl);
	grid-template-columns: 1fr;
}
@media (min-width: 768px) {
	.tb-footer__top { grid-template-columns: 1.4fr 1fr 1fr; }
}
@media (min-width: 1024px) {
	.tb-footer__top { grid-template-columns: 1.6fr 1fr 1fr 1fr; }
}

.tb-footer__brandblock { display: flex; flex-direction: column; gap: var(--tb-space-sm); }
.tb-footer .tb-wordmark { color: var(--tb-ink-inverse); font-size: var(--tb-headline-lg-size); }
.tb-footer .tb-wordmark__suffix { color: var(--tb-ink-inverse-muted); font-size: var(--tb-headline-md-size); }
.tb-footer__blurb {
	font-size: var(--tb-body-sm-size);
	line-height: var(--tb-body-sm-line);
	color: var(--tb-ink-inverse-muted);
	max-width: 32ch;
}

.tb-footer__col-title {
	font-size: var(--tb-label-size);
	font-weight: var(--tb-label-weight);
	letter-spacing: var(--tb-label-track);
	text-transform: uppercase;
	color: var(--tb-ink-inverse);
	padding-bottom: var(--tb-space-xs);
	margin-bottom: var(--tb-space-sm);
	border-bottom: var(--tb-rule-inverse);
}

.tb-footer__list { display: flex; flex-direction: column; gap: var(--tb-space-xs); }
.tb-footer__list a {
	font-size: var(--tb-body-sm-size);
	color: var(--tb-ink-inverse-muted);
	transition: color var(--tb-duration-fast) var(--tb-ease);
}
.tb-footer__list a:hover { color: var(--tb-ink-inverse); }

.tb-footer__social { display: flex; gap: var(--tb-space-xs); margin-top: var(--tb-space-xs); }
.tb-footer__social a {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2rem;
	height: 2rem;
	border: var(--tb-rule-inverse);
	color: var(--tb-ink-inverse-muted);
	transition: background-color var(--tb-duration-fast) var(--tb-ease),
	            color var(--tb-duration-fast) var(--tb-ease);
}
.tb-footer__social a:hover {
	background: var(--tb-primary);
	border-color: var(--tb-primary);
	color: var(--tb-on-primary);
}
.tb-footer__social svg { width: 16px; height: 16px; }

.tb-footer__bottom {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--tb-space-sm);
	padding-block: var(--tb-space-md);
	border-top: var(--tb-rule-inverse);
	font-size: var(--tb-caption-size);
	color: var(--tb-ink-inverse-muted);
}
.tb-footer__legal { display: flex; flex-wrap: wrap; gap: var(--tb-space-md); }
.tb-footer__legal a:hover { color: var(--tb-ink-inverse); }

/* ===================================================================
 * ADMIN BAR OFFSET
 * The masthead is position:fixed at top:0, and so is WordPress's admin
 * bar — which wins on z-index and covers the top of the header for
 * logged-in users. That hides the breaking banner from exactly the
 * people who need to know it is live.
 * WP uses 32px from 783px up, 46px below that.
 * ================================================================= */
.admin-bar .tb-header { top: 32px; }

@media screen and (max-width: 782px) {
	.admin-bar .tb-header { top: 46px; }
}

/* -------------------------------------------------------------------
 * BRANDMARK — wordmark with the strapline ruled beneath it
 *
 * The strapline has to be exactly as wide as the wordmark above it, and
 * the wordmark's width is not a number anyone can type: it changes with
 * the font size, with the condensed header state, and with whatever
 * TB_WORDMARK_SUFFIX happens to say.
 *
 * So the column sizes itself to the wordmark and the strapline is told
 * to fill it. `width: 0` removes the strapline from the intrinsic width
 * calculation — without it, a strapline longer than the wordmark would
 * set the column's width instead of the other way round — while
 * `min-width: 100%` makes it span whatever the wordmark decided.
 * ------------------------------------------------------------------- */

.tb-brandmark {
	display: inline-flex;
	flex-direction: column;
	align-items: stretch;
	gap: 0.2rem;
}

.tb-brandmark__strap {
	width: 0;
	min-width: 100%;
	font-family: var(--tb-font-body);

	/* Sized to the wordmark, not picked by eye.
	 *
	 * The wordmark grows from 202px to 294px across the breakpoint
	 * (--tb-display-hero-size steps 30px -> 48px), but this line was
	 * pinned at 9px. Measured: the text filled 95% of the mark on mobile
	 * — 2px per word gap — and only 65% on desktop, where justification
	 * had 102px to dump into four gaps at 25px each. That is the "so much
	 * space between words".
	 *
	 * 13px restores the desktop fill to 94%, which is the mobile look.
	 * The breakpoint matches the one in tb-tokens.css that moves the
	 * wordmark, so the two always step together. */
	font-size: 0.8125rem;   /* 13px, against a 48px wordmark */
	font-weight: 600;
	line-height: 1.2;
	text-transform: uppercase;
	color: var(--tb-ink-subtle);
	white-space: nowrap;

	/* Justify the last (and only) line so the words spread to both
	   edges. text-align alone does nothing to a single line — the last
	   line of a justified block is left-aligned by definition, and this
	   block is nothing but a last line. */
	text-align: justify;
	text-align-last: justify;
}

/* The condensed header shrinks the wordmark, so the strapline would sit
   under a narrower mark with the same type size and start to look
   oversized. It goes instead — the masthead is already stating less. */
.tb-header.is-condensed .tb-brandmark__strap {
	display: none;
}

/* Kept at tablet and phone widths — the strapline is the point of the
   lockup, and the wordmark is still wide enough there to justify across
   without the word gaps opening up. It goes only on the narrowest
   phones, where the mark shrinks far enough that five words stretched
   across it reads as broken spacing rather than a strapline. */
@media (max-width: 767px) {
	/* The wordmark steps down to 30px here, so the strapline steps with
	   it — same ratio, same tightness, same look. */
	.tb-brandmark__strap { font-size: 0.5625rem; }   /* 9px */
}

@media (max-width: 359px) {
	.tb-brandmark__strap { display: none; }
}

/* --- tb-chrome --- */
/*
 * Totoong Balita — Site chrome
 *
 * The parts of the old tb-pages.css that appear on every page rather
 * than only on the standing pages: the weather module in the sidebar,
 * the cookie bar, the form honeypot, and the footer's "more" link.
 *
 * They were split out because tb-pages.css was being loaded site-wide
 * on their account, and measuring it on the homepage showed 88% of its
 * 23 KB matching nothing there — 20 KB of standing-page CSS in the
 * critical path of every article and every archive, to carry about
 * 2.7 KB that was actually used.
 */

/* --- Form honeypot --- */
.tb-trap {
	position: absolute !important;
	left: -9999px !important;
	top: auto;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

/* ===================================================================
 * WEATHER
 * ================================================================= */
.tb-wx { display: flex; flex-direction: column; }

.tb-wx__row {
	display: grid;
	grid-template-columns: 1.75rem 1fr auto;
	align-items: center;
	gap: var(--tb-space-sm);
	padding-block: var(--tb-space-xs);
	border-bottom: var(--tb-rule);
}
.tb-wx__row:last-child { border-bottom: none; }

.tb-wx__glyph { display: flex; color: var(--tb-tertiary); }
.tb-wx__icon { width: 22px; height: 22px; }

.tb-wx__place { display: flex; flex-direction: column; min-width: 0; }
.tb-wx__place strong {
	font-size: var(--tb-body-sm-size);
	font-weight: 600;
	color: var(--tb-ink);
}
.tb-wx__place span {
	font-size: var(--tb-caption-size);
	color: var(--tb-ink-subtle);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.tb-wx__temp {
	font-family: var(--tb-font-headline);
	font-size: var(--tb-headline-sm-size);
	font-weight: 700;
	color: var(--tb-ink);
	font-variant-numeric: tabular-nums;
}

.tb-wx__more {
	display: inline-block;
	margin-top: var(--tb-space-sm);
	font-size: var(--tb-body-sm-size);
	font-weight: 600;
	color: var(--tb-secondary);
}
.tb-wx__more:hover { text-decoration: underline; }


/* ===================================================================
 * COOKIE CONSENT
 * A bar, not a modal: it doesn't trap the reader or block the story.
 * ================================================================= */
.tb-consent {
	position: fixed;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: var(--tb-z-modal);
	background: var(--tb-surface-inverse);
	color: var(--tb-ink-inverse);
	border-top: 3px solid var(--tb-primary);
	animation: tb-consent-in var(--tb-duration) var(--tb-ease);
}

@keyframes tb-consent-in {
	from { transform: translateY(100%); }
	to   { transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
	.tb-consent { animation: none; }
}

.tb-consent__inner {
	max-width: var(--tb-container-max);
	margin-inline: auto;
	padding: var(--tb-space-md) var(--tb-gutter);
	display: flex;
	flex-direction: column;
	gap: var(--tb-space-sm);
}
@media (min-width: 768px) {
	.tb-consent__inner {
		flex-direction: row;
		align-items: center;
		justify-content: space-between;
		gap: var(--tb-space-lg);
	}
}

.tb-consent__text {
	font-size: var(--tb-body-sm-size);
	line-height: 1.5;
	color: var(--tb-ink-inverse-muted);
}
.tb-consent__text strong { color: var(--tb-ink-inverse); }
.tb-consent__link { margin-top: var(--tb-space-xxs); }
.tb-consent__link a {
	color: var(--tb-ink-inverse);
	text-decoration: underline;
	font-size: var(--tb-caption-size);
}

/* Both choices are real buttons at the same size. Rejecting is exactly
   as easy as accepting. */
.tb-consent__actions {
	display: flex;
	gap: var(--tb-space-xs);
	flex-shrink: 0;
}
.tb-consent__actions .tb-btn { flex: 1; white-space: nowrap; }
@media (min-width: 768px) {
	.tb-consent__actions .tb-btn { flex: 0 0 auto; min-width: 9rem; }
}
.tb-consent__actions .tb-btn--outline {
	background: transparent;
	color: var(--tb-ink-inverse);
	border-color: var(--tb-border-inverse);
}
.tb-consent__actions .tb-btn--outline:hover { background: var(--tb-surface-inverse-alt); }

.tb-consent-reset { cursor: pointer; }


/* --- Footer 'more' link --- */
.tb-footer__more { font-weight: 600; }


/* ===================================================================
 * BREAKING BANNER
 * Above the masthead, so it is the first thing on the page.
 * ================================================================= */
.tb-breakingbar {
	background: var(--tb-primary);
	color: var(--tb-on-primary);
}
.tb-breakingbar__inner { display: flex; align-items: center; }

.tb-breakingbar__link {
	display: flex;
	align-items: center;
	gap: var(--tb-space-sm);
	padding-block: var(--tb-space-xs);
	min-width: 0;
	color: var(--tb-on-primary);
	width: 100%;
}
a.tb-breakingbar__link:hover .tb-breaking__text { text-decoration: underline; }

.tb-breaking__tag {
	display: inline-flex;
	align-items: center;
	gap: var(--tb-space-xxs);
	flex-shrink: 0;
	font-size: var(--tb-label-size);
	font-weight: var(--tb-label-weight);
	letter-spacing: var(--tb-label-track);
	text-transform: uppercase;
}
/* --- One line, always ----------------------------------------------
 * The text lives in a clipping viewport so it can travel when it is
 * too long for the bar, the way a broadcast crawl does. It only moves
 * if it has to: tb-ui.js measures and adds .is-scrolling, so a short
 * alert never budges.
 *
 * The still state is the default, which is also what a reader without
 * JavaScript gets — one line, ellipsis, no wrap. The mobile override
 * that used to let this wrap to three lines is gone: it pushed the
 * masthead down the page, and truncating instead cut the sentence
 * exactly where an evacuation alert names the towns.
 * ------------------------------------------------------------------ */
.tb-breaking__viewport {
	flex: 1;
	min-width: 0;
	overflow: hidden;
}

.tb-breaking__track {
	display: flex;
	min-width: 0;
}

.tb-breaking__text {
	font-size: var(--tb-body-sm-size);
	font-weight: 500;
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* --- Moving, because it did not fit -------------------------------- */
.tb-breakingbar.is-scrolling .tb-breaking__track {
	width: max-content;
	animation: tb-breaking-scroll var(--tb-breaking-duration, 20s) linear infinite;
}
/* No ellipsis while travelling — the end of the sentence is coming. */
.tb-breakingbar.is-scrolling .tb-breaking__text {
	overflow: visible;
	text-overflow: clip;
	padding-right: 3rem;
}

/* The track carries the sentence twice, so -50% lands the copy where
   the original started and the loop does not snap back. */
@keyframes tb-breaking-scroll {
	from { transform: translateX(0); }
	to   { transform: translateX(-50%); }
}

/* Stop while it is being read, tabbed through, or switched off. */
.tb-breakingbar:hover .tb-breaking__track,
.tb-breakingbar:focus-within .tb-breaking__track,
.tb-breakingbar.is-paused .tb-breaking__track {
	animation-play-state: paused;
}

/* --- Pause control, same two-bar/triangle icon as the ticker ------- */
.tb-breaking__toggle {
	flex-shrink: 0;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 1.5rem;
	height: 1.5rem;
	margin-left: var(--tb-space-xs);
	color: var(--tb-on-primary);
	opacity: 0.85;
}
.tb-breaking__toggle:hover { opacity: 1; }
.tb-breaking__toggle[hidden] { display: none; }

.tb-breaking__toggle-icon {
	width: 8px;
	height: 9px;
	border-left: 3px solid currentColor;
	border-right: 3px solid currentColor;
}
.tb-breakingbar.is-paused .tb-breaking__toggle-icon {
	width: 0;
	height: 0;
	border-left: 8px solid currentColor;
	border-right: 0;
	border-top: 5px solid transparent;
	border-bottom: 5px solid transparent;
}

/* Reduced motion: nothing moves. The viewport becomes a strip the
   reader can push sideways themselves, and the duplicate sentence is
   hidden so it is not there twice. */
@media (prefers-reduced-motion: reduce) {
	.tb-breakingbar.is-scrolling .tb-breaking__viewport {
		overflow-x: auto;
		scrollbar-width: none;
	}
	.tb-breakingbar.is-scrolling .tb-breaking__viewport::-webkit-scrollbar { display: none; }
	.tb-breakingbar.is-scrolling .tb-breaking__track { animation: none; }
	.tb-breaking__text[aria-hidden="true"] { display: none; }
	.tb-breaking__toggle { display: none; }
}

/* ===================================================================
 * FRONT-END EDIT LINK
 * Replaces the one useful thing the admin bar did, without putting a
 * band across the top of the masthead. Staff only.
 * ================================================================= */
.tb-editlink {
	position: fixed;
	right: var(--tb-space-md);
	bottom: var(--tb-space-md);
	z-index: var(--tb-z-sticky);
	display: inline-flex;
	align-items: center;
	gap: var(--tb-space-xs);
	padding: var(--tb-space-xs) var(--tb-space-md);
	background: var(--tb-surface-inverse);
	color: var(--tb-ink-inverse);
	font-family: var(--tb-font-body);
	font-size: var(--tb-body-sm-size);
	font-weight: 600;
	border-radius: var(--tb-radius);
	box-shadow: 0 2px 12px rgba(17, 20, 24, 0.22);
	transition: background-color var(--tb-duration-fast) var(--tb-ease);
}
.tb-editlink:hover { background: var(--tb-primary); }
.tb-editlink svg { width: 15px; height: 15px; }

/* A live blog is the one case where the edit route needs to be obvious —
   updates are the story. */
.tb-editlink.is-live { background: var(--tb-primary); }
.tb-editlink.is-live:hover { background: var(--tb-primary-dark); }

.tb-editlink__badge {
	display: inline-flex;
	align-items: center;
	padding: 1px var(--tb-space-xxs);
	background: var(--tb-on-primary);
	color: var(--tb-primary);
	font-size: 10px;
	font-weight: 700;
	letter-spacing: 0.06em;
	text-transform: uppercase;
}

/* The consent bar occupies the same corner; lift the button clear of it. */
.tb-consent ~ .tb-editlink,
body:has(.tb-consent) .tb-editlink { bottom: 6rem; }

@media (max-width: 479px) {
	/* Icon only on the narrowest screens, so it never covers content. */
	.tb-editlink__label { display: none; }
	.tb-editlink { padding: var(--tb-space-xs); }
}



/* ===================================================================
 * AVATARS — moved here from the article sheet.
 * They appear in bylines on cards, on author archives and under
 * stories, so they are chrome rather than article furniture. Filing
 * them with the article left the author archive drawing square
 * avatars, because tb-article.css does not load there.
 * ================================================================= */

/* ===================================================================
 * AVATARS
 * One of the two sanctioned circles in the system (the other is the
 * live pip). A writer's face earns it.
 * ================================================================= */
.tb-avatar {
	display: block;
	width: 4rem;
	height: 4rem;
	border-radius: var(--tb-radius-circle);
	object-fit: cover;
	background: var(--tb-surface-muted);
	flex-shrink: 0;
}

/* No Gravatar fallback — an initial on a solid disc beats a stranger's
   default avatar, and loads nothing third-party. */
.tb-avatar--initial {
	display: flex;
	align-items: center;
	justify-content: center;
	background: var(--tb-secondary);
	color: var(--tb-on-secondary);
	font-family: var(--tb-font-headline);
	font-size: 1.5rem;
	font-weight: 700;
	line-height: 1;
}


/* --- tb-lotto --- */
/**
 * Totoong Balita — PCSO results
 * Board, per-game archive, single draw. Tokens only: no raw hex, no raw px.
 *
 * The one visual liberty here is the ball: a circle, in a design system
 * whose only other sanctioned circle is the avatar. It earns the exception
 * by being the thing readers scan for — a lotto result that does not look
 * like lotto balls reads as a table of numbers, and people miss it.
 */

/* --- Page head ---------------------------------------------------- */
.tb-lhead { margin-bottom: var(--tb-space-lg); }

.tb-lhead__title {
	font-family: var(--tb-font-headline);
	font-size: var(--tb-headline-lg-size);
	line-height: 1.15;
	color: var(--tb-ink);
	margin: 0;
}
.tb-lhead__sub {
	margin: var(--tb-space-xs) 0 0;
	font-size: var(--tb-body-sm-size);
	color: var(--tb-ink-subtle);
}

/* --- Breadcrumbs -------------------------------------------------- */
.tb-crumbs { margin-bottom: var(--tb-space-md); }

.tb-crumbs__list {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--tb-space-xs);
	margin: 0;
	padding: 0;
	list-style: none;
	font-size: var(--tb-caption-size);
	color: var(--tb-ink-subtle);
}
.tb-crumbs__item { display: inline-flex; align-items: center; gap: var(--tb-space-xs); }

/* Separator as a pseudo-element, not a standalone flex item: a lone
   separator wrapping onto its own line was the dangling-bullet bug. */
.tb-crumbs__item + .tb-crumbs__item::before {
	content: "/";
	color: var(--tb-border-strong);
}
.tb-crumbs__item a { color: var(--tb-ink-subtle); }
.tb-crumbs__item a:hover { color: var(--tb-primary); }
.tb-crumbs__item [aria-current] { color: var(--tb-ink); }

/* --- Game switcher ------------------------------------------------ */
.tb-lgames {
	display: flex;
	gap: var(--tb-space-xs);
	overflow-x: auto;
	scrollbar-width: none;
	padding-bottom: var(--tb-space-xxs);
	margin-bottom: var(--tb-space-lg);
	border-bottom: var(--tb-rule);
}
.tb-lgames::-webkit-scrollbar { display: none; }

.tb-lgames__link {
	flex-shrink: 0;
	padding: var(--tb-space-xs) var(--tb-space-sm);
	font-size: var(--tb-label-size);
	font-weight: var(--tb-label-weight);
	letter-spacing: var(--tb-label-track);
	text-transform: uppercase;
	color: var(--tb-ink-subtle);
	border-bottom: 2px solid transparent;
	white-space: nowrap;
	transition: color var(--tb-duration-fast) var(--tb-ease),
	            border-color var(--tb-duration-fast) var(--tb-ease);
}
.tb-lgames__link:hover { color: var(--tb-ink); }
.tb-lgames__link.is-active {
	color: var(--tb-primary);
	border-bottom-color: var(--tb-primary);
}

/* --- The balls ---------------------------------------------------- */
.tb-balls {
	display: flex;
	flex-wrap: nowrap;
	gap: var(--tb-space-xs);
	margin: 0;
	padding: 0;
	list-style: none;

	/* nowrap keeps a six-number result reading as one draw, but it also
	   means an oversized row would push the whole page sideways. Contain
	   it here: the sizes below are tuned so this never actually engages,
	   and if a future game has more balls than any of them anticipate,
	   the row scrolls inside its card instead of breaking the layout. */
	min-width: 0;
	overflow-x: auto;
	scrollbar-width: none;
}
.tb-balls::-webkit-scrollbar { display: none; }

.tb-ball {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 2.5rem;
	height: 2.5rem;
	padding: 0 var(--tb-space-xs);
	font-family: var(--tb-font-headline);
	font-size: var(--tb-headline-sm-size);
	font-weight: 700;
	font-variant-numeric: tabular-nums;
	color: var(--tb-on-primary);
	background: var(--tb-primary);
	border-radius: var(--tb-radius-circle);
}

/* Digit games are not drawn from a tumbler of numbered balls — they are
   per-digit draws. Square tiles, so the two are not confused at a glance. */
.tb-balls--digit .tb-ball {
	border-radius: var(--tb-radius);
	background: var(--tb-secondary);
}

.tb-balls--sm .tb-ball {
	min-width: 1.875rem;
	height: 1.875rem;
	font-size: var(--tb-body-sm-size);
}
.tb-balls--lg .tb-ball {
	min-width: 3.25rem;
	height: 3.25rem;
	font-size: var(--tb-headline-lg-size);
}

/* --- Live board --------------------------------------------------- */
.tb-lboard__status {
	display: flex;
	align-items: center;
	gap: var(--tb-space-sm);
	margin-bottom: var(--tb-space-md);
	font-size: var(--tb-caption-size);
	color: var(--tb-ink-subtle);
}

.tb-lboard__live {
	display: inline-flex;
	align-items: center;
	gap: var(--tb-space-xs);
	font-size: var(--tb-label-size);
	font-weight: var(--tb-label-weight);
	letter-spacing: var(--tb-label-track);
	text-transform: uppercase;
	color: var(--tb-primary);
}
.tb-lboard__pip {
	width: 0.5rem;
	height: 0.5rem;
	border-radius: var(--tb-radius-circle);
	background: var(--tb-primary);
	animation: tb-lotto-pulse 2s ease-in-out infinite;
}
@keyframes tb-lotto-pulse {
	0%, 100% { opacity: 1; }
	50%      { opacity: 0.25; }
}

.tb-lboard__grid {
	display: grid;
	/* Sized to fit six 2.5rem balls plus their gaps and the card's own
	   padding on ONE line (6*2.5 + 5*0.5 + 2 = 19.5rem). A wrapped lotto
	   result reads as two separate draws at a glance. */
	grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
	gap: var(--tb-space-md);
}

.tb-lboard__empty,
.tb-lempty__lead { color: var(--tb-ink-muted); }

/* --- One slot ----------------------------------------------------- */
.tb-lslot {
	display: flex;
	flex-direction: column;
	gap: var(--tb-space-sm);
	padding: var(--tb-space-md);
	background: var(--tb-surface);
	border: var(--tb-rule);
}
.tb-lslot--waiting { background: var(--tb-surface-sunken); }
.tb-lslot--scheduled { opacity: 0.72; }

.tb-lslot__head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--tb-space-sm);
}
.tb-lslot__game {
	margin: 0;
	font-family: var(--tb-font-headline);
	font-size: var(--tb-body-md-size);
	font-weight: 700;
	line-height: 1.2;
}
.tb-lslot__game a { color: var(--tb-ink); }
.tb-lslot__game a:hover { color: var(--tb-primary); }

.tb-lslot__time {
	flex-shrink: 0;
	font-size: var(--tb-label-size);
	font-weight: var(--tb-label-weight);
	letter-spacing: var(--tb-label-track);
	text-transform: uppercase;
	color: var(--tb-ink-subtle);
	font-variant-numeric: tabular-nums;
}

.tb-lslot__meta {
	display: flex;
	flex-wrap: wrap;
	gap: var(--tb-space-md);
	margin: 0;
}
.tb-lslot__meta dt {
	font-size: var(--tb-label-size);
	font-weight: var(--tb-label-weight);
	letter-spacing: var(--tb-label-track);
	text-transform: uppercase;
	color: var(--tb-ink-subtle);
}
.tb-lslot__meta dd {
	margin: 0;
	font-size: var(--tb-body-sm-size);
	font-weight: 600;
	color: var(--tb-ink);
	font-variant-numeric: tabular-nums;
}

/* The previous draw, shown under a slot that has no result yet.
   Deliberately quieter than a live result: a reader glancing at the
   board must never take last night's numbers for tonight's. The date
   leads, and the whole block is set back behind a rule. */
.tb-lslot__prev {
	margin-top: var(--tb-space-sm);
	padding-top: var(--tb-space-sm);
	border-top: var(--tb-rule);
}
.tb-lslot__prev-label {
	margin: 0 0 var(--tb-space-xs);
	font-size: var(--tb-caption-size);
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: var(--tb-label-track);
	color: var(--tb-ink-subtle);
}
/* No recolouring here. A previous result is still a result, and it gets
   the same red circles and blue squares as a live one — the component
   already distinguishes a drawn ball from a digit tile, and greying
   them out threw that away to say something the date label says better.
   Only the size differs, via the component's own --sm variant. */

/* A slot showing a previous result is no longer empty, so it should not
   be dimmed like one — the numbers in it have to stay readable. */
.tb-lslot--scheduled:has(.tb-lslot__prev),
.tb-lslot--waiting:has(.tb-lslot__prev) { opacity: 1; }

.tb-lslot__pending {
	display: flex;
	align-items: center;
	gap: var(--tb-space-xs);
	margin: 0;
	font-size: var(--tb-body-sm-size);
	font-weight: 600;
	color: var(--tb-ink-muted);
}
.tb-lslot__dot {
	width: 0.5rem;
	height: 0.5rem;
	border-radius: var(--tb-radius-circle);
	background: var(--tb-primary);
	animation: tb-lotto-pulse 2s ease-in-out infinite;
}
.tb-lslot__note {
	margin: 0;
	font-size: var(--tb-caption-size);
	color: var(--tb-ink-subtle);
}
.tb-lslot__more {
	margin-top: auto;
	font-size: var(--tb-caption-size);
	font-weight: 600;
	color: var(--tb-primary);
}

/* A slot that just arrived gets one quiet flash, so a reader watching the
   page sees WHICH number changed rather than having to diff it by eye. */
.tb-lslot.is-fresh {
	animation: tb-lotto-arrive var(--tb-duration-slow) var(--tb-ease);
}
@keyframes tb-lotto-arrive {
	from { background: var(--tb-primary-tint); }
	to   { background: var(--tb-surface); }
}

/* --- Archive rows -------------------------------------------------- */
.tb-lrows { display: flex; flex-direction: column; }

.tb-lrow {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: var(--tb-space-sm) var(--tb-space-md);
	padding: var(--tb-space-sm) 0;
	border-bottom: var(--tb-rule);
}
.tb-lrow:hover { background: var(--tb-surface-sunken); }

.tb-lrow__date {
	flex: 1 1 11rem;
	min-width: 0;
	font-size: var(--tb-body-sm-size);
	color: var(--tb-ink);
}
.tb-lrow__time {
	display: block;
	font-size: var(--tb-caption-size);
	color: var(--tb-ink-subtle);
	font-variant-numeric: tabular-nums;
}
.tb-lrow__jackpot {
	margin-left: auto;
	font-size: var(--tb-body-sm-size);
	font-weight: 600;
	color: var(--tb-ink-muted);
	font-variant-numeric: tabular-nums;
}

/* --- Single draw --------------------------------------------------- */
.tb-ldraw {
	padding: var(--tb-space-lg);
	margin-bottom: var(--tb-space-lg);
	background: var(--tb-surface-sunken);
	border: var(--tb-rule);
}
.tb-ldraw__slot {
	margin: 0 0 var(--tb-space-sm);
	font-size: var(--tb-label-size);
	font-weight: var(--tb-label-weight);
	letter-spacing: var(--tb-label-track);
	text-transform: uppercase;
	color: var(--tb-primary);
}
.tb-ldraw__meta {
	display: flex;
	flex-wrap: wrap;
	gap: var(--tb-space-lg);
	margin: var(--tb-space-lg) 0 0;
}
.tb-ldraw__meta dt {
	font-size: var(--tb-label-size);
	font-weight: var(--tb-label-weight);
	letter-spacing: var(--tb-label-track);
	text-transform: uppercase;
	color: var(--tb-ink-subtle);
}
.tb-ldraw__meta dd {
	margin: var(--tb-space-xs) 0 0;
	font-size: var(--tb-body-md-size);
	font-weight: 600;
	color: var(--tb-ink);
	font-variant-numeric: tabular-nums;
}

/* --- Per-game feature ---------------------------------------------- */
.tb-lfeature {
	padding: var(--tb-space-lg);
	margin-bottom: var(--tb-space-xl);
	border: var(--tb-rule-strong);
}
.tb-lfeature__label {
	margin: 0;
	font-size: var(--tb-label-size);
	font-weight: var(--tb-label-weight);
	letter-spacing: var(--tb-label-track);
	text-transform: uppercase;
	color: var(--tb-primary);
}
.tb-lfeature__date {
	margin: var(--tb-space-xs) 0 var(--tb-space-md);
	font-size: var(--tb-body-sm-size);
	color: var(--tb-ink-subtle);
}
.tb-lfeature__jackpot {
	margin: var(--tb-space-md) 0 0;
	font-family: var(--tb-font-headline);
	font-size: var(--tb-headline-lg-size);
	color: var(--tb-ink);
	font-variant-numeric: tabular-nums;
}

/* --- Pager --------------------------------------------------------- */
.tb-lpager {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--tb-space-md);
	margin-top: var(--tb-space-lg);
	font-size: var(--tb-body-sm-size);
}
.tb-lpager__count { color: var(--tb-ink-subtle); }
.tb-lpager__link { font-weight: 600; color: var(--tb-primary); }

/* --- Empty states --------------------------------------------------- */
.tb-lempty {
	padding: var(--tb-space-lg);
	background: var(--tb-surface-sunken);
	border: var(--tb-rule);
}
.tb-lempty__lead {
	margin: 0 0 var(--tb-space-xs);
	font-weight: 600;
	color: var(--tb-ink);
}
.tb-lempty p:last-child {
	margin: 0;
	font-size: var(--tb-body-sm-size);
	color: var(--tb-ink-subtle);
}

/* --- Disclaimer ------------------------------------------------------
   Deliberately not styled down into fine print. People act on these
   numbers with money; the notice has to be readable, not dismissible. */
.tb-ldisclaim {
	margin-top: var(--tb-space-2xl);
	padding: var(--tb-space-md);
	border-left: 3px solid var(--tb-primary);
	background: var(--tb-surface-sunken);
}
.tb-ldisclaim__lead {
	margin: 0;
	font-size: var(--tb-body-sm-size);
	line-height: 1.55;
	color: var(--tb-ink-muted);
}
.tb-ldisclaim__note {
	margin: var(--tb-space-xs) 0 0;
	font-size: var(--tb-caption-size);
	color: var(--tb-ink-subtle);
}

@media (prefers-reduced-motion: reduce) {
	.tb-lboard__pip,
	.tb-lslot__dot { animation: none; }
	.tb-lslot.is-fresh { animation: none; }
}

/* --- Ball sizing on small screens -------------------------------------
   Six balls have to fit the card's inner width on one line. Budget at
   375px: 375 - 32 (container) - 32 (card) = 311px, so six balls plus five
   8px gaps must stay under 271px of ball -> 45px each. At 360px and below
   the budget drops to ~241px of ball, so they step down again. */
@media (max-width: 599px) {
	.tb-lboard__grid { grid-template-columns: 1fr; }
	.tb-ldraw { padding: var(--tb-space-md); }
	.tb-ldraw__meta { gap: var(--tb-space-md); }
	.tb-lrow__jackpot { margin-left: 0; }

	.tb-balls--lg .tb-ball {
		min-width: 2.75rem;               /* 44px -> 6*44 + 40 = 304 */
		height: 2.75rem;
		font-size: var(--tb-headline-sm-size);
	}
}

@media (max-width: 380px) {
	.tb-balls { gap: var(--tb-space-xxs); }

	.tb-ball {
		min-width: 2.25rem;                /* 36px -> 6*36 + 20 = 236 */
		height: 2.25rem;
		font-size: var(--tb-body-sm-size);
	}
	/* At this width there is no room left for a size hierarchy. The
	   feature and draw cards give 240px of inner width at 320px, so the
	   large row drops to the same 36px as everything else: 6*36 + 20 =
	   236, which fits without clipping. A lotto result that needs
	   scrolling to read is a broken lotto result. */
	.tb-balls--lg .tb-ball {
		min-width: 2.25rem;
		height: 2.25rem;
		font-size: var(--tb-body-sm-size);
	}

	/* Match the draw card so both offer the same inner budget. */
	.tb-lfeature { padding: var(--tb-space-md); }
	.tb-balls--sm .tb-ball {
		min-width: 1.625rem;
		height: 1.625rem;
	}
}

/* --- Date strip ------------------------------------------------------ */
.tb-ldates {
	display: flex;
	align-items: center;
	justify-content: space-between;
	flex-wrap: wrap;
	gap: var(--tb-space-sm);
	margin-bottom: var(--tb-space-md);
	font-size: var(--tb-body-sm-size);
}
.tb-ldates__link { color: var(--tb-ink-subtle); }
.tb-ldates__link:hover { color: var(--tb-primary); }
.tb-ldates__today {
	font-weight: 600;
	color: var(--tb-primary);
}

/* Attribution for the upstream data. Sits below the 18+ line, quieter than
   the disclaimer above it but with a visibly linked source name — a credit
   nobody can see is not a credit. */
.tb-ldisclaim__credit {
	margin: var(--tb-space-xs) 0 0;
	font-size: var(--tb-caption-size);
	color: var(--tb-ink-subtle);
}
.tb-ldisclaim__credit a {
	color: var(--tb-primary);
	font-weight: 600;
	text-decoration: underline;
	text-underline-offset: 0.15em;
}
.tb-ldisclaim__credit a:hover { color: var(--tb-primary-dark); }

/* Credit attached to an individual result block. Separated by a rule so it
   reads as provenance for the numbers above it rather than as another
   field in the meta list. */
.tb-lcredit {
	margin: var(--tb-space-md) 0 0;
	padding-top: var(--tb-space-sm);
	border-top: var(--tb-rule);
	font-size: var(--tb-caption-size);
	color: var(--tb-ink-subtle);
}
.tb-lcredit a {
	color: var(--tb-primary);
	font-weight: 600;
	text-decoration: underline;
	text-underline-offset: 0.15em;
}
.tb-lcredit a:hover { color: var(--tb-primary-dark); }

