/* tokens.css */
/* kbwen Lab design tokens — v5.1 (dark navy stage)
   IP anchor: #1E90FF bright blue · #0a1220 deep navy · #B8E8F0 cyan highlight
   The 9 category hues are desaturated + pulled toward the cool axis.
   Treat them as IDENTITY markers — do not invent new ones.
*/
:root {
  /* ---------- Type stacks ---------- */
  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui,
               "Helvetica Neue", Arial, "PingFang TC", "Noto Sans TC", sans-serif;
  --font-mono: "IBM Plex Mono", "JetBrains Mono", ui-monospace, "SF Mono",
               Menlo, Consolas, monospace;

  /* ---------- Core palette (dark-navy stage) ---------- */
  --color-bg:            #0a1220;
  --color-bg-deep:       #060c18;
  --color-surface:       #111b2e;
  --color-surface-2:     #182743;
  --color-surface-3:     #23375a;
  --color-text:          #eaf2ff;
  --color-text-muted:    #8ca0c0;
  --color-text-faint:    #7a90b3;  /* bumped from #5a6f8e (3.5:1) to clear WCAG AA 4.5:1 on dark navy bg */
  --color-border:        #213251;
  --color-border-muted:  #18253d;

  /* ---------- Brand primary ---------- */
  --color-primary:        #38a9ff;
  --color-primary-strong: #1e90ff;
  --color-accent:         #b8e8f0;

  /* ---------- 9 category accents ---------- */
  --color-cat-dev:    #f4c57a;
  --color-cat-ai:     #9d8bf5;
  --color-cat-quant:  #4fd4b5;
  --color-cat-games:  #f28a9b;
  --color-cat-text:   #c6dc6a;
  --color-cat-design: #6bc7ea;
  --color-cat-time:   #63d9d9;
  --color-cat-edu:    #b6a6f0;
  --color-cat-local:  #f6a85f;

  /* v5.1 shorthand aliases (used in shared-v5.1.css & homepage) */
  --cat-dev:    var(--color-cat-dev);
  --cat-ai:     var(--color-cat-ai);
  --cat-quant:  var(--color-cat-quant);
  --cat-games:  var(--color-cat-games);
  --cat-text:   var(--color-cat-text);
  --cat-design: var(--color-cat-design);
  --cat-time:   var(--color-cat-time);
  --cat-edu:    var(--color-cat-edu);
  --cat-local:  var(--color-cat-local);

  /* ---------- Semantic states ---------- */
  --color-success: #4fd4b5;
  --color-warning: #f6a85f;
  --color-danger:  #f28a9b;
  --color-new:     #c6dc6a;

  /* ---------- Semantic / chrome ---------- */
  --color-on-primary:     #07101e;  /* "ink" text on a primary-blue button */
  --color-border-section: var(--color-border-muted);

  /* ---------- Shadows ---------- */
  --shadow-sm: 0 4px 12px rgba(0,0,0,0.25);
  --shadow-md: 0 16px 48px rgba(0,0,0,0.4);

  /* ---------- Type scale ---------- */
  --text-xs:  12px;
  --text-sm:  14px;
  --text-md:  16px;
  --text-lg:  18px;
  --text-xl:  24px;
  --text-2xl: 32px;
  --text-3xl: 42px;
  --text-4xl: 56px;

  /* ---------- Spacing (4px base) ---------- */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  --space-8: 64px;
  --space-9: 96px;

  /* ---------- Radius ---------- */
  --radius-xs:   3px;
  --radius-sm:   10px;
  --radius-md:   16px;
  --radius-lg:   24px;
  --radius-pill: 999px;

  /* ---------- Motion ---------- */
  --transition-fast: 160ms ease;
  --transition-base: 220ms ease;
  --transition-slow: 400ms ease;

  /* ---------- Layout ---------- */
  --container-max: 1200px;
  --content-max:   860px;

  /* ---------- Z-index ---------- */
  --z-header:   40;
  --z-dropdown: 50;
  --z-modal:    60;

  /* line-height shorthand */
  --line-base: 1.6;
}

/* Category → card-accent mapping (used by card component) */
.card[data-category="ai"]     { --card-accent: var(--cat-ai);     --card-accent-bg: rgba(157,139,245,0.10); }
.card[data-category="quant"]  { --card-accent: var(--cat-quant);  --card-accent-bg: rgba(79,212,181,0.10); }
.card[data-category="dev"]    { --card-accent: var(--cat-dev);    --card-accent-bg: rgba(244,197,122,0.10); }
.card[data-category="games"]  { --card-accent: var(--cat-games);  --card-accent-bg: rgba(242,138,155,0.10); }
.card[data-category="local"]  { --card-accent: var(--cat-local);  --card-accent-bg: rgba(246,168,95,0.10); }
.card[data-category="design"] { --card-accent: var(--cat-design); --card-accent-bg: rgba(107,199,234,0.10); }
.card[data-category="text"]   { --card-accent: var(--cat-text);   --card-accent-bg: rgba(198,220,106,0.10); }
.card[data-category="time"]   { --card-accent: var(--cat-time);   --card-accent-bg: rgba(99,217,217,0.10); }
.card[data-category="edu"]    { --card-accent: var(--cat-edu);    --card-accent-bg: rgba(182,166,240,0.10); }

