/* Arc Growth customer portal — neutral, flat, hairline-structured admin UI.
   (Was "cobalt" until 2026-07-29; the accent now lives only in the logo.) */

/* Inter, self-hosted (v4.1, from github.com/rsms/inter). Self-hosted rather
   than on Google's CDN: one less third party, no customer IP handed over on
   every page load, and it can't break when someone else's CDN does.
   The variable cut is one 352KB file covering every weight, which is what lets
   the UI use 450 and 500 instead of jumping 400 -> 700. font-display: swap so
   text paints immediately in the fallback and swaps when the font lands —
   never a blank screen waiting on a font. */
@font-face {
  font-family: "InterVar";
  src: url("/static/fonts/InterVariable.woff2?v=1") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
:root {
  /* ---- no colour in the chrome (2026-07-29, second pass) ----
     The first pass warmed the greys but kept cobalt for links and the current
     nav item. That was worse, not better: warm grey is a warm temperature and
     cobalt is a cold one, so the two fought across every screen, and the blue
     wasn't rare enough to earn the clash. Operator's call, and he's right —
     "灰色加蓝色很奇怪".

     So the panel's rule applies here too: nothing in the interface is coloured.
     Ink for anything filled or active, green for live, amber for "needs you".
     The ONLY place the brand survives is the mark itself, which is exactly how
     it works in the panel — a logo is identity, not interface, and holding all
     the colour in one 26px shape makes it read louder than spraying it around
     ever did. */
  /* --accent-soft marks the current nav item and --hover is the pass-over
     tint. Both have to clear the sidebar's own #f4f4f2 now that the chrome is
     the tinted side — at the old values the hover was literally the same
     colour as the surface it sat on. */
  --accent: #16161a; --accent-dark: #2c2c30; --accent-soft: #e9e9e3; --hover: #f1f1ec;
  --ink: #16161a; --ink-soft: #2c2c30;
  --muted: #6e6e68; --faint: #9b9b94;
  /* Sampled off the reference, not guessed: page #f9f9f7, cards #fff, insets
     #f4f4f2. This was #f2f2ef — seven units darker — and it read as a fill
     rather than a surface, which is what makes an interface look like it's
     still primed rather than painted. Six units under white is enough for a
     card to sit on; the hairline does the rest of the separating. */
  /* The working side is white — properly white, not a tinted near-white. Cards
     are separated by their hairline, which is all Shopify's table uses too.
     --chrome is the sidebar only, and one notch lighter than before.
     --surface-2 is for insets on the WHITE side (KPI tiles, segment track,
     message body), so it has to stay faint: grey blocks piled on the working
     area were the "不干净" the operator was pointing at. */
  --bg: #ffffff; --panel: #ffffff; --chrome: #f6f6f4; --surface-2: #f7f7f5;
  --line: #e8e8e3; --line-soft: #f1f1ec;
  --ok: #157347; --ok-soft: #e6f4ec; --warn: #8a6414; --warn-soft: #f7efdc;
  --neutral-soft: #f0f0ec;
  /* Controls tighten to 6; cards stay 10. Rounded-everything is the app-store
     look, and the two sizes now mean different things. */
  --radius: 10px; --radius-sm: 6px;
  /* One reading width, shared by .topbar and .content so the header controls
     and the cards under them line up on the same two edges. */
  --gutter: max(var(--s5), calc((100% - 900px) / 2));
  /* Spacing scale. Every margin/padding/gap in this file resolves to one of
     these — the old ad-hoc 14/18/22/26 values each looked fine alone, but
     together they read as assembled rather than designed. */
  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 24px; --s6: 32px;
  /* Shadow now means "this floats above the page" — toasts, dialogs, the
     selected pill in a segmented control. Cards are flat, bordered surfaces. */
  --shadow: 0 1px 2px rgba(18,22,33,.04), 0 4px 16px rgba(18,22,33,.05);
  /* The system stack stays behind Inter: it covers the swap window, browsers
     too old for variable woff2, and CJK — Inter has no Chinese glyphs, so any
     Chinese in customer content falls through to YaHei automatically. */
  --font: "InterVar", -apple-system, "Segoe UI", system-ui, Roboto, "Microsoft YaHei", sans-serif;
}
* { box-sizing: border-box; }
body { margin: 0; background: var(--bg); color: var(--ink); font-family: var(--font); line-height: 1.5; -webkit-font-smoothing: antialiased; }
/* Underlined, because colour is no longer available to mark a link. Without the
   rule a link would be ink on white — the same as body text, and unrecognisable
   as something you can click. */
a { color: var(--accent); text-decoration: underline; text-decoration-thickness: 1px;
  text-underline-offset: 2px; text-decoration-color: var(--faint); }
a:hover { text-decoration-color: var(--accent); }
/* …but only for links inside prose. Plenty of interface here is built out of
   <a> — the whole sidebar, buttons, clickable rows — and underlining those
   turned the nav into a list of underscored words. They're recognisable as
   controls by their shape; the underline is only needed where a link is
   otherwise indistinguishable from the sentence around it. */
.nav-item, .btn, .qa-row, .lead-row, .iact, .seg a, .crow a, .side-user a { text-decoration: none; }
/* Weight does the work of hierarchy, so it has to have somewhere to go: 600 is
   the ceiling, not the default. Everything at 700 means nothing is emphasised,
   and reads as a 2016 admin template. Headings also get their own line-height —
   a single global 1.5 leaves display sizes looking slack. */
h1, h2, h3 { margin: 0; letter-spacing: -0.025em; line-height: 1.2; }
h1 { font-size: 29px; font-weight: 600; }
h2 { font-size: 18px; font-weight: 600; }
/* h3 had no rule at all, so it fell through to the UA default (~19px bold) and
   turned up larger than the h2s around it wherever one was used. */
h3 { font-size: 15px; font-weight: 600; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } }
.sub { color: var(--muted); margin: 8px 0 20px; font-size: 15px; }
.center-col { max-width: 460px; margin: 72px auto; padding: 0 20px; }

