/* src/web/static/public.css
   The three pages anyone may read: the landing page, the privacy notice and
   the paused notice. Loaded after app.css.

   This is the only place in the product that has to sell something, so it
   does not look like the dashboard. It is brighter, bluer and wider: a
   marketing page, in the idiom every HR product in this market uses.

   The whole palette is re-declared on `body.public`. Because app.css states
   every colour as a token, overriding the tokens here re-skins the buttons,
   cards and links it already defines, and because the override is scoped to
   this one element, nothing behind a sign-in changes. Do not move these into
   app.css's :root: that would repaint all 38 dashboard templates.

   Fonts are served from this origin. The CSP is font-src 'self', and a
   webfont from a CDN would need an exception and hand every reader's IP to
   a third party for the sake of a typeface. */

/* --------------------------------------------------------------- the face */
/* Poppins is Latin only; Khmer falls through to Noto Sans Khmer, which is a
   variable font, hence the one file across the whole weight range. */

@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/poppins-400.woff2") format("woff2");
}
@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/static/fonts/poppins-600.woff2") format("woff2");
}
@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/static/fonts/poppins-700.woff2") format("woff2");
}
@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url("/static/fonts/poppins-800.woff2") format("woff2");
}
@font-face {
  font-family: "Noto Sans Khmer";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/static/fonts/noto-sans-khmer.woff2") format("woff2");
}

/* ------------------------------------------------------------ the palette */

body.public {
  /* Cool where the dashboard is warm. A near-white blue reads as "software
     you buy"; the dashboard's #f7f7f6 reads as "software you work in". */
  --bg:            #f8fbfe;
  --surface:       #ffffff;
  --surface-2:     #eef4fb;
  --surface-3:     #e4edf7;
  --border:        #e2ebf3;
  --border-strong: #cfdae6;

  --text:          #0f2944;
  --text-muted:    #3e556f;
  --text-faint:    #6b7f95;

  --accent:        #257cff;
  --accent-hover:  #1a5fc9;
  --accent-soft:   #e8f0ff;
  --accent-text:   #ffffff;

  /* Kept from the dashboard's vocabulary so the check marks and the "what it
     costs" panel still mean what they mean there. */
  --ok:            #00926e;
  --ok-soft:       #e3f7f0;

  --radius:        14px;
  --radius-sm:     10px;
  --radius-lg:     22px;

  --shadow:    0 2px 6px rgba(15, 41, 68, .06);
  --shadow-md: 0 8px 24px rgba(15, 41, 68, .08);
  --shadow-lg: 0 20px 50px rgba(15, 41, 68, .12);
  --shadow-brand: 0 6px 18px rgba(37, 124, 255, .30);

  --sans: "Poppins", "Noto Sans Khmer", system-ui, -apple-system, "Segoe UI", sans-serif;

  --nav-h: 72px;
  --pad-x: clamp(20px, 5vw, 64px);
  --wide: 1180px;
  /* The one content column. Every band is full-bleed and lays its
     contents inside this, so a heading, the wordmark above it and the
     card below it all start at the same x at every width. */
  --col: min(var(--wide), calc(100% - var(--pad-x) * 2));

  background: var(--bg);
  color: var(--text);
  margin: 0;
  padding-top: var(--nav-h);
  /* Khmer needs more line height than Latin: stacked vowels and subscripts
     collide at the dashboard's 1.5. */
  line-height: 1.65;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}
body.public:lang(km) { line-height: 1.85; }

/* app.css centres <main> in a 1180px column and pads it, which is right for a
   dashboard page and wrong here: it stops a band's background reaching the
   edge of the window, and narrows the hero to two thirds of the width it was
   drawn for. The column comes back inside each band, on .pub-band-inner.

   Only the landing page opts out. The privacy and paused notices are one
   column of prose and want exactly what app.css already gives them. */
body.public main.pub-main { max-width: none; margin: 0; padding: 0; }

/* Khmer has no Poppins, and the fallback metrics differ enough that a line
   set in both scripts sits unevenly unless Khmer leads. */
body.public:lang(km) h1,
body.public:lang(km) h2,
body.public:lang(km) h3 { font-family: "Noto Sans Khmer", var(--sans); }

body.public h1, body.public h2, body.public h3 { letter-spacing: -0.02em; }
body.public:lang(km) h1,
body.public:lang(km) h2,
body.public:lang(km) h3 { letter-spacing: 0; }

/* --------------------------------------------------------------- the bar */
/* Fixed, because on a long page the way to sign in should not be something
   you scroll back up to find. */

.pub-bar {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 50;
  height: var(--nav-h);
  display: flex; align-items: center; gap: 12px;
  padding: 0 calc((100% - var(--col)) / 2);
  background: rgba(236, 241, 254, .88);
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
  border-bottom: 1px solid var(--border);
}
.pub-brand {
  display: inline-flex; align-items: center; gap: 10px;
  margin-right: auto;
  font-weight: 700; font-size: 18px;
  color: var(--text); text-decoration: none;
  letter-spacing: -0.01em;
}
.pub-brand img { border-radius: 8px; display: block; }
.pub-nav { display: flex; align-items: center; gap: 8px; }
.pub-lang {
  padding: 8px 14px; border-radius: 9px;
  color: var(--text-muted); text-decoration: none;
  font-size: 14px; font-weight: 500;
  transition: background .2s, color .2s;
}
.pub-lang:hover { color: var(--accent); background: var(--accent-soft); }

/* app.css gives .btn its shape; the lift and the coloured shadow are what
   make a marketing button look like one. */