/* Legacy cat-* class shorthand (also used by browse pages) */
.cat-ai     { --card-accent: var(--cat-ai); }
.cat-quant  { --card-accent: var(--cat-quant); }
.cat-dev    { --card-accent: var(--cat-dev); }
.cat-games  { --card-accent: var(--cat-games); }
.cat-local  { --card-accent: var(--cat-local); }
.cat-design { --card-accent: var(--cat-design); }
.cat-text   { --card-accent: var(--cat-text); }
.cat-time   { --card-accent: var(--cat-time); }
.cat-edu    { --card-accent: var(--cat-edu); }

/* layout.css */
[hidden] { display: none !important; }

/* Skip-to-content link — visible only on keyboard focus */
.skip-link {
    position: absolute;
    left: 16px;
    top: 8px;
    z-index: 100;
    padding: 8px 12px;
    background: var(--color-primary);
    color: var(--color-on-primary);
    font: 500 13px/1 var(--font-sans);
    border-radius: var(--radius-sm);
    text-decoration: none;
    transform: translateY(-200%);
    transition: transform 160ms ease;
}
.skip-link:focus,
.skip-link:focus-visible {
    transform: translateY(0);
    outline: 2px solid var(--color-text);
    outline-offset: 2px;
}

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

html,
body {
    margin: 0;
    padding: 0;
    background: var(--color-bg);
    color: var(--color-text);
    font-family: var(--font-sans);
    line-height: var(--line-base);
}

/* Sticky header (57px) plus breathing room so anchor scrolls land below
   the chrome instead of under it. Mirrors the rule in homepage.css for
   chrome parity. Affects skip-link target #main and any in-page anchor. */
html {
    scroll-padding-top: 72px;
}

a {
    color: inherit;
    text-decoration: none;
    touch-action: manipulation;
}

img {
    max-width: 100%;
    display: block;
}

.page-shell {
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
}

.site-header,
.site-footer {
    border-bottom: 1px solid var(--color-border);
    background: var(--color-bg);
}

.site-footer {
    border-top: 1px solid var(--color-border);
    border-bottom: 0;
    margin-top: auto;
}

.site-header-bar,
.site-header-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: nowrap;
}

.site-brand {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-weight: 600;
    font-size: var(--text-md);
    letter-spacing: -0.01em;
}

/* v5.1 brand glyph — used by both card-variant and tool-variant headers */
.site-brand-mark {
    width: 20px;
    height: 20px;
    border-radius: 6px;
    background: linear-gradient(135deg, var(--color-primary) 0%, var(--cat-ai) 100%);
    display: inline-block;
    flex: none;
}
.site-brand-lab {
    color: var(--chrome-ink-muted, var(--color-text-muted));
    font-weight: 500;
    margin-left: 2px;
}

.site-nav {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-3);
    font-size: var(--text-sm);
    color: var(--color-text-muted);
}

.site-nav a {
    transition: color var(--transition-fast);
}

.site-nav a:hover,
.site-nav a:focus-visible,
.site-nav a[aria-current="page"] {
    color: var(--color-text);
}

/* Card-variant header nav layout. Mirror of homepage.css/browse.css so the
   tool.css bundle (which concatenates layout.css) gets the same chrome on
   any tool page that ever embeds a card-variant header (e.g. iframe-tool
   template). Same selectors enforced by scripts/validate-chrome-parity.mjs. */
.site-header[data-header-variant="cards"] .site-nav {
    display: flex;
    align-items: center;
    gap: var(--space-5);
}
.site-header[data-header-variant="cards"] .site-nav-links {
    display: flex;
    gap: var(--space-5);
}
.site-header[data-header-variant="cards"] .site-nav-links a {
    font-size: var(--text-sm);
    color: var(--color-text-muted);
    padding: var(--space-2) 0;
    transition: color var(--transition-fast);
}
.site-header[data-header-variant="cards"] .site-nav-links a:hover,
.site-header[data-header-variant="cards"] .site-nav-links a[aria-current="page"] { color: var(--color-text); }
.site-header[data-header-variant="cards"] .site-nav-links a:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
    border-radius: 2px;
}
@media (max-width: 719px) {
    .site-header[data-header-variant="cards"] .site-nav-links { display: none; }
}

/* Lang dropdown — full widget shared by homepage AND tool pages. The block
   below is an exact copy of the .lang-dropdown* styles in homepage.css so
   pages built from the tool.css bundle (which concatenates layout.css) get
   the same chrome. Tool pages now use the full dropdown menu, not a single
   toggle button — homepage already used this widget; round-9 unifies them. */