/* ---------- auth: split screen ---------- */
.auth { display: grid; grid-template-columns: 1.05fr 1fr; min-height: 100vh; }
.auth-brand { background: linear-gradient(150deg, #0e68de 0%, #0a4fae 55%, #08387e 100%); color: #fff; padding: 56px 60px; display: flex; flex-direction: column; position: relative; overflow: hidden; }
.auth-brand::after { content: ""; position: absolute; right: -180px; bottom: -220px; width: 520px; height: 520px; border-radius: 50%; background: rgba(255,255,255,.06); }
.auth-brand-mark { display: flex; align-items: center; gap: 11px; font-weight: 600; font-size: 20px; letter-spacing: -0.02em; }
.auth-brand-body { margin-top: auto; }
.auth-brand h1 { font-size: 40px; line-height: 1.08; font-weight: 600; max-width: 12ch; }
.auth-brand p { margin: var(--s4) 0 0; font-size: 16px; color: rgba(255,255,255,.82); max-width: 42ch; }
.auth-brand .foot { margin-top: var(--s6); font-size: 13px; color: rgba(255,255,255,.6); }
.auth-form { display: flex; align-items: center; justify-content: center; padding: var(--s6); }
.auth-card { width: 100%; max-width: 380px; }
.auth-card h2 { font-size: 26px; font-weight: 600; }
.auth-card .sub { color: var(--muted); margin: var(--s2) 0 var(--s5); font-size: 15px; }
.auth-note { margin-top: var(--s5); font-size: 13px; color: var(--faint); text-align: center; }
@media (max-width: 820px) { .auth { grid-template-columns: 1fr; } .auth-brand { display: none; } }

/* ---------- forms ---------- */
.field { margin-bottom: var(--s4); }
.field label { display: block; font-size: 13px; font-weight: 500; color: var(--ink); margin-bottom: var(--s2); }
input, textarea, select { width: 100%; padding: 11px 13px; border: 1px solid var(--line); border-radius: var(--radius-sm); font: inherit; background: #fff; color: var(--ink); transition: border-color .15s, box-shadow .15s; }
/* Border one shade darker, no ring. --accent is ink now, so the old
   "accent border + soft halo" would frame every focused field in black — the
   same thing that had to be undone in the panel. */
input:focus, textarea:focus, select:focus { outline: none; border-color: var(--faint); }
textarea { min-height: 90px; resize: vertical; }
label.hint { font-weight: 400; color: var(--muted); font-size: 13px; }

/* ---------- buttons ---------- */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--s2); padding: 10px var(--s4); border: 1px solid transparent; border-radius: var(--radius-sm); background: var(--ink); color: #fff; font: inherit; font-weight: 500; font-size: 14px; cursor: pointer; text-decoration: none; transition: background .15s, transform .05s; }
.btn:hover { background: var(--ink-soft); }
.btn:active { transform: translateY(1px); }
.btn[disabled] { opacity: .55; cursor: default; pointer-events: none; }
.btn.full { width: 100%; }
.btn.ghost { background: #fff; color: var(--ink); border-color: var(--line); }
.btn.ghost:hover { background: var(--surface-2); border-color: var(--faint); }
.btn.danger { color: #a33; }
.btn.danger:hover { background: #fdf2f2; border-color: #f3c9c9; }

/* ---- icon action with a drawn tooltip ----
   Square icon for the frequent row actions. The label comes from data-tip and
   is drawn here rather than by title="", whose ~1s delay and OS-styled box are
   most of why icon rows look cheap. 250ms delay so sweeping the mouse across a
   row doesn't set off a trail of them.

   The button itself is never hover-dependent — only its label is. And on touch,
   where hover doesn't exist at all, the tooltip is switched off rather than
   left to be triggered by a tap and stick there. */
.iact { position: relative; display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; flex: 0 0 auto; border: 1px solid var(--line);
  border-radius: var(--radius-sm); background: var(--panel); color: var(--muted);
  cursor: pointer; transition: background .12s, color .12s, border-color .12s; }
.iact:hover { background: var(--surface-2); color: var(--ink); border-color: var(--faint); }
.iact[data-tip]::after { content: attr(data-tip); position: absolute; bottom: calc(100% + 6px);
  left: 50%; transform: translateX(-50%); background: var(--ink); color: #fff;
  font-size: 12px; font-weight: 400; line-height: 1.4; padding: 4px 8px; border-radius: 6px;
  white-space: nowrap; opacity: 0; pointer-events: none; z-index: 40; transition: opacity .12s ease; }
.iact[data-tip]:hover::after, .iact[data-tip]:focus-visible::after { opacity: 1; transition-delay: .25s; }
@media (hover: none) { .iact[data-tip]::after { content: none; } }

/* ---- overflow menu (the ⋯ on a content row) ----
   Native <details>, so it works with no JS. Known limit: clicking elsewhere on
   the page doesn't close it — you tap ⋯ again. Lives here rather than in the
   Pages-only <style> block because Blog uses it too, and the two listings are
   meant to be the same screen. */
.rowmenu { position: relative; display: inline-block; }
.rowmenu > summary { list-style: none; cursor: pointer; white-space: nowrap; }
.rowmenu > summary::-webkit-details-marker { display: none; }
.rowmenu > summary::marker { content: ""; }
.rowmenu[open] > summary { background: var(--surface-2); color: var(--ink); border-color: var(--faint); }
.rowmenu .pop { position: absolute; right: 0; top: calc(100% + 4px); z-index: 30;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 6px; min-width: 180px;
  display: flex; flex-direction: column; gap: 2px; }
.rowmenu .pop form { margin: 0; }
.rowmenu .pop button { width: 100%; justify-content: flex-start; border: 0; background: none; text-align: left; }
.rowmenu .pop button:hover { background: var(--surface-2); }

/* ---- content row: shared by Pages and Blog ---- */
.crow { display: flex; align-items: center; gap: var(--s3); padding: var(--s3) 0;
  border-top: 1px solid var(--line-soft); }
.crow:first-child { border-top: none; }
.crow.dim { opacity: .55; }
.crow-main { min-width: 0; flex: 1; }
.crow-t { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
  font-weight: 500; font-size: 14.5px; }
/* Two lines, then ellipsis: a long name should never be able to turn one row
   into a paragraph, and the route underneath already disambiguates. */
.crow-name { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; }
.crow-m { color: var(--faint); font-size: 12.5px; margin-top: 2px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.crow-acts { display: flex; gap: var(--s2); flex: 0 0 auto; }
/* No mobile override here: the phone rules live in the one mobile block at the
   bottom of this file. Two media queries for the same component is how the
   earlier "wrap the actions onto their own line" rule survived being replaced. */
.btn.sm { padding: 6px var(--s3); font-size: 13px; }

/* ---------- app shell ---------- */
.app { display: grid; grid-template-columns: 244px 1fr; min-height: 100vh; }
/* Chrome is the tinted side; the working area is the white one. This was the
   other way round — a white sidebar competing with white cards for the same
   "this is the content" reading, which is why the eye had nowhere to settle.
   Shopify, Linear and Notion all put the grey on the navigation: the sidebar is
   the desk, the right-hand side is the paper. */
.sidebar { background: var(--chrome); border-right: 1px solid var(--line); display: flex; flex-direction: column; padding: var(--s4) var(--s3); position: sticky; top: 0; height: 100vh; }
.side-brand { display: flex; align-items: center; gap: 10px; font-weight: 600; font-size: 18px; letter-spacing: -0.02em; padding: var(--s1) var(--s2) var(--s5); color: var(--ink); }
.side-nav { display: flex; flex-direction: column; gap: 2px; }
/* Sentence case. Uppercase + letter-spacing is the same 2016 admin-template tic
   that came out of the tables — it survived here because nobody looks at a
   sidebar label twice, which is exactly why it kept sounding old. */
.side-sec { font-size: 12px; font-weight: 500; color: var(--faint); padding: var(--s4) 10px var(--s1); }
.nav-item { display: flex; align-items: center; gap: 11px; padding: var(--s2) 11px; border-radius: var(--radius-sm); color: var(--muted); font-size: 14px; font-weight: 450; transition: background .12s, color .12s; }
.nav-item:hover { background: var(--hover); color: var(--ink); }
.nav-item.on { background: var(--accent-soft); color: var(--accent-dark); font-weight: 500; }
.nav-item svg { width: 18px; height: 18px; flex: 0 0 auto; }
.nav-item .soon, .nav-item .count { margin-left: auto; font-size: 10px; font-weight: 600; padding: 2px 7px; border-radius: 20px; }
.nav-item .soon { color: var(--faint); background: var(--surface-2); }
/* An unread count is the reason a customer opens the portal at all, so unlike
   a "Soon" label it survives the mobile collapse (see the media query below). */
.nav-item .count { color: #fff; background: var(--accent); }
.side-foot { margin-top: auto; border-top: 1px solid var(--line); padding-top: 12px; }
.side-user { display: flex; align-items: center; gap: 10px; padding: 8px 10px; font-size: 13px; color: var(--muted); }
.avatar { width: 30px; height: 30px; border-radius: 50%; background: var(--accent-soft); color: var(--accent-dark); display: grid; place-items: center; font-weight: 600; font-size: 13px; flex: 0 0 auto; }

.main { display: flex; flex-direction: column; min-width: 0; }
/* The topbar's contents sit on the same column as the cards below. They used to
   run the full width, so Publish and Sign out floated 189px to the right of
   every card edge on the page — the eye tracks down from them and finds
   everything has stepped left. Same misalignment the panel had between its
   composer and its transcript. */
.topbar { height: 56px; background: var(--panel); border-bottom: 1px solid var(--line); display: flex; align-items: center; gap: var(--s3); padding: 0 var(--gutter); position: sticky; top: 0; z-index: 10; }
.site-switch { display: flex; align-items: center; gap: 9px; font-weight: 500; font-size: 14px; }
.site-switch .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--warn); }
.site-switch .dot.live { background: var(--ok); }
.topbar .spacer { margin-left: auto; }
/* Centred, and narrower than the shell allows: past ~90 characters a line of
   text gets tiring to read, and a column pinned to the sidebar leaves the page
   visually lopsided on a wide monitor. */
.content { padding: var(--s6) var(--gutter) 60px; width: 100%; }

/* ---------- page head + cards ---------- */
/* Air under the title is most of what separates "laid out" from "stacked". */
.page-head { margin-bottom: var(--s6); }
.page-head h1 { font-size: 29px; font-weight: 600; }
.page-head p { color: var(--muted); margin: var(--s1) 0 0; font-size: 14.5px; }
.card { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); padding: var(--s5); margin-bottom: var(--s4); }
.card-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--s1); }
.card-head h2 { font-size: 15px; font-weight: 600; }
.card-sub { color: var(--muted); font-size: 13.5px; margin: 2px 0 var(--s4); }

/* KPI row */
.kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--s3); margin-bottom: var(--s5); }
/* Tinted fill, no border. These tiles live INSIDE a bordered card on Home, so
   giving them their own border made a box in a box — the exact thing flagged as
   wrong when reviewing the competitor, then done here anyway. Standing alone on
   Reports they still read fine: the fill is doing the separating. */
