/* GENERATED by scripts/sync_public_css.py — do not edit.
   Layers 01-03 are copied verbatim from public_html/src/css/ so the app's
   public pages and getconverzo.in cannot drift apart. Edit the marketing
   source, or public-extras.css, then re-run the script. */

/* ==== public_html/src/css/01-tokens.css ==== */
/* ============================================================================
   CONVERZO DESIGN SYSTEM — 01 TOKENS
   ----------------------------------------------------------------------------
   Every colour, size and duration in the site resolves to a token declared
   here. No component below this file may contain a raw hex value.

   The palette is derived from the brand artwork, not chosen: the greens are
   sampled from logos/Converzo.png (#0BBA43 wordmark mark, #43C155 icon) and
   the neutrals carry a faint green cast (hue ~163, saturation ~6%) so that
   "grey" on this site belongs to the brand instead of being stock slate.

   Contrast is not eyeballed. Every text/surface pair below was computed
   against WCAG 2.2 and the ratio is recorded in the comment. Anything that
   carries meaning clears 4.5:1 in BOTH themes; UI borders clear 3:1.

   Note on the primary green: the brand green #0BBA43 gives only 2.59:1 with
   white text, so it CANNOT be a light-mode button fill. --brand-700 (#07802E)
   is the same hue darkened until white text clears AA (5.08:1). The true brand
   green stays the accent — the mark, ticks, chart fills, and the dark-theme
   button, where it pairs with dark ink at 8.4:1.
   ========================================================================= */

:root {
  /* -- brand ramp: one hue, sampled from the logo ------------------------ */
  --brand-50:  #E9F8EE;
  --brand-100: #CDEFD9;
  --brand-200: #9DDFB5;
  --brand-300: #63CE8B;
  --brand-400: #2BC855;   /* dark-theme button fill  */
  --brand-500: #0BBA43;   /* THE brand green — the mark, ticks, accents     */
  --brand-600: #099A38;
  --brand-700: #07802E;   /* light-theme button fill — white text 5.08:1    */
  --brand-800: #06661F;
  --brand-900: #04491A;

  /* -- neutral ramp: green-cast, not stock slate ------------------------- */
  --n-0:   #FFFFFF;
  --n-25:  #FAFCFB;
  --n-50:  #F4F7F5;
  --n-100: #EAEFEC;
  --n-200: #DDE5E1;
  --n-300: #C3CFCA;
  --n-400: #93A6A0;
  --n-500: #6E827C;
  --n-600: #5F736E;
  --n-700: #41544F;
  --n-800: #23322D;
  --n-900: #17201C;
  --n-950: #0E1512;
  --n-990: #0A100E;

  /* -- status ------------------------------------------------------------ */
  --ok-fg-light:   #07802E;  --ok-bg-light:   #E9F8EE;
  --warn-fg-light: #8A5300;  --warn-bg-light: #FDF3E2;
  --err-fg-light:  #B3251E;  --err-bg-light:  #FDECEB;
  --info-fg-light: #10627F;  --info-bg-light: #E6F4F9;
  --ok-fg-dark:    #63CE8B;  --ok-bg-dark:    #10281A;
  --warn-fg-dark:  #F0B454;  --warn-bg-dark:  #2A1F0C;
  --err-fg-dark:   #F08C86;  --err-bg-dark:   #2B1412;
  --info-fg-dark:  #6FC5E3;  --info-bg-dark:  #0C2530;

  /* -- type -------------------------------------------------------------- */
  --font-display: 'Plus Jakarta Sans', ui-sans-serif, system-ui, -apple-system,
                  'Segoe UI', Roboto, sans-serif;
  --font-text:    'Inter', ui-sans-serif, system-ui, -apple-system,
                  'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  --font-mono:    ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;

  /* Mobile is the floor of every clamp, so headings can never overflow a
     320px viewport — that is the width the old hero broke at. */
  --t-display: clamp(2rem,    1.34rem + 3.30vw, 3.5rem);
  --t-h1:      clamp(1.875rem,1.35rem + 2.60vw, 3rem);
  --t-h2:      clamp(1.5rem,  1.19rem + 1.55vw, 2.375rem);
  --t-h3:      clamp(1.1875rem,1.10rem + 0.44vw,1.4375rem);
  --t-h4:      1.0625rem;
  --t-lead:    clamp(1.0625rem,1.01rem + 0.28vw, 1.1875rem);
  --t-body:    1rem;
  --t-sm:      0.9375rem;
  --t-xs:      0.8125rem;
  --t-label:   0.75rem;

  --lh-tight: 1.08;
  --lh-snug:  1.25;
  --lh-body:  1.65;
  --ls-tight: -0.028em;
  --ls-snug:  -0.015em;
  --ls-label:  0.085em;

  /* -- space: 4px base --------------------------------------------------- */
  --s-1: 4px;   --s-2: 8px;   --s-3: 12px;  --s-4: 16px;  --s-5: 20px;
  --s-6: 24px;  --s-8: 32px;  --s-10: 40px; --s-12: 48px; --s-16: 64px;
  --s-20: 80px; --s-24: 96px; --s-30: 120px;
  --section-y: clamp(64px, 8vw, 112px);

  /* -- layout ------------------------------------------------------------ */
  --container: 1200px;
  --container-wide: 1320px;
  --container-prose: 720px;
  --gutter: 20px;

  /* -- radii: four steps, deliberately NOT one pill --------------------- */
  --r-0:  0;       /* rules, dividers, full-bleed bands  */
  --r-sm: 6px;     /* buttons, inputs, chips, tags       */
  --r-md: 10px;    /* cards, panels, menus               */
  --r-lg: 16px;    /* the product window only            */
  --r-full: 999px; /* avatars and status dots ONLY       */

  /* -- motion ------------------------------------------------------------ */
  --e-out: cubic-bezier(0.22, 0.61, 0.24, 1);
  --e-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --d-micro: 140ms;
  --d-base: 220ms;
  --d-reveal: 320ms;

  --z-nav: 100;
  --z-drawer: 200;
  --z-modal: 300;
}