.lang-dropdown {
    position: relative;
    margin-left: var(--space-3);
}
.lang-dropdown-trigger {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 10px 6px 8px;
    background: transparent;
    border: 1px solid var(--color-border-muted);
    border-radius: var(--radius-pill);
    color: var(--chrome-ink, var(--color-text));
    font: 500 12px/1 var(--font-sans);
    cursor: pointer;
    transition: border-color var(--transition-fast), background var(--transition-fast);
}
.lang-dropdown-trigger:hover {
    border-color: var(--color-border);
    background: color-mix(in oklch, var(--color-primary) 5%, transparent);
}
.lang-dropdown-trigger:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}
.lang-dropdown-globe { width: 14px; height: 14px; color: var(--chrome-ink-muted, var(--color-text-muted)); }
.lang-dropdown-current {
    font: 500 12px/1 var(--font-mono);
    letter-spacing: 0.06em;
    color: var(--chrome-ink, var(--color-text));
}
.lang-dropdown-caret {
    width: 8px; height: 5px;
    color: var(--color-text-faint);
    transition: transform var(--transition-fast);
}
.lang-dropdown[data-open="true"] .lang-dropdown-caret { transform: rotate(180deg); }
.lang-dropdown-menu {
    position: absolute;
    right: 0;
    top: calc(100% + 6px);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: 4px;
    min-width: 200px;
    box-shadow: var(--shadow-md);
    z-index: var(--z-dropdown);
}
.lang-dropdown-menu[hidden] { display: none; }
.lang-dropdown-item {
    display: grid;
    grid-template-columns: auto 1fr auto;
    gap: 10px;
    align-items: center;
    padding: 8px 10px;
    border-radius: var(--radius-sm);
    text-decoration: none;
    color: var(--color-text);
    font: 500 13px/1 var(--font-sans);
    transition: background var(--transition-fast);
}
.lang-dropdown-item:hover { background: var(--color-surface-2); }
.lang-dropdown-code {
    font: 500 11px/1 var(--font-mono);
    letter-spacing: 0.08em;
    color: var(--color-text-muted);
    padding: 2px 6px;
    border: 1px solid var(--color-border-muted);
    border-radius: var(--radius-xs);
}
.lang-dropdown-name { color: var(--color-text); }
.lang-dropdown-check { color: var(--color-primary); font-weight: 700; opacity: 0; }
.lang-dropdown-item.is-active .lang-dropdown-check { opacity: 1; }
.lang-dropdown-item.is-active .lang-dropdown-code {
    border-color: var(--color-primary);
    color: var(--color-primary);
}
.lang-dropdown-item.is-disabled { opacity: 0.45; pointer-events: none; }
.lang-dropdown-soon {
    font: 500 9px/1 var(--font-mono);
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--color-text-faint);
    padding: 3px 6px;
    background: var(--color-bg-deep);
    border-radius: var(--radius-xs);
}
@media (max-width: 900px) {
    .lang-dropdown-trigger {
        min-height: 44px;
        padding: 8px 14px;
    }
    /* Mobile tap target — chrome links rendered as bare text height (22-26px)
       leave fingers fishing on the footer column links and the brand mark.
       Bump them to the 44px floor with vertical padding on the anchor itself
       so the underline / hover area matches the tappable area. */
    .site-footer-col a {
        min-height: 44px;
        display: flex;
        align-items: center;
    }
    .site-header[data-header-variant="cards"] .site-brand,
    .site-header[data-header-variant="tool"] .site-brand {
        min-height: 44px;
        align-items: center;
    }
}

.site-nav--compact {
    gap: 10px;
}

.site-nav--footer {
    justify-content: flex-start;
    gap: 10px 14px;
}

.site-nav--footer-compact {
    align-self: center;
}

.container {
    width: min(100% - 32px, var(--container-max));
    margin-inline: auto;
}

.section {
    padding-block: var(--space-7);
}

.section-sm {
    padding-block: var(--space-6);
}

.stack {
    display: grid;
    gap: var(--space-5);
}

.cluster {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    align-items: center;
}

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

.grid-2 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

.grid-3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

.page-title {
    font-size: var(--text-3xl);
    line-height: 1.15;
    margin: 0;
    /* Avoid 1-2-char orphan lines on titles like "Character & Word
       Counter" or "字數 / 字元計數器". balance distributes line
       lengths; keep-all stops CJK word breaking ("計數器" stays
       together — without it Chinese titles can break to a single
       trailing glyph orphan). For Latin, keep-all is identical to
       default — words still break at whitespace.
       NOT using overflow-wrap: anywhere because it bites on
       multi-word Latin titles at narrow viewports
       ("Year System Converter" → "Convert" / "er" mid-glyph break). */
    text-wrap: balance;
    word-break: keep-all;
}

.page-title--display {
    font-size: clamp(42px, 6vw, 64px);
}

.page-subtitle {
    color: var(--color-text-muted);
    max-width: 72ch;
    margin: 0;
    text-wrap: pretty;
}