/* --surface-2, not --bg: the page is only six units off white now, so filling
   these with it would make them vanish against the card they sit in. */
.kpi { background: var(--surface-2); border-radius: var(--radius-sm); padding: var(--s4); }
/* tabular-nums so a changing figure doesn't shift the tile's width */
.kpi .n { font-size: 26px; font-weight: 600; letter-spacing: -0.025em; font-variant-numeric: tabular-nums; }
.kpi .l { font-size: 12.5px; color: var(--muted); margin-top: 2px; }

/* badges — neutral by default. Amber has to mean "this needs you", so a plain
   state like Draft (which is perfectly normal) must not wear it. */
.badge { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 500; padding: 3px 10px; border-radius: 20px; background: var(--neutral-soft); color: var(--muted); }
.badge.warn { background: var(--warn-soft); color: var(--warn); }
.badge.live, .badge[data-s=live], .badge[data-s=paid] { background: var(--ok-soft); color: var(--ok); }
.badge.dot::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }

/* tables — sentence case, regular weight. Uppercase + letter-spaced headers are
   a 2016 admin-template tic, and they shout louder than the data underneath. */
.table { width: 100%; border-collapse: collapse; font-size: 14px; }
.table th { text-align: left; font-size: 13px; font-weight: 400; color: var(--faint); padding: 0 var(--s3) var(--s2); }
.table td { padding: var(--s3); border-top: 1px solid var(--line-soft); vertical-align: top; }
.table tr:hover td { background: var(--surface-2); }