/* ---------------------------------------------------------------- LIGHT ----
   The default. Not pure white everywhere: --bg is white, --surface is a
   1.5%-tinted paper, --elevated returns to white so a card lifts off the
   band behind it without needing a heavy shadow. */
:root,
[data-theme='light'] {
  color-scheme: light;

  --bg:            var(--n-0);
  --bg-alt:        var(--n-50);      /* alternating section band            */
  --surface:       var(--n-50);
  --surface-2:     var(--n-100);
  --elevated:      var(--n-0);
  --inverse-bg:    var(--n-950);     /* the one dark band in a light page   */

  --text:          #0C1411;          /* 18.7:1 on --bg                      */
  --text-2:        var(--n-700);     /*  8.1:1                              */
  --text-muted:    var(--n-600);     /*  5.0:1 — still AA, never below      */
  --text-inverse:  var(--n-25);
  --text-on-brand: var(--n-0);       /*  5.1:1 on --brand-700               */

  --border:        var(--n-200);     /* decorative rules                    */
  --border-subtle: var(--n-100);
  --border-strong: var(--n-400);     /*  3.1:1 — inputs, ghost buttons      */

  --brand:         var(--brand-700); /* fills that carry text               */
  --brand-text:    var(--brand-700); /*  5.1:1 as text                      */
  --brand-mark:    var(--brand-500); /* the logo green, untinted            */
  --brand-soft:    var(--brand-50);
  --brand-soft-br: var(--brand-100);
  --brand-hover:   var(--brand-800);
  --brand-active:  var(--brand-900);

  --ok-fg: var(--ok-fg-light);     --ok-bg: var(--ok-bg-light);
  --warn-fg: var(--warn-fg-light); --warn-bg: var(--warn-bg-light);
  --err-fg: var(--err-fg-light);   --err-bg: var(--err-bg-light);
  --info-fg: var(--info-fg-light); --info-bg: var(--info-bg-light);

  /* Shadows are near-invisible by design. Elevation comes from the border
     and the surface step, not from a drop shadow — that is most of the
     difference between "designed" and "bootstrap card". */
  --sh-sm: 0 1px 2px rgba(12, 20, 17, 0.05);
  --sh-md: 0 2px 4px rgba(12, 20, 17, 0.04), 0 8px 16px -8px rgba(12, 20, 17, 0.08);
  --sh-lg: 0 4px 8px rgba(12, 20, 17, 0.04), 0 24px 48px -16px rgba(12, 20, 17, 0.12);
  --sh-nav: 0 1px 0 var(--border), 0 4px 20px -12px rgba(12, 20, 17, 0.18);
}

/* ----------------------------------------------------------------- DARK ----
   Not an inversion. The background is a desaturated near-black with the same
   green cast as the light neutrals, surfaces step UP in lightness (never via
   opacity, which stacks unpredictably), and the accent moves to --brand-400
   because #07802E is unreadable on black. */
[data-theme='dark'] {
  color-scheme: dark;

  --bg:            var(--n-950);
  --bg-alt:        var(--n-900);
  --surface:       var(--n-900);
  --surface-2:     var(--n-800);
  --elevated:      #1B2621;
  --inverse-bg:    var(--n-800);

  --text:          #EDF3F0;          /* 16.5:1 on --bg                      */
  --text-2:        #B4C3BE;          /* 10.1:1                              */
  --text-muted:    #8FA29D;          /*  6.9:1                              */
  --text-inverse:  #0C1411;
  --text-on-brand: var(--n-990);     /*  8.4:1 on --brand-400               */

  --border:        #2A3733;
  --border-subtle: #202B27;
  --border-strong: #475852;

  --brand:         var(--brand-400);
  --brand-text:    var(--brand-300);
  --brand-mark:    var(--brand-500);
  --brand-soft:    #10281A;
  --brand-soft-br: #1D4430;
  --brand-hover:   var(--brand-300);
  --brand-active:  var(--brand-200);

  --ok-fg: var(--ok-fg-dark);     --ok-bg: var(--ok-bg-dark);
  --warn-fg: var(--warn-fg-dark); --warn-bg: var(--warn-bg-dark);
  --err-fg: var(--err-fg-dark);   --err-bg: var(--err-bg-dark);
  --info-fg: var(--info-fg-dark); --info-bg: var(--info-bg-dark);

  --sh-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
  --sh-md: 0 2px 4px rgba(0, 0, 0, 0.3), 0 8px 16px -8px rgba(0, 0, 0, 0.5);
  --sh-lg: 0 4px 8px rgba(0, 0, 0, 0.3), 0 24px 48px -16px rgba(0, 0, 0, 0.6);
  --sh-nav: 0 1px 0 var(--border), 0 4px 20px -12px rgba(0, 0, 0, 0.7);
}