.section-surface-hero {
    position: relative;
    padding-block: clamp(52px, 9vw, 88px);
    background:
        linear-gradient(180deg, var(--color-bg) 0%, var(--color-surface) 100%),
        radial-gradient(circle at top right, rgba(88, 166, 255, 0.08), transparent 48%);
}

.section-card-surface {
    position: relative;
    padding-block: clamp(40px, 7vw, 64px);
    background: var(--color-bg);
}

.section-card-surface+.section-card-surface {
    border-top: 1px solid var(--color-border-section);
}

.section-faq {
    padding-block: clamp(32px, 5vw, 48px);
    background: var(--color-bg);
    border-top: 1px solid var(--color-border-section);
}

.faq-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    max-width: 72ch;
}

.faq-item {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: var(--space-4);
}

.faq-item summary.faq-question {
    cursor: pointer;
    font-weight: 600;
    list-style: none;
    user-select: none;
}

.faq-item summary.faq-question::-webkit-details-marker {
    display: none;
}

.faq-item p.faq-answer {
    margin-top: var(--space-3);
    color: var(--color-text-muted);
    line-height: 1.6;
}

.surface-hero {
    display: grid;
    gap: var(--space-3);
}

.surface-hero--center {
    max-width: 68ch;
    margin-inline: auto;
    justify-items: center;
    text-align: center;
}

.surface-hero--left {
    max-width: 60ch;
}

.surface-eyebrow {
    margin: 0;
    color: var(--color-primary);
    text-transform: uppercase;
    letter-spacing: 0.14em;
    font-size: 11px;
    font-weight: 700;
}

.surface-section-heading {
    display: grid;
    gap: 6px;
    max-width: 58ch;
}

.section-title {
    margin: 0;
    font-size: clamp(28px, 4vw, 38px);
    line-height: 1.15;
}

.section-copy {
    margin: 0;
    color: var(--color-text-muted);
    max-width: 70ch;
}

.surface-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
}

.tool-shell {
    padding: var(--space-5);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    background: var(--color-surface);
    box-shadow: var(--shadow-sm);
}

.tool-ui {
    display: grid;
    gap: var(--space-4);
}

.page-shell--tool .site-header.section-sm,
.page-shell--tool .site-footer.section-sm,
.page-shell--cards .site-header.section-sm,
.page-shell--cards .site-footer.section-sm {
    padding-block: 14px;
}

.section-tool-page {
    padding-block: var(--space-5);
}

.tool-frame {
    display: grid;
    gap: var(--space-4);
}

.tool-frame>* {
    min-width: 0;
}

.tool-frame--utility {
    gap: var(--space-4);
}

.tool-frame--analysis,
.tool-frame--playful {
    gap: var(--space-5);
}

.tool-header-block {
    display: grid;
    gap: 6px;
    max-width: 60ch;
}

.footer-grid {
    display: grid;
    gap: var(--space-4);
    align-items: start;
}

.footer-grid--cards {
    grid-template-columns: minmax(0, 1.3fr) minmax(0, 1.5fr) minmax(0, 1fr);
}

.footer-grid--tool {
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    column-gap: 18px;
}

.footer-brand {
    display: grid;
    gap: 4px;
    min-width: 0;
}

.footer-title {
    font-size: var(--text-md);
    letter-spacing: 0.02em;
}

.footer-copy,
.footer-heading {
    margin: 0;
}

.footer-copy {
    max-width: 42ch;
    color: var(--color-text-muted);
    font-size: var(--text-sm);
}

.footer-nav-group {
    display: grid;
    gap: 8px;
    min-width: 0;
}

.footer-connect-stack {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px 14px;
}

/* Card-style site-footer (shared by home, category, AND tool pages — Round 9
   delegates renderToolFooter to renderCardFooter). Copied from homepage.css
   so tool.css bundle picks it up and tool pages render with the same
   typography (eyebrow 11px, tagline 16px, credit 12px). Without this block
   tool pages fell back to base 16px on every line and the user complained
   the footer "全錯". */
.site-footer[data-footer-variant="cards"] {
    background:
        linear-gradient(180deg, transparent 0%, color-mix(in oklch, var(--color-primary) 4%, transparent) 100%),
        var(--color-bg);
    border-top: 1px solid var(--color-border-muted);
    padding: var(--space-8) 0 var(--space-6);
    margin-top: var(--space-8);
    color: var(--color-text-muted);
    font-size: var(--text-sm);
}
.site-footer[data-footer-variant="cards"] .site-footer-inner {
    display: grid;
    grid-template-columns: minmax(0, 1.4fr) minmax(0, 1.6fr);
    gap: var(--space-7);
    align-items: start;
}
.site-footer-brand { max-width: 480px; }
/* Chrome text color tokens — themed tools that override .site-header /
   .site-footer backgrounds map their ink palette into these two custom
   props on their root class (e.g. `.color-studio-page { --chrome-ink:
   var(--cs-ink); --chrome-ink-muted: var(--cs-ink-soft); }`). The fallback
   is the canonical dark-theme palette so tools that don't theme the
   chrome get the default cascade. */