/* site card (dashboard) */
.site-row { display: flex; align-items: center; gap: var(--s4); padding: var(--s4) 0; border-top: 1px solid var(--line-soft); }
.site-row:first-child { border-top: none; }
.site-meta { min-width: 0; }
.site-meta .nm { font-weight: 500; font-size: 15px; }
.site-meta .u { color: var(--muted); font-size: 13px; }
.site-row .acts { margin-left: auto; display: flex; gap: var(--s2); }

/* ---------- mobile ----------
   Two rewrites of this. First the sidebar collapsed into a wrapping row of nav
   pills — fine at four items, a wall of tags at fourteen. Then it became an
   accordion, which shoved the page content down every time it opened and left
   the sidebar and the topbar stacked as two bars, the upper one holding just a
   logo. Now it's a proper left drawer: it slides in over the page, the page
   doesn't move, and the hamburger lives in the single remaining bar.

   No JS. #navtoggle is a checkbox sitting beside .sidebar so the CSS can move
   the sidebar itself; .nav-scrim is a second <label> pointing at the same
   checkbox, which is what makes a tap outside close the drawer. */
.nav-toggle, .nav-burger, .nav-scrim { display: none; }
@media (max-width: 820px) {
  .app { grid-template-columns: 1fr; }
  .sidebar { position: fixed; top: 0; left: 0; bottom: 0; z-index: 60;
    width: min(78vw, 290px); height: 100dvh; overflow-y: auto;
    flex-direction: column; padding: var(--s4) var(--s3);
    border-right: 1px solid var(--line);
    transform: translateX(-100%); transition: transform .22s ease; }
  .nav-toggle:checked ~ .sidebar { transform: none; }
  .nav-scrim { display: block; position: fixed; inset: 0; z-index: 55;
    background: rgba(18, 22, 33, .35); opacity: 0; pointer-events: none;
    transition: opacity .22s ease; }
  .nav-toggle:checked ~ .nav-scrim { opacity: 1; pointer-events: auto; }
  .nav-burger { display: inline-flex; align-items: center; gap: 6px;
    padding: var(--s2); margin-left: calc(var(--s2) * -1); cursor: pointer;
    color: var(--ink); border-radius: var(--radius-sm); }
  .nav-burger svg { width: 22px; height: 22px; }
  .nav-burger .count { color: #fff; background: var(--accent); font-size: 10px;
    font-weight: 600; padding: 2px 6px; border-radius: 20px; }
  .side-brand { padding: var(--s1) var(--s2) var(--s4); }
  .nav-item { padding: 11px; font-size: 15px; }   /* comfortable touch target */
  .nav-item .soon { display: none; }   /* .count deliberately stays visible */

  /* Compactness: the desktop rhythm wastes a lot of a phone screen. */
  .topbar { height: 52px; padding: 0 var(--s3); gap: var(--s2); }
  .content { padding: var(--s4) var(--s4) 40px; }
  .page-head { margin-bottom: var(--s4); }
  .page-head h1, h1 { font-size: 23px; }
  .page-head p { font-size: 13.5px; }
  .card { padding: var(--s4); margin-bottom: var(--s3); }
  .kpis { grid-template-columns: 1fr 1fr; gap: var(--s2); margin-bottom: var(--s4); }
  .kpi { padding: var(--s3); }
  .kpi .n { font-size: 22px; }
  .site-row { flex-wrap: wrap; gap: var(--s2); }
  .site-row .acts { margin-left: 0; width: 100%; }
  .seg a { padding: 6px 11px; }
  /* Actions stay on the title's line. Wrapping them onto their own full-width
     row cost ~56px per item — on a 14-page site that was 800px of scrolling to
     show nothing extra. Three 32px icons need 112px; the row is 310px wide. */
  .crow { gap: var(--s2); }
  .crow-t { font-size: 14px; }
  .crow-m { font-size: 12px; }
  .crow-acts { gap: 6px; }
  /* Shop tables still scroll sideways inside their wrapper — tighten them so
     more fits before that's needed. */
  .table { font-size: 13px; }
  .table td { padding: var(--s2); }
}