/* System preference, only while the visitor has not chosen. The toggle writes
   data-theme onto <html>, and the attribute selector above outranks this. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']):not([data-theme='dark']) {
    color-scheme: dark;
    --bg: var(--n-950);            --bg-alt: var(--n-900);
    --surface: var(--n-900);       --surface-2: var(--n-800);
    --elevated: #1B2621;           --inverse-bg: var(--n-800);
    --text: #EDF3F0;               --text-2: #B4C3BE;
    --text-muted: #8FA29D;         --text-inverse: #0C1411;
    --text-on-brand: var(--n-990);
    --border: #2A3733;             --border-subtle: #202B27;
    --border-strong: #475852;
    --brand: var(--brand-400);     --brand-text: var(--brand-300);
    --brand-soft: #10281A;         --brand-soft-br: #1D4430;
    --brand-hover: var(--brand-300); --brand-active: var(--brand-200);
    --ok-fg: var(--ok-fg-dark);     --ok-bg: var(--ok-bg-dark);
    --warn-fg: var(--warn-fg-dark); --warn-bg: var(--warn-bg-dark);
    --err-fg: var(--err-fg-dark);   --err-bg: var(--err-bg-dark);
    --info-fg: var(--info-fg-dark); --info-bg: var(--info-bg-dark);
    --sh-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
    --sh-md: 0 2px 4px rgba(0, 0, 0, 0.3), 0 8px 16px -8px rgba(0, 0, 0, 0.5);
    --sh-lg: 0 4px 8px rgba(0, 0, 0, 0.3), 0 24px 48px -16px rgba(0, 0, 0, 0.6);
    --sh-nav: 0 1px 0 var(--border), 0 4px 20px -12px rgba(0, 0, 0, 0.7);
  }
}

@media (min-width: 768px)  { :root { --gutter: 32px; --t-body: 1.0625rem; } }
@media (min-width: 1280px) { :root { --gutter: 40px; } }

/* ---------------------------------------------------------------------------
   METRIC-MATCHED FALLBACKS
   ---------------------------------------------------------------------------
   Between first paint and the webfont arriving, the page is set in the local
   fallback. If that fallback is a different width and height, every line
   re-flows when the real font lands — which is where most of this page's
   cumulative layout shift came from.

   The numbers below are measured, not guessed: the same string was rendered in
   each webfont and in Arial in a real browser, and the ratio recorded.
     Plus Jakarta Sans / Arial advance width = 101.15%   ascent 104 vs 91
     Inter              / Arial advance width = 106.32%   ascent  97 vs 91
   Overriding ascent/descent as well keeps the line box height identical, so
   the swap moves nothing at all.
   ------------------------------------------------------------------------- */
@font-face {
  font-family: 'Jakarta Fallback';
  src: local('Arial'), local('Helvetica'), local('Liberation Sans');
  size-adjust: 101.15%;
  ascent-override: 102.8%;
  descent-override: 21.7%;
  line-gap-override: 0%;
}
@font-face {
  font-family: 'Inter Fallback';
  src: local('Arial'), local('Helvetica'), local('Liberation Sans');
  size-adjust: 106.32%;
  ascent-override: 91.2%;
  descent-override: 22.6%;
  line-gap-override: 0%;
}
:root {
  --font-display: 'Plus Jakarta Sans', 'Jakarta Fallback', ui-sans-serif, system-ui,
                  -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-text: 'Inter', 'Inter Fallback', ui-sans-serif, system-ui,
               -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

/* ==== public_html/src/css/02-base.css ==== */
/* ============================================================================
   02 BASE — reset, document, typography, focus, layout primitives
   ========================================================================= */

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

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Anchors must not land under the sticky header. */
  scroll-padding-top: 96px;
}

body {
  font-family: var(--font-text);
  font-size: var(--t-body);
  line-height: var(--lh-body);
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-synthesis-weight: none;
  /* overflow-x:hidden on <body> is a bug-hider; it also breaks position:sticky
     in some engines. Sections clip their own overflow instead. */
  overflow-wrap: break-word;
}

/* Any component that sets its own `display` silently defeats the `hidden`
   attribute, because the UA rule for [hidden] is display:none at author-level
   zero. This is why all thirteen industry panels rendered at once. Declared
   once, here, rather than per component. */
[hidden] { display: none !important; }

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

a { color: inherit; text-decoration: none; }
ul, ol { list-style: none; }

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

/* Numerals line up in tables and price grids. */
table, .tabular, .price, .stat-value { font-variant-numeric: tabular-nums; }

/* -------------------------------------------------------------- focus ----
   The previous stylesheet contained ZERO focus rules across 54KB, so keyboard
   users had no visible position anywhere on the site. Two rings: the brand
   ring reads on light surfaces, the offset ring in the page background keeps
   it visible when the element sits on a coloured or dark fill. */
:focus { outline: none; }
:focus-visible {
  outline: 2px solid var(--brand-500);
  outline-offset: 2px;
  border-radius: var(--r-sm);
  box-shadow: 0 0 0 4px var(--bg);
}
.skip-link {
  /* fixed, not absolute: absolute positions it relative to the top of the
     DOCUMENT, so a visitor who tabbed after scrolling got a focused link
     sitting somewhere far above the viewport. */
  position: fixed;
  left: var(--s-4);
  top: -100px;
  z-index: 999;
  padding: var(--s-3) var(--s-5);
  background: var(--brand);
  color: var(--text-on-brand);
  font-weight: 600;
  border-radius: var(--r-sm);
  transition: top var(--d-micro) var(--e-out);
}
.skip-link:focus { top: var(--s-4); }

::selection { background: var(--brand-100); color: var(--n-950); }
[data-theme='dark'] ::selection { background: var(--brand-800); color: var(--n-0); }

/* ---------------------------------------------------------- typography ----
   Display face for headings only. Body face everywhere else — Inter is the
   better reader at 15–17px and has the numerals the pricing tables need. */
h1, h2, h3, h4, h5, h6, .display {
  font-family: var(--font-display);
  font-weight: 800;
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-tight);
  color: var(--text);
  text-wrap: balance;
}
h1, .t-display { font-size: var(--t-display); }
h2, .t-h2 { font-size: var(--t-h2); line-height: var(--lh-snug); }
h3, .t-h3 { font-size: var(--t-h3); font-weight: 700; line-height: 1.3; letter-spacing: var(--ls-snug); }
h4, .t-h4 { font-size: var(--t-h4); font-weight: 700; line-height: 1.4; letter-spacing: 0; }

p { color: var(--text-2); }
p + p { margin-top: var(--s-4); }

.lead {
  font-size: var(--t-lead);
  line-height: 1.55;
  color: var(--text-2);
  text-wrap: pretty;
}
.t-sm    { font-size: var(--t-sm); }
.t-xs    { font-size: var(--t-xs); }
.muted   { color: var(--text-muted); }
.ink     { color: var(--text); }
.measure { max-width: 62ch; }

/* The one accent treatment for headline words. A flat colour swap, not a
   gradient fill — gradient text was the single loudest tell of the old
   design, and it also broke text selection and forced-colours mode. */