.site-footer-eyebrow {
    display: flex;
    align-items: center;
    gap: 8px;
    font: 500 11px/1 var(--font-mono);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--chrome-ink-muted, var(--color-text-muted));
    margin-bottom: var(--space-3);
    flex-wrap: wrap;
}
.site-footer-eyebrow .mono {
    color: var(--color-primary);
    background: color-mix(in oklch, var(--color-primary) 10%, transparent);
    padding: 3px 8px;
    border-radius: var(--radius-xs);
    font-weight: 600;
}
.site-footer-eyebrow-sep { opacity: 0.5; }
.site-footer-tagline {
    font-size: var(--text-md);
    line-height: 1.55;
    color: var(--chrome-ink, var(--color-text));
    margin: 0 0 var(--space-4);
    text-wrap: pretty;
}
.site-footer-credit {
    font-size: var(--text-xs);
    color: var(--chrome-ink-muted, var(--color-text-muted));
    letter-spacing: 0.02em;
}
.site-footer-cols {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-5);
}
.site-footer-col { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.site-footer-col-title {
    font: 600 11px/1 var(--font-mono);
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--chrome-ink, var(--color-text));
    margin-bottom: 4px;
}
.site-footer-col-links {
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.site-footer-col-links--wrap {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 6px var(--space-4);
}
.site-footer-col a {
    font-size: var(--text-sm);
    color: var(--chrome-ink-muted, var(--color-text-muted));
    text-decoration: none;
    transition: color var(--transition-fast), padding var(--transition-fast);
}
.site-footer-col a:hover {
    color: var(--chrome-ink, var(--color-text));
    padding-left: 4px;
}
.site-footer-col a:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
    border-radius: 2px;
}
.site-footer-col .footer-kofi-widget { margin-top: 4px; }
.site-footer-col .footer-kofi-button:hover { padding-left: 14px; }
@media (max-width: 900px) {
    .site-footer[data-footer-variant="cards"] .site-footer-inner { grid-template-columns: 1fr; }
}
@media (max-width: 600px) {
    .site-footer-cols { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 479px) {
    .site-footer-cols { grid-template-columns: 1fr; }
    /* Browse column keeps its 2-col grid on mobile — 10 links in a single
       475px-tall stack reads like dead space. Two columns of 5 rows is
       scannable and roughly half the height. */
}

.footer-connect-stack .site-nav {
    flex: 1 1 auto;
    min-width: 0;
}

.footer-kofi-widget {
    display: inline-flex;
    align-items: center;
    min-height: 32px;
    flex: 0 0 auto;
}

.footer-tool-links {
    min-width: 0;
    justify-self: start;
}

.footer-tool-links .site-nav {
    justify-content: flex-start;
}

.footer-tool-kofi {
    display: flex;
    align-items: center;
    justify-self: end;
    flex: 0 0 auto;
}

/* Ko-fi button — used by both card-variant and tool-variant footers.
   Selector is double-classed so its `color` wins over `.site-footer-col a
   { color: var(--color-text-muted) }`, which previously bled muted-grey
   text onto the bright primary-blue button (~1.05:1 contrast). */
.site-footer .footer-kofi-button,
.footer-kofi-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 8px 14px;
    border-radius: var(--radius-sm);
    background: var(--color-primary);
    color: var(--color-on-primary);
    font: 500 13px/1 var(--font-sans);
    text-decoration: none;
    transition: background var(--transition-fast), transform var(--transition-fast);
}
.footer-kofi-button:hover {
    background: var(--color-primary-strong);
    color: #fff;
    transform: translateY(-1px);
}

.footer-cta-row {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    align-items: center;
}

.footer-cta-row--tool {
    width: min(100% - 32px, var(--container-max));
    margin: 12px auto 0;
}

.footer-cta-button {
    justify-content: center;
}

.footer-banner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 12px 16px;
    padding: 14px 0 18px;
    border-bottom: 1px solid var(--color-border);
    margin-bottom: 18px;
}

.footer-banner-copy-group {
    display: grid;
    gap: 4px;
    min-width: 0;
}

.footer-banner-eyebrow,
.footer-banner-title,
.footer-banner-copy {
    margin: 0;
}

.footer-banner-eyebrow {
    color: var(--color-primary);
    text-transform: uppercase;
    letter-spacing: 0.12em;
    font-size: 11px;
    font-weight: 700;
}

.footer-banner-title {
    font-size: var(--text-md);
}

.footer-banner-copy {
    color: var(--color-text-muted);
    font-size: var(--text-sm);
    max-width: 56ch;
}

.footer-heading {
    color: var(--color-primary);
    text-transform: uppercase;
    letter-spacing: 0.12em;
    font-size: 11px;
    font-weight: 700;
}