.empty { text-align: center; padding: 56px var(--s5); color: var(--muted); }
.signout { color: var(--muted); font-size: 13px; }
.req-list { list-style: none; padding: 0; margin: var(--s4) 0 0; }
.req-list li { font-size: 13px; padding: var(--s3) 0; border-top: 1px solid var(--line-soft); }
.req-top { display: flex; gap: var(--s3); align-items: baseline; }
.req-when { color: var(--faint); white-space: nowrap; }
.req-msg { flex: 1; }
.req-status { font-size: 12px; font-weight: 500; color: var(--accent); }
/* our reply to a change request (two-way board, 2026-07-16) */
.req-reply { margin: var(--s2) 0 2px 0; padding: var(--s3); background: var(--accent-soft);
  border-radius: var(--radius-sm); color: var(--ink); line-height: 1.55; }
.req-reply-head { font-size: 12px; font-weight: 500;
  color: var(--accent); margin-bottom: var(--s1); }
/* admin reply box */
.rp-form { display: flex; gap: 8px; align-items: flex-start; margin-top: 8px; }
.rp-form textarea { flex: 1; min-height: 60px; font: inherit; padding: 8px 10px;
  border: 1px solid var(--line); border-radius: var(--radius-sm); resize: vertical; }
.rp-sent { background: var(--ok-soft); color: var(--ok); padding: 8px 10px;
  border-radius: var(--radius-sm); font-size: 12.5px; white-space: pre-wrap; }