.hl { color: var(--brand-text); }

/* Section label. Hangs on the rule above the heading like an editorial
   masthead rather than floating in a pill. */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  font-family: var(--font-text);
  font-size: var(--t-label);
  font-weight: 600;
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--text-muted);
}
.eyebrow::before {
  content: '';
  width: 18px;
  height: 2px;
  background: var(--brand-mark);
  flex: none;
}

/* -------------------------------------------------------------- layout ----*/
.container {
  width: 100%;
  max-width: calc(var(--container) + var(--gutter) * 2);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.container--wide  { max-width: calc(var(--container-wide) + var(--gutter) * 2); }
.container--prose { max-width: calc(var(--container-prose) + var(--gutter) * 2); }

.section { padding-block: var(--section-y); position: relative; }
.section--tight { padding-block: clamp(48px, 6vw, 80px); }
.section--alt   { background: var(--bg-alt); }

/* The rule system. Sections are separated by a hairline that spans the
   container, not the viewport — so every section edge lines up with the
   content column. This is the site's structural signature. */
.section--ruled { border-top: 1px solid var(--border-subtle); }
.rule { border: 0; border-top: 1px solid var(--border); margin-block: var(--s-12); }

/* Section head: label + heading + optional lead, left-aligned by default.
   Centre only where a section genuinely has no left-hand anchor. */
.section-head { max-width: 46rem; margin-bottom: clamp(32px, 4vw, 56px); }
.section-head > * + * { margin-top: var(--s-4); }
.section-head--center { margin-inline: auto; text-align: center; }
.section-head--center .eyebrow { justify-content: center; }

.stack > * + * { margin-top: var(--s-4); }
.stack-lg > * + * { margin-top: var(--s-6); }

.grid { display: grid; gap: var(--s-6); }
@media (min-width: 640px)  { .grid-sm-2 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 768px)  { .grid-md-2 { grid-template-columns: repeat(2, 1fr); }
                             .grid-md-3 { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1024px) { .grid-lg-2 { grid-template-columns: repeat(2, 1fr); }
                             .grid-lg-3 { grid-template-columns: repeat(3, 1fr); }
                             .grid-lg-4 { grid-template-columns: repeat(4, 1fr); } }

.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;
}

/* -------------------------------------------------------------- motion ----*/
.reveal {
  opacity: 0;
  transform: translateY(10px);
  transition: opacity var(--d-reveal) var(--e-out),
              transform var(--d-reveal) var(--e-out);
}
.reveal.is-in { opacity: 1; transform: none; }
.reveal-stagger > * {
  opacity: 0;
  transform: translateY(10px);
  transition: opacity var(--d-reveal) var(--e-out),
              transform var(--d-reveal) var(--e-out);
}
.reveal-stagger.is-in > * { opacity: 1; transform: none; }
.reveal-stagger.is-in > *:nth-child(2) { transition-delay: 60ms; }
.reveal-stagger.is-in > *:nth-child(3) { transition-delay: 120ms; }
.reveal-stagger.is-in > *:nth-child(4) { transition-delay: 180ms; }
.reveal-stagger.is-in > *:nth-child(5) { transition-delay: 240ms; }
.reveal-stagger.is-in > *:nth-child(6) { transition-delay: 300ms; }

/* Reduced motion removes movement, never content. Everything still arrives —
   it just arrives instantly and stays put. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .reveal, .reveal-stagger > * { opacity: 1 !important; transform: none !important; }
}

/* Windows High Contrast / forced colours: hand control to the OS. */
@media (forced-colors: active) {
  .btn, .card, .panel { border: 1px solid ButtonBorder; }
  :focus-visible { outline: 2px solid Highlight; }
}

/* ==== public_html/src/css/03-components.css ==== */
/* ============================================================================
   03 COMPONENTS — buttons, chips, cards, forms, tables, accordion, tabs
   ----------------------------------------------------------------------------
   Radius discipline (the thing that most separates a designed page from a
   generated one): --r-sm on controls, --r-md on containers, --r-full ONLY on
   avatars and status dots. Nothing here is a pill.
   ========================================================================= */

/* --------------------------------------------------------------- button ----
   Minimum height 44px on every variant and size, so every control clears the
   touch target guideline without a separate mobile rule. */
.btn {
  --btn-h: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: var(--btn-h);
  padding: 0 var(--s-5);
  font-family: var(--font-text);
  font-size: var(--t-sm);
  font-weight: 600;
  letter-spacing: -0.006em;
  line-height: 1;
  white-space: nowrap;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  cursor: pointer;
  transition: background-color var(--d-micro) var(--e-out),
              border-color var(--d-micro) var(--e-out),
              color var(--d-micro) var(--e-out),
              transform var(--d-micro) var(--e-out);
}
.btn:active { transform: translateY(1px); }
.btn .icon { width: 16px; height: 16px; flex: none; }

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

.btn--secondary {
  background: var(--bg);
  color: var(--text);
  border-color: var(--border-strong);
}
.btn--secondary:hover { background: var(--surface); border-color: var(--text-muted); }

.btn--ghost { background: transparent; color: var(--text-2); }
.btn--ghost:hover { background: var(--surface); color: var(--text); }

/* For use on --inverse-bg bands only. */
.btn--onDark {
  background: transparent;
  color: #EDF3F0;
  border-color: rgba(237, 243, 240, 0.32);
}
.btn--onDark:hover { background: rgba(237, 243, 240, 0.1); border-color: rgba(237, 243, 240, 0.55); }

.btn--lg { --btn-h: 52px; font-size: var(--t-body); padding: 0 var(--s-6); }
.btn--sm { --btn-h: 38px; font-size: var(--t-xs); padding: 0 var(--s-4); }
/* On a touch device the compact variants stop being compact. --btn-h is the
   only thing that has to change, which is the reason it is a variable.
   (.icon-btn's coarse override lives at the end of 04-chrome.css, because it
   is declared there and would otherwise lose to its own base rule.) */
@media (pointer: coarse) {
  .btn--sm { --btn-h: 44px; }
  /* A .link that stands alone is a control and needs a 44px box. One sitting
     inside a sentence is exempt (WCAG 2.5.8 "Inline") and must keep the line
     height of the prose around it, so the :not(p) guard matters. */
  :not(p) > .link { min-height: 44px; }
}
.btn--block { display: flex; width: 100%; }

.btn-row { display: flex; flex-wrap: wrap; gap: var(--s-3); }
@media (max-width: 479px) { .btn-row > .btn { flex: 1 1 100%; } }

/* Text link that reads as an action without being a button. */
.link {
  display: inline-flex;
  align-items: center;
  /* WCAG 2.5.8 wants 24px of height even on an inline link. */
  min-height: 24px;
  gap: var(--s-2);
  color: var(--brand-text);
  font-weight: 600;
  /* Underline is not decoration here — it is the non-colour signal, so the
     link is still identifiable to a reader who cannot distinguish the green. */
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  text-decoration-color: color-mix(in srgb, currentColor 35%, transparent);
  transition: text-decoration-color var(--d-micro) var(--e-out);
}
.link:hover { text-decoration-color: currentColor; }
.link .icon { width: 14px; height: 14px; transition: transform var(--d-micro) var(--e-out); }
.link:hover .icon { transform: translateX(2px); }

/* ----------------------------------------------------------- chip / tag ----*/
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: 5px var(--s-3);
  font-size: var(--t-xs);
  font-weight: 500;
  color: var(--text-2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
}
.chip .icon { width: 14px; height: 14px; flex: none; color: var(--brand-mark); }
.chip--brand { background: var(--brand-soft); border-color: var(--brand-soft-br); color: var(--brand-text); }
.chip--plain { background: transparent; }

.chip-row { display: flex; flex-wrap: wrap; gap: var(--s-2); }

/* Status dot — one of only two places --r-full is allowed. */
.dot { width: 7px; height: 7px; border-radius: var(--r-full); background: var(--brand-mark); flex: none; }

/* ----------------------------------------------------------------- card ----
   Border-first elevation. A card is a border + a surface step; the shadow is
   almost subliminal. Cards are used to GROUP, never as default packaging. */
.card {
  background: var(--elevated);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: var(--s-6);
  transition: border-color var(--d-base) var(--e-out),
              box-shadow var(--d-base) var(--e-out);
}
/* Grid children stretch, so cards in a row are already equal height. Making
   the card a column and pushing the tag row down aligns those rows across the
   whole grid instead of leaving them ragged at whatever height the copy ends. */
.grid > .card { display: flex; flex-direction: column; }
.card > .chip-row:last-child { margin-top: auto; padding-top: var(--s-5); }

/* Vertical rhythm inside a card. Without this the eyebrow, heading and body
   sit flush against each other — every child is a block with margin:0 from the
   reset, so the card has to supply the spacing itself. */
.card > * + * { margin-top: var(--s-3); }
.card > .eyebrow + * { margin-top: var(--s-2); }
.card > .chip-row { margin-top: var(--s-5); }
.card--pad-lg { padding: clamp(24px, 3vw, 40px); }
.card--link:hover { border-color: var(--border-strong); box-shadow: var(--sh-md); }
.card--brand { border-color: var(--brand-soft-br); background: var(--brand-soft); }
.card--flush { background: transparent; border: 0; padding: 0; }

.card-title { font-size: var(--t-h4); font-weight: 700; font-family: var(--font-display); }
.card-body { color: var(--text-2); font-size: var(--t-sm); margin-top: var(--s-2); }

/* Icon plate. Square with a small radius — not a circle, not a gradient blob. */
.plate {
  display: grid;
  place-items: center;
  width: 40px; height: 40px;
  flex: none;
  border-radius: var(--r-sm);
  background: var(--brand-soft);
  border: 1px solid var(--brand-soft-br);
  color: var(--brand-text);
}
.plate .icon { width: 20px; height: 20px; }

/* ------------------------------------------------------------ tick list ----
   The list bullet is the WhatsApp double tick. Product-native iconography
   instead of the ✅ emoji the old markup used, which screen readers announced
   as "white heavy check mark" before every single feature. */
.ticks { display: grid; gap: var(--s-3); }
.ticks li { display: flex; gap: var(--s-3); align-items: flex-start; font-size: var(--t-sm); color: var(--text-2); }
.ticks .icon { width: 18px; height: 18px; margin-top: 2px; flex: none; color: var(--brand-mark); }

/* ---------------------------------------------------------------- forms ----*/
.field { display: grid; gap: var(--s-2); }
.field label { font-size: var(--t-sm); font-weight: 600; color: var(--text); }
.field .hint { font-size: var(--t-xs); color: var(--text-muted); }
.input, .select, .textarea {
  min-height: 44px;
  padding: 10px var(--s-4);
  width: 100%;
  font-size: var(--t-body);
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  transition: border-color var(--d-micro) var(--e-out), box-shadow var(--d-micro) var(--e-out);
}
.input::placeholder, .textarea::placeholder { color: var(--text-muted); }
.input:hover, .select:hover, .textarea:hover { border-color: var(--text-muted); }
.input:focus-visible, .select:focus-visible, .textarea:focus-visible {
  border-color: var(--brand);
  outline: 2px solid var(--brand-500);
  outline-offset: 1px;
}
.field[data-invalid] .input { border-color: var(--err-fg); }
.field .error { display: flex; gap: 6px; align-items: center; font-size: var(--t-xs); color: var(--err-fg); font-weight: 500; }
.btn[aria-busy='true'] { pointer-events: none; opacity: 0.75; }

/* ------------------------------------------------------------ accordion ----
   Native <details>. Keyboard and screen-reader behaviour comes free; the only
   custom part is the marker and the height transition. */
.accordion { border-top: 1px solid var(--border); }
.acc-item { border-bottom: 1px solid var(--border); }
.acc-item > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  padding: var(--s-5) 0;
  min-height: 44px;
  cursor: pointer;
  list-style: none;
  font-family: var(--font-display);
  font-size: var(--t-h4);
  font-weight: 700;
  color: var(--text);
}
.acc-item > summary::-webkit-details-marker { display: none; }
.acc-item > summary:hover { color: var(--brand-text); }
.acc-mark {
  position: relative;
  width: 20px; height: 20px;
  flex: none;
  color: var(--text-muted);
  transition: transform var(--d-base) var(--e-out), color var(--d-base) var(--e-out);
}
.acc-mark::before, .acc-mark::after {
  content: ''; position: absolute; inset: 50% 0 auto 0;
  height: 1.5px; background: currentColor; border-radius: 2px;
}
.acc-mark::after { transform: rotate(90deg); transition: opacity var(--d-base) var(--e-out); }
.acc-item[open] > summary .acc-mark { transform: rotate(90deg); color: var(--brand-text); }
.acc-item[open] > summary .acc-mark::after { opacity: 0; }
.acc-panel { padding-bottom: var(--s-5); color: var(--text-2); font-size: var(--t-sm); max-width: 62ch; }
.acc-panel a { color: var(--brand-text); text-decoration: underline; text-underline-offset: 3px; }