.tool-frame--utility .tool-header-block {
    max-width: 38ch;
}

.tool-frame--analysis .tool-header-block {
    max-width: 50ch;
}

.tool-frame--playful .tool-header-block {
    max-width: 46ch;
}

.tool-header-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin: 0;
    color: var(--color-text-muted);
    font-size: var(--text-sm);
}

.tool-notes {
    display: grid;
    gap: var(--space-3);
}

.tool-notes>* {
    margin: 0;
}

.tool-note-inline {
    margin: 0;
    color: var(--color-text-muted);
    font-size: var(--text-sm);
}

@media (max-width: 960px) {
    .section-tool-page {
        padding-block: var(--space-4);
    }

    .tool-frame--analysis,
    .tool-frame--playful {
        gap: var(--space-4);
    }
}

@media (max-width: 720px) {

    .page-shell--tool .site-header.section-sm,
    .page-shell--tool .site-footer.section-sm,
    .page-shell--cards .site-header.section-sm,
    .page-shell--cards .site-footer.section-sm {
        padding-block: 12px;
    }

    .section-tool-page {
        padding-block: 12px;
    }

    .tool-header-block {
        gap: 4px;
    }

    .tool-header-meta,
    .tool-note-inline {
        font-size: 12px;
    }

    .footer-banner,
    .footer-cta-row--tool {
        width: min(100% - 32px, var(--container-max));
    }

    .footer-grid--tool {
        grid-template-columns: 1fr;
        align-items: start;
    }

    .footer-tool-links,
    .footer-tool-kofi {
        justify-self: start;
    }

    .footer-tool-links .site-nav {
        justify-content: flex-start;
    }
}

@media (max-width: 480px) {
    .container {
        width: min(100% - 24px, var(--container-max));
    }

    .tool-frame {
        gap: 12px;
    }

    .footer-cta-row--tool,
    .footer-banner {
        width: min(100% - 24px, var(--container-max));
    }
}

/* ─────────────────────────────────────────────────────────────────
   Focus-visible safety net.

   `:where()` carries zero specificity, so any component that already
   has a `.foo:focus-visible { … }` rule will still win. This rule
   only fires for interactive elements nobody else has styled — the
   historical foot-gun is a per-tool `outline: none` that strips the
   browser default without leaving a visible replacement. The shared
   chrome (`.button`, `.skip-link`, `.lang-dropdown-trigger`,
   `.site-footer-col a`, etc.) already has its own rings above and
   continues to use them.

   2px is the WCAG minimum for non-text contrast on focus indicators;
   `--color-primary` reads on every theme stage (dark navy, paper,
   mist) because each theme override re-defines the token.
   ───────────────────────────────────────────────────────────────── */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

/* ─────────────────────────────────────────────────────────────────
   iOS Safari zoom-on-focus prevention.

   When an input/textarea/select renders with computed font-size below
   16px, iOS Safari (and iPadOS Safari) zooms the viewport in on focus,
   which then leaves the user manually pinch-zooming back out. The
   accepted fix is to ensure ≥16px font-size on every text-entry field
   at mobile widths. We use `max(16px, 1em)` so any tool that already
   set a larger size keeps it; only sub-16px values get bumped.

   Scoped to ≤720px so per-tool typography on desktop / iPad-landscape
   is left alone. `:where()` keeps specificity at 0 — any tool that
   needs to override (e.g. mono code editors that intentionally use
   smaller type and are content with the zoom) can do so with a
   single class-level rule.
   ───────────────────────────────────────────────────────────────── */
@media (max-width: 720px) {
    /* `!important` is intentional. Many per-tool styles set inputs/textarea/
       select font-size below 16px (e.g. `.nbc-input--mono { font-size:
       0.88rem }`, `.ts-tz-select { font-size: 0.8rem }`) which trips iOS
       Safari's zoom-on-focus across half the toolbox. Pretty selectors
       with stacked :not() climb to (0,7,1) but still lose to two-class
       overrides on textarea/select; the only stable way to suppress the
       zoom universally is to mark this rule winning. */
    input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]):not([type="file"]):not([type="range"]):not([type="color"]),
    textarea,
    select {
        font-size: max(16px, 1em) !important;
    }
}

/* ─────────────────────────────────────────────────────────────────
   Reduced motion safety net (applies site-wide via tool.css and
   browse.css bundles).

   homepage.css already shipped this rule for `/`; this layout-level
   copy ensures every tool page also collapses transitions and
   animations when the user has set the system preference. Per-tool
   stylesheets that intentionally keep a specific motion (e.g. a
   timing-gauge that still needs to pulse for the breathing pacer to
   make sense) can opt back in with `animation-duration: revert;`
   inside their own (prefers-reduced-motion: reduce) block.
   ───────────────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

/* cards.css */
@keyframes card-enter {
    from { opacity: 0; transform: translateY(12px); }
    to   { opacity: 1; transform: translateY(0); }
}