body.public .btn-primary { box-shadow: var(--shadow-brand); }
body.public .btn-primary:hover { transform: translateY(-1px); box-shadow: 0 10px 24px rgba(37, 124, 255, .38); }
body.public .btn { transition: transform .2s, box-shadow .2s, background .2s, border-color .2s, color .2s; }
body.public .btn:not(.btn-primary):hover { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }

/* --------------------------------------------------------------- the hero */

.pub-hero {
  position: relative;
  padding: clamp(40px, 7vw, 84px) 0 clamp(48px, 8vw, 96px);
  overflow: hidden;
}
/* Two out-of-focus colour fields and a dot grid, masked to fade at the edges.
   Cheap to paint, and it stops a white page from looking unfinished. */
.pub-hero-bg { position: absolute; inset: 0; z-index: 0; pointer-events: none; }
.pub-blob { position: absolute; border-radius: 50%; filter: blur(110px); opacity: .42; }
.pub-blob-1 { width: 520px; height: 520px; top: -160px; right: -80px; background: #9dc4ff; }
.pub-blob-2 { width: 420px; height: 420px; bottom: -200px; left: -120px; background: #b7f0dd; }
.pub-dots {
  position: absolute; inset: 0;
  background-image: radial-gradient(circle, rgba(15, 41, 68, .08) 1px, transparent 1px);
  background-size: 30px 30px;
  -webkit-mask-image: radial-gradient(ellipse 70% 70% at 50% 45%, #000 35%, transparent 100%);
  mask-image: radial-gradient(ellipse 70% 70% at 50% 45%, #000 35%, transparent 100%);
}

.pub-hero-inner {
  position: relative; z-index: 1;
  width: var(--col); margin: 0 auto;
  display: grid; grid-template-columns: 1.15fr .85fr;
  gap: clamp(32px, 5vw, 56px); align-items: center;
}

.pub-badge {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 9px 20px; margin-bottom: 20px;
  background: var(--accent-soft); color: var(--accent);
  border-radius: 100px;
  font-size: 14px; font-weight: 600;
}
.pub-badge::before {
  content: ""; width: 9px; height: 9px; border-radius: 50%;
  background: var(--accent); flex: none;
  animation: pub-pulse 2.4s ease-in-out infinite;
}
@keyframes pub-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: .45; transform: scale(.82); }
}

.pub-hero h1 {
  font-size: clamp(30px, 4.6vw, 54px);
  font-weight: 800; line-height: 1.12;
  margin: 0 0 18px; text-wrap: balance;
}
.pub-hero h1:lang(km) { line-height: 1.45; font-weight: 700; }

.pub-lead {
  font-size: clamp(16px, 1.3vw, 18px);
  color: var(--text-muted);
  margin: 0; max-width: 54ch;
}
.pub-cta { margin-top: 30px; display: flex; gap: 14px; flex-wrap: wrap; }
.btn-lg { min-height: 52px; padding: 13px 28px; font-size: 16px; font-weight: 600; }

/* The handle in words beside the button, for a reader who would rather
   search Telegram for it than follow a link. */
.pub-handle { margin: 12px 0 0; font-size: 15px; color: var(--text-muted); }
.pub-handle a { color: var(--accent); font-weight: 600; text-decoration: none; }
.pub-handle a:hover { text-decoration: underline; }
.pub-cta-inner p.pub-handle { margin-top: 10px; }

.pub-trust {
  margin: 22px 0 0; padding-top: 22px;
  border-top: 1px solid var(--border);
  font-size: 14px; color: var(--text-faint);
  display: flex; align-items: flex-start; gap: 9px;
  max-width: 46ch;
}
.pub-trust .ico { color: var(--ok); margin-top: 2px; }

/* ------------------------------------------------------------ the mock-up */
/* The bot's own chat, drawn rather than photographed: header, wallpaper,
   bubbles with tails, delivery ticks, an inline keyboard and the message
   bar. A shop owner recognises this shape in a glance, which a stylised
   panel does not buy. Every string in it is real copy from the table.

   No Telegram wordmark or logo appears here. This depicts our bot, which
   happens to live in a chat app; it does not borrow their identity. */

.pub-hero-art {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  animation: pub-rise .7s .15s both;
}
.pub-hero-art::before {
  content: ""; position: absolute;
  width: 116%; height: 116%; top: -8%; left: -8%;
  background: radial-gradient(circle, var(--accent-soft) 0%, transparent 68%);
  filter: blur(26px); z-index: 0;
}

.pub-phone {
  position: relative; z-index: 1;
  width: min(304px, 100%);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 30px;
  padding: 9px;
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}

/* ------- the chat header */
.pub-phone-bar {
  display: flex; align-items: center; gap: 9px;
  padding: 9px 4px 11px;
  border-bottom: 1px solid var(--border);
  font-size: 13.5px; font-weight: 600; color: var(--text);
}
.pub-phone-back { width: 17px; height: 17px; flex: none; color: var(--accent); }
.pub-phone-bar img { border-radius: 50%; flex: none; }
.pub-phone-bar span { display: block; line-height: 1.25; }
.pub-phone-bar small {
  display: block; font-weight: 400; font-size: 11px; color: var(--ok);
}

/* ------- the wallpaper */
/* Telegram's default paper is a pattern, not a flat fill; a faint dot grid
   over a warm tint reads the same at this size without shipping an image. */
.pub-phone-screen {
  position: relative;
  background:
    radial-gradient(circle, rgba(15, 41, 68, .07) 1px, transparent 1px) 0 0 / 16px 16px,
    linear-gradient(170deg, #e7eef7, #dfe9f4);
  padding: 13px 10px 14px;
  /* Fixed, because the reader adds messages: the phone must not grow down
     the page and shove the section below it about. Bottom-anchored until
     there is enough to scroll, the way a chat actually sits. */
  height: 300px;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: none;
  display: flex; flex-direction: column; justify-content: flex-end; gap: 8px;
  scroll-behavior: smooth;
}
.pub-phone-screen::-webkit-scrollbar { display: none; }
/* Once it scrolls, flex-end stops being right: the first message must be
   reachable at the top. */
.pub-phone-screen > * { flex: 0 0 auto; }

/* ------- the messages */
.pub-msg {
  position: relative;
  max-width: 84%;
  padding: 7px 11px 6px;
  border-radius: 14px;
  font-size: 12.5px; line-height: 1.5;
  box-shadow: 0 1px 2px rgba(15, 41, 68, .13);
}
/* The bot puts one thing per line inside a paragraph -- a week is seven
   lines, a check-out is three. Those single newlines survive into the text
   node, so they have to be honoured rather than collapsed. */
.pub-msg p { margin: 0; white-space: pre-line; }
.pub-msg strong { display: block; font-weight: 600; }
.pub-msg-time {
  display: block; text-align: right;
  margin-top: 1px; font-size: 10px; line-height: 1.4;
  opacity: .62;
}
.pub-msg-ticks { width: 13px; height: 8px; vertical-align: -.5px; margin-left: 1px; }

/* A tail on the last-sent side, the detail that makes it read as a chat. */
.pub-msg-in {
  align-self: flex-start;
  background: #fff; color: var(--text);
  border-bottom-left-radius: 5px;
}
.pub-msg-out {
  align-self: flex-end;
  background: var(--accent); color: #fff;
  border-bottom-right-radius: 5px;
}
.pub-msg-out .pub-msg-time { opacity: .85; }

/* ------- the inline keyboard */
/* Both ways in, side by side, because the shop chooses which it offers and
   many offer both: share where you are, or scan the code on the wall. */
.pub-keys {
  display: flex; flex-wrap: wrap; align-content: flex-start; gap: 5px;
  padding: 8px 6px 9px;
  background: var(--surface);
  /* Two rows' worth, always. The keyboard changes with the shift -- one
     button, then three -- and without this the phone grows and shrinks and
     shoves the whole hero up and down while somebody is reading it. */
  min-height: 81px;
}
.pub-key {
  flex: 1 1 calc(50% - 5px);
  display: block; text-align: center;
  padding: 9px 8px;
  border: 0; border-radius: 9px;
  background: var(--surface-2);
  color: var(--accent);
  font-family: inherit; font-size: 11.5px; font-weight: 600;
  line-height: 1.35;
}
/* A button only looks pressable once the script has made it one. */
.pub-demo.is-live .pub-key {
  cursor: pointer;
  transition: background .15s, transform .08s;
}
.pub-demo.is-live .pub-key:hover { background: var(--accent-soft); }
.pub-demo.is-live .pub-key:active { transform: scale(.97); }
.pub-demo.is-live .pub-key:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}

/* The beat before the bot answers. */
.pub-typing { display: flex; gap: 4px; align-items: center; padding: 11px 13px; }
.pub-typing span {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--text-faint);
  animation: pub-blink 1.2s ease-in-out infinite;
}
.pub-typing span:nth-child(2) { animation-delay: .18s; }
.pub-typing span:nth-child(3) { animation-delay: .36s; }
@keyframes pub-blink {
  0%, 60%, 100% { opacity: .28; }
  30%           { opacity: 1; }
}
  30%, 34%  { background: var(--accent); color: #fff; }
  38%, 100% { background: rgba(255, 255, 255, .92); color: var(--accent); }
}


/* A message arrives rather than appearing, which is most of what makes the
   widget feel like a chat and not a list being filled in. */
@keyframes pub-arrive {
  from { opacity: 0; transform: translateY(8px) scale(.98); }
  to   { opacity: 1; transform: none; }
}
.pub-demo .pub-msg { animation: pub-arrive .32s cubic-bezier(.22, .61, .36, 1) both; }

/* The handover. Hidden until the opening has finished; gone again the moment
   somebody takes the hint, because by then it is stating the obvious. */
.pub-try {
  display: none;
  align-items: center; justify-content: center; gap: 7px;
  margin: 0; padding: 2px 6px 10px;
  font-size: 12px; font-weight: 600; color: var(--accent);
}
.pub-try .ico { color: var(--accent); }
.pub-demo.is-open .pub-try {
  display: flex;
  animation: pub-arrive .4s cubic-bezier(.22, .61, .36, 1) both;
}
/* Two nudges, then it leaves the buttons alone: a pulse that outlasts five
   seconds is moving content somebody has to be able to stop. */
.pub-demo.is-open .pub-key { animation: pub-nudge 2.2s ease-in-out 2; }
@keyframes pub-nudge {
  0%, 45%, 100% { box-shadow: 0 0 0 0 rgba(37, 124, 255, 0); }
  18%           { box-shadow: 0 0 0 4px rgba(37, 124, 255, .20); }
}

@keyframes pub-rise {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: none; }
}