/* ---- data-table refinements (2026-07-16 shop-admin pass) ---- */
.table td { vertical-align: middle; }
.table td .btn.sm { padding: 4px 10px; }

/* ---- quick actions: the "what should I do now" list on Home ---- */
.qa-row { display: flex; align-items: center; gap: var(--s3); padding: var(--s3) 0;
  border-top: 1px solid var(--line-soft); color: var(--ink); }
.qa-row:first-child { border-top: none; padding-top: 2px; }
.qa-row:last-child { padding-bottom: 2px; }
.qa-t { font-weight: 500; font-size: 14.5px; }
.qa-s { color: var(--muted); font-size: 13px; margin-top: 2px; }
.qa-row:hover .qa-t, .qa-row:hover .qa-go { color: var(--accent); }
.qa-go { margin-left: auto; color: var(--faint); font-size: 18px; flex: 0 0 auto; }

/* ---- leads list + detail ----
   Was a seven-column table (when / name / contact / message / page / status /
   action) squeezed into the reading column, so the message — the only part
   anyone actually reads — got clipped. It's a list of rows now, one tap into
   the full enquiry. */
.lead-row { display: flex; align-items: flex-start; gap: var(--s3); padding: var(--s3) 0;
  border-top: 1px solid var(--line-soft); color: var(--ink); }