.card {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    box-shadow: var(--shadow-sm);
    padding: var(--space-5);
    display: grid;
    gap: var(--space-3);
    min-height: 100%;
    transition: transform var(--transition-fast), border-color var(--transition-fast), box-shadow var(--transition-fast);
    animation: card-enter 0.4s ease both;
}

.card:nth-child(2) { animation-delay: 60ms; }
.card:nth-child(3) { animation-delay: 120ms; }
.card:nth-child(4) { animation-delay: 180ms; }
.card:nth-child(5) { animation-delay: 240ms; }

@media (prefers-reduced-motion: reduce) {
    .card { animation: none; }
}

@media (hover: hover) {
    .card:hover {
        transform: translateY(-2px);
        border-color: var(--color-primary);
        box-shadow: var(--shadow-md);
    }
}

.card:focus-within {
    border-color: var(--color-primary);
    box-shadow: var(--shadow-md);
}

/* Category accent fallback (overridden by inline --card-accent when per-tool accent is set) */
.card[data-category="ai"]          { --card-accent: var(--color-cat-ai); --card-accent-bg: rgba(167, 139, 250, 0.12); }
.card[data-category="quant"]       { --card-accent: var(--color-cat-quant); --card-accent-bg: rgba(52, 211, 153, 0.12); }
.card[data-category="dev"]         { --card-accent: var(--color-cat-dev); --card-accent-bg: rgba(251, 191, 36, 0.12); }
.card[data-category="games"]       { --card-accent: var(--color-cat-games); --card-accent-bg: rgba(248, 113, 113, 0.12); }
.card[data-category="local"]       { --card-accent: var(--color-cat-local); --card-accent-bg: rgba(251, 146, 60, 0.12); }
.card[data-category="design"]      { --card-accent: var(--color-cat-design); --card-accent-bg: rgba(56, 189, 248, 0.12); }
.card[data-category="text"]        { --card-accent: var(--color-cat-text); --card-accent-bg: rgba(163, 230, 53, 0.12); }
.card[data-category="time"]        { --card-accent: var(--color-cat-time); --card-accent-bg: rgba(34, 211, 238, 0.12); }
.card[data-category="edu"]         { --card-accent: var(--color-cat-edu); --card-accent-bg: rgba(192, 132, 252, 0.12); }

/* Left border follows per-tool accent (unified with icon frame + eyebrow color) */
.card[data-category] { border-left: 3px solid var(--card-accent); }

/* ── Card header: icon badge + eyebrow in one row ──────────────────────── */

.card-header {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-height: 36px;             /* stable row height with or without icon */
}

.card-icon-frame {
    width: 36px;
    height: 36px;
    border-radius: var(--radius-sm);
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    background: var(--card-accent-bg);
    color: var(--card-accent, var(--color-primary));
}

.card-icon {
    width: 22px;
    height: 22px;
}

/* ── Card eyebrow ───────────────────────────────────────────────────────── */

.card-eyebrow {
    margin: 0;
    color: var(--card-accent, var(--color-primary));
    text-transform: uppercase;
    letter-spacing: 0.12em;
    font-size: 11px;
    font-weight: 700;
}

.card-count-badge {
    display: inline-block;
    margin-left: var(--space-2);
    padding: 1px 8px;
    border-radius: var(--radius-sm);
    background: var(--color-border);
    color: var(--color-text-muted);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: none;
    vertical-align: middle;
}

.card-title {
    margin: 0;
    font-size: var(--text-xl);
    line-height: 1.2;
}

.card-text {
    margin: 0;
    color: var(--color-text-muted);
}

.card-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
    margin-top: auto;
}