/* ------------------------------------------------------------- the bands */

.pub-band { padding: clamp(48px, 7vw, 88px) 0; }
.pub-band-alt { background: var(--surface-2); border-block: 1px solid var(--border); }
.pub-band-white { background: var(--surface); border-block: 1px solid var(--border); }
.pub-band-inner { width: var(--col); margin: 0 auto; }

.pub-band h2 {
  font-size: clamp(24px, 3vw, 36px);
  font-weight: 700; line-height: 1.2;
  margin: 0 0 8px; text-wrap: balance;
}
.pub-band h2:lang(km) { line-height: 1.45; }
.pub-band-lead { color: var(--text-muted); margin: 0 0 34px; max-width: 58ch; font-size: 16px; }

/* ------------------------------------------------------------- the steps */

.pub-steps {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 20px;
}
.pub-steps li {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 26px 24px;
  box-shadow: var(--shadow);
  transition: transform .25s, box-shadow .25s, border-color .25s;
}
.pub-steps li:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); border-color: var(--border-strong); }
.pub-step-n {
  display: inline-flex; align-items: center; justify-content: center;
  width: 42px; height: 42px; margin-bottom: 16px;
  border-radius: 13px;
  background: var(--accent); color: #fff;
  font-size: 17px; font-weight: 700;
  box-shadow: var(--shadow-brand);
}
.pub-steps strong { display: block; font-size: 17px; font-weight: 600; margin-bottom: 5px; }
.pub-steps p { margin: 0; color: var(--text-muted); font-size: 14.5px; }
.pub-note {
  margin: 22px 0 0; font-size: 14px; color: var(--text-faint);
  display: flex; align-items: flex-start; gap: 9px;
}
.pub-note .ico { color: var(--ok); margin-top: 2px; }