/* ----------------------------------------------------------------- tabs ----*/
.tabs {
  display: flex;
  gap: var(--s-1);
  overflow-x: auto;
  scrollbar-width: none;
  border-bottom: 1px solid var(--border);
}
.tabs::-webkit-scrollbar { display: none; }
.tab {
  position: relative;
  padding: var(--s-3) var(--s-4);
  min-height: 44px;
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--text-muted);
  white-space: nowrap;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color var(--d-micro) var(--e-out), border-color var(--d-micro) var(--e-out);
}
.tab:hover { color: var(--text); }
.tab[aria-selected='true'] { color: var(--text); border-bottom-color: var(--brand-mark); }

/* ---------------------------------------------------------------- table ----*/
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
table.data { width: 100%; border-collapse: collapse; font-size: var(--t-sm); min-width: 560px; }
table.data th, table.data td { padding: var(--s-3) var(--s-4); text-align: left; border-bottom: 1px solid var(--border-subtle); }
table.data thead th { font-weight: 600; color: var(--text-muted); font-size: var(--t-xs);
  text-transform: uppercase; letter-spacing: var(--ls-label); border-bottom-color: var(--border); }
table.data tbody tr:hover { background: var(--surface); }

/* The tick that prefixes every feature bullet is drawn by CSS, not by an
   inline <svg> repeated a hundred and thirty times in the markup. A mask
   rather than a background-image, so it still inherits the theme colour. */
