* { box-sizing: border-box; }

/* Dark is the site-wide default regardless of system preference; light mode only */
/* applies once the user has explicitly switched to it (persisted via localStorage, */
/* see the inline theme script in head.ejs / dash-head.ejs) and stays sticky until */
/* they switch back — the JS always tags <html data-theme> on load, so this bare */
/* :root block only matters as the no-JS fallback, and it matches the default. */
:root {
  color-scheme: dark;
  --bg: #262624;
  /* Faint chat-bubble tile behind the page. Panels set an opaque --panel-bg, so this is only
     ever visible in the gutters around them and never sits behind a table or a form. */
  --chat-pattern: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%27320%27%20height%3D%27320%27%20viewBox%3D%270%200%20320%20320%27%3E%3Cg%20fill%3D%27none%27%20stroke%3D%27rgba%28217%2C119%2C87%2C0.09%29%27%20stroke-width%3D%271.85%27%20stroke-linejoin%3D%27round%27%20stroke-linecap%3D%27round%27%3E%3Cg%20transform%3D%27translate%2814%2C10%29%20scale%281.10%29%20rotate%28-8%2012%2012%29%27%3E%3Cpath%20d%3D%27M3%207a4%204%200%200%201%204-4h10a4%204%200%200%201%204%204v6a4%204%200%200%201-4%204h-7l-7%205v-5a4%204%200%200%201%200-10z%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28104%2C22%29%20scale%280.84%29%20rotate%2812%2012%2012%29%27%3E%3Cpath%20d%3D%27M12%2020s-7-4.6-7-9.2A4%204%200%200%201%2012%208a4%204%200%200%201%207%202.8C19%2015.4%2012%2020%2012%2020z%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28196%2C12%29%20scale%280.99%29%20rotate%280%2012%2012%29%27%3E%3Cpath%20d%3D%27M2%2012s4-7%2010-7%2010%207%2010%207-4%207-10%207-10-7-10-7z%27%2F%3E%3Ccircle%20cx%3D%2712%27%20cy%3D%2712%27%20r%3D%273%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28272%2C40%29%20scale%280.72%29%20rotate%2816%2012%2012%29%27%3E%3Cpath%20d%3D%27M12%203l2.7%206.3%206.8.6-5.1%204.4%201.5%206.7L12%2017.5%206.1%2021l1.5-6.7L2.5%209.9l6.8-.6z%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%2852%2C74%29%20scale%281.02%29%20rotate%28-14%2012%2012%29%27%3E%3Cpath%20d%3D%27M2%2012l19-8-7%2019-3-8-9-3z%27%2F%3E%3Cpath%20d%3D%27M21%204L10%2015%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28148%2C84%29%20scale%280.93%29%20rotate%286%2012%2012%29%27%3E%3Ccircle%20cx%3D%2712%27%20cy%3D%278%27%20r%3D%273.6%27%2F%3E%3Cpath%20d%3D%27M5%2020a7%207%200%200%201%2014%200%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28236%2C92%29%20scale%280.87%29%20rotate%28-10%2012%2012%29%27%3E%3Cpath%20d%3D%27M13%202L4%2014h7l-1%208%209-12h-7z%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%2818%2C138%29%20scale%280.84%29%20rotate%2810%2012%2012%29%27%3E%3Cpath%20d%3D%27M9%2018V5l10-2v13%27%2F%3E%3Ccircle%20cx%3D%276.5%27%20cy%3D%2718%27%20r%3D%272.6%27%2F%3E%3Ccircle%20cx%3D%2716.5%27%20cy%3D%2716%27%20r%3D%272.6%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28100%2C148%29%20scale%280.99%29%20rotate%280%2012%2012%29%27%3E%3Cpath%20d%3D%27M2%2013l4.5%204.5L14%207%27%2F%3E%3Cpath%20d%3D%27M10.5%2015.5L13%2018l8-11%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28190%2C146%29%20scale%280.90%29%20rotate%28-6%2012%2012%29%27%3E%3Cpath%20d%3D%27M7%2021V10l5-7%201.5%201.2a2%202%200%200%201%20.6%202L13%2010h5.5a2.5%202.5%200%200%201%202.4%203.1l-1.6%206A2.5%202.5%200%200%201%2017%2021z%27%2F%3E%3Cpath%20d%3D%27M7%2010H3v11h4%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28278%2C140%29%20scale%280.77%29%20rotate%288%2012%2012%29%27%3E%3Ccircle%20cx%3D%2712%27%20cy%3D%2712%27%20r%3D%279%27%2F%3E%3Cpath%20d%3D%27M12%207v5.5l3.5%202%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%2856%2C208%29%20scale%280.79%29%20rotate%2814%2012%2012%29%27%3E%3Cpath%20d%3D%27M3%207a4%204%200%200%201%204-4h10a4%204%200%200%201%204%204v6a4%204%200%200%201-4%204h-7l-7%205v-5a4%204%200%200%201%200-10z%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28146%2C212%29%20scale%280.93%29%20rotate%280%2012%2012%29%27%3E%3Ccircle%20cx%3D%276%27%20cy%3D%2712%27%20r%3D%272.6%27%2F%3E%3Ccircle%20cx%3D%2718%27%20cy%3D%276%27%20r%3D%272.6%27%2F%3E%3Ccircle%20cx%3D%2718%27%20cy%3D%2718%27%20r%3D%272.6%27%2F%3E%3Cpath%20d%3D%27M8.3%2010.8l7.4-3.6M8.3%2013.2l7.4%203.6%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28240%2C206%29%20scale%280.87%29%20rotate%28-8%2012%2012%29%27%3E%3Cpath%20d%3D%27M12%202c3.5%202.4%205.5%206.2%205.5%2010.3L12%2017l-5.5-4.7C6.5%208.2%208.5%204.4%2012%202z%27%2F%3E%3Ccircle%20cx%3D%2712%27%20cy%3D%279.5%27%20r%3D%272%27%2F%3E%3Cpath%20d%3D%27M9%2017l-2%204%205-2%205%202-2-4%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28300%2C232%29%20scale%280.64%29%20rotate%284%2012%2012%29%27%3E%3Cpath%20d%3D%27M12%2020s-7-4.6-7-9.2A4%204%200%200%201%2012%208a4%204%200%200%201%207%202.8C19%2015.4%2012%2020%2012%2020z%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%2812%2C262%29%20scale%280.81%29%20rotate%28-12%2012%2012%29%27%3E%3Cpath%20d%3D%27M12%203l2.7%206.3%206.8.6-5.1%204.4%201.5%206.7L12%2017.5%206.1%2021l1.5-6.7L2.5%209.9l6.8-.6z%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28104%2C272%29%20scale%280.72%29%20rotate%286%2012%2012%29%27%3E%3Cpath%20d%3D%27M2%2012s4-7%2010-7%2010%207%2010%207-4%207-10%207-10-7-10-7z%27%2F%3E%3Ccircle%20cx%3D%2712%27%20cy%3D%2712%27%20r%3D%273%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28196%2C268%29%20scale%280.77%29%20rotate%28-10%2012%2012%29%27%3E%3Ccircle%20cx%3D%2712%27%20cy%3D%278%27%20r%3D%273.6%27%2F%3E%3Cpath%20d%3D%27M5%2020a7%207%200%200%201%2014%200%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28280%2C286%29%20scale%280.70%29%20rotate%2816%2012%2012%29%27%3E%3Cpath%20d%3D%27M2%2012l19-8-7%2019-3-8-9-3z%27%2F%3E%3Cpath%20d%3D%27M21%204L10%2015%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%2862%2C300%29%20scale%280.81%29%20rotate%280%2012%2012%29%27%3E%3Cpath%20d%3D%27M6%2016v-6a6%206%200%200%201%2012%200v6l2%202H4z%27%2F%3E%3Cpath%20d%3D%27M10%2020a2%202%200%200%200%204%200%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28160%2C306%29%20scale%280.64%29%20rotate%28-6%2012%2012%29%27%3E%3Cpath%20d%3D%27M9%2018V5l10-2v13%27%2F%3E%3Ccircle%20cx%3D%276.5%27%20cy%3D%2718%27%20r%3D%272.6%27%2F%3E%3Ccircle%20cx%3D%2716.5%27%20cy%3D%2716%27%20r%3D%272.6%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28118%2C60%29%20scale%280.60%29%20rotate%2820%2012%2012%29%27%3E%3Cpath%20d%3D%27M13%202L4%2014h7l-1%208%209-12h-7z%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28216%2C38%29%20scale%280.56%29%20rotate%28-14%2012%2012%29%27%3E%3Ccircle%20cx%3D%2712%27%20cy%3D%2712%27%20r%3D%279%27%2F%3E%3Cpath%20d%3D%27M12%207v5.5l3.5%202%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%2810%2C200%29%20scale%280.58%29%20rotate%2810%2012%2012%29%27%3E%3Cpath%20d%3D%27M7%2021V10l5-7%201.5%201.2a2%202%200%200%201%20.6%202L13%2010h5.5a2.5%202.5%200%200%201%202.4%203.1l-1.6%206A2.5%202.5%200%200%201%2017%2021z%27%2F%3E%3Cpath%20d%3D%27M7%2010H3v11h4%27%2F%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E");
  --panel-bg: #30302e;
  --panel-border: #45443f;
  --text: #f5f4ef;
  --text-secondary: #c2c0b6;
  --text-muted: #9a988e;
  --text-faint: #918f85;
  --border: #45443f;
  --border-strong: #55534c;
  /* Filter chips: ~2.3:1 against the panel (was 1.36:1 on --panel-border), so each one reads as a
     button before it's hovered. Deliberately short of 3:1 — nine chips at that strength turn
     into a toolbar and compete with the filled active chip. */
  --chip-border: #67655d;
  --chip-border-hover: #8a887e;
  --navbar-bg: #1e1e1c;
  --navbar-text: #f5f4ef;
  --accent: #d97757;
  --accent-hover: #c86a4d;
  --accent-contrast: #ffffff;
  --accent-soft-bg: #3a2c24;
  --accent-soft-text: #e2967a;
  --link: #d97757;
  --input-bg: #35342f;
  --input-border: #55534c;
  --code-bg: #3a3935;
  --row-hover: #35342f;
  --category-bg: #35342f;
  --category-border: #45443f;
  --surface-soft: #35342f;
  --surface-soft-border: #45443f;
  --auth-backdrop-bg: #1e1e1c;
  --modal-overlay-bg: rgba(0,0,0,0.7);
  --alert-error-bg: #3a2323;
  --alert-error-text: #f5a89e;
  --alert-error-border: #5c2f2f;
  --alert-success-bg: #1f3327;
  --alert-success-text: #7fd99a;
  --alert-success-border: #2f4d3a;
  --shadow-soft: rgba(0,0,0,0.35);
  --shadow-strong: rgba(0,0,0,0.5);
  --catalog-gradient: linear-gradient(120deg, #d97757, #e2967a, #f2c14e, #e2967a, #d97757);
}

:root[data-theme="light"] {
  color-scheme: light;
  --bg: #f5f5f5;
  --chat-pattern: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%27320%27%20height%3D%27320%27%20viewBox%3D%270%200%20320%20320%27%3E%3Cg%20fill%3D%27none%27%20stroke%3D%27rgba%2842%2C110%2C187%2C0.08%29%27%20stroke-width%3D%271.85%27%20stroke-linejoin%3D%27round%27%20stroke-linecap%3D%27round%27%3E%3Cg%20transform%3D%27translate%2814%2C10%29%20scale%281.10%29%20rotate%28-8%2012%2012%29%27%3E%3Cpath%20d%3D%27M3%207a4%204%200%200%201%204-4h10a4%204%200%200%201%204%204v6a4%204%200%200%201-4%204h-7l-7%205v-5a4%204%200%200%201%200-10z%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28104%2C22%29%20scale%280.84%29%20rotate%2812%2012%2012%29%27%3E%3Cpath%20d%3D%27M12%2020s-7-4.6-7-9.2A4%204%200%200%201%2012%208a4%204%200%200%201%207%202.8C19%2015.4%2012%2020%2012%2020z%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28196%2C12%29%20scale%280.99%29%20rotate%280%2012%2012%29%27%3E%3Cpath%20d%3D%27M2%2012s4-7%2010-7%2010%207%2010%207-4%207-10%207-10-7-10-7z%27%2F%3E%3Ccircle%20cx%3D%2712%27%20cy%3D%2712%27%20r%3D%273%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28272%2C40%29%20scale%280.72%29%20rotate%2816%2012%2012%29%27%3E%3Cpath%20d%3D%27M12%203l2.7%206.3%206.8.6-5.1%204.4%201.5%206.7L12%2017.5%206.1%2021l1.5-6.7L2.5%209.9l6.8-.6z%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%2852%2C74%29%20scale%281.02%29%20rotate%28-14%2012%2012%29%27%3E%3Cpath%20d%3D%27M2%2012l19-8-7%2019-3-8-9-3z%27%2F%3E%3Cpath%20d%3D%27M21%204L10%2015%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28148%2C84%29%20scale%280.93%29%20rotate%286%2012%2012%29%27%3E%3Ccircle%20cx%3D%2712%27%20cy%3D%278%27%20r%3D%273.6%27%2F%3E%3Cpath%20d%3D%27M5%2020a7%207%200%200%201%2014%200%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28236%2C92%29%20scale%280.87%29%20rotate%28-10%2012%2012%29%27%3E%3Cpath%20d%3D%27M13%202L4%2014h7l-1%208%209-12h-7z%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%2818%2C138%29%20scale%280.84%29%20rotate%2810%2012%2012%29%27%3E%3Cpath%20d%3D%27M9%2018V5l10-2v13%27%2F%3E%3Ccircle%20cx%3D%276.5%27%20cy%3D%2718%27%20r%3D%272.6%27%2F%3E%3Ccircle%20cx%3D%2716.5%27%20cy%3D%2716%27%20r%3D%272.6%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28100%2C148%29%20scale%280.99%29%20rotate%280%2012%2012%29%27%3E%3Cpath%20d%3D%27M2%2013l4.5%204.5L14%207%27%2F%3E%3Cpath%20d%3D%27M10.5%2015.5L13%2018l8-11%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28190%2C146%29%20scale%280.90%29%20rotate%28-6%2012%2012%29%27%3E%3Cpath%20d%3D%27M7%2021V10l5-7%201.5%201.2a2%202%200%200%201%20.6%202L13%2010h5.5a2.5%202.5%200%200%201%202.4%203.1l-1.6%206A2.5%202.5%200%200%201%2017%2021z%27%2F%3E%3Cpath%20d%3D%27M7%2010H3v11h4%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28278%2C140%29%20scale%280.77%29%20rotate%288%2012%2012%29%27%3E%3Ccircle%20cx%3D%2712%27%20cy%3D%2712%27%20r%3D%279%27%2F%3E%3Cpath%20d%3D%27M12%207v5.5l3.5%202%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%2856%2C208%29%20scale%280.79%29%20rotate%2814%2012%2012%29%27%3E%3Cpath%20d%3D%27M3%207a4%204%200%200%201%204-4h10a4%204%200%200%201%204%204v6a4%204%200%200%201-4%204h-7l-7%205v-5a4%204%200%200%201%200-10z%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28146%2C212%29%20scale%280.93%29%20rotate%280%2012%2012%29%27%3E%3Ccircle%20cx%3D%276%27%20cy%3D%2712%27%20r%3D%272.6%27%2F%3E%3Ccircle%20cx%3D%2718%27%20cy%3D%276%27%20r%3D%272.6%27%2F%3E%3Ccircle%20cx%3D%2718%27%20cy%3D%2718%27%20r%3D%272.6%27%2F%3E%3Cpath%20d%3D%27M8.3%2010.8l7.4-3.6M8.3%2013.2l7.4%203.6%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28240%2C206%29%20scale%280.87%29%20rotate%28-8%2012%2012%29%27%3E%3Cpath%20d%3D%27M12%202c3.5%202.4%205.5%206.2%205.5%2010.3L12%2017l-5.5-4.7C6.5%208.2%208.5%204.4%2012%202z%27%2F%3E%3Ccircle%20cx%3D%2712%27%20cy%3D%279.5%27%20r%3D%272%27%2F%3E%3Cpath%20d%3D%27M9%2017l-2%204%205-2%205%202-2-4%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28300%2C232%29%20scale%280.64%29%20rotate%284%2012%2012%29%27%3E%3Cpath%20d%3D%27M12%2020s-7-4.6-7-9.2A4%204%200%200%201%2012%208a4%204%200%200%201%207%202.8C19%2015.4%2012%2020%2012%2020z%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%2812%2C262%29%20scale%280.81%29%20rotate%28-12%2012%2012%29%27%3E%3Cpath%20d%3D%27M12%203l2.7%206.3%206.8.6-5.1%204.4%201.5%206.7L12%2017.5%206.1%2021l1.5-6.7L2.5%209.9l6.8-.6z%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28104%2C272%29%20scale%280.72%29%20rotate%286%2012%2012%29%27%3E%3Cpath%20d%3D%27M2%2012s4-7%2010-7%2010%207%2010%207-4%207-10%207-10-7-10-7z%27%2F%3E%3Ccircle%20cx%3D%2712%27%20cy%3D%2712%27%20r%3D%273%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28196%2C268%29%20scale%280.77%29%20rotate%28-10%2012%2012%29%27%3E%3Ccircle%20cx%3D%2712%27%20cy%3D%278%27%20r%3D%273.6%27%2F%3E%3Cpath%20d%3D%27M5%2020a7%207%200%200%201%2014%200%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28280%2C286%29%20scale%280.70%29%20rotate%2816%2012%2012%29%27%3E%3Cpath%20d%3D%27M2%2012l19-8-7%2019-3-8-9-3z%27%2F%3E%3Cpath%20d%3D%27M21%204L10%2015%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%2862%2C300%29%20scale%280.81%29%20rotate%280%2012%2012%29%27%3E%3Cpath%20d%3D%27M6%2016v-6a6%206%200%200%201%2012%200v6l2%202H4z%27%2F%3E%3Cpath%20d%3D%27M10%2020a2%202%200%200%200%204%200%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28160%2C306%29%20scale%280.64%29%20rotate%28-6%2012%2012%29%27%3E%3Cpath%20d%3D%27M9%2018V5l10-2v13%27%2F%3E%3Ccircle%20cx%3D%276.5%27%20cy%3D%2718%27%20r%3D%272.6%27%2F%3E%3Ccircle%20cx%3D%2716.5%27%20cy%3D%2716%27%20r%3D%272.6%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28118%2C60%29%20scale%280.60%29%20rotate%2820%2012%2012%29%27%3E%3Cpath%20d%3D%27M13%202L4%2014h7l-1%208%209-12h-7z%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%28216%2C38%29%20scale%280.56%29%20rotate%28-14%2012%2012%29%27%3E%3Ccircle%20cx%3D%2712%27%20cy%3D%2712%27%20r%3D%279%27%2F%3E%3Cpath%20d%3D%27M12%207v5.5l3.5%202%27%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%27translate%2810%2C200%29%20scale%280.58%29%20rotate%2810%2012%2012%29%27%3E%3Cpath%20d%3D%27M7%2021V10l5-7%201.5%201.2a2%202%200%200%201%20.6%202L13%2010h5.5a2.5%202.5%200%200%201%202.4%203.1l-1.6%206A2.5%202.5%200%200%201%2017%2021z%27%2F%3E%3Cpath%20d%3D%27M7%2010H3v11h4%27%2F%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E");
  --panel-bg: #ffffff;
  --panel-border: #dddddd;
  --text: #333333;
  --text-secondary: #666666;
  --text-muted: #999999;
  --text-faint: #8a97a3;
  --border: #eeeeee;
  --border-strong: #dddddd;
  --chip-border: #b8b8b8;
  --chip-border-hover: #8f8f8f;
  --navbar-bg: #2c3e50;
  --navbar-text: #ecf0f1;
  --accent: #2a6ebb;
  --accent-hover: #1f5a9e;
  --accent-contrast: #ffffff;
  --accent-soft-bg: #eaf2fc;
  --accent-soft-text: #1f5a9e;
  --link: #2a6ebb;
  --input-bg: #ffffff;
  --input-border: #cccccc;
  --code-bg: #f0f2f5;
  --row-hover: #fafcff;
  --category-bg: #eef2f6;
  --category-border: #dde5ed;
  --surface-soft: #f8fafc;
  --surface-soft-border: #eef1f4;
  --auth-backdrop-bg: #eef1f4;
  --modal-overlay-bg: rgba(0,0,0,0.5);
  --alert-error-bg: #fdecea;
  --alert-error-text: #922b21;
  --alert-error-border: #f5c6cb;
  --alert-success-bg: #e9f7ef;
  --alert-success-text: #1e7e34;
  --alert-success-border: #c3e6cb;
  --shadow-soft: rgba(0,0,0,0.12);
  --shadow-strong: rgba(0,0,0,0.18);
  --catalog-gradient: linear-gradient(120deg, #2a6ebb, #4aa3c9, #8fb6e0, #4aa3c9, #2a6ebb);
}

/* The page scrollbar isn't drawn — scrolling itself (wheel, trackpad, touch, keys) is unchanged.
   Same technique as the modal and dashboard sidebar below. */
html { scrollbar-width: none; -ms-overflow-style: none; }
html::-webkit-scrollbar { display: none; }

body {
  margin: 0;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 14px;
  color: var(--text);
  background-color: var(--bg);
  background-image: var(--chat-pattern);
  background-repeat: repeat;
  transition: background 0.15s ease, color 0.15s ease;
}
a { color: var(--link); text-decoration: none; }
a:hover { text-decoration: underline; }

code {
  font-family: "Courier New", Courier, monospace;
  background: var(--code-bg);
  padding: 2px 6px;
  border-radius: 3px;
  font-size: 13px;
}

.theme-toggle {
  background: transparent;
  border: 1px solid rgba(255,255,255,0.3);
  color: var(--navbar-text);
  width: 30px;
  height: 30px;
  border-radius: 50%;
  cursor: pointer;
  font-size: 14px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  padding: 0;
}
.theme-toggle:hover { background: rgba(255,255,255,0.1); }
.theme-toggle svg { width: 16px; height: 16px; }

.contact-panel {
  text-align: center;
  padding: 40px 30px;
}
.contact-icon { margin-bottom: 16px; }
.contact-panel h2 { border: none; padding: 0; margin: 0 0 8px; }
.contact-sub { color: var(--text-secondary); font-size: 13px; margin-bottom: 22px; }
a.contact-btn.btn { display: inline-block; padding: 13px 30px; font-size: 16px; }

/* Dashboard support: compact single-row Telegram banner */
.contact-panel--inline {
  display: flex;
  align-items: center;
  gap: 16px;
  text-align: left;
  padding: 16px 20px;
}
.contact-panel--inline .contact-icon { margin: 0; flex-shrink: 0; line-height: 0; }
.contact-panel--inline .contact-text { flex: 1; min-width: 0; }
.panel.contact-panel--inline h2 { font-size: 16px; margin: 0 0 2px; padding: 0; border: none; }
.contact-panel--inline .contact-sub { margin: 0; }
.contact-panel--inline a.contact-btn.btn { flex-shrink: 0; padding: 10px 20px; font-size: 14px; white-space: nowrap; }
@media (max-width: 600px) {
  .contact-panel--inline { flex-wrap: wrap; }
  .contact-panel--inline a.contact-btn.btn { width: 100%; text-align: center; }
}

.api-key-row {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--surface-soft);
  border: 1px solid var(--surface-soft-border);
  border-radius: 3px;
  padding: 10px 12px;
}
.account-email { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.api-key-row code {
  flex: 1;
  background: none;
  padding: 0;
  word-break: break-all;
}

.navbar {
  background: var(--navbar-bg);
  padding: 16px 26px;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 16px;
}
/* Desktop: the bar stays at the top while the page scrolls. z-index sits above the catalog's own
   dropdowns (20-30) and the back-to-top button (40) and below modals (100) and tooltips (1200).
   scroll-padding keeps in-page anchors (api-docs) from landing underneath it. Phones keep the
   tall wrapped navbar scrolling away — pinned it would eat a third of the screen. */
@media (min-width: 901px) {
  html { scroll-padding-top: 84px; }
  .navbar { position: sticky; top: 0; z-index: 50; }
}
.navbar .brand {
  display: flex;
  align-items: center;
  gap: 8px;
  color: #fff;
  font-size: 22px;
  font-weight: bold;
  justify-self: start;
}
.navbar .brand:hover { text-decoration: none; }
.navbar .brand .brand-accent { color: var(--accent); }
.brand-logo {
  width: 40px;
  height: 40px;
  border-radius: 9px;
  display: block;
}
.navbar .nav-center {
  justify-self: center;
  display: flex;
  gap: 34px;
}
.navbar .nav-center a {
  position: relative;
  color: rgba(255,255,255,0.72);
  font-size: 15px;
  padding-block: 4px 6px;
}
.navbar .nav-center a:hover { color: #fff; text-decoration: none; }
/* The page you're on: full-strength label with an accent rule along the navbar's bottom edge.
   Offset from the link rather than the bar so it still lands right when the phone navbar
   stacks the links onto a row of their own. */
.navbar .nav-center a.nav-active { color: #fff; font-weight: bold; }
.navbar .nav-center a.nav-active::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  bottom: -10px;
  height: 2px;
  border-radius: 2px;
  background: var(--accent);
}

.navbar .nav-links {
  justify-self: end;
  display: flex;
  align-items: center;
  gap: 14px;
  padding-right: 6px;
}
.navbar .nav-links a {
  color: var(--navbar-text);
  font-size: 14px;
}
.navbar .nav-links a:hover { color: var(--accent); text-decoration: none; }
.navbar .nav-links .btn-outline {
  background: transparent;
  border: 1px solid rgba(255,255,255,0.4);
  color: #fff !important;
}
.navbar .nav-links .btn-outline:hover { background: rgba(255,255,255,0.1); }
.navbar .nav-auth { display: contents; }
/* Only one of the two carries weight: Register is the call to action, so Log in sits beside it
   as a plain label rather than a second outlined button competing with it. */
.navbar .nav-links .nav-login {
  color: var(--navbar-text);
  font-size: 14px;
  white-space: nowrap;
}
.navbar .nav-links .nav-register-btn {
  padding: 10px 20px;
  border-radius: 10px;
  font-size: 14px;
  white-space: nowrap;
}
/* Squarer than the pill controls elsewhere, to match the navbar's other boxed control. */
.navbar .lang-select {
  border-radius: 10px;
  padding: 8px 12px;
}
.navbar .theme-toggle {
  width: 36px;
  height: 36px;
  border-color: rgba(255,255,255,0.22);
}
.navbar .theme-toggle svg { width: 17px; height: 17px; }
/* A roomier pill than .btn-small's, same 12px text, so Dashboard carries more weight. The
   padding matches Register's so the two sit at the same height side by side. */
.navbar .nav-links .nav-dashboard-btn { padding: 9px 18px; border-radius: 10px; white-space: nowrap; }
/* Logout as a plain nav link: a <button> (it POSTs) wearing the same look as the links beside it. */
.navbar .nav-logout {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  font-size: 14px;
  color: var(--navbar-text);
  cursor: pointer;
}
.navbar .nav-logout:hover { color: #5dade2; }

@media (max-width: 700px) {
  .navbar {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    row-gap: 10px;
  }
  .navbar .brand { justify-self: unset; width: 100%; justify-content: center; }
  .navbar .nav-center { justify-self: unset; flex-wrap: wrap; justify-content: center; gap: 10px 20px; width: 100%; }
  .navbar .nav-links { justify-self: unset; flex-wrap: wrap; justify-content: center; width: 100%; padding-right: 0; }
}
/* Phones: more air between the three rows (brand / links / controls), roomier tap targets on
   the links, and one 34px height for every control so the row lines up instead of mixing 30
   and 31px pieces. The tighter control gap keeps Logout on the same line as Dashboard down to
   360px wide rather than wrapping onto a line of its own. Order is unchanged. */
@media (max-width: 600px) {
  .navbar { row-gap: 14px; padding: 14px 16px; }
  .navbar .nav-center { gap: 2px 14px; }
  .navbar .nav-center a { padding: 4px 2px; }
  .navbar .nav-links { gap: 10px; }
  .navbar .nav-links .lang-select { box-sizing: border-box; height: 34px; padding-block: 0; }
  .navbar .theme-toggle { width: 34px; height: 34px; }
  .navbar .nav-links .btn-small { box-sizing: border-box; height: 34px; display: inline-flex; align-items: center; padding-block: 0; }
  .navbar .nav-links .nav-dashboard-btn { padding-inline: 16px; }
  .navbar .nav-links .nav-register-btn { padding-inline: 16px; }
  .navbar .nav-links .nav-login { display: inline-flex; align-items: center; height: 34px; }
  .navbar .lang-select { padding-inline: 10px; }
  .navbar .brand { font-size: 20px; }
  .brand-logo { width: 36px; height: 36px; }
  /* The halo behind a heading reaches 34px past each side, which on a phone-wide heading ran
     ~15px past the screen edge and let the whole page slide sideways. */
  :root .heading-clear::before { inset: -20px -16px; }

  /* Three rows: brand with the language + theme controls on its right, then the page links,
     then the account buttons. .nav-links dissolves (display: contents) so its children become
     items of the navbar row itself and can be placed individually — the markup, and therefore
     the desktop layout, is untouched. */
  .navbar { column-gap: 10px; }
  /* The brand grows to fill whatever the two controls leave, so nothing else can join row one. */
  .navbar .brand { width: auto; flex: 1 0 auto; justify-content: flex-start; order: 0; }
  .navbar .nav-links { display: contents; }
  .navbar .nav-links .lang-select { order: 1; }
  .navbar .nav-links .theme-toggle { order: 2; }
  .navbar .nav-center { order: 3; }
  .navbar .nav-links > :not(.lang-select):not(.theme-toggle) { order: 4; }
  /* Logged out, the page links sit on the left of the second row and Login + Register on its
     right. They move as one pair (.nav-auth, pushed right by its auto margin), so a translation
     too long for one line drops both to a third row of their own, still right-aligned, rather
     than stranding Register by itself. */
  .navbar-guest { column-gap: 8px; justify-content: flex-start; }
  .navbar-guest .nav-center { width: auto; gap: 2px 10px; justify-content: flex-start; }
  .navbar-guest .nav-center a { font-size: 13px; }
  .navbar-guest .nav-auth { display: flex; align-items: center; gap: 10px; margin-inline-start: auto; }
  .navbar-guest .nav-links .btn-small { padding-inline: 10px; }
}

.container {
  max-width: 1000px;
  margin: 0 auto;
  padding: 24px 20px;
}
.container-wide { max-width: 1400px; }

.panel {
  background: var(--panel-bg);
  border: 1px solid var(--panel-border);
  border-radius: 3px;
  padding: 16px 20px;
  margin-bottom: 20px;
}
.panel h2 {
  margin-top: 0;
  font-size: 21px;
  border-bottom: 1px solid var(--border);
  padding-bottom: 8px;
}
.order-description-heading {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
.order-description-stats {
  display: flex;
  align-items: baseline;
  gap: 14px;
}
.order-avg-time {
  font-size: 13px;
  font-weight: normal;
  color: var(--text-muted);
  white-space: nowrap;
}
.order-avg-time-green { color: #27ae60; }
/* Phones: side by side, "About this service" got squeezed into three lines by the stats. The
   heading takes its own line and the stats sit on the one below. */
@media (max-width: 600px) {
  .order-description-heading { flex-direction: column; align-items: flex-start; gap: 6px; }
  .order-description-heading > span:first-child { white-space: nowrap; }
  .order-description-stats { flex-wrap: wrap; row-gap: 4px; }
}

@media (max-width: 700px) {
  .home-hero-title { font-size: 21px; gap: 9px; }
  .hero-bonus { font-size: 18px; }
  .home-hero-sub { font-size: 13.5px; margin-bottom: 22px; }
  table { display: block; overflow-x: auto; white-space: nowrap; -webkit-overflow-scrolling: touch; }

  /* On the services catalog specifically, hide the lower-priority columns (ID/Min/Max —
     all still available via the "Info" button) so Service/Rate/Order stay visible without
     needing to scroll the table at all, instead of leaving the Order button off-screen. */
  .catalog-table {
    table-layout: fixed;
    display: table;
    overflow-x: visible;
    white-space: normal;
  }
  .catalog-table th:nth-child(1),
  .catalog-table th:nth-child(4),
  .catalog-table th:nth-child(5),
  .catalog-table th:nth-child(6),
  .catalog-table th:nth-child(7),
  .catalog-table tr.service-row td:nth-child(1),
  .catalog-table tr.service-row td:nth-child(4),
  .catalog-table tr.service-row td:nth-child(5),
  .catalog-table tr.service-row td:nth-child(6),
  .catalog-table tr.service-row td:nth-child(7) {
    display: none;
  }
  .catalog-table th:nth-child(2), .catalog-table td:nth-child(2) { width: 46%; }
  .catalog-table th:nth-child(3), .catalog-table td:nth-child(3) { width: 22%; }
  .catalog-table th:nth-child(8), .catalog-table td:nth-child(8) { width: 32%; }
  /* "Price per 1,000" broke over three lines in the narrow Price column, so phones get the
     short form on one line: the word, then a smaller "/ 1,000" beside it. */
  .catalog-table thead th { padding-left: 6px; padding-right: 6px; }
  .catalog-table .rate-label-long { display: none; }
  .catalog-table .rate-label-short { display: inline; white-space: nowrap; }
  .catalog-table .rate-label-unit { font-size: 80%; margin-inline-start: 3px; }
  .catalog-table tr.service-row td:nth-child(2) { white-space: normal; word-break: break-word; }
  .catalog-table th { white-space: normal; }
  .catalog-table tr.service-row td { padding-top: 18px; padding-bottom: 18px; }
  .catalog-table .actions-cell {
    white-space: normal;
  }
  .catalog-table .actions-cell-inner {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 8px;
  }
  /* No margin here: it stacked on top of the flex gap above, doubling the space between the
     two buttons and making every row taller than it needed to be. */
  .catalog-table .actions-cell .btn { margin-left: 0; margin-bottom: 0; text-align: center; }
  /* The desktop 110px min-width is wider than the actions column gets at this size, so the
     Order button spilled past the edge of the screen instead of shrinking into its cell. */
  .catalog-table .actions-cell .catalog-action-btn {
    min-width: 0;
    max-width: 100%;
    padding: 0 8px;
    gap: 6px;
  }

  /* API param tables (Param/Required/Description) — same fixed-layout technique so
     descriptions wrap in place instead of forcing a horizontal scroll for 3 short columns. */
  .api-params {
    table-layout: fixed;
    display: table;
    overflow-x: visible;
    white-space: normal;
  }
  .api-params th, .api-params td { white-space: normal; overflow-wrap: break-word; padding: 6px 6px; }
  .api-params th:nth-child(1), .api-params td:nth-child(1) { width: 29%; }
  .api-params th:nth-child(2), .api-params td:nth-child(2) { width: 27%; }
  .api-params th:nth-child(3), .api-params td:nth-child(3) { width: 44%; }
  .api-params code { padding: 2px 4px; }
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 15px;
}
th, td {
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  text-align: left;
  vertical-align: middle;
}
th {
  background: var(--code-bg);
  font-weight: bold;
  border-bottom: 2px solid var(--panel-border);
}
/* Column titles that explain themselves on hover (public/js/tooltip.js). No underline and no help
   cursor — the headers stay plain text and the tip simply appears when you point at one. */
.catalog-table thead th.th-tip:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* Section bars aren't clickable, so they keep their own colour instead of lighting up like a
   service row. */
tr:not(.category-row):hover td { background: var(--row-hover); }

.category-row td {
  background: var(--category-bg);
  font-weight: bold;
  color: var(--text);
  padding: 11px 10px;
  border-bottom: 1px solid var(--category-border);
}
:root:not([data-theme="light"]) .category-row td {
  background: rgba(217, 119, 87, 0.12);
  border-bottom-color: rgba(217, 119, 87, 0.3);
}

/* Only present on categories whose own text carries no emoji — see lib/categoryIcon.js. Its
   own span purely so the gap to the label doesn't depend on a literal space in the string. */
.category-icon {
  margin-inline-end: 6px;
}

.category-label {
  display: inline-block;
  font-size: 14px;
  letter-spacing: 0.2px;
  line-height: 1.6;
  white-space: normal;
  overflow-wrap: anywhere;
}

.history-table thead th {
  padding-block: 11px;
  font-size: 11px;
  font-weight: 600;
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: 0.4px;
}
.history-table td { font-size: 14px; }
/* Prev / "Page 2 of 5" / Next under the table, 50 orders a page (HISTORY_PAGE_SIZE). Empty
   spans stand in for a missing Prev or Next so the count stays centred. */
.history-pager {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 12px;
  margin-top: 16px;
}
.history-pager > :last-child { justify-self: end; }
.history-pager > :first-child { justify-self: start; }
.history-pager-count { font-size: 13px; color: var(--text-muted); }
/* Sortable headers (Qty / Charge / Status / Placed) are plain links that look exactly like the
   unsorted header text: no colour, underline or weight change, only the pointer. */
.history-table thead th a.history-sort,
.history-table thead th a.history-sort:hover {
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  text-decoration: none;
}
.history-row { cursor: pointer; }
.history-table .history-id-cell { color: var(--text-faint); font-size: 13px; }
.history-table .history-service-cell { font-weight: 500; padding-inline-end: 24px; }
/* ID and actions shrink to their content (width: 1% + nowrap is the table idiom for it), so the
   card's spare width goes to the data columns instead of piling up after the last button. The
   buttons sit flush right, on the same 30px inset as the header text. */
.history-table .history-id-cell { width: 1%; white-space: nowrap; }
.history-table .actions-cell { width: 1%; text-align: right; }
/* Tables ignore max-width on a cell, so the cap sits on the name itself: long names wrap onto a
   second line instead of running up against Qty. white-space: normal overrides the phone rule
   that keeps table cells on one line for horizontal scrolling. */
.history-table .history-service-name { display: block; max-width: 300px; white-space: normal; }
@media (max-width: 600px) {
  .history-table .history-service-name { max-width: 200px; }
}
.history-table .history-num-cell { color: var(--text-secondary); }
.history-table .history-date-cell { color: var(--text-muted); font-size: 13px; white-space: nowrap; }
.history-table .history-date-cell .history-date,
.history-table .history-date-cell .history-time { display: block; }
.history-table .history-date-cell .history-time { color: var(--text-faint); font-size: 12px; }
/* Same edge-to-edge treatment as .catalog-table: the table runs through the panel's side and
   top padding so the header bar sits on the card's top border and spans its full width. The
   outer cells take the 20px back as padding (10px + 20px = 30px), so no text moves. */
.panel > .history-table { width: calc(100% + 40px); margin: -16px -20px 0; }
.panel > .history-table th:first-child,
.panel > .history-table td:first-child { padding-inline-start: 30px; }
.panel > .history-table th:last-child,
.panel > .history-table td:last-child { padding-inline-end: 30px; }
.panel > .history-table thead th:first-child { border-start-start-radius: 3px; }
.panel > .history-table thead th:last-child { border-start-end-radius: 3px; }

.actions-cell {
  white-space: nowrap;
  text-align: left;
}
.actions-cell .btn { margin-left: 6px; }
.actions-cell .btn:first-child { margin-left: 0; }

.catalog-search-divider {
  border-bottom: 1px solid transparent;
  margin: 4px 0 20px;
}
.catalog-table thead th {
  padding-block: 10px;
  background: var(--accent-soft-bg);
  color: var(--accent-soft-text);
  font-size: 13px;
  text-transform: capitalize;
  letter-spacing: 0.4px;
}
:root:not([data-theme="light"]) .catalog-table thead th {
  background: var(--code-bg);
  color: var(--text);
  border-bottom: 2px solid var(--accent);
}
.catalog-table .actions-cell { text-align: right; }
.rate-label-short { display: none; }
/* The table runs through the panel's 20px side padding, so the header and category bars span
   the whole card. The outer cells take that 20px back as padding, so the bars' backgrounds and
   row rules span the card while the content stays inset. */
.panel > .catalog-table { width: calc(100% + 40px); margin-inline: -20px; }
/* The ID column and category bars start at 20px, flush with the search box and type chips
   above the table. */
.panel > .catalog-table th:first-child,
.panel > .catalog-table tr.service-row td:first-child,
.panel > .catalog-table tr.category-row td { padding-inline-start: 20px; }
.panel > .catalog-table th:last-child,
.panel > .catalog-table .actions-cell { padding-inline-end: 30px; }
/* ≤700px hides the ID column, so the Service column becomes the first visible one. */
@media (max-width: 700px) {
  .panel > .catalog-table th:nth-child(2),
  .panel > .catalog-table tr.service-row td:nth-child(2) { padding-inline-start: 30px; }
}
/* Phones: tighter than desktop — names at 20px, and the Order buttons pulled to 10px from the
   card's edge instead of 30px. */
@media (max-width: 600px) {
  .panel > .catalog-table th:nth-child(2),
  .panel > .catalog-table tr.service-row td:nth-child(2),
  .panel > .catalog-table tr.category-row td { padding-inline-start: 20px; }
  .panel > .catalog-table .actions-cell,
  .panel > .catalog-table th:last-child { padding-inline-end: 10px; }
}
/* Min/Max hidden on every catalog table (home page and signed-in dashboard): both are one
   click away in the Info modal. The cells are still rendered so the markup keeps its
   8-column shape (see services-catalog.ejs); drop this rule to bring the columns back. */
.catalog-table th:nth-child(4),
.catalog-table th:nth-child(5),
.catalog-table tr.service-row td:nth-child(4),
.catalog-table tr.service-row td:nth-child(5) { display: none; }
/* The compact (dashboard) table has Avg. time in cell 3 and Price in cell 6 — the reverse of
   the home page (see services-catalog.ejs). These undo the positional rules written for the
   home page's order: header sizes, cell sizes, and on phones which of the two is shown. */
/* Doubled class: the rules being undone sit further down the file at equal specificity. */
.catalog-table.catalog-table-compact thead th:nth-child(3) { font-size: 12px; }
.catalog-table.catalog-table-compact thead th:nth-child(6) { font-size: 13px; }
.dash-main .catalog-table.catalog-table-compact tr.service-row td:nth-child(3) { font-size: 12px; }
.dash-main .catalog-table.catalog-table-compact tr.service-row td:nth-child(6) { font-size: 14.5px; }
/* With the times at 12px the columns came out narrower and "Average Time" / "Price Per
   1,000" / "Active Accounts" wrapped onto two lines; the headers stay on one. */
.catalog-table.catalog-table-compact thead th { white-space: nowrap; }
@media (max-width: 700px) {
  .catalog-table-compact th:nth-child(3),
  .catalog-table-compact tr.service-row td:nth-child(3) { display: none; }
  .catalog-table-compact th:nth-child(6),
  .catalog-table-compact tr.service-row td:nth-child(6) { display: table-cell; width: 22%; }
}
/* Phones: just the Order button in the catalog rows, on the home page and the dashboard. */
@media (max-width: 600px) {
  .catalog-table .catalog-action-info { display: none; }
}
.catalog-table tr.service-row td:nth-child(4),
.catalog-table tr.service-row td:nth-child(5) {
  font-size: 12px;
  color: var(--text-muted);
}
.avg-time-cell { color: var(--text-muted); white-space: nowrap; font-size: 12px; }
.avg-time-value { display: inline-flex; align-items: center; gap: 4px; }
.avg-time-icon { flex-shrink: 0; opacity: 0.75; }
.catalog-table th:nth-child(6), .catalog-table td:nth-child(6) { text-align: center; }
.catalog-table th:nth-child(7), .catalog-table td:nth-child(7) { text-align: center; }
.catalog-table thead th:nth-child(4),
.catalog-table thead th:nth-child(5),
.catalog-table thead th:nth-child(6),
.catalog-table thead th:nth-child(7) { font-size: 12px; }
/* The order modal's headline figure. It replaced a 90px sliver sitting in a label/value row
   next to "Progress", which made the one number people open the modal for — how much of what
   they paid for has actually arrived — the least visible thing on the screen. Full width, at
   the top, with the count above it and what's left below, so the answer reads without
   hunting. Colour comes from the order's own state rather than always green: an order that
   stopped at 60% should not look like a finished one. */
/* Set off by a rule above and below, matching the separators between the detail rows under
   it, so the block reads as its own band rather than as text crowded against the title and
   the first row. The padding inside is what actually does the work — the lines only mark
   where it starts and ends. */
.order-progress {
  --progress-colour: var(--accent);
  margin: 10px 0 2px;
  padding: 16px 4px 18px;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}
.order-progress-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin-bottom: 11px; }
/* Tabular figures so the count doesn't jitter sideways as digits change on refresh. */
.order-progress-count { font-size: 20px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
.order-progress-total { font-size: 14px; font-weight: 600; color: var(--text-muted); }
.order-progress-percent { font-size: 16px; font-weight: 700; color: var(--progress-colour); font-variant-numeric: tabular-nums; }
.order-progress-track {
  height: 10px;
  border-radius: 999px;
  background: var(--surface-soft);
  border: 1px solid var(--surface-soft-border);
  overflow: hidden;
}
.order-progress-fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--progress-colour);
  transition: width 0.45s ease;
}
.order-progress-foot { display: flex; justify-content: space-between; gap: 12px; margin-top: 10px; font-size: 12px; color: var(--text-muted); }

.order-progress[data-state="done"] { --progress-colour: #27ae60; }
.order-progress[data-state="partial"] { --progress-colour: #e0a13a; }
/* Cancelled or refunded: the bar still shows what arrived, but drained of colour so it never
   reads as progress still being made. */
.order-progress[data-state="stopped"] { --progress-colour: var(--text-muted); }

/* A slow, soft highlight only while delivery is actually running. It keeps the bar feeling
   alive without the urgency of the old fast-moving stripes. */
.order-progress[data-state="running"] .order-progress-fill {
  background-image: linear-gradient(100deg,
    transparent 0%, transparent 34%, rgba(255, 255, 255, 0.30) 50%,
    transparent 66%, transparent 100%);
  background-size: 220% 100%;
  animation: order-progress-glow 3.2s ease-in-out infinite;
}
@keyframes order-progress-glow {
  0%, 18% { background-position: 115% 0; }
  82%, 100% { background-position: -115% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .order-progress[data-state="running"] .order-progress-fill { animation: none; }
  .order-progress-fill { transition: none; }
}

.modal-progress { display: inline-flex; align-items: center; gap: 8px; }
.modal-progress-bar {
  display: inline-block;
  width: 90px;
  height: 6px;
  border-radius: 3px;
  background: var(--surface-soft);
  overflow: hidden;
}
.modal-progress-fill { display: block; height: 100%; background: #27ae60; }
.active-accounts-cell { color: var(--text-muted); white-space: nowrap; font-size: 12px; }
.active-accounts-value { color: #27ae60; font-weight: 600; }
.catalog-table tr.service-row td:nth-child(2) {
  max-width: 480px;
  white-space: normal;
  overflow-wrap: break-word;
  /* Pulled a little closer to the ID badge beside it. Only this column's left padding moves —
     the ID column keeps its own padding, so the badges stay exactly where they were and it is
     the name that closes the gap. The header cell shifts with it or the column stops lining up. */
  padding-left: 5px;
}
.catalog-table th:nth-child(2) { padding-left: 5px; }
.dash-main .catalog-table tr.service-row td:nth-child(2) { font-size: 14px; }
.dash-main .catalog-table tr.service-row td:nth-child(3) { font-size: 14.5px; }
.container-wide .catalog-table tr.service-row td:nth-child(2) { font-size: 14px; }
.container-wide .catalog-table tr.service-row td:nth-child(3) { font-size: 14px; }
/* Scoped to service rows on purpose: the category header rows are a single colspan'd cell,
   which is also :nth-child(1) — it used to inherit this nowrap and could never wrap, forcing
   the whole page wider than the screen on mobile for long category names. */
.catalog-table th:nth-child(1), .catalog-table tr.service-row td:nth-child(1) { width: 1%; white-space: nowrap; }
.catalog-table tr.service-row td:nth-child(1) { font-size: 14.5px; }
.catalog-table th:nth-child(3), .catalog-table td:nth-child(3) { text-align: center; }
.catalog-table tr.service-row { cursor: pointer; }
.price-pill {
  display: block;
  width: 80%;
  margin: 0 auto;
  box-sizing: border-box;
  border-radius: 3px;
  padding: 4px 8px;
}

.btn-info { background: #7f8c8d; }
.btn-info:hover { background: #6c7a7a; }

.panel h2.step-heading {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 8px;
  margin: 0 0 14px;
  padding-bottom: 0;
  border-bottom: none;
  font-size: 13px;
  font-weight: bold;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--text-faint);
}
.panel h2.step-heading:not(:first-child) { margin-top: 32px; }
.step-number {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 17px;
  height: 17px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  font-size: 10px;
  font-weight: bold;
  flex-shrink: 0;
}

.platform-tabs {
  display: flex;
  justify-content: center;
  gap: 14px;
  margin-bottom: 22px;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--border);
}
.platform-tab {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 22px;
  border: 1px solid var(--panel-border);
  border-radius: 24px;
  color: var(--text-secondary);
  background: var(--panel-bg);
  font-size: 15px;
  font-weight: bold;
  box-shadow: 0 1px 2px var(--shadow-soft);
}
.platform-tab svg { flex-shrink: 0; }
.platform-tab:hover {
  border-color: var(--border-strong);
  background: var(--surface-soft);
  text-decoration: none;
}
.platform-tab.active {
  background: var(--accent-soft-bg);
  border-color: var(--accent);
  color: var(--accent-soft-text);
  box-shadow: 0 1px 3px var(--shadow-soft);
}
.platform-tab-disabled {
  cursor: not-allowed;
  position: relative;
}
.platform-tab-disabled:hover { border-color: var(--panel-border); background: var(--panel-bg); }
/* Opacity lives on the icon/label wrapper only, not the whole tab — otherwise it also
   washes out the "Coming soon" badge, which should stay fully vivid. */
.platform-tab-disabled-content { display: inline-flex; align-items: center; gap: 8px; opacity: 0.6; }
.platform-tab-badge {
  position: absolute;
  top: -9px;
  right: -10px;
  background: none;
  color: #fff;
  border: 1px solid #fff;
  font-size: 10px;
  font-weight: bold;
  text-transform: uppercase;
  letter-spacing: 0.3px;
  padding: 3px 8px;
  border-radius: 10px;
  white-space: nowrap;
  box-shadow: 0 1px 2px var(--shadow-soft);
  opacity: 0.8;
}
:root[data-theme="light"] .platform-tab-badge { background: var(--accent); color: #fff; border-color: var(--accent); opacity: 1; }

.empty-state {
  text-align: center;
  padding: 40px 20px;
  color: var(--text-muted);
}

/* Shown only when a search matches nothing in the active type tab — the table (or dropdown rows)
   is hidden behind it, so this carries the offer to search the whole catalog instead. */
.catalog-no-results,
.order-no-results {
  text-align: center;
  padding: 28px 20px;
  color: var(--text-muted);
}
.catalog-no-results p,
.order-no-results p {
  margin: 0 0 10px;
}
/* The tint is its own shade (not --bg) so the auth card reads as a framed, focused step, but
   the icon pattern is the same one <body> carries everywhere else — the login/register pages
   are full pages, not an overlay, and a flat panel of color there looked like a different site
   from the one the visitor just clicked away from. Painted here rather than letting the body
   pattern show through: the backdrop needs its own opaque tint underneath, and a transparent
   background would put the body's --bg behind the icons instead. */
.auth-backdrop {
  min-height: calc(100vh - 53px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px 20px;
  background-color: var(--auth-backdrop-bg);
  background-image: var(--chat-pattern);
  background-repeat: repeat;
}
/* Phones only: centred, the form floated mid-screen with a dead band above it. Pinned to the
   top instead, with a fixed gap under the navbar. Desktop keeps the centring above. */
@media (max-width: 600px) {
  .auth-backdrop { align-items: flex-start; padding-top: 32px; }
}
.auth-box {
  background: var(--panel-bg);
  border-radius: 6px;
  box-shadow: 0 4px 24px var(--shadow-soft);
  padding: 28px 30px;
  width: 100%;
  max-width: 380px;
  position: relative;
}
.auth-close {
  position: absolute;
  top: 12px;
  right: 14px;
  font-size: 20px;
  line-height: 1;
  color: var(--text-muted);
}
.auth-close:hover { color: var(--text); text-decoration: none; }
.auth-tabs {
  display: flex;
  gap: 6px;
  margin-bottom: 20px;
  border-bottom: 1px solid var(--border);
}
.auth-tab {
  flex: 1;
  text-align: center;
  padding: 10px 0 12px;
  font-weight: bold;
  font-size: 14px;
  color: var(--text-muted);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.auth-tab:hover { text-decoration: none; color: var(--text-secondary); }
.auth-tab.active { color: var(--accent); border-bottom-color: var(--accent); }
.auth-box h2 { margin: 0 0 4px; font-size: 18px; text-align: center; border: none; padding: 0; color: var(--text); }
.auth-box .auth-sub { text-align: center; color: var(--text-muted); font-size: 12px; margin-bottom: 18px; }
/* Login has no subtitle, so an error would otherwise sit 4px under the title. */
.auth-box h2 + .alert { margin-top: 14px; }
/* Login only: its title is followed straight by the form, so it needs the breathing room.
   Register carries a subtitle now, which owns the gap instead (.auth-sub margin-bottom) —
   keeping 22px here left a blank line between the two lines of the same heading block. */
#auth-panel-login h2 { margin-bottom: 22px; }

.btn.btn-large { padding: 15px 20px; font-size: 17px; }

input.two-fa-code-input {
  font-size: 30px;
  letter-spacing: 10px;
  text-align: center;
  font-weight: bold;
  padding: 14px 12px;
  max-width: 260px;
  width: 100%;
  margin: 10px 0;
  display: block;
}
/* The login page's code entry sits in a centered auth box, so keep it centered only there. */
.auth-box input.two-fa-code-input { margin: 10px auto; }

.modal-overlay {
  display: none;
  position: fixed;
  top: 0; left: 0; right: 0; bottom: 0;
  background: var(--modal-overlay-bg);
  align-items: center;
  justify-content: center;
  z-index: 100;
}
.modal-box {
  background: var(--panel-bg);
  border-radius: 4px;
  padding: 20px 24px;
  width: 90%;
  max-width: 420px;
  max-height: 90vh;
  overflow-y: auto;
  position: relative;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.modal-box::-webkit-scrollbar { display: none; }
.modal-box h3 { margin: 0 0 7px; padding-right: 24px; font-size: 16px; color: var(--text); }
#si-name { margin-bottom: 16px; }
.modal-description { color: var(--text-secondary); font-size: 15px; line-height: 1.6; margin-bottom: 18px; white-space: pre-line; }
.modal-description-list {
  margin: 0 0 18px;
  padding-left: 0;
  list-style: none;
  color: var(--text-secondary);
  font-size: 15px;
  line-height: 1.6;
}
.modal-description-list li { margin-bottom: 10px; }
.modal-description-list .line-icon { margin-right: 0.45em; }
.modal-description-list li:last-child { margin-bottom: 0; }
.modal-close {
  position: absolute;
  top: 10px;
  right: 14px;
  border: none;
  background: none;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  color: var(--text-muted);
}
.modal-close:hover { color: var(--text); }

.modal-section-title {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--text-muted);
  margin: 16px 0 8px;
  border-top: 1px solid var(--border);
  padding-top: 12px;
}
.modal-box .modal-section-title:first-of-type { border-top: none; padding-top: 0; }

.modal-list {
  display: flex;
  flex-direction: column;
  margin-bottom: 4px;
}
.modal-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 4px;
  border-bottom: 1px solid var(--border);
}
.modal-row:last-child { border-bottom: none; }
.modal-row-icon { font-size: 16px; width: 20px; text-align: center; flex-shrink: 0; }
.modal-row-label { flex: 1; font-size: 13px; color: var(--text-secondary); }
.modal-row-value { font-size: 14px; font-weight: bold; color: var(--text); text-align: right; }
/* Same #27ae60 as the catalog's active-accounts figure (.active-accounts-value), so a number
   read in the table looks identical in the modal. Declared after .modal-row-value because
   both are single-class selectors — equal specificity, so source order decides the colour. */
.modal-row-value-green { color: #27ae60; }

/* The modal's only action, so it's sized like the order page's submit button (46px, 15px)
   rather than the 32px catalog-row button whose classes it shares. Two classes so it
   outranks .catalog-action-btn, which is declared further down the file. */
.modal-box .modal-order-btn {
  display: flex;
  width: 100%;
  min-height: 46px;
  margin-top: 16px;
  padding: 12px 20px;
  gap: 8px;
  border-radius: 24px;
  font-size: 15px;
  letter-spacing: 0.2px;
}
.modal-box .modal-order-btn .catalog-action-icon svg { width: 16px; height: 16px; }

.api-quickfacts {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 12px;
}
.api-quickfact {
  display: flex;
  gap: 12px;
  align-items: baseline;
  padding: 8px 10px;
  background: var(--surface-soft);
  border: 1px solid var(--surface-soft-border);
  border-radius: 3px;
}
.api-quickfact-label {
  flex: 0 0 90px;
  font-size: 12px;
  font-weight: bold;
  text-transform: uppercase;
  color: var(--text-faint);
}
.api-quickfact-value {
  flex: 1 1 0;
  min-width: 0;
  overflow-wrap: break-word;
}
.api-quickfact-value code { overflow-wrap: break-word; }

.api-toc {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.api-toc a {
  padding: 6px 14px;
  border: 1px solid var(--panel-border);
  border-radius: 20px;
  background: var(--surface-soft);
  color: var(--accent);
  font-size: 13px;
  font-family: "Courier New", Courier, monospace;
}
.api-toc a:hover { background: var(--accent-soft-bg); text-decoration: none; }

.api-action-title {
  font-family: "Courier New", Courier, monospace;
  background: var(--navbar-bg);
  color: #fff;
  display: inline-block;
  padding: 4px 12px;
  border-radius: 3px;
  margin: 0 0 12px;
  font-size: 15px;
}

.api-params { margin: 16px 0; }
.api-params td, .api-params th { font-size: 13px; }

.api-example { margin-top: 18px; }
.api-example-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--text-faint);
  margin-bottom: 6px;
  font-weight: bold;
}

.code-block {
  background: var(--code-bg);
  border: 1px solid var(--panel-border);
  border-radius: 3px;
  padding: 12px 14px;
  font-family: "Courier New", Courier, monospace;
  font-size: 13px;
  overflow-x: auto;
  white-space: pre;
  margin: 0;
  color: var(--text);
}

.btn {
  -webkit-appearance: none;
  appearance: none;
  display: inline-block;
  padding: 7px 16px;
  background: var(--accent);
  color: #fff !important;
  border: none;
  border-radius: 999px;
  cursor: pointer;
  font-size: 13px;
  font-weight: bold;
  font-family: inherit;
  box-shadow: 0 1px 2px var(--shadow-soft);
  transition: transform 0.12s ease, box-shadow 0.12s ease, background 0.12s ease;
}
.btn:hover {
  background: var(--accent-hover);
  text-decoration: none;
  transform: translateY(-1px);
  box-shadow: 0 3px 8px var(--shadow-strong);
}
.btn:active { transform: translateY(0); box-shadow: 0 1px 2px var(--shadow-soft); }
.btn-small { padding: 4px 12px; font-size: 12px; }
.btn-green { background: #27ae60; }
.btn-green:hover { background: #1e8449; }
.btn-red { background: #c0392b; }
.btn-red:hover { background: #922b21; }

.btn-outline {
  background: transparent;
  border: 1px solid var(--accent);
  color: var(--accent) !important;
  box-shadow: none;
}
.btn-outline:hover { background: var(--accent-soft-bg); }

.btn.btn-loading {
  color: transparent !important;
  pointer-events: none;
  position: relative;
}
.btn.btn-loading::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 16px;
  height: 16px;
  margin: -8px 0 0 -8px;
  border: 2px solid rgba(255,255,255,0.4);
  border-top-color: #fff;
  border-radius: 50%;
  animation: btn-spin 0.7s linear infinite;
}
@keyframes btn-spin {
  to { transform: rotate(360deg); }
}

@keyframes copy-btn-pop {
  0% { transform: scale(1); }
  35% { transform: scale(1.1); }
  100% { transform: scale(1); }
}
.copy-btn-flash {
  background: #27ae60 !important;
  animation: copy-btn-pop 0.25s ease;
}
.copy-btn-flash:hover { background: #27ae60 !important; }

form.inline { display: inline; }

input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
  opacity: 1;
  cursor: pointer;
  zoom: 1.5;
}

input[type=text], input[type=email], input[type=password], input[type=number] {
  padding: 6px 8px;
  border: 1px solid var(--input-border);
  border-radius: 3px;
  font-size: 13px;
  width: 100%;
  max-width: 320px;
  background: var(--input-bg);
  color: var(--text);
}
.auth-box input[type=email], .auth-box input[type=password] {
  padding: 12px 14px;
  font-size: 15px;
  border-radius: 6px;
}
.auth-box label { margin: 20px 0 8px; }
label { display: block; margin: 10px 0 4px; font-weight: bold; font-size: 13px; color: var(--text); }

/* The one form on the site that a customer fills in from scratch rather than edits, so it gets
   room to breathe instead of the compact 10px/4px rhythm the settings panels use for fields
   that are mostly read. Every field is spaced identically — label to its own input, and field
   to field — and the submit button sits clear of the last input rather than tucked under it. */
.setup-form .setup-field { margin-top: 20px; }
.setup-form .setup-field:first-child { margin-top: 6px; }
.setup-form .setup-field label { margin: 0 0 8px; }
.setup-form .setup-actions { margin-top: 28px; }
label.order-sublabel { font-size: 12px; color: var(--text-secondary); margin-top: 0; }

.alert {
  padding: 10px 14px;
  border-radius: 3px;
  margin-bottom: 16px;
  font-size: 13px;
}
.alert-error { background: var(--alert-error-bg); color: var(--alert-error-text); border: 1px solid var(--alert-error-border); }
.alert-success { background: var(--alert-success-bg); color: var(--alert-success-text); border: 1px solid var(--alert-success-border); }
/* .filter-tabs carries no bottom margin — whatever follows it supplies its own top spacing
   (.order-select-wrap uses 30px). .alert only ever set margin-bottom, so an alert rendered
   directly under the tab row sat flush against the chips. Scoped to the adjacent-sibling
   case so alerts that open a panel keep sitting flush to its top padding as before. */
.filter-tabs + .alert { margin-top: 16px; }
/* The type-tabs partial ends with a <script>, so on the order page the banner is not the tabs'
   *adjacent* sibling and the rule above never matched — the link-problem banner sat flush
   against the chips. The general-sibling form reaches past the script. */
.service-type-tabs ~ .alert { margin-top: 16px; }

/* A link-problem banner (lib/linkCheck.js) carries up to three parts — what's wrong, why,
   and the fix — where a normal .alert carries one sentence. Stacking them as blocks keeps
   each on its own line at every width without the parts running together mid-sentence, and
   the extra padding gives the longer text room to breathe. Colors stay entirely inherited
   from .alert-error, so both themes follow it automatically. */
.order-error {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 14px;
  padding-right: 38px;
  line-height: 1.5;
}
.order-error strong { font-size: 14px; }
.order-error-fix { font-weight: 600; }
/* Close button follows .modal-close's geometry, but takes its color from the banner instead
   of --text-muted so it stays inside the alert's own red palette in both themes. The
   padding-right on .order-error above keeps the longest title from running under it. */
.order-error-dismiss {
  position: absolute;
  top: 8px;
  right: 10px;
  padding: 0;
  border: none;
  background: none;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  color: inherit;
  opacity: 0.55;
}
.order-error-dismiss:hover { opacity: 1; }

.balance {
  font-size: 22px;
  font-weight: bold;
  color: #27ae60;
}

.badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 3px;
  font-size: 11px;
  color: #fff;
  background: #7f8c8d;
  text-transform: uppercase;
}
.badge-pending { background: #e67e22; }
.badge-completed { background: #27ae60; }
.badge-expired { background: #7f8c8d; }
.badge-processing, .badge-in-progress, .badge-active { background: #2980b9; }
.badge-partial { background: #d4ac0d; }
.badge-canceled, .badge-rejected { background: #c0392b; }
.badge-refunded { background: #7f8c8d; }
.badge-approved { background: #27ae60; }
.badge-paid { background: #27ae60; }
.badge-open { background: #2980b9; }
.badge-closed { background: #7f8c8d; }

.seo-block h2 {
  font-size: 15px;
  color: var(--text-faint);
  border-bottom: none;
  padding-bottom: 0;
  margin-bottom: 10px;
}
.seo-block p {
  font-size: 12px;
  color: var(--text-faint);
  line-height: 1.7;
  margin: 0 0 10px;
}
.seo-block p:last-child { margin-bottom: 0; }

footer {
  text-align: center;
  padding: 20px;
  color: var(--text-muted);
  font-size: 12px;
}
.footer-links { margin-bottom: 14px; }
.footer-links a { color: var(--text-secondary); }
.footer-links a:hover { color: var(--accent); }
.footer-sep { margin: 0 8px; color: var(--border-strong); }

.legal-page h3 { margin: 22px 0 8px; font-size: 15px; color: var(--text); }
.legal-page h3:first-of-type { margin-top: 16px; }
.legal-page p, .legal-page li { font-size: 13px; line-height: 1.6; color: var(--text-secondary); }
.legal-page ul { padding-left: 20px; }
.legal-updated { color: var(--text-muted); font-size: 12px; margin-top: -6px; }

.dash-layout {
  display: flex;
  min-height: 100vh;
}

.dash-sidebar {
  width: 230px;
  flex-shrink: 0;
  background: var(--panel-bg);
  border-right: 1px solid var(--panel-border);
  display: flex;
  flex-direction: column;
  padding: 20px 16px;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
  /* Still scrolls, the bar just isn't painted — the nav is short enough that a visible
     track was mostly visual noise. */
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.dash-sidebar::-webkit-scrollbar { width: 0; height: 0; }
.dash-brand {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 18px;
  font-weight: bold;
  color: var(--text);
  margin-bottom: 28px;
  padding: 0 6px;
}
.dash-brand:hover { text-decoration: none; }
.dash-brand .brand-logo { width: 30px; height: 30px; }
.dash-brand .brand-accent { color: var(--accent); }

.dash-nav {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.dash-nav-divider {
  height: 1px;
  background: var(--border);
  margin: 16px 6px;
}
.dash-nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 6px;
  color: var(--text-secondary);
  font-size: 14px;
  font-weight: 500;
}
.dash-nav-item:hover { background: var(--surface-soft); text-decoration: none; color: var(--text); }
.dash-nav-item.active { background: var(--accent-soft-bg); color: var(--accent-soft-text); }
/* New order is what the dashboard is for, so it's the one row that stands out: accent icon and a
   heavier label, no fill. A fill would read as "you are here" on every page and blur the
   active row's highlight; once it is the active row it just looks like the others. */
.dash-nav-cta { font-weight: 600; }
.dash-nav-cta:not(.active) .dash-nav-icon { color: var(--accent); }
/* The deposit-bonus gift on the Billing row, shown only when the balance has run low.
   Pushed to the trailing edge with an auto margin, which no-ops in the horizontal mobile
   sidebar where rows are content-width — it simply trails the label there instead.
   Sized a little larger than the row's 17px line icons (and given negative block margins
   so the taller artwork doesn't push the row's height out) because the illustration's
   ribbon and bow stop reading below roughly this size. */
/* The deposit bonus on the Billing row, shown only while the balance is low. It used to be an
   illustrated gift whose meaning lived entirely in a title attribute — which touch devices
   never show, so on a phone it said nothing. The figure says it instead, and reads without
   hovering. The percentage comes from TOPUP_BONUS_PERCENT, so it can't drift from the bonus
   that actually gets credited.

   Deliberately quiet: no fill, a hairline outline and the row's own muted text colour, so it
   reads as a note on the Billing row rather than a second thing competing with the label for
   attention. The border is mixed from currentColor, which keeps it correct in both themes
   without a second token; --border is the fallback where color-mix isn't supported. */
.dash-nav-bonus {
  margin-inline-start: auto;
  flex-shrink: 0;
  padding: 1px 6px;
  border-radius: 999px;
  border: 1px solid var(--border);
  border-color: color-mix(in srgb, currentColor 28%, transparent);
  color: var(--text-muted);
  font-size: 11px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1.5;
}
/* Hovering or opening Billing is the moment it becomes relevant, so it picks up the accent
   there instead of announcing itself the rest of the time. */
.dash-nav-item:hover .dash-nav-bonus,
.dash-nav-item.active .dash-nav-bonus { color: var(--accent-soft-text); }

.dash-nav-icon { width: 18px; height: 18px; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; }
.dash-nav-icon svg { width: 17px; height: 17px; }

.dash-sidebar-footer {
  border-top: 1px solid var(--border);
  padding-top: 8px;
  margin-top: auto;
}
.dash-docs-link { color: var(--text-muted); margin-bottom: 6px; }
/* Hairline between API Docs and Logout, so Logout doesn't read as part of the docs link. */
.dash-sidebar-footer form { border-top: 1px solid var(--border); padding-top: 14px; }

/* Phone-only "More" menu (see the ≤600px block). On wider screens the toggle is hidden and
   the wrapper is display: contents, so the sidebar is laid out as if it weren't there. */
.dash-more { display: contents; }
.dash-more-toggle { display: none; }
.dash-phone-tools { display: none; }
.dash-nav-item.dash-phone-only { display: none; }

.dash-main {
  flex: 1;
  /* Flex items floor at their content width; without this a wide table (the catalog)
     pushes .dash-main past the viewport instead of scrolling inside its own wrapper. */
  min-width: 0;
  padding: 28px 32px;
  /* Was a flat 1080px, which left a third of a 2560px screen empty beside the sidebar.
     Tracks the viewport instead, with the upper stop keeping text columns and table rows
     short enough to still scan comfortably. */
  max-width: clamp(1080px, 84vw, 1560px);
}
@media (min-width: 1600px) {
  .dash-main { padding: 32px 44px; }
}
/* At heading size the doodles read through the letterforms, so each heading carries its own
   clearing. Attached to the TEXT elements rather than to their containers, and sized to hug
   them with roughly 20px of breathing room — clearing the whole header band would strip the
   pattern out of the header entirely, which is not what's wanted.
   z-index:-1 rather than lifting the text with `> * { position: relative }`, because
   .dash-page-title's label is a bare text node with no element to lift; a positioned ::before
   would otherwise paint over it. Negative z-index still sits above the body's canvas
   background, and no ancestor here opens a stacking context. */
.heading-clear {
  position: relative;
  width: fit-content;
  max-width: 100%;
}
.heading-clear::before {
  content: "";
  position: absolute;
  inset: -20px -34px;
  z-index: -1;
  background: radial-gradient(ellipse at center, var(--bg) 0%, var(--bg) 62%, transparent 100%);
  pointer-events: none;
}
.home-hero-title.heading-clear, .home-hero-sub.heading-clear,
.hero-bonus.heading-clear { margin-inline: auto; }

.dash-topbar {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 20px;
  flex-wrap: wrap;
}
.dash-page-title {
  font-size: 22px;
  margin: 0;
  color: var(--text);
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.tg-icon { flex-shrink: 0; vertical-align: middle; }
.home-hero-last { white-space: nowrap; }
@media (max-width: 600px) {
  .home-hero-pair { white-space: nowrap; }
}
.home-hero-last .tg-icon { margin-inline-start: 12px; vertical-align: -0.2em; }
/* Home hero: names what the catalog below actually is, and carries the same Telegram mark as
   the dashboard header so both pages read as the same product. */
.home-hero-title {
  display: block;
  text-align: center;
  font-size: clamp(24px, 2.6vw, 34px);
  line-height: 1.25;
  margin: 4px 0 12px;
  color: var(--text);
}
.home-hero-sub {
  text-align: center;
  margin: 0 0 28px;
  color: var(--text-muted);
  font-size: 15px;
}

/* One centred column: the heading, then the free-test-balance line under it. Signing up is the
   navbar's single button, so the hero carries no call to action of its own. */
.home-hero { margin-bottom: 28px; }
.hero-bonus {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 11px;
  margin: 0;
  font-size: 21px;
  font-weight: bold;
  line-height: 1.25;
  color: var(--text);
}
.hero-bonus-icon { flex-shrink: 0; color: var(--accent); }
.hero-bonus-amount { color: var(--accent); }
.dash-topbar-right {
  display: flex;
  align-items: center;
  gap: 10px;
}
.balance-chip {
  background: var(--surface-soft);
  border: 1px solid var(--surface-soft-border);
  color: #27ae60;
  font-weight: bold;
  font-size: 14px;
  padding: 7px 14px;
  border-radius: 20px;
  display: inline-block;
}
.balance-chip:hover { text-decoration: none; background: var(--accent-soft-bg); }
.lang-select {
  display: flex;
  align-items: center;
  gap: 6px;
  background: var(--surface-soft);
  border: 1px solid var(--surface-soft-border);
  border-radius: 20px;
  padding: 6px 12px;
  color: var(--text-secondary);
}
.lang-select select {
  border: none;
  background: none;
  color: var(--text-secondary);
  font-size: 13px;
  font-weight: bold;
}
/* Phones: the options inherited the closed control's 13px, which is tiny in a full-width
   list. Only the options grow; the button in the navbar keeps its size. */
@media (max-width: 600px) {
  .lang-select select option { font-size: 16px; font-weight: normal; }
}
.dash-topbar .theme-toggle {
  border: 1px solid var(--panel-border);
  color: var(--text-secondary);
}
.dash-topbar .theme-toggle:hover { background: var(--surface-soft); }

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 14px;
  margin-bottom: 24px;
}
.stat-card {
  background: var(--panel-bg);
  border: 1px solid var(--panel-border);
  border-radius: 6px;
  padding: 16px 18px;
}
.stat-card-label {
  font-size: 12px;
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: 0.4px;
  margin-bottom: 6px;
}
.stat-card-value { font-size: 24px; font-weight: bold; color: var(--text); }
/* A card that opens a page (Active orders → History): looks like its neighbours until hovered. */
.stat-card-link { display: block; text-decoration: none; transition: border-color 0.12s ease; }
.stat-card-link:hover { text-decoration: none; border-color: var(--accent); }

.email-verify-banner {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 10px;
  background: var(--accent-soft-bg);
  border: 1px solid var(--accent-soft-text);
  border-radius: 6px;
  padding: 16px 44px 16px 18px;
  margin-bottom: 20px;
}
.email-verify-dismiss { position: absolute; top: 8px; right: 8px; margin: 0; }
.email-verify-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--accent-soft-text);
  opacity: 0.65;
  cursor: pointer;
  border-radius: 50%;
  transition: opacity 0.12s ease, background 0.12s ease;
}
.email-verify-close:hover { opacity: 1; background: rgba(0,0,0,0.06); }
.email-verify-text {
  display: flex;
  flex-direction: column;
  gap: 6px;
  color: var(--text);
  font-size: 13.5px;
}
.email-verify-text strong { color: var(--accent-soft-text); font-size: 15px; }
.email-verify-banner .alert-error { margin: 0; }
.email-verify-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
}
.email-verify-controls form {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
}
.email-verify-controls .btn {
  display: inline-flex;
  align-items: center;
  height: 32px;
  box-sizing: border-box;
}
/* Desktop: lift the controls up onto the same row as the text instead of stacking them
   underneath, with the text stretching (flex: 1 1 auto) so the button is pushed out to the
   right-hand end of the banner. Below 800px the banner stays a stack, where a row would
   squeeze the button into an unreadable sliver. */
@media (min-width: 801px) {
  .email-verify-banner {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    column-gap: 30px;
    row-gap: 10px;
  }
  .email-verify-text { flex: 1 1 auto; }
  /* Clear of the banner's own right padding, which is reserved for the absolutely-positioned
     close button — the controls sit on the same rows as that X, so without the extra offset a
     right-aligned button would slide underneath it. */
  .email-verify-controls { padding-right: 20px; }
  /* The error alert is a sibling of the text/controls, so in row mode it would otherwise
     try to share their line; force it onto its own full-width row above them. */
  .email-verify-banner .alert-error { flex-basis: 100%; }
}

.email-verify-code-form input.two-fa-code-input {
  display: inline-block;
  width: auto;
  min-width: 110px;
  max-width: 130px;
  height: 32px;
  box-sizing: border-box;
  font-size: 14px;
  letter-spacing: 3px;
  text-align: center;
  padding: 0 10px;
  margin: 0;
}

@media (max-width: 800px) {
  .dash-layout { flex-direction: column; }
  .dash-sidebar { width: 100%; height: auto; position: static; flex-direction: row; flex-wrap: wrap; align-items: center; }
  .dash-nav { flex-direction: row; flex-wrap: wrap; }
  .dash-sidebar-footer { display: flex; align-items: center; gap: 10px; border-top: none; margin-top: 0; }
  .dash-sidebar-footer form { border-top: none; padding-top: 0; }
  .dash-main { padding: 20px; }
}

/* Phones: the wrapped sidebar ran to five rows of links before any content. Now it's the
   brand with a More toggle, a fixed 3-column grid of the everyday pages (Overview, New Order,
   Subscriptions / History, Billing, Support), and everything else folded into the More menu. */
@media (max-width: 600px) {
  .dash-nav-item.dash-phone-only { display: flex; }
  .dash-nav-item.dash-hide-phone { display: none; }
  .dash-sidebar { display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 10px; padding: 12px 16px; }
  .dash-brand { margin-bottom: 0; }

  .dash-more-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    position: relative;
    padding: 7px 10px 7px 12px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: transparent;
    color: var(--text-secondary);
    font: inherit;
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
  }
  .dash-more-toggle svg { width: 16px; height: 16px; transition: transform 0.15s; }
  .dash-more-toggle[aria-expanded="true"] svg { transform: rotate(180deg); }
  .dash-more-toggle.active { background: var(--accent-soft-bg); color: var(--accent-soft-text); border-color: transparent; }

  .dash-nav-primary { grid-column: 1 / -1; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
  .dash-nav-primary .dash-nav-item {
    flex-direction: column;
    justify-content: center;
    gap: 4px;
    min-width: 0;
    padding: 8px 4px;
    font-size: 12px;
    line-height: 1.2;
    text-align: center;
    overflow-wrap: anywhere;
  }
  .dash-nav-primary .dash-nav-item { position: relative; }
  /* The +5% bonus badge can't trail the label in a stacked tile, so it sits in the corner. */
  .dash-nav-primary .dash-nav-bonus { position: absolute; top: 4px; inset-inline-end: 4px; margin: 0; font-size: 10px; padding: 0 5px; }
  .dash-nav-primary .dash-nav-item:nth-child(-n+2) { order: -2; }
  .dash-nav-primary .dash-nav-subs { order: -1; }

  .dash-more { display: none; grid-column: 1 / -1; border-top: 1px solid var(--border); padding-top: 8px; }
  .dash-more.open { display: block; }
  .dash-more .dash-nav-divider { display: none; }
  .dash-more .dash-nav { flex-direction: column; flex-wrap: nowrap; gap: 2px; }
  .dash-more .dash-sidebar-footer { display: block; border-top: 1px solid var(--border); margin-top: 8px; padding-top: 2px; }
  .dash-more .dash-docs-link { margin-bottom: 6px; }
  .dash-more .dash-sidebar-footer form { border-top: 1px solid var(--border); padding-top: 14px; }

  /* Language + theme as two 34px icons between the brand and More (see dash-head.ejs), which
     frees the page header for just the title and the balance. */
  .dash-sidebar { grid-template-columns: 1fr auto auto; gap: 8px; }
  .dash-phone-tools { display: flex; align-items: center; gap: 10px; }
  /* The brand's grid cell is 1fr wide; without this the link filled it and a tap on the
     empty space after "Dude" went to the home page. */
  .dash-brand { justify-self: start; }
  .balance-chip.balance-chip-phone-hide { display: none; }
  .dash-phone-lang,
  .dash-phone-tools .theme-toggle {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    margin: 0;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: transparent;
    color: var(--text-secondary);
    cursor: pointer;
  }
  .dash-phone-lang svg { width: 16px; height: 16px; }
  /* 16px also stops iOS zooming the page when the picker opens. */
  .dash-phone-lang select {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    font-size: 16px;
    cursor: pointer;
  }
  .dash-phone-lang:focus-within { outline: 2px solid var(--accent); outline-offset: 1px; }
  .dash-topbar { flex-wrap: nowrap; gap: 12px; }
  .dash-page-title { min-width: 0; }
  .dash-topbar-right .lang-select,
  .dash-topbar-right .theme-toggle { display: none; }
}

.catalog-filterbar {
  display: flex;
  align-items: stretch;
  border: 1.5px solid transparent;
  border-radius: 10px;
  overflow: visible;
  margin-top: 18px;
  margin-bottom: 16px;
  background-image: linear-gradient(var(--surface-soft), var(--surface-soft)), var(--catalog-gradient);
  background-origin: border-box;
  background-clip: padding-box, border-box;
  background-size: 100% 100%, 300% 300%;
  background-position: 0 0, 0% 50%;
  animation: catalog-gradient-shimmer 8s ease infinite;
}
@keyframes catalog-gradient-shimmer {
  0%, 100% { background-position: 0 0, 0% 50%; }
  50% { background-position: 0 0, 100% 50%; }
}
@media (prefers-reduced-motion: reduce) {
  .catalog-filterbar { animation: none; }
}
.catalog-filter-dropdown-wrap { position: relative; }
.catalog-filter-btn {
  background: var(--accent);
  color: #fff;
  border: none;
  padding: 10px 16px;
  border-radius: 24px 0 0 24px;
  display: flex;
  align-items: center;
  gap: 6px;
  font-weight: bold;
  font-size: 13px;
  cursor: pointer;
  white-space: nowrap;
  height: 100%;
}
.catalog-filter-btn:hover { background: var(--accent-hover); }
.catalog-filter-dropdown {
  position: absolute;
  top: 100%;
  left: 0;
  background: var(--panel-bg);
  border: 1px solid var(--panel-border);
  border-radius: 8px;
  box-shadow: 0 4px 16px var(--shadow-soft);
  min-width: 240px;
  max-height: 320px;
  overflow-y: auto;
  z-index: 20;
  margin-top: 6px;
  padding: 6px;
}
.catalog-filter-option {
  padding: 8px 10px;
  border-radius: 6px;
  font-size: 13px;
  color: var(--text-secondary);
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.catalog-filter-option:hover { background: var(--surface-soft); }
.catalog-filter-option.active { background: var(--accent-soft-bg); color: var(--accent-soft-text); font-weight: bold; }

.catalog-search-wrap {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 16px;
  color: var(--text-muted);
}
.catalog-search-wrap input {
  border: none;
  background: none;
  flex: 1;
  font-size: 14px;
  padding: 12px 0;
  color: var(--text);
  max-width: none;
}
.catalog-search-wrap input:focus { outline: none; }

.stat-card-with-action {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 8px;
}
.stat-add-btn {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  border: none;
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.stat-add-btn:hover { background: var(--accent-hover); text-decoration: none; }
/* Dashboard overview on phones: the four cards as a compact 2×2 instead of a tall stack.
   Scoped to the overview; the referral and loyalty pages keep their own grids. */
@media (max-width: 600px) {
  .stat-grid-overview { grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 18px; }
  .stat-grid-overview .stat-card { padding: 11px 12px; min-width: 0; }
  .stat-grid-overview .stat-card-label { font-size: 10.5px; letter-spacing: 0.3px; margin-bottom: 4px; }
  .stat-grid-overview .stat-card-value { font-size: 18px; }
  .stat-grid-overview .stat-add-btn { width: 22px; height: 22px; font-size: 14px; }
}

.danger-zone { border-color: var(--alert-error-border); }
.danger-zone h2 { color: var(--alert-error-text); }

.balance-value { color: #27ae60; font-size: 30px; }

/* ---- Billing / top-up ------------------------------------------------------------- */
/* Two-column checkout card: amount controls on the leading side, a live summary rail on
   the trailing one. The <form> itself is the grid, because the Pay button sits in the
   summary column — see the note in views/dashboard/billing.ejs. Everything below draws
   from the theme tokens rather than fixed hexes, so the card holds up in light mode too. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.topup-balance {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  margin-bottom: 20px;
  padding: 16px 22px;
  background: var(--panel-bg);
  border: 1px solid var(--panel-border);
  border-inline-start: 3px solid var(--alert-success-text);
  border-radius: 8px;
}
.topup-balance-label { margin: 0; color: var(--text-muted); font-size: 13px; }
.topup-balance-value { display: block; margin-top: 2px; line-height: 1.2; letter-spacing: -0.5px; font-weight: bold; }
.topup-balance-icon {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 46px;
  height: 46px;
  border-radius: 12px;
  color: var(--alert-success-text);
  background: var(--alert-success-bg);
  border: 1px solid var(--alert-success-border);
}
.topup-balance-icon svg { width: 24px; height: 24px; }

.topup-card {
  display: grid;
  grid-template-columns: minmax(0, 1.24fr) minmax(0, 1fr);
  margin-bottom: 20px;
  padding: 26px;
  background: var(--panel-bg);
  border: 1px solid var(--panel-border);
  border-radius: 9px;
}
.topup-card h2 {
  margin: 0;
  padding: 0;
  border: none;
  font-size: 21px;
  letter-spacing: -0.3px;
}
.topup-amount-col { padding-inline-end: 28px; }
.topup-intro { margin: 6px 0 0; color: var(--text-secondary); }

.topup-amount-label {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 10px;
  margin: 24px 0 12px;
}
.topup-amount-label strong { font-size: 14px; }
.topup-minimum { font-size: 13px; color: var(--text-muted); }

.topup-presets {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 8px;
}
.topup-preset {
  min-height: 46px;
  padding: 8px 2px;
  border: 1px solid var(--border-strong);
  border-radius: 7px;
  color: var(--text);
  background: var(--surface-soft);
  font-size: 14px;
  font-weight: bold;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
.topup-preset:hover { border-color: var(--accent); }
/* aria-pressed is the selected state, so the styling can't drift from what a screen
   reader announces the way a separate .active class would. */
.topup-preset[aria-pressed="true"] {
  color: var(--accent-soft-text);
  background: var(--accent-soft-bg);
  border-color: var(--accent);
}

.topup-custom-label { display: block; margin: 22px 0 8px; }
.topup-input-wrap {
  display: flex;
  height: 50px;
  align-items: center;
  overflow: hidden;
  background: var(--input-bg);
  border: 1px solid var(--input-border);
  border-radius: 6px;
}
.topup-input-wrap:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft-bg); }
.topup-currency {
  display: grid;
  place-items: center;
  flex: 0 0 44px;
  height: 100%;
  border-inline-end: 1px solid var(--input-border);
  color: var(--text-secondary);
  font-size: 18px;
}
.topup-input-wrap input {
  /* Beats the global input[type=text] max-width, which would otherwise stop the field
     short and leave the stepper floating in the middle of the row. */
  flex: 1;
  width: 100%;
  max-width: none;
  min-width: 0;
  height: 100%;
  margin: 0;
  padding: 0 14px;
  border: 0;
  background: transparent;
  color: var(--text);
  font-size: 19px;
  font-variant-numeric: tabular-nums;
  outline: none;
}
.topup-stepper {
  display: flex;
  flex-direction: column;
  align-self: stretch;
  flex: 0 0 42px;
  border-inline-start: 1px solid var(--input-border);
}
.topup-step {
  display: grid;
  place-items: center;
  flex: 1;
  padding: 0;
  border: 0;
  color: var(--text-secondary);
  background: var(--surface-soft);
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}
.topup-step + .topup-step { border-top: 1px solid var(--input-border); }
.topup-step:hover:not(:disabled) { background: var(--accent-soft-bg); color: var(--accent-soft-text); }
.topup-step:disabled { color: var(--text-faint); cursor: default; }
.topup-step svg { width: 15px; height: 15px; }

.topup-error { margin: 8px 0 0; color: var(--alert-error-text); font-size: 13px; }
.topup-error:empty { display: none; }

.topup-bonus-card {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-top: 24px;
  padding: 16px 14px;
  background: var(--accent-soft-bg);
  border: 1px solid var(--surface-soft-border);
  border-radius: 7px;
}
.topup-gift { flex: 0 0 72px; width: 72px; height: 78px; overflow: visible; }
.topup-bonus-card h3 { margin: 0; color: var(--accent-soft-text); font-size: 16px; line-height: 1.35; }
.topup-bonus-card p { margin: 5px 0 0; color: var(--text-secondary); font-size: 13px; line-height: 1.5; }

.topup-summary {
  padding-inline-start: 28px;
  border-inline-start: 1px solid var(--border);
}
.topup-summary h2 { font-size: 17px; }
.topup-summary dl { margin: 22px 0 0; }
.topup-summary-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-top: 14px;
}
.topup-summary-row:first-child { margin-top: 0; }
.topup-summary-row dt { color: var(--text-secondary); }
.topup-summary-row dd {
  margin: 0;
  font-size: 17px;
  font-weight: bold;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.topup-bonus-value { color: var(--accent-soft-text); }
.topup-summary-total {
  margin-top: 18px;
  padding-top: 18px;
  border-top: 1px solid var(--border);
}
.topup-summary-total dt { color: var(--text); font-weight: bold; font-size: 15px; }
.topup-summary-total dd { font-size: 21px; color: var(--alert-success-text); }
.topup-summary-after { margin-top: 12px; }
.topup-summary-after dt, .topup-summary-after dd { color: var(--text-muted); font-size: 13px; font-weight: normal; }

.topup-pay {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  width: 100%;
  height: 52px;
  margin-top: 46px;
  border: 1px solid var(--accent);
  border-radius: 8px;
  background: var(--accent);
  color: var(--accent-contrast);
  font-size: 16px;
  font-weight: bold;
  cursor: pointer;
  transition: background 0.15s, transform 0.08s;
}
.topup-pay:hover:not(:disabled) { background: var(--accent-hover); }
/* Presses register instantly, before any network work starts. */
.topup-pay:active:not(:disabled) { transform: translateY(1px); background: var(--accent-hover); }
/* Disabled until the amount is valid, so the only way to reach checkout is with a figure
   the summary above has already priced. */
.topup-pay:disabled { opacity: 0.55; cursor: not-allowed; }
.topup-pay svg { width: 20px; height: 20px; }

/* Busy state while the server asks the payment provider for a checkout URL. It keeps full
   opacity — this button is working, not unavailable — and outranks the dimming above
   because .topup-pay.is-loading:disabled is the more specific selector. */
.topup-pay.is-loading:disabled { opacity: 1; cursor: progress; }
.topup-pay-spinner { display: none; animation: topup-spin 0.7s linear infinite; }
.topup-pay.is-loading .topup-pay-arrow { display: none; }
.topup-pay.is-loading .topup-pay-spinner { display: block; }
@keyframes topup-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .topup-pay { transition: none; }
  .topup-pay-spinner { animation: none; }
}

.topup-security {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-top: 20px;
  color: var(--text-muted);
  font-size: 12px;
}
.topup-security svg { width: 22px; height: 25px; flex-shrink: 0; color: var(--accent); }
.topup-security strong { color: var(--text-secondary); }

/* The "+$0.30" credited alongside a past top-up, in the history table. */
.topup-bonus-tag {
  font-size: 12px;
  font-weight: bold;
  color: var(--accent-soft-text);
  background: var(--accent-soft-bg);
  border-radius: 10px;
  padding: 2px 7px;
  white-space: nowrap;
}

/* Stacks well before the dashboard's own 800px breakpoint: the sidebar eats 230px, so the
   card is already cramped at two columns by the time the viewport is this narrow. */
@media (max-width: 1060px) {
  .topup-card { display: block; padding: 22px; }
  .topup-amount-col { padding-inline-end: 0; }
  .topup-summary {
    margin-top: 24px;
    padding-inline-start: 0;
    padding-top: 22px;
    border-inline-start: 0;
    border-top: 1px solid var(--border);
  }
}
@media (max-width: 420px) {
  .topup-presets { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .topup-gift { flex-basis: 56px; width: 56px; height: 60px; }
  .topup-bonus-card { gap: 10px; }
}

.integration-panel { border-color: var(--accent); }
.integration-panel h2 { border: none; padding: 0; display: flex; align-items: center; gap: 10px; }
.optional-badge {
  font-size: 11px;
  font-weight: bold;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--text-faint);
  background: var(--surface-soft);
  border: 1px solid var(--surface-soft-border);
  border-radius: 12px;
  padding: 3px 10px;
}
.integration-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 12px 0;
}
.integration-copy-confirm { color: #27ae60; font-size: 13px; font-weight: bold; }
.integration-prompt-block {
  max-height: 420px;
  white-space: pre-wrap;
}

.service-preview {
  display: flex;
  gap: 20px;
  margin-top: 12px;
  padding: 12px 16px;
  background: var(--surface-soft);
  border: 1px solid var(--surface-soft-border);
  border-radius: 6px;
}
.service-preview-row { display: flex; flex-direction: column; gap: 2px; }
.service-preview-label { font-size: 11px; color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.4px; }
.service-preview-value { font-size: 15px; font-weight: bold; color: var(--text); }
.field-hint { font-size: 12px; color: var(--text-muted); margin-top: 4px; }
#service-select { min-height: 200px; }

.mass-order-textarea {
  width: 100%;
  min-height: 220px;
  padding: 12px 14px;
  border: 1px solid var(--input-border);
  border-radius: 6px;
  background: var(--input-bg);
  color: var(--text);
  font-family: "Courier New", Courier, monospace;
  font-size: 13px;
  resize: vertical;
}
/* Same box as .mass-order-textarea, but for prose: a support ticket is written in
   sentences, not in the "id | link | qty" columns that make monospace worth it on the
   mass-order page. 16px matches .subject-input-large directly above it in the same form,
   and is also the threshold below which iOS Safari zooms the page in on focus. */
.ticket-textarea {
  width: 100%;
  min-height: 140px;
  padding: 12px 14px;
  border: 1px solid var(--input-border);
  border-radius: 6px;
  background: var(--input-bg);
  color: var(--text);
  font-family: inherit;
  font-size: 16px;
  line-height: 1.5;
  resize: vertical;
}

.mass-order-ok { color: #1e8449; font-weight: bold; }
.mass-order-fail { color: var(--alert-error-text); font-weight: bold; }

.loyalty-current {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: var(--surface-soft);
  border: 1px solid var(--surface-soft-border);
  border-radius: 6px;
  padding: 12px 16px;
  margin-bottom: 14px;
  flex-wrap: wrap;
  gap: 8px;
}
.loyalty-tier-badge { font-size: 14px; color: var(--text); }
.loyalty-tier-badge strong { color: var(--accent); }
.loyalty-spend { font-size: 13px; color: var(--text-secondary); }
.loyalty-progress-wrap { margin-bottom: 4px; }
.loyalty-progress-bar {
  height: 8px;
  background: var(--surface-soft);
  border: 1px solid var(--surface-soft-border);
  border-radius: 4px;
  overflow: hidden;
}
.loyalty-progress-fill { height: 100%; background: var(--accent); }
.loyalty-progress-label { font-size: 12px; color: var(--text-muted); margin-top: 6px; }
.loyalty-row-active td { background: var(--accent-soft-bg); font-weight: bold; }

.subscriptions-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 16px;
}
.subscription-card { margin-bottom: 0; }
.subscription-card-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 12px;
}
.subscription-card-service { font-size: 15px; font-weight: bold; color: var(--text); }
.subscription-card-channel { font-size: 13px; color: var(--text-muted); }
.subscription-fact {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 13px;
  color: var(--text-secondary);
  padding: 3px 0;
}
.subscription-fact-icon { flex-shrink: 0; width: 18px; text-align: center; }
.subscription-card-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 14px;
  flex-wrap: wrap;
}

.chat-form {
  display: flex;
  gap: 8px;
  margin-top: 12px;
}
.chat-form input {
  flex: 1;
  max-width: none;
  padding: 12px 14px;
  font-size: 15px;
  border-radius: 8px;
}
.chat-form button {
  padding: 12px 24px;
  font-size: 14px;
}

.ticket-header {
  display: flex;
  align-items: center;
  gap: 10px;
  border-bottom: 1px solid var(--border);
  padding-bottom: 10px;
}
.ticket-header h2 { margin: 0; border: none; padding: 0; flex: 1; }

.ticket-thread {
  height: 420px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 16px;
  background: var(--surface-soft);
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin: 16px 0;
}
.ticket-empty { text-align: center; color: var(--text-muted); margin: auto; font-size: 13px; }
.ticket-row { display: flex; }
.ticket-row-self { justify-content: flex-start; }
.ticket-row-other { justify-content: flex-start; }
.ticket-bubble {
  max-width: 78%;
  padding: 10px 14px;
  border-radius: 10px;
  font-size: 13px;
}
.ticket-row-self .ticket-bubble { background: var(--accent); color: #fff; border-bottom-left-radius: 2px; }
.ticket-row-other .ticket-bubble { background: var(--panel-bg); border: 1px solid var(--panel-border); color: var(--text); border-bottom-left-radius: 2px; }
.ticket-bubble-label { font-size: 11px; font-weight: bold; margin-bottom: 4px; opacity: 0.85; }
.ticket-bubble-text { font-size: 14px; line-height: 1.5; word-wrap: break-word; white-space: pre-wrap; }
.ticket-bubble-time { font-size: 10px; margin-top: 6px; opacity: 0.7; }

.ticket-actions { margin-top: 14px; }

/* Image attachments on support messages (public/js/ticketAttach.js). */
/* The new-ticket paperclip sits inside the message box, bottom-left (the resize grip has the
   other corner). The textarea's bottom padding keeps typed text from running under it. */
.ticket-textarea-wrap { position: relative; }
.ticket-textarea-wrap .ticket-textarea { display: block; padding-bottom: 40px; }
.ticket-attach-inline {
  position: absolute;
  inset-inline-start: 8px;
  bottom: 8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 6px;
  color: var(--text-muted);
  opacity: 0.75;
  cursor: pointer;
  transition: opacity 0.12s ease, background 0.12s ease, color 0.12s ease;
}
/* #ticket-form label (an id rule) would otherwise give it an 18px top margin. */
#ticket-form .ticket-attach-inline { margin: 0; }
.ticket-attach-inline:hover { opacity: 1; color: var(--text); background: var(--surface-soft); }
.ticket-attach-inline:focus-within { opacity: 1; outline: 2px solid var(--accent); outline-offset: 1px; }
.ticket-attach-icon {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.ticket-attach-icon {
  flex: 0 0 auto;
  justify-content: center;
  padding: 0 14px;
}
.ticket-attach-preview {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 12px;
  padding: 8px;
  max-width: 420px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface-soft);
}
.ticket-attach-preview[hidden],
#ticket-form-error[hidden],
#reply-error[hidden] { display: none; }
.ticket-attach-preview img {
  width: 56px;
  height: 56px;
  object-fit: cover;
  border-radius: 4px;
  flex-shrink: 0;
}
.ticket-attach-name {
  flex: 1;
  min-width: 0;
  font-size: 13px;
  color: var(--text-secondary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ticket-attach-remove {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  border: none;
  border-radius: 50%;
  background: none;
  color: var(--text-muted);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}
.ticket-attach-remove:hover { background: var(--border); color: var(--text); }
.ticket-bubble-image { display: block; margin-top: 6px; }
.ticket-bubble-image img {
  display: block;
  max-width: 100%;
  max-height: 260px;
  border-radius: 6px;
}

.filter-tabs { display: flex; gap: 5px; }
/* Padding/gap kept tight on purpose: the 8 service-type tabs have to fit the dashboard's
   ~944px content column on a 1280px laptop in every locale, and the labels run ~70px wider in
   Russian ("Запуск бота + Премиум") than in English. */
.filter-tab {
  padding: 8px 8px;
  border: 1px solid var(--chip-border);
  border-radius: 999px;
  color: var(--text-secondary);
  color: color-mix(in srgb, var(--text) 45%, var(--text-secondary));
  background: var(--surface-soft);
  font-size: 13px;
}
.filter-tab:hover { background: var(--accent-soft-bg); border-color: var(--chip-border-hover); text-decoration: none; }
.filter-tab.active { background: var(--accent); border-color: var(--accent); color: #fff; }

.service-type-tabs { margin-top: 14px; flex-wrap: wrap; }
.service-type-tabs .filter-tab { display: inline-flex; align-items: center; gap: 5px; }
/* Phones: the 5px gap left wrapped rows of chips touching and made them small tap targets.
   Only vertical room is added — any extra horizontal padding or gap wraps the chips onto a
   fourth row on a ~390px screen. */
@media (max-width: 600px) {
  .service-type-tabs { row-gap: 8px; }
  .service-type-tabs .filter-tab { padding-block: 8px; }
}
/* Home page only, one size up: its catalog gets the full-width container (1198px at 1280),
   not the dashboard's ~944px column. At this size the longest locale needs 1119px (vi; ru is
   1115) for one row, so it starts at 1220px — narrower screens keep the compact chips, which
   still fit there in most locales, rather than wrapping big ones onto a second row. */
@media (min-width: 1220px) {
  .service-type-tabs-lg { gap: 7px; }
  .service-type-tabs-lg .filter-tab { padding: 10px 14px; font-size: 14px; gap: 7px; }
}

/* Sized to sit alongside the site's other small buttons rather than tower over them: pill
   radius like the nav/form buttons, no forced min-width, and a height that doesn't dictate
   the catalog row height across a few hundred rows. */
.catalog-action-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-width: 0;
  min-height: 32px;
  padding: 0 14px;
  border-radius: 20px;
  font-size: 12.5px;
  letter-spacing: 0.01em;
}
.catalog-action-icon-only {
  min-width: 0;
  width: 32px;
  padding: 0;
  gap: 0;
}
.catalog-action-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}
.catalog-action-label {
  white-space: nowrap;
}
.catalog-action-info {
  background: transparent;
  border: 1px solid var(--surface-soft-border);
  color: var(--text) !important;
  box-shadow: none;
  min-width: 0;
  padding: 0 10px;
}
.catalog-action-info .catalog-action-icon {
  background: transparent;
  color: inherit;
}
.catalog-action-info:hover {
  background: var(--surface-soft);
  border-color: var(--accent);
  color: var(--accent) !important;
}
.catalog-action-order {
  background: linear-gradient(135deg, var(--accent), var(--accent-hover));
  border: 1px solid transparent;
  box-shadow: 0 1px 2px var(--shadow-soft);
}
.catalog-action-order .catalog-action-icon {
  background: transparent;
}
.catalog-action-order:hover {
  background: linear-gradient(135deg, var(--accent-hover), var(--accent));
}

/* Dark mode only (light is left as it was). White on the brand orange is just 3.1:1, too weak for
   a 12.5px label on the page's main action, so Order takes a step darker orange: 4.4:1 at the
   lighter end of the gradient, 4.8:1 at the other. Scoped to Order rather than --accent itself, so
   Register, the active chip and the rest of the orange keep the brand colour. */
:root:not([data-theme="light"]) .catalog-action-order {
  background: linear-gradient(135deg, #c25a3b, #b85535);
}
:root:not([data-theme="light"]) .catalog-action-order:hover {
  background: linear-gradient(135deg, #b85535, #a84c30);
}
/* The orange tint on category bars and ID badges made the whole table warm, so Order had nothing
   cooler to stand out from. Both go quieter / neutral in dark mode, which leaves the buttons as the
   warmest thing on screen. */
:root:not([data-theme="light"]) .category-row td {
  background: rgba(217, 119, 87, 0.07);
  border-bottom-color: rgba(217, 119, 87, 0.2);
}
:root:not([data-theme="light"]) .service-id-badge { background: var(--code-bg); }
/* A calmer green for the Active accounts figure so it stops competing with the orange buttons in
   the same row (5.1:1 on the row, up from 4.6:1). The modal repeats the number, so it follows. */
:root:not([data-theme="light"]) .active-accounts-value,
:root:not([data-theme="light"]) .modal-row-value-green { color: #62b083; }

.order-select-wrap { position: relative; margin-top: 30px; }
.order-select-trigger {
  display: flex;
  align-items: center;
  gap: 10px;
  border: 1.5px solid transparent;
  border-radius: 8px;
  padding: 10px 14px;
  color: var(--text-muted);
  cursor: text;
  background-image: linear-gradient(var(--input-bg), var(--input-bg)), var(--catalog-gradient);
  background-origin: border-box;
  background-clip: padding-box, border-box;
  background-size: 100% 100%, 300% 300%;
  background-position: 0 0, 0% 50%;
  animation: catalog-gradient-shimmer 8s ease infinite;
}

/* The selected service's rate, parked at the right end of the picker. It sits inside the
   field rather than under it so it reads as a property of the thing that's selected — the
   number and the name the user just picked stay on one line, in one place.
   flex-shrink:0 keeps the price whole; the search input shrinks instead (see below), because
   a truncated price ("$10.40..." ) is worse than a truncated service name the user just chose. */
.order-select-rate {
  flex-shrink: 0;
  white-space: nowrap;
  font-size: 13px;
  font-weight: 700;
  color: var(--accent-soft-text);
  background: var(--accent-soft-bg);
  border-radius: 999px;
  padding: 3px 10px;
  /* Nudged off the chevron (on top of the trigger's own 10px gap). margin-inline-end rather
     than margin-right so it shifts the correct way in the RTL locale. */
  margin-inline-end: 6px;
}
/* Without this the input's width:100% refuses to give ground on a narrow screen and pushes
   the price (and the chevron) out past the edge of the field. */
#service-search { min-width: 0; }
@media (prefers-reduced-motion: reduce) {
  .order-select-trigger { animation: none; }
}
.order-select-trigger.open { box-shadow: 0 0 0 3px var(--accent-soft-bg); }
.order-select-trigger.needs-attention {
  box-shadow: 0 0 0 3px var(--accent-soft-bg);
}
.order-select-trigger.needs-attention input::placeholder { color: var(--accent-soft-text); }
.order-select-trigger input {
  border: none;
  background: none;
  flex: 1;
  font-size: 14px;
  color: var(--text);
  padding: 0;
  max-width: none;
}
.order-select-trigger input:focus { outline: none; }
#order-select-chevron { flex-shrink: 0; transition: transform 0.15s ease; }
.order-select-trigger.open #order-select-chevron { transform: rotate(180deg); }

.order-select-panel {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  margin-top: 6px;
  max-height: 460px;
  overflow-y: auto;
  border: 1px solid var(--panel-border);
  border-radius: 8px;
  background: var(--panel-bg);
  box-shadow: 0 8px 24px var(--shadow-strong);
  z-index: 30;
}
.order-category-label,
.order-sorted-label {
  background: var(--category-bg);
  color: var(--text);
  font-weight: bold;
  font-size: 15px;
  padding: 11px 14px;
  position: sticky;
  top: 0;
  z-index: 1;
}
/* Room on the label's trailing side for the column headers floating over it (Avg. time,
   Price, Accounts): the time column ends 222px from the edge and is 84px wide, plus a gap. */
.order-category-label,
.order-sorted-label { padding-inline-end: 318px; }
.order-select-panel.is-sorted .order-category-group,
.order-select-panel.no-matches .order-sort-bar,
.order-select-panel.no-matches .order-sorted-list { display: none; }
/* Zero-height and stuck to the top of the scrolling panel, above the sticky labels (z-index 1),
   so the button rides on whichever label is showing. Its trailing edge lines up with the price
   column's: the row's 24px end padding + the 90px accounts column + the 12px gap between. */
.order-sort-bar {
  position: sticky;
  top: 0;
  height: 0;
  z-index: 2;
}
.order-sort-price {
  position: absolute;
  top: 0;
  inset-inline-end: 126px;
  height: 43px;
  display: inline-flex;
  align-items: center;
  padding: 0;
  border: none;
  background: none;
  font: inherit;
  font-size: 12.5px;
  font-weight: bold;
  color: var(--text-muted);
  cursor: pointer;
}
.order-sort-price:hover { color: var(--text); }
.order-sort-price[data-sort="asc"],
.order-sort-price[data-sort="desc"] { color: var(--accent); }
.order-sort-price:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }
/* Header for the accounts column, same look as Price but not a sort control. Trailing edge
   on the row's 24px end padding, where the right-aligned counts end. */
.order-sort-accounts {
  position: absolute;
  top: 0;
  inset-inline-end: 24px;
  height: 43px;
  display: inline-flex;
  align-items: center;
  font-size: 12.5px;
  font-weight: bold;
  color: var(--text-muted);
}
/* Over the avg-time column: its trailing edge is the accounts column (90px) + the price
   column (84px) + two 12px gaps + the row's 24px end padding. */
.order-sort-time { inset-inline-end: 222px; }
.order-service-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 14px;
  /* Both the rate and the account count are fixed-width columns pinned to the end of the row,
     so widening the row's trailing padding is what moves the pair inward — padding on the
     columns themselves would only eat into their own right-aligned text. Logical property so
     it stays the trailing side under RTL. */
  padding-inline-end: 24px;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
}
.order-service-row:hover { background: var(--surface-soft); }
.order-service-row.selected { background: var(--accent-soft-bg); box-shadow: inset 3px 0 0 var(--accent); }
.order-service-row-main { flex: 1 1 auto; min-width: 0; }
.order-service-row-name {
  font-size: 14px;
  color: var(--text);
  white-space: normal;
  overflow-wrap: break-word;
}
.order-service-row-meta {
  font-size: 13px;
  color: var(--text-muted);
  margin-top: 6px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.order-service-id-badge {
  display: inline-block;
  vertical-align: 1px;
  margin-inline-end: 2px;
  background: var(--accent-soft-bg);
  color: var(--accent-soft-text);
  border-radius: 3px;
  padding: 1px 6px;
  font-size: 12px;
  font-weight: 600;
}
.service-id-badge {
  display: inline-block;
  background: var(--accent-soft-bg);
  border-radius: 3px;
  padding: 1px 6px;
}
.order-service-row-rate {
  font-size: 12.5px;
  font-weight: bold;
  color: var(--text);
  white-space: nowrap;
  flex: 0 0 84px;
  text-align: right;
}
.order-service-row-rate span { display: block; font-size: 11px; font-weight: normal; color: var(--text-muted); }
.order-service-row-accounts {
  font-size: 12.5px;
  color: var(--text-muted);
  white-space: nowrap;
  flex: 0 0 90px;
  text-align: right;
}
.order-service-row-accounts-green { color: #27ae60; }
.order-service-row-time {
  flex: 0 0 84px;
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  gap: 4px;
  font-size: 12px;
  color: var(--text-muted);
  opacity: 0.8;
  white-space: nowrap;
}
.order-service-row-time svg { flex-shrink: 0; opacity: 0.75; }

@media (max-width: 640px) {
  .order-service-row-accounts,
  .order-service-row-time,
  .order-sort-accounts { display: none; }
  /* No accounts column on phones, so the price column ends at the row's 24px padding. */
  .order-sort-price { inset-inline-end: 24px; }
  .order-category-label,
  .order-sorted-label { padding-inline-end: 96px; }
}

/* Supporting info, not a call to action: no filled/bordered cards competing with the order
   form's own bordered inputs right below. Each stat gets an icon so it stays individually
   scannable without a box around it, and one divider anchors the row as a group. */

input.payout-amount-input { max-width: 200px; display: inline-block; }
select.payout-method-select {
  padding: 6px 8px;
  border: 1px solid var(--input-border);
  border-radius: 3px;
  font-size: 13px;
  width: 100%;
  max-width: 320px;
  background: var(--input-bg);
  color: var(--text);
}
.payout-submit-btn { margin-left: 10px; }

input.subject-input-large { font-size: 16px; padding: 10px 12px; max-width: none; width: 100%; }


.mode-picker {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  margin-bottom: 14px;
}
@media (max-width: 600px) {
  .mode-picker { grid-template-columns: 1fr; }
}
.mode-card {
  display: block;
  border: 1px solid var(--panel-border);
  border-radius: 8px;
  padding: 14px 16px;
  background: var(--surface-soft);
  cursor: pointer;
  transition: border-color 0.12s ease, background 0.12s ease;
  position: relative;
}
.mode-card:hover { border-color: var(--accent); }
.mode-card.selected {
  border-color: var(--accent);
  background: var(--accent-soft-bg);
  cursor: default;
}
.mode-card.selected::after {
  content: "Active";
  position: absolute;
  top: 12px;
  right: 14px;
  font-size: 10px;
  font-weight: bold;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--accent);
}
.mode-card-title { font-size: 15px; font-weight: bold; color: var(--text); margin-bottom: 4px; padding-right: 50px; }
.mode-card-desc { font-size: 12px; color: var(--text-secondary); line-height: 1.5; }

.referral-panel-intro { margin: 0 0 16px; color: var(--text-secondary); }
.referral-lifetime-note {
  /* Had no bottom margin, so it sat flush against the tier box that follows and the two read
     as one collapsed block. */
  margin: 18px 0 16px;
  padding: 10px 14px;
  background: var(--alert-success-bg);
  border: 1px solid var(--alert-success-border);
  border-radius: 8px;
  color: var(--alert-success-text);
  font-size: 13px;
  font-weight: normal;
  line-height: 1.5;
}

.mode-confirm-body { color: var(--text-secondary); line-height: 1.6; margin: 0 0 20px; }
.mode-confirm-actions { display: flex; gap: 10px; }

.referral-mode-tabs {
  display: flex;
  gap: 8px;
  margin-bottom: 20px;
  border-bottom: 1px solid var(--border);
}
.referral-mode-tab {
  padding: 10px 18px;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  font-size: 14px;
  font-weight: bold;
  color: var(--text-muted);
  cursor: pointer;
  margin-bottom: -1px;
}
.referral-mode-tab:hover { color: var(--text); }
.referral-mode-tab.active { color: var(--accent); border-bottom-color: var(--accent); }

.referral-calc { text-align: center; }
.referral-calc label { display: block; font-weight: bold; font-size: 13px; color: var(--text); margin-bottom: 14px; }
.referral-calc-volume { font-size: 22px; font-weight: bold; color: var(--accent); margin: 10px 0 20px; }
.referral-calc-stats { margin-top: 10px; }

.range-slider {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 8px;
  border-radius: 4px;
  background: var(--surface-soft-border);
  outline: none;
  cursor: pointer;
}
.range-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--accent);
  border: 3px solid var(--panel-bg);
  box-shadow: 0 1px 4px var(--shadow-strong);
  cursor: pointer;
}
.range-slider::-moz-range-thumb {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--accent);
  border: 3px solid var(--panel-bg);
  box-shadow: 0 1px 4px var(--shadow-strong);
  cursor: pointer;
}

.mode-confirm-cancel { background: var(--surface-soft); color: var(--text) !important; box-shadow: none; }
.mode-confirm-cancel:hover { background: var(--border); }
.mode-confirm-submit { flex: 1; }


input.order-detail-input,
select.order-detail-input {
  font-size: 14px;
  padding: 10px 14px;
  border-radius: 8px;
  max-width: none;
  width: 100%;
  height: 42px;
  box-sizing: border-box;
  border: 1px solid var(--input-border);
  background: var(--input-bg);
  color: var(--text);
}
input.order-detail-input[readonly] { background: var(--surface-soft); color: var(--text-secondary); cursor: default; }
select.order-detail-input {
  -webkit-appearance: none;
  appearance: none;
  padding-right: 34px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239a988e' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 14px;
}

.order-two-col-row { display: flex; gap: 16px; }
.order-two-col { flex: 1; min-width: 0; }

.subscription-fields-help {
  background: var(--surface-soft);
  border: 1px solid var(--surface-soft-border);
  border-radius: 8px;
  padding: 12px 14px;
  margin-bottom: 16px;
}
.subscription-help-row {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 13px;
  color: var(--text-secondary);
  line-height: 1.5;
}
.subscription-help-row:not(:last-child) { margin-bottom: 8px; }
.subscription-help-icon { flex-shrink: 0; }

.expiry-input-wrap { position: relative; }
.expiry-required-msg { display: inline-block; margin-inline-start: 8px; color: var(--alert-error-text); font-size: 13px; font-weight: 600; }
.expiry-required-msg[hidden] { display: none; }
.expiry-required-msg::before { content: "⚠ "; }
#expiry-display.input-error { border-color: #c0392b; box-shadow: 0 0 0 3px rgba(192, 57, 43, 0.18); }
#expiry-display { padding-right: 38px; cursor: pointer; }
#expiry-display.order-detail-input[readonly] { background: var(--input-bg); color: var(--text); cursor: pointer; }
.expiry-clear-btn {
  position: absolute;
  right: 4px;
  top: 50%;
  transform: translateY(-50%);
  background: none;
  border: none;
  color: var(--text-muted);
  cursor: pointer;
  padding: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 6px;
  z-index: 1;
}
.expiry-clear-btn:hover { color: var(--text); background: var(--surface-soft); }

.calendar-dropdown {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  min-width: 280px;
  background: var(--panel-bg);
  border: 1px solid var(--panel-border);
  border-radius: 12px;
  box-shadow: 0 8px 24px var(--shadow-strong);
  padding: 14px;
  z-index: 20;
}
/* Phones: Expiry is the right-hand column, so a 280px calendar anchored left ran off-screen. */
@media (max-width: 600px) { .calendar-dropdown { left: auto; right: 0; } }
.calendar-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.calendar-title { font-size: 14px; font-weight: bold; color: var(--text); }
.calendar-nav-btn {
  background: none;
  border: none;
  color: var(--text-secondary);
  cursor: pointer;
  padding: 6px;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.calendar-nav-btn:hover { background: var(--surface-soft); color: var(--text); }
.calendar-weekdays {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  text-align: center;
  font-size: 11px;
  color: var(--text-faint);
  font-weight: bold;
  margin-bottom: 4px;
}
.calendar-days {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 3px;
}
.calendar-day {
  aspect-ratio: 1;
  border: none;
  background: none;
  border-radius: 8px;
  font-size: 13px;
  color: var(--text);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.calendar-day:hover:not(.calendar-day-disabled) { background: var(--surface-soft); }
.calendar-day-today { box-shadow: inset 0 0 0 1px var(--accent); color: var(--accent); font-weight: bold; }
.calendar-day-selected { background: var(--accent) !important; color: #fff !important; font-weight: bold; }
.calendar-day-disabled { color: var(--text-faint); cursor: not-allowed; opacity: 0.4; }
.calendar-day-empty { cursor: default; }

.qty-input-wrap { position: relative; }
input.qty-input { padding-right: 42px; -moz-appearance: textfield; }
input.qty-input::-webkit-inner-spin-button,
input.qty-input::-webkit-outer-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.qty-stepper {
  position: absolute;
  top: 5px;
  right: 5px;
  bottom: 5px;
  width: 30px;
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.qty-step-btn {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: var(--surface-soft);
  border: 1px solid var(--input-border);
  border-radius: 5px;
  color: var(--text-muted);
  cursor: pointer;
}
.qty-step-btn:hover { background: var(--accent-soft-bg); border-color: var(--accent); color: var(--accent); }
.qty-step-btn:active { background: var(--accent); border-color: var(--accent); color: #fff; }
#order-form label { margin: 18px 0 6px; }
#ticket-form label { margin: 18px 0 8px; }
#ticket-form label:first-of-type { margin-top: 0; }
.order-submit-row { margin-top: 28px; display: flex; justify-content: center; }

.dripfeed-section {
  margin-top: 18px;
  padding: 14px 16px;
  background: var(--surface-soft);
  border: 1px solid var(--surface-soft-border);
  border-radius: 8px;
}
.dripfeed-toggle-label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: bold;
  font-size: 13px;
  color: var(--text);
  cursor: pointer;
  margin: 0;
}
.dripfeed-toggle-label input[type=checkbox] {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  width: 18px;
  height: 18px;
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  flex-shrink: 0;
  border: 1px solid var(--input-border);
  border-radius: 4px;
  background: var(--input-bg);
  cursor: pointer;
  position: relative;
  vertical-align: middle;
}
.dripfeed-toggle-label input[type=checkbox]:checked {
  background: var(--accent);
  border-color: var(--accent);
}
.dripfeed-toggle-label input[type=checkbox]:checked::after {
  content: "";
  position: absolute;
  left: 5px;
  top: 1px;
  width: 5px;
  height: 9px;
  border: solid #fff;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
.dripfeed-fields { margin-top: 14px; }
/* One line, so align-items:center (the old multi-line hint needed flex-start). */
.dripfeed-note {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 10px;
  font-size: 12px;
  color: var(--text-muted);
}
.dripfeed-note svg { flex-shrink: 0; color: var(--text-faint); }
/* The one committing action on the page: a solid accent pill. Size and weight still come from
   .btn.btn-large. */
.order-submit-btn {
  --order-fill-start: var(--accent);
  --order-fill-end: var(--accent-hover);
  --order-glow: rgba(217, 119, 87, 0.18);
  position: relative;
  isolation: isolate;
  overflow: hidden;
  width: 100%;
  min-width: 0;
  min-height: 46px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 12px 20px;
  font-size: 15px;
  letter-spacing: 0.2px;
  border-radius: 24px;
  background: var(--accent);
  box-shadow: 0 2px 10px var(--shadow-soft), 0 0 12px var(--order-glow);
  transition: transform 180ms ease, box-shadow 180ms ease;
}
:root[data-theme="light"] .order-submit-btn { --order-glow: rgba(42, 110, 187, 0.14); }
.order-submit-btn::after {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(135deg, var(--order-fill-start), var(--order-fill-end));
  pointer-events: none;
}
/* Only ever disabled while the order is being placed (to stop double-submits); with no service
   picked it stays clickable and opens #select-service-modal instead. */
.order-submit-btn:disabled {
  cursor: progress;
  transform: none;
}
.order-submit-btn:hover:not(:disabled) {
  transform: translateY(-1px);
  box-shadow: 0 5px 16px var(--shadow-soft), 0 0 12px var(--order-glow);
}
.order-submit-btn:active:not(:disabled) { transform: translateY(0); box-shadow: 0 1px 4px var(--shadow-soft); }
.order-submit-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) {
  .order-submit-btn { transition: none; }
}
/* "Select a service first" — what Place order opens when nothing is picked yet. */
.select-service-modal-box { text-align: center; }
.select-service-modal-box h3 { padding: 6px 24px 0; margin-bottom: 18px; }
.select-service-modal-btn { padding: 10px 24px; font-size: 14px; }

/* The charge, riding along on the button. Slightly recessed so it reads as a detail of the
   action rather than competing with the verb. */
.order-submit-amount {
  font-weight: 700;
  opacity: 0.85;
  padding-inline-start: 8px;
  border-inline-start: 1px solid rgba(255, 255, 255, 0.35);
}
.order-submit-spinner {
  width: 14px;
  height: 14px;
  border: 2px solid rgba(255, 255, 255, 0.4);
  border-top-color: #fff;
  border-radius: 50%;
  animation: order-submit-spin 0.6s linear infinite;
}
@keyframes order-submit-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .order-submit-btn:hover:not(:disabled) { transform: none; }
  .order-submit-spinner { animation-duration: 2s; }
}

/* RTL support (Arabic). Flex/grid containers below already mirror automatically via the
   `dir` attribute — only rules using physical left/right values need explicit overrides here.
   Scoped to "usable and readable," not pixel-perfect mirroring of every micro-detail. */
[dir="rtl"] .navbar .nav-links { padding-right: 0; padding-left: 6px; }
[dir="rtl"] table th, [dir="rtl"] table td { text-align: right; }
[dir="rtl"] .actions-cell { text-align: right; }
[dir="rtl"] .actions-cell .btn { margin-left: 0; margin-right: 6px; }
[dir="rtl"] .actions-cell .btn:first-child { margin-right: 0; }
[dir="rtl"] .catalog-table .actions-cell { text-align: left; }
[dir="rtl"] .auth-close, [dir="rtl"] .modal-close { right: auto; left: 14px; }
[dir="rtl"] .order-error-dismiss { right: auto; left: 10px; }
[dir="rtl"] .order-error { padding-right: 14px; padding-left: 38px; }
[dir="rtl"] .modal-box h3 { padding-right: 0; padding-left: 24px; }
[dir="rtl"] .modal-row-value { text-align: left; }
[dir="rtl"] .legal-page ul { padding-left: 0; padding-right: 20px; }
[dir="rtl"] .order-service-row-rate { text-align: left; }
[dir="rtl"] .payout-submit-btn { margin-left: 0; margin-right: 10px; }
[dir="rtl"] .mode-card.selected::after { right: auto; left: 14px; }
[dir="rtl"] .mode-card-title { padding-right: 0; padding-left: 50px; }
[dir="rtl"] input.qty-input { padding-right: 12px; padding-left: 42px; }
[dir="rtl"] .qty-stepper { right: auto; left: 5px; }
[dir="rtl"] .platform-tab-badge { right: auto; left: -10px; }
[dir="rtl"] .expiry-clear-btn { right: auto; left: 4px; }
[dir="rtl"] #expiry-display { padding-right: 14px; padding-left: 38px; }
[dir="rtl"] select.order-detail-input { padding-right: 14px; padding-left: 34px; background-position: left 12px center; }

/* The dashboard's own confirmation dialog (public/js/confirmDialog.js), built on the same
   .modal-overlay/.modal-box the order and service modals use so a destructive prompt looks
   like part of the product rather than a browser alert. */
.confirm-box { max-width: 380px; }
.confirm-box h3 { margin-bottom: 10px; }
.confirm-message { margin: 0 0 20px; color: var(--text-secondary); font-size: 14px; line-height: 1.55; }
.confirm-actions { display: flex; justify-content: flex-end; gap: 10px; flex-wrap: wrap; }
/* The safe option is first in the DOM (it takes focus, so a stray Enter dismisses) and reads
   first left-to-right; the destructive one sits where the eye lands last. */
.confirm-actions .btn { min-width: 112px; }
@media (max-width: 420px) {
  .confirm-actions { flex-direction: column-reverse; }
  .confirm-actions .btn { width: 100%; }
}

/* "Couldn't be cancelled" carries its explanation on hover (public/js/tooltip.js). No dotted
   underline and no help cursor — it reads as the plain status line it is, and the tooltip is
   there for anyone who hovers to ask why. */
.cancel-unavailable { cursor: default; }

/* Why a refund happened, next to its amount in the order Details modal; the full sentence is
   on hover (public/js/tooltip.js). */
.refund-reason-tag {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 7px;
  border: 1px solid var(--border);
  border-radius: 10px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-muted);
  white-space: nowrap;
  cursor: default;
  vertical-align: 1px;
}

.tip-bubble {
  position: fixed;
  z-index: 1200;
  max-width: 280px;
  padding: 9px 11px;
  border-radius: 6px;
  background: var(--panel-bg);
  border: 1px solid var(--border-strong);
  box-shadow: 0 6px 22px rgba(0, 0, 0, 0.32);
  /* A point bigger than the 12px hints it explains — a tooltip that is smaller than the text
     it belongs to is the one thing nobody can read. */
  font-size: 13px;
  line-height: 1.5;
  color: var(--text);
  white-space: normal;
  text-align: left;
  pointer-events: none;
  opacity: 0;
  transform: translateY(2px);
  transition: opacity 0.12s ease, transform 0.12s ease;
}
/* Larger variant for the catalog's column headers: they're read cold by first-time visitors, so
   the text is a point bigger than the standard tip and the bubble a little wider. */
.tip-bubble.tip-lg { max-width: 320px; padding: 12px 14px; font-size: 14.5px; line-height: 1.5; }
.tip-bubble.is-visible { opacity: 1; transform: translateY(0); }
.tip-bubble.is-below { transform: translateY(-2px); }
.tip-bubble.is-below.is-visible { transform: translateY(0); }
@media (prefers-reduced-motion: reduce) { .tip-bubble { transition: none; } }

/* Payout borrows the billing page's card language — same grid, padding, radius and summary
   rail — so "take money out" and "put money in" look like two halves of one product instead
   of two pages built months apart. */
.payout-card {
  display: grid;
  grid-template-columns: minmax(0, 1.24fr) minmax(0, 1fr);
  padding: 26px;
  background: var(--panel-bg);
  border: 1px solid var(--panel-border);
  border-radius: 9px;
  margin-bottom: 20px;
}
.payout-card h2 { margin: 0; padding: 0; border: none; font-size: 21px; letter-spacing: -0.3px; }
.payout-fields-col { padding-inline-end: 28px; }
.payout-intro { margin: 6px 0 22px; color: var(--text-secondary); }
.payout-card label { margin: 18px 0 6px; }
.payout-card .payout-amount-input,
.payout-card .payout-method-select,
.payout-card input[type="text"] { max-width: none; width: 100%; }

.payout-rail {
  align-self: start;
  padding: 20px;
  background: var(--surface-soft);
  border: 1px solid var(--surface-soft-border);
  border-radius: 8px;
}
.payout-rail-label { margin: 0; color: var(--text-muted); font-size: 13px; }
.payout-rail-value { display: block; margin-top: 2px; font-size: 26px; font-weight: bold; letter-spacing: -0.5px; line-height: 1.2; color: var(--text); }
.payout-summary { margin: 18px 0 0; border-top: 1px solid var(--border); padding-top: 14px; }
.payout-summary-row { display: flex; justify-content: space-between; gap: 12px; font-size: 13px; padding: 5px 0; }
.payout-summary-row dt { color: var(--text-secondary); margin: 0; }
.payout-summary-row dd { margin: 0; color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }
.payout-submit-btn { width: 100%; margin-top: 18px; border-radius: 24px; }
.payout-submit-btn:disabled { opacity: 0.5; cursor: not-allowed; box-shadow: none; }

@media (max-width: 860px) {
  .payout-card { grid-template-columns: 1fr; padding: 20px; }
  .payout-fields-col { padding-inline-end: 0; }
  .payout-rail { margin-top: 22px; }
}

/* Confirm + Resend for the 2FA code: one row, not a stack. They wrap to two lines only when
   the panel is too narrow to hold both. */
.two-fa-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin-top: 16px; }


/* Back-to-top. Deliberately quiet: a small see-through disc with a hairline ring and a thin,
   muted glyph, so it reads as a utility sitting on the page rather than a call to action
   competing with the buttons in the catalog. inset-inline-end (not right) puts it on the correct side in Arabic, and the
   safe-area inset keeps it clear of the home indicator on phones. */
.back-to-top {
  position: fixed;
  inset-inline-end: 14px;
  bottom: calc(14px + env(safe-area-inset-bottom, 0px));
  z-index: 40;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--panel-bg);
  background: color-mix(in srgb, var(--panel-bg) 72%, transparent);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  color: var(--text-muted);
  cursor: pointer;
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.08);
  transition: color .15s ease, border-color .15s ease, transform .15s ease;
}
.back-to-top:hover {
  color: var(--accent);
  border-color: var(--accent);
  transform: translateY(-2px);
}
.back-to-top:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .back-to-top { transition: none; } }
/* Not on phones: it sat over the Order buttons in the catalog's right-hand column, and a
   phone already has a scroll-to-top gesture (tapping the status bar on iOS). */
@media (max-width: 600px) {
  .back-to-top { display: none !important; }
}