.lead-row:first-child { border-top: none; }
.lead-row:hover .lead-name, .lead-row:hover .qa-go { color: var(--accent); }
.lead-dot, .lead-nodot { width: 8px; height: 8px; border-radius: 50%; margin-top: 7px; flex: 0 0 auto; }
.lead-dot { background: var(--accent); }
.lead-main { min-width: 0; flex: 1; }
.lead-top { display: flex; align-items: center; gap: var(--s2); }
.lead-name { font-weight: 500; font-size: 14.5px; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lead-when { color: var(--faint); font-size: 12.5px; margin-left: auto; white-space: nowrap; }
.lead-msg { color: var(--muted); font-size: 13.5px; margin-top: 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* One line. Email + phone + source page wrapped to two on a phone, which cost
   more height than the message everyone actually reads. */
.lead-meta { color: var(--faint); font-size: 12.5px; margin-top: 3px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lead-body { margin: var(--s4) 0; padding: var(--s4); background: var(--surface-2);
  border-radius: var(--radius-sm); line-height: 1.6; font-size: 14px; white-space: normal; }
.lead-acts { display: flex; gap: var(--s2); flex-wrap: wrap; }
.lead-acts form { margin: 0; }

/* ---- segmented range picker (Reports) ---- */
.seg { display: inline-flex; background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--radius-sm); padding: 3px; gap: 2px; }
.seg a { padding: 6px 14px; border-radius: 6px; font-size: 13px; font-weight: 500; color: var(--muted); }
.seg a.on { background: var(--panel); color: var(--ink); box-shadow: var(--shadow); }
.seg a:hover:not(.on) { color: var(--ink); }

/* Status colour on the change-request board (default stays accent = in progress) */
.req-status[data-s=new] { color: var(--warn); }
.req-status[data-s=done] { color: var(--ok); }

/* ---- topbar publish state ----
   Answers "is what I edited actually on my public site?" from every page, not
   just Home. Editing and publishing are separate steps here, and that is the
   single thing customers get wrong most often. */
.pubbar { display: flex; align-items: center; gap: 9px; font-size: 13px; color: var(--muted); }
.pubbar form { margin: 0; }
.pubbar .pdot { width: 8px; height: 8px; border-radius: 50%; background: var(--ok); flex: 0 0 auto; }
.pubbar.pending { color: var(--warn); font-weight: 500; }
.pubbar.pending .pdot { background: var(--warn); }

/* ---- impersonation banner ----
   Deliberately the loudest thing on the page. It sits above the topbar rather
   than inside the content column so it can't be scrolled away from. */
.impbar { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
  background: #4a2c00; color: #ffe9c7; font-size: 13px; font-weight: 500;
  padding: 9px var(--s5); }
.impbar b { color: #fff; }
.impbar a { margin-left: auto; color: #fff; text-decoration: underline;
  text-underline-offset: 2px; white-space: nowrap; }
@media (max-width: 820px) { .impbar { padding: 9px var(--s3); } }

/* ---- toast ----
   A long form (Business info, Marketing) used to redirect to a banner at the
   very top of the page, so the confirmation landed off-screen from wherever
   you were working. This floats instead and fades itself out. */
.toast { position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%);
  background: var(--ink); color: #fff; font-size: 13.5px; font-weight: 500;
  padding: 11px var(--s4); border-radius: var(--radius-sm); box-shadow: var(--shadow);
  z-index: 50; animation: toast-in .18s ease-out, toast-out .4s ease-in 3.4s forwards; }
@keyframes toast-in { from { opacity: 0; transform: translate(-50%, 8px); } }
@keyframes toast-out { to { opacity: 0; transform: translate(-50%, 8px); visibility: hidden; } }

@media (max-width: 820px) {
  /* 32px is under the ~44px minimum for a comfortable touch target, and these
     small buttons carry real row actions (mark read, fulfil). */
  .btn.sm { padding: 10px 15px; font-size: 13.5px; }
  .pubbar span.plabel { display: none; }   /* dot + button is enough on a phone */
}