.ticks li::before,
.hero-trust li::before {
  content: '';
  flex: none;
  width: 18px;
  height: 18px;
  background: var(--brand-mark);
  -webkit-mask: var(--tick) center / contain no-repeat;
  mask: var(--tick) center / contain no-repeat;
}
.ticks li::before { margin-top: 1px; }
.hero-trust li::before { width: 15px; height: 15px; }
:root {
  --tick: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
}

/* ==== app/static/css/public-extras.css ==== */
/* ============================================================================
   PUBLIC PAGES — app.getconverzo.in only
   ----------------------------------------------------------------------------
   Layers 01-03 above are the shared design system, copied from the marketing
   site. Everything here is chrome the app's logged-out pages need and the
   marketing site does not: the split auth screen, the booking-form shell, and
   the small nav/footer those pages carry.
   ========================================================================= */

/* Theme-swapped brand lockup. The marketing site declares this in its layer 04
   (chrome), which this bundle does not include — without it both variants render
   side by side. */
.logo-dark { display: none; }
[data-theme='dark'] .logo-light { display: none; }
[data-theme='dark'] .logo-dark { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) .logo-light { display: none; }
  :root:not([data-theme='light']) .logo-dark { display: block; }
}

/* ------------------------------------------------------------- app nav ----*/
.pub-header {
  position: sticky; top: 0; z-index: var(--z-nav);
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  -webkit-backdrop-filter: saturate(1.4) blur(12px);
  backdrop-filter: saturate(1.4) blur(12px);
  border-bottom: 1px solid var(--border-subtle);
}
@supports not (backdrop-filter: blur(1px)) { .pub-header { background: var(--bg); } }
.pub-header-inner { display: flex; align-items: center; gap: var(--s-4); min-height: 64px; }
.pub-brand { display: flex; align-items: center; min-height: 44px; flex: none; }
.pub-brand img { width: 132px; height: 38px; object-fit: contain; object-position: left center; }
/* At 320px the lockup plus the CTA is 4px too wide for the content column.
   Shrink the mark rather than drop the button — the CTA is the point of the
   header. */
@media (max-width: 360px) {
  .pub-brand img { width: 108px; height: 31px; }
  .pub-actions .btn--sm { padding-inline: var(--s-3); }
}
.pub-actions { display: flex; align-items: center; gap: var(--s-2); margin-left: auto; }
/* Same action priority as the marketing header: the primary CTA never leaves,
   the language selector and "Log in" step aside as the header narrows. Without
   this the three of them overflowed a 390px viewport. */
.pub-actions .lang-slot { display: none; }
@media (min-width: 560px) { .pub-actions .lang-slot { display: block; } }
.pub-actions .btn--ghost { display: none; }
@media (min-width: 480px) { .pub-actions .btn--ghost { display: inline-flex; } }

.pub-footer {
  border-top: 1px solid var(--border);
  background: var(--surface);
  padding-block: var(--s-8);
  font-size: var(--t-xs);
  color: var(--text-muted);
}
.pub-footer-inner {
  display: flex; flex-wrap: wrap; gap: var(--s-3) var(--s-6);
  align-items: center; justify-content: space-between;
}
.pub-footer nav { display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-5); }
.pub-footer a { display: inline-flex; align-items: center; min-height: 40px; }
.pub-footer a:hover { color: var(--text); text-decoration: underline; text-underline-offset: 3px; }
/* Short words like "Terms" are only ~38px wide; give every footer link a full
   44px box so they are the same size to a thumb. */