.card-cta {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.card-link {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    /* Ensure WCAG 2.5.5 target size — related-tools links inside the
       tool-discovery card render at 26px without this floor, which is
       under 32px even in the relaxed AA threshold. min-height keeps the
       tap target ≥44px without forcing vertical padding when surrounding
       grid gap already spaces them. */
    min-height: 44px;
    color: var(--color-primary);
    font-weight: 600;
}

.category-hint {
    margin: 0;
    color: var(--color-text-muted);
    font-size: var(--text-sm);
    font-style: italic;
}

.tool-discovery {
    display: grid;
    gap: var(--space-4);
    margin-top: var(--space-5);
    /* Tool-discovery sits on the dark surface and must stay readable
       regardless of the host tool's theme. id-validator (light cream
       theme) inherited #2a2440 dark-navy text down through here, which
       was nearly invisible on the dark `.card` background. Pin the
       cascade to the canonical dark-theme tokens so any future light-
       themed tool can't accidentally make the discovery copy unreadable. */
    color: var(--color-text);
}
.tool-discovery .card-eyebrow { color: var(--color-primary); }
.tool-discovery .card-title { color: var(--color-text); }
.tool-discovery .card-text,
.tool-discovery__list,
.tool-discovery__faq,
.tool-discovery__search-note {
    color: var(--color-text-muted);
}

.tool-discovery__intro {
    display: grid;
    gap: var(--space-2);
    max-width: 64ch;
}

.tool-discovery__title {
    margin: 0;
    font-size: clamp(1.35rem, 2.2vw, 1.8rem);
    line-height: 1.15;
}

.tool-discovery__search-notes {
    display: grid;
    gap: 6px;
}

.tool-discovery__search-note {
    margin: 0;
    color: var(--color-text-muted);
    font-size: var(--text-sm);
}

.tool-discovery__grid {
    align-items: stretch;
}

.tool-discovery__card {
    align-content: start;
}

.tool-discovery__list,
.tool-discovery__faq {
    margin: 0;
    color: var(--color-text-muted);
}

.tool-discovery__list {
    padding-left: 1.1rem;
    display: grid;
    gap: var(--space-2);
}

.tool-discovery__list--plain {
    list-style: none;
    padding-left: 0;
}

.tool-discovery__example-item {
    display: grid;
    gap: 4px;
}

.tool-discovery__example-item strong {
    color: var(--color-text);
}

.tool-discovery__faq {
    display: grid;
    gap: var(--space-3);
}

.tool-discovery__faq-item {
    display: grid;
    gap: 4px;
    margin: 0;
}

.tool-discovery__faq-item dt {
    font-weight: 700;
    color: var(--color-text);
}

.tool-discovery__faq-item dd {
    margin: 0;
}

.tool-discovery__related {
    display: grid;
    gap: var(--space-2);
    padding-top: var(--space-3);
    border-top: 1px solid var(--color-border);
}

.tool-discovery__subheading {
    margin: 0;
    font-size: var(--text-base);
    line-height: 1.2;
}

.browse-discovery {
    padding-top: 0;
}

.browse-discovery__intro {
    display: grid;
    gap: var(--space-2);
    max-width: 72ch;
}

.browse-discovery__grid {
    align-items: stretch;
}

.browse-discovery__card {
    align-content: start;
}

.browse-discovery__list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: grid;
    gap: var(--space-3);
}

.browse-discovery__item {
    display: grid;
    gap: 4px;
}

.browse-discovery__link {
    color: var(--color-text);
    font-weight: 700;
}

.browse-discovery__detail {
    color: var(--color-text-muted);
    font-size: var(--text-sm);
}

/* buttons.css */
.button {
    appearance: none;
    border: 1px solid var(--color-border);
    background: var(--color-surface-2);
    color: var(--color-text);
    border-radius: var(--radius-sm);
    padding: 12px 20px;
    min-height: 44px;
    font: inherit;
    cursor: pointer;
    touch-action: manipulation;
    transition: border-color var(--transition-fast), background var(--transition-fast);
}

@media (hover: hover) {
    .button:hover {
        border-color: var(--color-primary);
        transform: translateY(-1px);
    }
}

.button:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
    border-color: var(--color-primary);
}

.button-primary {
    background: var(--color-primary);
    border-color: var(--color-primary);
    color: #07111d;
    font-weight: 700;
}

.button-secondary {
    background: transparent;
}

.button-sm {
    padding: 6px 14px;
    min-height: 0;
    font-size: 0.82rem;
}

.input,
.textarea {
    width: 100%;
    border: 1px solid var(--color-border);
    background: var(--color-surface-2);
    color: var(--color-text);
    border-radius: var(--radius-sm);
    padding: 12px 14px;
    font: inherit;
}

.textarea {
    min-height: 160px;
    resize: vertical;
}

/* responsive.css */
@media (max-width: 1024px) {
    .grid-3 {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .page-title {
        font-size: 36px;
    }

    .site-header-bar {
        align-items: flex-start;
    }

    .footer-grid--cards {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    }
}

@media (max-width: 720px) {
    .section {
        padding-block: var(--space-6);
    }

    .section-sm {
        padding-block: var(--space-5);
    }

    .grid-2,
    .grid-3 {
        grid-template-columns: 1fr;
    }

    .container {
        width: min(100% - 24px, var(--container-max));
    }

    .page-title {
        font-size: 30px;
    }

    .page-title--display {
        font-size: clamp(38px, 12vw, 52px);
    }

    .tool-shell {
        padding: var(--space-4);
    }

    .site-header-bar {
        flex-direction: column;
        align-items: flex-start;
    }

    .site-nav {
        justify-content: flex-start;
        gap: 10px;
        font-size: 12px;
    }

    .footer-grid--cards,
    .footer-grid--tool {
        grid-template-columns: 1fr;
    }

    .site-nav--footer-compact {
        align-self: flex-start;
    }

    .section-surface-hero,
    .section-card-surface {
        padding-block: var(--space-6);
    }
}

@media (max-width: 480px) {
    .site-nav {
        row-gap: 6px;
    }

    .surface-section-heading,
    .surface-hero {
        gap: var(--space-2);
    }

    .card {
        padding: var(--space-4);
    }
}