/* ---------------------------------------------------------- the features */

.pub-features {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 20px;
}
.pub-features li {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 26px 24px;
  box-shadow: var(--shadow);
  transition: transform .25s, box-shadow .25s, border-color .25s;
}
.pub-features li:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); border-color: var(--border-strong); }
.pub-feat-ico {
  display: inline-flex; align-items: center; justify-content: center;
  width: 46px; height: 46px; margin-bottom: 15px;
  border-radius: 14px;
  background: var(--accent-soft); color: var(--accent);
}
.pub-features strong { display: block; font-size: 17px; font-weight: 600; margin-bottom: 5px; }
.pub-features p { margin: 0; color: var(--text-muted); font-size: 14.5px; }

/* ------------------------------------------------------------- the plans */
/* Three tiers and Custom. The first tier is raised and filled in: it is the
   size most shops pick. */

/* Monthly / yearly. Two visually hidden radio buttons ahead of the switch
   and the cards; each card carries both prices, and :checked decides which
   one shows. No script, so no flash of the wrong price and nothing for the
   CSP to block. The inputs stay focusable: arrow keys move between them,
   and the focus ring is drawn on the label they belong to. */
.pub-billing { display: flex; flex-direction: column; align-items: center; }
.pub-bill-input {
  position: absolute; width: 1px; height: 1px; opacity: 0;
  pointer-events: none;
}
.pub-bill-switch {
  display: inline-flex; gap: 4px; padding: 4px; margin: 0 0 30px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 999px; box-shadow: var(--shadow);
}
.pub-bill-switch label {
  display: inline-flex; align-items: center; gap: 8px;
  min-height: 40px; padding: 8px 18px; border-radius: 999px;
  font-size: 14.5px; font-weight: 600; color: var(--text-muted);
  cursor: pointer; transition: background .15s ease, color .15s ease;
}
.pub-bill-deal {
  padding: 2px 8px; border-radius: 999px;
  background: var(--ok-soft, #e3f6ec); color: var(--ok, #1a7f4b);
  font-size: 12px; font-weight: 700;
}
#bill-month:checked ~ .pub-bill-switch label[for="bill-month"],
#bill-year:checked ~ .pub-bill-switch label[for="bill-year"] {
  background: var(--accent, #257cff); color: #fff;
}
#bill-year:checked ~ .pub-bill-switch .pub-bill-deal {
  background: rgba(255, 255, 255, .22); color: #fff;
}
#bill-month:focus-visible ~ .pub-bill-switch label[for="bill-month"],
#bill-year:focus-visible ~ .pub-bill-switch label[for="bill-year"] {
  outline: 2px solid var(--accent, #257cff); outline-offset: 2px;
}
/* Two classes, so it beats .pub-plan-fig's display: flex further down. */
.pub-plans .pub-fig-year { display: none; }
#bill-year:checked ~ .pub-plans .pub-fig-month { display: none; }
#bill-year:checked ~ .pub-plans .pub-plan-fig.pub-fig-year { display: flex; }
#bill-year:checked ~ .pub-plans .pub-plan-yr.pub-fig-year { display: block; }
.pub-plan-yr { margin: 6px 0 0; font-size: 13px; font-weight: 600; color: var(--ok, #1a7f4b); }
.pub-plan-best .pub-plan-yr { color: #fff; }

.pub-plans {
  width: 100%;
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 18px;
  /* One row, one baseline at the bottom. Natural heights left four cards
     ending at four different places, which reads as a page that did not
     finish rather than as a set of choices. The raised card keeps its lift
     because that is a deliberate 10px, not an accident of content length. */
  align-items: stretch;
}

/* The terms, under all four cards. */
.pub-plans-foot {
  margin: 22px 0 0; text-align: center;
  font-size: 14px; color: var(--text-muted);
}
/* Custom: the price is a sentence, so it is set smaller than a figure, and
   its one button sits at the foot so the card ends where the others do. */
.pub-plan-custom .pub-plan-fig b { font-size: clamp(24px, 2.4vw, 30px); }
.pub-plan-custom .pub-plan-list { margin-bottom: 22px; }
.pub-plan-custom .btn { margin-top: auto; align-self: flex-start; }
.pub-plan-ask {
  background: transparent; border: 1px solid var(--border-strong, #c9d3e1);
  color: var(--text);
}
.pub-plan-ask:hover { border-color: var(--accent, #257cff); color: var(--accent, #257cff); }
.pub-plan {
  position: relative;
  display: flex; flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 26px 26px 24px;
  box-shadow: var(--shadow);
  transition: transform .2s ease, box-shadow .2s ease;
}
.pub-plan:hover { transform: translateY(-2px); box-shadow: var(--shadow-lg); }
.pub-plan-top { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; }
.pub-plan-ico {
  display: inline-grid; place-items: center; flex: none;
  width: 36px; height: 36px; border-radius: 10px;
  background: var(--accent-soft, #e8f0ff); color: var(--accent, #257cff);
}
.pub-plan-name {
  display: block;
  font-size: 16px; font-weight: 700; color: var(--text);
}
/* How big a team it holds: the words, and a bar against the biggest plan,
   so the three sizes compare at a glance instead of by reading. */
.pub-plan-size { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--border); }
.pub-plan-size .pub-plan-note {
  display: flex; align-items: center; gap: 7px; margin: 0 0 10px;
  font-size: 14px; font-weight: 600; color: var(--text-muted);
}
.pub-plan-meter { display: block; width: 100%; height: 6px; }
.pub-plan-meter-track { fill: var(--surface-2, #eef2f8); }
.pub-plan-meter-fill { fill: var(--accent, #257cff); }
.pub-plan-more { margin: 12px 0 0; font-size: 13px; color: var(--text-faint); }
.pub-plan-fig { margin: 0; display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.pub-plan-fig b {
  font-size: clamp(30px, 3.4vw, 40px); font-weight: 800;
  line-height: 1.1; letter-spacing: -0.02em;
}
.pub-plan-fig span { font-size: 14px; color: var(--text-muted); }
.pub-plan-note { margin: 6px 0 0; font-size: 13px; color: var(--text-faint); }

/* What each plan actually includes. A tick per line, because the question a
   reader is holding is "and what do I get", and a sentence makes them find
   the answer inside it. */
.pub-plan-list {
  list-style: none; margin: 20px 0 0; padding: 0;
  display: grid; gap: 10px;
}
.pub-plan-list li {
  display: flex; align-items: flex-start; gap: 9px;
  font-size: 14px; color: var(--text-muted); line-height: 1.5;
}
.pub-plan-list .ico { color: var(--ok); margin-top: 3px; }

/* The one being sold. Lifted a little, so the eye lands here first. */
.pub-plan-best {
  background: linear-gradient(150deg, #1a5fc9, #257cff 62%, #3f8cff);
  border-color: transparent; color: #fff;
  box-shadow: var(--shadow-lg);
}
.pub-plan-best .pub-plan-name { color: #fff; }
.pub-plan-best .pub-plan-ico { background: rgba(255, 255, 255, .18); color: #fff; }
.pub-plan-best .pub-plan-size { border-top-color: rgba(255, 255, 255, .22); }
.pub-plan-best .pub-plan-size .pub-plan-note { color: rgba(255, 255, 255, .92); }
.pub-plan-best .pub-plan-meter-track { fill: rgba(255, 255, 255, .22); }
.pub-plan-best .pub-plan-meter-fill { fill: #fff; }
.pub-plan-best:hover { transform: translateY(-2px); }
.pub-plan-best .pub-plan-fig span { color: rgba(255, 255, 255, .84); }
.pub-plan-best .pub-plan-note { color: rgba(255, 255, 255, .88); }
.pub-plan-best .pub-plan-list li { color: rgba(255, 255, 255, .94); }
.pub-plan-best .pub-plan-list .ico { color: #fff; }
.pub-plan-best .btn {
  background: #fff; border-color: #fff; color: #1a5fc9;
  box-shadow: 0 6px 18px rgba(10, 32, 62, .22);
}
.pub-plan-best .btn:hover { background: #fff; border-color: #fff; color: #1a5fc9; transform: translateY(-1px); }

/* The launch deal, said once, where the price is. Stated as the extra months
   rather than a struck-through number, because there is no old price. */
.pub-plan-flag {
  /* A badge on the card's top edge, out of the flow, so the card holding it
     starts its name and price on the same line as every other card. Inside
     the flow it pushed that card's contents down and the row went crooked. */
  position: absolute; top: 0; right: 18px; transform: translateY(-50%);
  padding: 5px 12px; border-radius: 999px;
  background: #fff; color: #1a5fc9;
  border: 1px solid rgba(26, 95, 201, .25);
  box-shadow: 0 4px 12px rgba(10, 32, 62, .14);
  font-size: 12px; font-weight: 700; line-height: 1.4; white-space: nowrap;
}

/* Four across needs room; between a laptop and a tablet they go two by two. */
@media (max-width: 1180px) and (min-width: 981px) {
  .pub-plans { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .pub-plan-best { margin-top: 0; }
}

/* --------------------------------------------------------------- the ask */

.pub-cta-band {
  padding: clamp(44px, 6vw, 76px) 0;
  background: var(--surface-2);
  border-top: 1px solid var(--border);
}
.pub-cta-inner {
  width: var(--col); margin: 0 auto;
  display: flex; align-items: center; justify-content: space-between;
  gap: 28px; flex-wrap: wrap;
}
.pub-cta-inner h2 { font-size: clamp(22px, 2.6vw, 32px); font-weight: 700; margin: 0 0 6px; }
.pub-cta-inner p { margin: 0; color: var(--text-muted); }

/* ------------------------------------------------------------ the footer */

.pub-foot {
  background: var(--text); color: #cfdce9;
  padding: 34px 0;
}
.pub-foot-inner {
  width: var(--col); margin: 0 auto;
  display: flex; align-items: center; gap: 12px 26px; flex-wrap: wrap;
  font-size: 14px;
}
.pub-foot .pub-brand { color: #fff; margin-right: auto; }
.pub-foot a { color: #cfdce9; text-decoration: none; }
.pub-foot a:hover { color: #fff; }

/* ------------------------------------------------------------- the notice */
/* The privacy and paused pages: one column, no selling. They take the
   palette and the typeface and nothing else. */

/* A notice is one column of prose, and a narrow one. Narrowing the page's
   column rather than the article alone brings the bar in with it, so the
   wordmark sits above the first word instead of 200px to the left of it. */
body.public.doc { --wide: 760px; }

.pub-doc { width: var(--col); margin: 0 auto; padding: 40px 0 64px; }
.pub-doc h1 {
  font-size: clamp(26px, 4vw, 38px); font-weight: 700;
  line-height: 1.2; margin: 8px 0 14px;
}
.pub-doc h1:lang(km) { line-height: 1.5; }
.pub-doc h2 { font-size: 19px; font-weight: 600; margin: 32px 0 8px; }
.pub-doc ul { margin: 0; padding-left: 20px; }
.pub-doc li { margin: 6px 0; }
.pub-doc p { margin: 8px 0; }
.pub-doc .pub-lead { font-size: 17px; margin-bottom: 22px; }
.pub-updated { margin-top: 32px !important; font-size: 13px; color: var(--text-faint); }

/* ---------------------------------------------------------- the narrowing */

@media (max-width: 980px) {
  .pub-hero-inner { grid-template-columns: 1fr; text-align: center; }
  .pub-lead, .pub-trust { margin-inline: auto; }
  .pub-trust { justify-content: center; }
  .pub-cta { justify-content: center; }
  .pub-hero-copy .pub-handle { text-align: center; }
  /* The picture stays under the headline. Stacked above it, a 300px phone
     pushes the one sentence that explains the product, and both buttons,
     off the bottom of the screen. */
  .pub-hero-art { margin-top: 12px; }
  .pub-features { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* Four cards no longer fit side by side; two by two until the phone
     layout below turns them into a swipe. */
  .pub-plans { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .pub-plan-best { margin-top: 0; }
}

@media (max-width: 720px) {
  body.public { --nav-h: 64px; }
  .pub-band-lead { margin-bottom: 22px; }
  .pub-cta .btn { flex: 1 1 100%; justify-content: center; }

  /* ---------------------------------------------------------- swiping */
  /* Stacked, the three sections are eleven full-height cards and a very
     long thumb-scroll for a page someone is skimming. Side by side they
     are three swipes, and the card peeking in at the right edge is what
     says there is more without a row of dots to explain it.

     The row bleeds to both screen edges so a card can pass under the
     gutter, then pads back in by the same amount so the first card still
     starts where the heading does. The maths uses --pad-x, not --col:
     --col is a percentage, and a percentage resolves against whatever it
     is used on, which here is the wrong box. */
  .pub-steps, .pub-features, .pub-plans {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 76%;
    grid-template-columns: none;
    /* Natural heights here, unlike the desktop row above, and for the
       opposite reason. Side by side, four cards ending at four heights read
       as unfinished. Swiped one at a time, stretching them to match the
       tallest gave Free and Shop about ninety blank pixels under their last
       line, on a screen where ninety pixels is an eighth of everything the
       reader can see. The section does not jump between swipes either way:
       the grid track is already the height of the tallest card, so all this
       changes is whether the short ones pad themselves out to fill it. */
    align-items: start;
    gap: 12px;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    margin-inline: calc(-1 * var(--pad-x));
    padding: 4px var(--pad-x) 12px;
    /* Snapping aligns a card to the scroller's edge, which is the screen's
       edge here, not the padded one. Without this the first card snaps
       flush against the glass instead of lining up with the heading. */
    scroll-padding-inline: var(--pad-x);
  }
  .pub-steps::-webkit-scrollbar,
  .pub-features::-webkit-scrollbar,
  .pub-plans::-webkit-scrollbar { display: none; }
  .pub-steps > li,
  .pub-features > li,
  .pub-plans > * { scroll-snap-align: start; }

  /* Smaller, now that they sit beside each other rather than filling the
     width one at a time. */
  .pub-steps li, .pub-features li, .pub-plan { padding: 20px 18px; }
  .pub-steps strong, .pub-features strong { font-size: 15.5px; }
  .pub-steps p, .pub-features p { font-size: 13.5px; }
  .pub-plan-list { margin-top: 16px; gap: 8px; }
  .pub-plan-list li { font-size: 13px; }
  .pub-step-n { width: 36px; height: 36px; margin-bottom: 12px; font-size: 15px; }
  .pub-feat-ico { width: 40px; height: 40px; margin-bottom: 12px; }
  .pub-feat-ico .ico-lg { width: 22px; height: 22px; }
  .pub-plan-flag { font-size: 11.5px; padding: 4px 10px; }
  /* The swiping row clips at its own edge, so it needs room above the cards
     for the badge that sits on one's top border. */
  .pub-plans { padding-top: 16px; }
  .pub-plan-top { margin-bottom: 12px; }

  /* Four things will not fit across a phone. The bar keeps the wordmark, the
     language and the one button that is the point of the page; signing in is
     a button away in the hero and a link away in the footer. */
  .pub-bar .btn:not(.btn-primary) { display: none; }
  /* Every item in this row is something a thumb aims at, and all three were
     under the 44px the rest of the product holds to: the button at 38, the
     language switch at 39, the wordmark at 30. The bar is 64px tall here, so
     the room was always there. The language switch is the one that actually
     gets mis-tapped, because it sits between two things people do want. */
  .pub-bar .btn { min-height: 44px; padding: 10px 15px; font-size: 14px; }
  .pub-brand { font-size: 16px; min-width: 0; min-height: 44px; }
  .pub-brand span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .pub-lang {
    padding: 8px 12px;
    min-height: 44px;
    display: inline-flex; align-items: center;
  }
}

/* A page that moves while you are trying to read it is a page you leave. */
@media (prefers-reduced-motion: reduce) {
  body.public *, body.public *::before, body.public *::after {
    animation: none !important;
    transition: none !important;
  }
  body.public .btn-primary:hover,
  body.public .pub-steps li:hover,
  body.public .pub-features li:hover,
  body.public .pub-plan-best .btn:hover { transform: none; }
}


/* ------------------------------------------------- shops already running on it
   Social proof under the hero. The marks are square social avatars, not
   transparent wordmarks: four sit on white and two are full bleed colour, so
   each gets one tile shape and one hairline. Without the hairline the white
   ones read as loose artwork on the band while the pink ones read as tiles,
   and six logos stop looking like one set. Colour is kept rather than
   greyscaled, because for two of these the colour is the brand.

   The heading selector is two classes deep on purpose. `.pub-band h2` above
   is (0,1,1) and clamps to 24px minimum, so a lone `.pub-clients-h` at (0,1,0)
   would lose to it and never apply. The name is .pub-clients rather than
   .pub-trust because .pub-trust is already the hero's one line trust note. */

.pub-clients { padding: clamp(34px, 5vw, 56px) 0; }

.pub-clients .pub-clients-h {
  font-size: 17px;
  font-weight: 650;
  line-height: 1.4;
  color: var(--text);
  text-align: center;
  margin: 0 0 26px;
}
.pub-clients .pub-clients-h:lang(km) { line-height: 1.6; }

/* The strip runs right to left rather than sitting still, so six shops read
   as a row that continues past the edge rather than as all the customers
   there are. Both copies of the list scroll together and the travel is one
   copy plus one gap, which puts copy two exactly where copy one started.

   Three things this owes the reader. It pauses on hover, so anything that
   catches the eye can be looked at. It stops dead under prefers-reduced-
   motion, where the track becomes an ordinary scrollable row that a thumb
   or a wheel moves instead. And the edges are masked rather than cut, so a
   logo leaves by fading rather than by being sliced at the band's edge. */

.pub-clients-track {
  display: flex;
  gap: 34px;              /* matches the row gap, so the seam is evenly spaced */
  overflow: hidden;
  mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
}
.pub-clients-track:hover .pub-clients-row { animation-play-state: paused; }

.pub-clients-row {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: nowrap; justify-content: flex-start;
  flex: 0 0 auto;
  gap: 22px 34px;
  /* Slower than it was: the marks are half again as big, and the same
     seconds across a longer track reads as hurried rather than calm. */
  animation: pub-clients-scroll 46s linear infinite;
}

@keyframes pub-clients-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(calc(-100% - 34px)); }
}

@media (prefers-reduced-motion: reduce) {
  .pub-clients-row { animation: none; }
  .pub-clients-track { overflow-x: auto; mask-image: none; -webkit-mask-image: none; }
}
/* Just the mark. The 96px width and the column stack were making room for a
   name underneath; without it the tile is the whole item, and a fixed width
   would only pad the gaps unevenly. */
.pub-clients-row li { display: flex; flex: 0 0 auto; }
/* Big enough to read. At 72px the fine marks, Le Moon's line art and Pretty
   Berry's monogram, were grey mush, and an illegible logo proves nothing to
   anybody: a row of smudges is decoration, a row of real shops is evidence.
   Served at 240px so they stay sharp on a phone's 2x screen.

   The shadow and the ring do the work the missing name used to: they make
   six unrelated images, four on white and two full bleed, read as one set of
   tiles rather than as pictures someone pasted onto the page. */
.pub-clients-row img {
  width: 104px; height: 104px;
  border-radius: 22px;
  border: 1px solid var(--border);
  background: var(--surface);
  object-fit: cover;
  box-shadow: 0 2px 6px rgba(20, 20, 18, .07), 0 8px 20px rgba(20, 20, 18, .05);
  transition: transform .18s ease, box-shadow .18s ease;
}
/* The track already pauses under the cursor, so a mark the reader has stopped
   on lifts to say it is the one they stopped on. */
.pub-clients-row li:hover img {
  transform: translateY(-3px);
  box-shadow: 0 4px 10px rgba(20, 20, 18, .10), 0 14px 30px rgba(20, 20, 18, .08);
}
@media (prefers-reduced-motion: reduce) {
  .pub-clients-row img, .pub-clients-row li:hover img { transition: none; transform: none; }
}


/* -------------------------------------------------------- the trust lines */
/* The second tick in the hero sits under the first without a second rule. */
.pub-trust-more { margin-top: 8px; padding-top: 0; border-top: 0; }

/* --------------------------------------------------------- the figures */
/* Live totals under the logos: three numbers, big enough to be the point. */
.pub-figures {
  list-style: none; margin: 34px 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px;
  text-align: center;
}
.pub-figures b {
  display: block; font-size: clamp(26px, 3.4vw, 38px); font-weight: 800;
  color: var(--accent); line-height: 1.1; letter-spacing: -0.02em;
}
.pub-figures span { font-size: 14px; color: var(--text-muted); }

/* ------------------------------------------------------ the owner screen */
/* The dashboard's Today page as a picture in a browser frame. Its own small
   type scale, because it depicts a screen rather than being one. */
.pub-screen {
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  max-width: 920px;
}
.pub-screen-bar {
  display: flex; align-items: center; gap: 14px;
  padding: 11px 16px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
}
.pub-screen-dots { display: inline-flex; gap: 6px; }
.pub-screen-dots i { width: 10px; height: 10px; border-radius: 50%; background: var(--border-strong); }
.pub-screen-url {
  flex: 1; max-width: 320px;
  padding: 4px 12px; border-radius: 8px;
  background: var(--surface); color: var(--text-faint); font-size: 12.5px;
}
.pub-screen-body { padding: clamp(16px, 3vw, 28px); display: grid; gap: 16px; background: var(--bg); }
.pub-screen-tasks { display: flex; flex-wrap: wrap; gap: 10px; }
.pub-screen-task {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 12px; border-radius: 12px;
  background: var(--surface); border: 1px solid var(--border);
  font-size: 13.5px; font-weight: 500;
}
.pub-screen-task .ico { color: #d97706; }
.pub-screen-task b {
  min-width: 22px; padding: 1px 7px; border-radius: 999px;
  background: var(--accent); color: #fff; font-size: 12px; text-align: center;
}
.pub-screen-stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.pub-screen-stats div {
  padding: 14px 16px; border-radius: 14px;
  background: var(--surface); border: 1px solid var(--border);
}
.pub-screen-stats small { display: block; font-size: 12.5px; color: var(--text-faint); }
.pub-screen-stats b { font-size: clamp(18px, 2.4vw, 26px); font-weight: 700; }
.pub-screen-card {
  padding: 16px; border-radius: 14px;
  background: var(--surface); border: 1px solid var(--border);
  overflow-x: auto;
}
.pub-screen-card strong { display: block; font-size: 15px; margin-bottom: 8px; }
.pub-screen-card table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.pub-screen-card th {
  text-align: left; font-weight: 500; font-size: 12px; color: var(--text-faint);
  padding: 6px 8px; border-bottom: 1px solid var(--border);
}
.pub-screen-card td { padding: 9px 8px; border-bottom: 1px solid var(--border); white-space: nowrap; }
.pub-screen-card tr:last-child td { border-bottom: 0; }
.pub-screen-dot {
  display: inline-block; width: 8px; height: 8px; margin-right: 8px;
  border-radius: 50%; background: var(--ok); vertical-align: middle;
}
.pub-screen-muted { color: var(--text-muted); }
.pub-screen-mono { font-variant-numeric: tabular-nums; }
.pub-screen-chip {
  display: inline-block; margin-right: 4px; padding: 1px 8px; border-radius: 999px;
  background: var(--surface-3); color: var(--text-muted); font-size: 11.5px;
}

/* ------------------------------------------------------------- the FAQ */
.pub-faq-wrap {
  display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
  gap: clamp(24px, 4vw, 48px); align-items: start;
}
.pub-faq h2 { margin-bottom: 18px; }
.pub-faq details {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  margin-bottom: 10px;
}
.pub-faq summary {
  cursor: pointer; list-style: none;
  padding: 16px 48px 16px 20px; position: relative;
  font-weight: 600; font-size: 15.5px;
}
.pub-faq summary::-webkit-details-marker { display: none; }
.pub-faq summary::after {
  content: "+"; position: absolute; right: 20px; top: 50%;
  transform: translateY(-50%);
  font-size: 22px; font-weight: 400; color: var(--accent);
}
.pub-faq details[open] summary::after { content: "−"; }
.pub-faq details p { margin: 0; padding: 0 20px 18px; color: var(--text-muted); font-size: 15px; }

.pub-people {
  position: sticky; top: calc(var(--nav-h) + 24px);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 26px 24px;
  box-shadow: var(--shadow-md);
}
.pub-people h3 { margin: 0 0 6px; font-size: 19px; font-weight: 700; }
.pub-people p { margin: 0; color: var(--text-muted); font-size: 15px; }

@media (max-width: 860px) {
  .pub-faq-wrap { grid-template-columns: 1fr; }
  .pub-people { position: static; }
}
@media (max-width: 720px) {
  .pub-figures { gap: 8px; }
  .pub-figures span { font-size: 12.5px; }
  .pub-screen-stats { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
  .pub-screen-stats div { padding: 10px; }
  .pub-screen-stats small { font-size: 11px; }
  .pub-screen-task { font-size: 12.5px; }
}
/* app.css turns every table into a stack of cards on a phone, which is right
   for the dashboard and wrong for a picture of it: four people became four
   tall cards. The mock stays a table, as .plan-matrix does, and the shop
   column goes, since both shops still read from the rows around it. */
@media (max-width: 720px) {
  .pub-screen-card table { display: table; }
  .pub-screen-card thead { display: table-header-group; position: static; left: auto; }
  .pub-screen-card tbody { display: table-row-group; }
  .pub-screen-card tr { display: table-row; border: 0; border-radius: 0; margin: 0; padding: 0; background: transparent; }
  .pub-screen-card th, .pub-screen-card td {
    display: table-cell; width: auto; text-align: left;
    padding: 8px 6px; border-bottom: 1px solid var(--border); font-size: 12.5px;
  }
  .pub-screen-card tr:last-child td { border-bottom: 0; }
  .pub-screen-card th:nth-child(2), .pub-screen-card td:nth-child(2) { display: none; }
  .pub-screen-card { padding: 12px; }
}
@media (max-width: 720px) {
  .pub-screen-card th, .pub-screen-card td { padding: 8px 4px; }
  .pub-screen-chip { margin-right: 0; padding: 1px 6px; font-size: 11px; }
  .pub-screen-dot { margin-right: 5px; }
}