@media (pointer: coarse) {
  .pub-footer a { min-height: 44px; min-width: 44px; justify-content: center; }
}

/* ---------------------------------------------------------- auth screen ----
   Two panels: a quiet pitch on the left, the form on the right. On a phone the
   pitch is dropped entirely rather than stacked — someone arriving at a login
   screen on their phone came to sign in, not to read marketing copy. */
.auth { min-height: 100dvh; display: grid; }
@media (min-width: 1000px) { .auth { grid-template-columns: 1fr 1fr; } }

.auth-pitch { display: none; }
@media (min-width: 1000px) {
  .auth-pitch {
    display: flex; flex-direction: column; justify-content: space-between;
    padding: clamp(40px, 4vw, 64px);
    background: var(--surface);
    border-right: 1px solid var(--border);
  }
}
.auth-pitch h1 { font-size: clamp(2rem, 1.4rem + 1.8vw, 2.75rem); max-width: 15ch; }
.auth-pitch .lead { margin-top: var(--s-5); max-width: 40ch; }
.auth-pitch .ticks { margin-top: var(--s-8); }

.auth-panel {
  display: flex; flex-direction: column; justify-content: center;
  padding: clamp(24px, 5vw, 56px) var(--gutter);
}
.auth-card { width: 100%; max-width: 27rem; margin-inline: auto; }
.auth-card > header { margin-bottom: var(--s-6); }
.auth-card h2 { font-size: var(--t-h2); }
.auth-card header p { margin-top: var(--s-2); }

/* Tabs. A two-button group, not a sliding pill — and the panels are swapped
   with [hidden] rather than with opacity + pointer-events, so a keyboard user
   cannot tab into the form that is not showing. */
.auth-tabs {
  display: grid; grid-template-columns: 1fr 1fr; gap: 2px;
  padding: 3px; margin-bottom: var(--s-6);
  border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--surface);
}
.auth-tabs button {
  min-height: 40px; font-size: var(--t-sm); font-weight: 600;
  color: var(--text-muted); border-radius: 4px;
  transition: color var(--d-micro) var(--e-out), background-color var(--d-micro) var(--e-out);
}
.auth-tabs button[aria-selected='true'] { color: var(--text); background: var(--bg); box-shadow: var(--sh-sm); }
@media (pointer: coarse) { .auth-tabs button { min-height: 44px; } }

.auth-form { display: grid; gap: var(--s-4); }
.auth-form .btn { margin-top: var(--s-2); }

/* Password field: the reveal button sits inside the input, so the input needs
   room for it and the button needs a real 44px box. */
.field-pw { position: relative; }
.field-pw .input { padding-right: 46px; }
.pw-toggle {
  position: absolute; right: 2px; bottom: 0;
  display: grid; place-items: center;
  width: 44px; height: 44px;
  color: var(--text-muted); border-radius: var(--r-sm);
}
.pw-toggle:hover { color: var(--text); }
.pw-toggle .icon { width: 18px; height: 18px; }
.pw-toggle .i-hide { display: none; }
.pw-toggle[aria-pressed='true'] .i-show { display: none; }
.pw-toggle[aria-pressed='true'] .i-hide { display: block; }

.field-row { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-3); }
.field-row a { font-size: var(--t-xs); }

/* Divider with a word in it. */
.or {
  display: flex; align-items: center; gap: var(--s-4);
  margin-block: var(--s-5);
  font-size: var(--t-xs); color: var(--text-muted);
  text-transform: uppercase; letter-spacing: var(--ls-label);
}
.or::before, .or::after { content: ''; flex: 1; height: 1px; background: var(--border); }

/* Alerts. Icon plus text, never colour alone. */
.alert {
  display: flex; gap: var(--s-3); align-items: flex-start;
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-sm);
  border: 1px solid;
  border-left-width: 3px;
  font-size: var(--t-sm);
  margin-bottom: var(--s-5);
}
.alert .icon { width: 18px; height: 18px; flex: none; margin-top: 1px; }
.alert p { color: inherit; }
.alert--error { background: var(--err-bg); border-color: var(--err-fg); color: var(--err-fg); }
.alert--ok { background: var(--ok-bg); border-color: var(--ok-fg); color: var(--ok-fg); }
.alert button {
  display: block; margin-top: var(--s-2);
  font-weight: 600; color: inherit;
  text-decoration: underline; text-underline-offset: 3px;
}

/* ------------------------------------------------------- booking / forms ----*/
.form-page { padding-block: clamp(40px, 6vw, 80px); }
.form-shell {
  max-width: 40rem; margin-inline: auto;
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--elevated);
  padding: clamp(24px, 4vw, 40px);
}
.form-shell > header { margin-bottom: var(--s-6); }
.form-grid { display: grid; gap: var(--s-4); }
@media (min-width: 560px) { .form-grid--2 { grid-template-columns: repeat(2, 1fr); } }
.form-grid .btn { margin-top: var(--s-2); }

.pub-note { font-size: var(--t-xs); color: var(--text-muted); margin-top: var(--s-4); }
.pub-note a { color: var(--brand-text); text-decoration: underline; text-underline-offset: 3px; }

/* Google Translate widget chrome — the app drives translation from its own
   selector, so the injected banner, tooltip and gadget stay hidden. */
.goog-te-banner-frame, .goog-te-banner, .skiptranslate, .goog-te-gadget,
#goog-gt-tt, .goog-te-balloon-frame, #google_translate_element,
.VIpgJd-ZGain-xl05Fe-OJuWdf { display: none !important; }
body { top: 0 !important; position: static !important; }
.goog-tooltip, .goog-tooltip:hover { display: none !important; }
.goog-text-highlight { background-color: transparent !important; box-shadow: none !important; }
font { background-color: transparent !important; box-shadow: none !important; }

/* Consent checkbox: the box keeps its own 20px hit area but the whole label is
   clickable, which is the part people actually aim at. */
.consent {
  display: flex; align-items: flex-start; gap: var(--s-3);
  padding: var(--s-3) 0;
  font-size: var(--t-sm); color: var(--text-2);
  cursor: pointer;
}
.consent input[type='checkbox'] {
  width: 20px; height: 20px; flex: none; margin-top: 1px;
  accent-color: var(--brand);
  cursor: pointer;
}
.consent:hover { color: var(--text); }

/* --------------------------------------------------------- demo booking ----*/
.booking-grid { display: grid; gap: var(--s-6); align-items: start; }
@media (min-width: 900px) { .booking-grid { grid-template-columns: 1fr 1.15fr; } }
.booking-grid .form-shell { max-width: none; margin-inline: 0; }
.phone-row { display: flex; gap: var(--s-2); }
.phone-row .select { width: 108px; flex: none; }
.phone-row .input { flex: 1; min-width: 0; }
.alert--warn { background: var(--warn-bg); border-color: var(--warn-fg); color: var(--warn-fg); }
.alert--warn .btn { color: var(--text); }
/* booking.js writes this into #slotStatus. It used to be an inline #FCA5A5,
   which is a dark-theme red and unreadable on the light background. */
.slot-error { color: var(--err-fg); font-weight: 500; }

.auth-top {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-4); margin-bottom: var(--s-8);
}

/* --------------------------------------------- language selector glyphs ----
   partials/language_selector.html is shared with the authenticated app, where
   Font Awesome is still loaded, so its <i class="fas fa-globe"> markup cannot
   change. These pages dropped that stylesheet, which left the globe and the
   selected-language tick rendering as 0x0 nothing.

   Drawn here with a mask instead, scoped to public.css so the authenticated
   templates are untouched and keep using the real Font Awesome glyphs. */
.cz-lang-icon,
.cz-lang-tick {
  display: inline-block;
  width: 16px;
  height: 16px;
  background: currentColor;
  vertical-align: -3px;
  -webkit-mask-position: center; mask-position: center;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-size: contain; mask-size: contain;
}
.cz-lang-icon {
  -webkit-mask-image: var(--globe); mask-image: var(--globe);
}
.cz-lang-tick {
  -webkit-mask-image: var(--tick); mask-image: var(--tick);
}
:root {
  --globe: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9.2'/%3E%3Cpath d='M2.8 12h18.4'/%3E%3Cpath d='M12 2.8a14 14 0 0 1 0 18.4 14 14 0 0 1 0-18.4'/%3E%3C/svg%3E");
}

/* One-time codes: wide tracking and tabular figures so six digits read as six
   digits rather than a word. */
.input--code {
  font-size: 1.375rem;
  font-weight: 600;
  letter-spacing: 0.34em;
  text-align: center;
  font-variant-numeric: tabular-nums;
  padding-left: calc(var(--s-4) + 0.34em);
}
.input--code::placeholder { letter-spacing: 0.34em; font-weight: 400; }

/* ------------------------------------------------------- onboarding OTP ----*/
.otp-block {
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--surface);
  padding: var(--s-4);
}
.otp-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-3); margin-bottom: var(--s-3);
  font-size: var(--t-sm); font-weight: 600; color: var(--text);
}
.otp-head > span { display: inline-flex; align-items: center; gap: 7px; }
.otp-head .icon { width: 16px; height: 16px; color: var(--text-muted); }
.otp-done { font-size: var(--t-xs); font-weight: 600; color: var(--ok-fg); }
.otp-done .icon { color: var(--ok-fg); width: 14px; height: 14px; }
.otp-block .input:disabled { color: var(--text-muted); background: var(--surface-2); cursor: not-allowed; }
.otp-actions {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--s-3) var(--s-4); margin-top: var(--s-5);
}
.otp-actions > div { display: flex; flex-wrap: wrap; gap: var(--s-4); }

/* ------------------------------------------------------------ theme toggle --
   The marketing site declares .icon-btn and the sun/moon swap in its layer 04
   (chrome), which this bundle does not include. Same geometry and same tokens,
   so the control reads identically on both domains.

   These pages always carry an explicit data-theme (the shells render
   data-theme="light" and their pre-paint script upgrades it to "dark"), so the
   swap needs no prefers-color-scheme fallback: light is the default, including
   with JavaScript off. */
.icon-btn {
  display: grid; place-items: center;
  width: 40px; height: 40px;
  flex: none;
  color: var(--text-2);
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: color var(--d-micro) var(--e-out), background-color var(--d-micro) var(--e-out);
}
.icon-btn:hover { color: var(--text); background: var(--surface); }
.icon-btn .icon { width: 18px; height: 18px; }
/* WCAG 2.5.8: a 40px box is fine for a mouse, not for a thumb. */
@media (pointer: coarse) { .icon-btn { width: 44px; height: 44px; } }

/* Sun shows in light (press it for dark), moon shows in dark. Exactly one is in
   the DOM flow at a time so the button never changes width. */
.theme-toggle .i-moon { display: none; }
[data-theme='dark'] .theme-toggle .i-sun { display: none; }
[data-theme='dark'] .theme-toggle .i-moon { display: block; }

/* The auth card's top row is brand | controls. The toggle and the language
   selector travel together on the right, so they need their own flex box —
   dropped straight into .auth-top they would space-between three ways. */
.auth-top-actions { display: flex; align-items: center; gap: var(--s-2); }

/* Same action priority as the marketing header, and for the same reason: at
   375px the brand plus a 44px toggle plus the CTA is wider than the content
   column, and the CTA is the point of the header. The toggle steps aside
   first. Nothing is lost — the auth screens keep theirs at every width, and
   the choice is stored per origin, so setting it there carries to here. */
.pub-actions .theme-toggle { display: none; }
@media (min-width: 480px) { .pub-actions .theme-toggle { display: grid; } }
