/* Skratch Creative brand palette.
   Source: Skratch-Creative-Brand-Guide.pdf, scraped from skratchcreative.com
   30 July 2026. A copy lives at docs/Skratch-Creative-Brand-Guide.pdf.

     #1F8A70  Skratch Green   accents, section numbers, links
     #222222  Ink             body copy
     #17181F  Near Black      dark panels and headers, never body text
     #F8F8F8  Off White       page background, zebra rows, callouts
     #798099  Slate           captions, labels, footers
     #E6E8F0  Line            borders, dividers, field rules
     #003345  Deep Teal       reserved second dark tone

   This supersedes reference/DESIGN_NOTES.md, which names a royal blue
   #2D43E6 and a red spark #FF453E. Neither is in the brand guide, and the
   guide says of the red: "That red appears nowhere else in the brand.
   Replace it." DESIGN_NOTES is older and wrong.

   Variable names are left alone on purpose. Renaming --teal across thirty
   templates breaks every inline style already referencing it and buys
   nothing, so --teal simply holds Skratch Green now. */
:root {
  /* Two greens on purpose. #1F8A70 is the brand green and it is only 4.26:1
     against white, which is under the 4.5:1 needed for normal text. So the
     brand green is used for borders, pills and accents, and the darker
     #176B57 (6.41:1) carries anything with words on it: filled buttons and
     link text. Measured, not guessed. */
  --teal: #1F8A70;          /* Skratch Green, accents and borders */
  --teal-dark: #176B57;     /* text-safe green, buttons and links */
  --slate: #798099;
  --slate-dark: #5E6478;
  --deep: #176B57;          /* filled button background, white text on it */
  --deep-2: #0F4C3E;        /* button hover */
  --bg: #F8F8F8;            /* Off White */
  --surface: #FFFFFF;
  --text: #222222;          /* Ink */
  /* Slate #798099 is the brand grey, but as text on the page it is 3.69:1,
     under AA. Kept as --slate for borders and rules; --muted is a shade
     deeper so secondary text is actually readable. */
  --muted: #666C80;
  /* Amber had one value for both themes. #E0A500 is 8:1 on the dark surface
     and 2.07:1 on the light one, so every warning in light mode was close to
     unreadable. One token, two values. */
  --warn: #8A6100;
  /* The label colour that goes ON --warn, for the same reason --on-accent
     exists: --warn is dark in light mode and light in dark mode, so a
     hardcoded #fff is correct in one theme and 1.86:1 in the other. It was
     hardcoded, and the amber "PART LIVE" badge on the calendar was the pair
     that failed. White on #8A6100 is 5.54:1. */
  --on-warn: #FFFFFF;
  --warn-bg: rgba(138, 97, 0, 0.10);
  --warn-line: #B08300;
  /* Filled accent controls. Two tokens instead of a hardcoded #fff, because
     the correct label colour is white in light mode and near-black in dark
     mode. Hardcoding either one breaks the other theme, which is exactly how
     the invisible button happened in the first place. */
  --accent-fill: #176B57;
  --on-accent: #FFFFFF;
  --danger-fill: #C0392B;
  --on-danger: #FFFFFF;         /* Slate */
  --border: #E6E8F0;        /* Line */
  --high: #C0392B;          /* urgency. Not the retired #FF3D3D favicon red. */
  --med: #B7791F;
  --low: #798099;
  --shadow: 0 1px 2px rgba(23, 24, 31, 0.04), 0 4px 12px rgba(23, 24, 31, 0.05);
  --bg-elevated: #EFF0F4;
  /* Binary status. See docs/DESIGN_LANGUAGE.md section 5.
     Two states and a neutral, nothing else. Amber stays out of this set on
     purpose: a three-colour traffic light has to be read, and the whole point
     of the pattern is that you do not read it. Every ratio here was measured
     against its own tint, not against the page background. */
  --yes-bg: #E6F2EE;
  --yes-fg: #176B57;        /* 5.58:1 on --yes-bg */
  --no-bg: #FBEAE8;
  --no-fg: #A32E22;         /* 6.07:1 on --no-bg */
  --neutral-bg: #EFF0F4;
  --neutral-fg: #5E6478;    /* 5.17:1 on --neutral-bg */
}
[data-theme="dark"] {
  --teal: #2FB894;          /* Skratch Green lifted to read on Near Black */
  --teal-dark: #46CFA9;
  --slate: #9AA0B5;
  --slate-dark: #798099;
  --deep: #2FB894;
  --deep-2: #46CFA9;
  --bg: #17181F;            /* Near Black, the guide's dark band colour */
  --surface: #1F212A;
  --text: #ECEDF2;
  --muted: #9AA0B5;
  --warn: #F0B429;          /* amber lifted for the dark surface */
  --on-warn: #17181F;       /* near-black label on the lifted amber, 9.49:1 */
  --warn-bg: rgba(240, 180, 41, 0.12);
  --warn-line: #8A6D00;
  --accent-fill: #2FB894;
  --on-accent: #17181F;     /* near-black label on the lifted green */
  --danger-fill: #E8695C;
  --on-danger: #17181F;
  --border: #2C2F3A;
  --high: #E8695C;
  --med: #D9A441;
  --low: #9AA0B5;
  --shadow: 0 1px 2px rgba(0,0,0,0.4), 0 4px 12px rgba(0,0,0,0.4);
  --bg-elevated: #262932;
  --yes-bg: #1E3B34;
  --yes-fg: #46CFA9;        /* 6.22:1 on --yes-bg */
  --no-bg: #3B2320;
  --no-fg: #F0968B;         /* 6.52:1 on --no-bg */
  --neutral-bg: #262932;
  --neutral-fg: #9AA0B5;    /* 5.58:1 on --neutral-bg */
}
[data-theme="dark"] .btn-primary { background: var(--teal); color: var(--bg); }
[data-theme="dark"] .btn-primary:hover { background: var(--teal-dark); color: var(--bg); }
[data-theme="dark"] .btn-teal { background: var(--teal); color: var(--bg); }
/* Without this, .btn-teal:hover fell through to the light rule (white text)
   and in dark mode --deep-2 is a pale mint, so the label washed out on hover. */
[data-theme="dark"] .btn-teal:hover { background: var(--teal-dark); color: var(--bg); }
[data-theme="dark"] .pill.connected { background: rgba(47,184,148,0.18); color: var(--teal); }
[data-theme="dark"] .pill.high { background: rgba(232,105,92,0.18); color: var(--high); }
[data-theme="dark"] .pill.med, [data-theme="dark"] .pill.medium { background: rgba(217,164,65,0.18); color: var(--med); }
[data-theme="dark"] .pill.low { background: rgba(154,160,181,0.18); color: var(--slate); }
[data-theme="dark"] th { background: var(--surface); }
[data-theme="dark"] .field input, [data-theme="dark"] .field select, [data-theme="dark"] .field textarea {
  background: var(--bg); color: var(--text); border-color: var(--border);
}
[data-theme="dark"] .sidebar .brand { color: var(--teal); }
[data-theme="dark"] .sidebar .nav-item.active { background: rgba(47,184,148,0.14); color: var(--teal); }
[data-theme="dark"] .page-head h1 { color: var(--teal); }

/* Theme toggle */
.theme-toggle {
  position: fixed; bottom: 16px; left: 16px; z-index: 200;
  background: var(--surface); border: 1px solid var(--border);
  width: 36px; height: 36px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; box-shadow: var(--shadow);
  font-size: 16px;
}
.theme-toggle:hover { border-color: var(--teal); }
/* Typography, per the brand guide.
   Display is Roc Grotesk, licensed through Adobe Fonts kit jsg5lcr, and H1 is
   Gilroy, self-hosted on the website. Neither licence is set up here, and the
   guide names the substitutes to use in that case: Archivo SemiBold for
   display, Inter Regular for body. Both are open licence, so this is the
   documented answer rather than a compromise.

   System stack behind them, because an internal tool should not go ugly when
   a font CDN has a bad day. */
* { box-sizing: border-box; margin: 0; padding: 0; }
h1, h2, h3, .brand, .page-head h1 {
  font-family: "Archivo", -apple-system, BlinkMacSystemFont, "Segoe UI",
               system-ui, sans-serif;
  font-weight: 600;
  letter-spacing: -0.015em;
}
body {
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  background: var(--bg);
  color: var(--text);
  line-height: 1.55;
  font-size: 14px;
}
a { color: var(--deep); text-decoration: none; }
a:hover { color: var(--teal-dark); }
.app { display: grid; grid-template-columns: 240px 1fr; min-height: 100vh; }

/* Sidebar */
.sidebar {
  background: var(--surface);
  border-right: 1px solid var(--border);
  padding: 20px 12px;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
}
.sidebar .brand {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 12px 16px; margin-bottom: 14px;
  border-bottom: 1px solid var(--border);
  font-weight: 800; font-size: 16px; color: var(--deep);
  letter-spacing: -0.01em; min-height: 40px;
}
.sidebar .brand .logo { max-width: 100%; max-height: 36px; height: auto; width: auto; display: block; object-fit: contain; }
.sidebar .brand .logo-night { display: none; }
[data-theme="dark"] .sidebar .brand .logo-day { display: none; }
[data-theme="dark"] .sidebar .brand .logo-night { display: block; }
.sidebar .group { font-size: 11px; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--muted); padding: 14px 12px 6px; }
.sidebar .nav-item {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 12px; border-radius: 8px; margin: 2px 0;
  color: var(--text); font-weight: 500; font-size: 13px;
}
.sidebar .nav-item:hover { background: var(--bg); color: var(--deep); }
.sidebar .nav-item.active { background: rgba(69, 217, 197, 0.18); color: var(--deep); font-weight: 600; }
/* Unread messages. Deliberately louder than .badge: this is the only
   notification chat has, so a quiet grey pill would defeat the point. */
.sidebar .nav-item .nav-badge {
  margin-left: auto; background: var(--accent-fill); color: var(--on-accent);
  padding: 1px 8px; border-radius: 999px; font-size: 11px; font-weight: 800;
}
.sidebar .nav-item .badge {
  margin-left: auto; background: var(--bg); color: var(--muted);
  padding: 1px 8px; border-radius: 999px; font-size: 11px; font-weight: 600;
}
/* How many post requests are waiting. Sits against the label rather than
   pushed to the far edge, because the number belongs to those words and a
   count floating at the other end of the row reads as a separate control.
   Red and white are fixed, not themed: --on-danger is dark text in night
   mode, and the owner asked for white on red in both. min-width with equal
   padding keeps a single digit round instead of drawing an oval. */
.sidebar .nav-item .nav-count {
  margin-left: 6px; background: #C0392B; color: #FFFFFF;
  min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px;
  font-size: 11px; font-weight: 800; line-height: 18px; text-align: center;
  display: inline-block; flex: 0 0 auto;
}
/* Nav icons: same visual weight as the label, dimmed a touch until the row is
   hovered or active so the eye still lands on text first, glyph second. */
.sidebar .nav-item svg {
  width: 16px; height: 16px; flex: 0 0 16px;
  color: var(--muted); opacity: 0.75;
}
.sidebar .nav-item:hover svg,
.sidebar .nav-item.active svg { color: var(--deep); opacity: 1; }
[data-theme="dark"] .sidebar .nav-item.active svg { color: var(--teal); }

/* Main */
.main { padding: 24px 32px 80px; max-width: 100%; }
.page-head { display: flex; align-items: flex-end; justify-content: space-between;
  padding-bottom: 14px; border-bottom: 1px solid var(--border); margin-bottom: 24px; }
.page-head .crumbs { color: var(--muted); font-size: 12px; }
.page-head h1 { font-size: 26px; font-weight: 800; letter-spacing: -0.02em;
  color: var(--deep); margin-top: 4px; }
.page-head .actions { display: flex; gap: 8px; }

/* Buttons */
.btn { display: inline-flex; align-items: center; justify-content: center;
  gap: 6px; padding: 8px 14px; border-radius: 8px; font-weight: 600;
  font-size: 13px; cursor: pointer; border: 1px solid transparent;
  text-decoration: none; transition: 0.12s; }
/* The "off" pill beside a nav item for a module this client is not on. Staff
   only -- the client branch of the sidebar does not draw the item at all. The
   word carries the meaning, not the colour. */
.nav-off { font-size: 9.5px; font-weight: 700; letter-spacing: .06em;
           text-transform: uppercase; padding: 1px 5px; border-radius: 4px;
           background: var(--warn); color: var(--on-warn); vertical-align: middle; }

.btn-primary { background: var(--accent-fill); color: var(--on-accent); }
.btn-primary:hover { background: var(--deep-2); color: var(--on-accent); }
/* .btn-teal used to be `background: var(--teal); color: var(--deep)`. When both
   tokens became the same green the text vanished into the button. Filled green
   buttons carry white text, always. */
.btn-teal { background: var(--accent-fill); color: var(--on-accent); }
.btn-teal:hover { background: var(--deep-2); color: var(--on-accent); }
/* .btn-teal is a colour modifier, not a button. It carries no box at all, so
   `class="btn-teal"` on its own paints a background behind raw inline text and
   reads as highlighted text rather than a control. That is what broke the Add
   Client button on /clients: the markup omitted .btn. Fixed at the call site,
   not here, because thirty-odd templates already write `btn btn-teal` and
   duplicating padding/radius onto the modifier would double-specify the box on
   every one of them.
   The focus ring does belong here. No .btn rule declared one, so a filled green
   button had no keyboard-visible state at all. Outlined in --text rather than
   an accent because the accent IS the button fill, and a green ring on a green
   button is not a ring. --text sits at high contrast against --bg in both
   themes, so one declaration covers light and dark. */
.btn-teal:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }
.btn-ghost { background: var(--surface); color: var(--text);
  border: 1px solid var(--border); }
.btn-ghost:hover { border-color: var(--teal); color: var(--deep); }
.btn-danger { background: var(--surface); color: var(--high);
  border: 1px solid var(--border); }
.btn-danger:hover { background: rgba(232, 101, 78, 0.08); border-color: var(--high); }
.btn-sm { padding: 5px 10px; font-size: 12px; }

/* Cards */
.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 12px; padding: 20px; box-shadow: var(--shadow);
}
.card h3 { font-size: 14px; font-weight: 700; color: var(--deep);
  margin-bottom: 8px; }
.card .label { font-size: 11px; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--muted); }
.card .value { font-size: 28px; font-weight: 800; color: var(--deep);
  letter-spacing: -0.02em; margin-top: 4px; }

.grid { display: grid; gap: 14px; }
.grid-2 { grid-template-columns: 1fr 1fr; }
.grid-3 { grid-template-columns: 1fr 1fr 1fr; }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 900px) {
  .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; }
}

/* Forms */
.field { margin-bottom: 12px; }
.field label { display: block; font-size: 12px; font-weight: 600;
  color: var(--text); margin-bottom: 4px; }
.field input, .field select, .field textarea {
  width: 100%; padding: 9px 12px; border: 1px solid var(--border);
  border-radius: 8px; font-size: 13px; background: var(--bg);
  font-family: inherit;
}
/* `background: white` was hardcoded here, and it is the white-on-white this
   was reported as. It is NOT the autofill rule below: this fires on every
   field, for every dark-mode user, the moment they click into one, with no
   password manager involved. The resting field is var(--bg) with
   color: var(--text), so in dark mode focusing it painted #FFFFFF under
   #ECEDF2 -- MEASURED AT 1.17:1 in Chrome, on this app's own form, with the
   window actually focused. (:focus does not match while the window is in the
   background, which is why a first pass reads a healthy 15.13:1 and sees
   nothing wrong.)

   var(--surface) keeps the design intent -- the field lifts a shade when
   focused -- in both themes instead of only in the light one. In light mode
   --surface IS #FFFFFF, so nothing changes there. */
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none; border-color: var(--teal); background: var(--surface);
}

/* ── Autofilled fields ───────────────────────────────────────────────────
   Chrome paints an autofilled field with its own background AND its own text
   colour, and an author `color` does not beat it. Measured here, on this
   app's sign-in form, against a field Chrome had really filled: computed
   background rgb(232,240,254), computed color rgb(0,0,0) -- black, even with
   our dark-theme rule asking for var(--text).

   So this block is not repairing a contrast failure. What Chrome paints is
   black on pale blue at 18.33:1 and perfectly readable. What it repairs is
   that the pale blue box is a LIGHT control sitting in the middle of a dark
   form: we never declare the `color-scheme` property, so Chrome autofills as
   though the page were light whatever our theme says.

   Three mechanics, each needed for its own reason:
     - the background is repainted with an INSET BOX-SHADOW, because the UA
       rule wins on background-color and cannot touch a shadow;
     - -webkit-text-fill-color is the only way to set the glyphs, `color`
       having already lost;
     - caret-color separately, or the text cursor keeps the UA colour.
   The very long transition delay is the standard way to stop Chrome's fade
   showing its own background before ours lands. */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active,
select:-webkit-autofill,
select:-webkit-autofill:hover,
select:-webkit-autofill:focus,
select:-webkit-autofill:active,
textarea:-webkit-autofill,
textarea:-webkit-autofill:hover,
textarea:-webkit-autofill:focus,
textarea:-webkit-autofill:active {
  -webkit-box-shadow: inset 0 0 0 100px var(--surface);
  box-shadow: inset 0 0 0 100px var(--surface);
  -webkit-text-fill-color: var(--text);
  caret-color: var(--text);
  transition: background-color 100000s ease-in-out 0s;
}
.field textarea { min-height: 100px; resize: vertical; font-family: ui-monospace, monospace; font-size: 12px; }
.field .hint { font-size: 11px; color: var(--muted); margin-top: 3px; }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.checkbox-row { display: flex; gap: 14px; flex-wrap: wrap; }
.checkbox-row label { display: flex; align-items: center; gap: 6px;
  font-size: 13px; cursor: pointer; }

/* Tables */
table { width: 100%; border-collapse: collapse; font-size: 13px; }
th { text-align: left; padding: 10px; background: var(--bg);
  border-bottom: 2px solid var(--border); color: var(--muted);
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; }
td { padding: 10px; border-bottom: 1px solid var(--border); vertical-align: top; }
tr:last-child td { border-bottom: none; }
.table-card { background: var(--surface); border: 1px solid var(--border);
  border-radius: 12px; overflow: hidden; box-shadow: var(--shadow); }

/* Pills */
.pill { display: inline-block; padding: 2px 8px; border-radius: 999px;
  font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.05em; }
.pill.high { background: rgba(232, 101, 78, 0.18); color: #B6402F; }
.pill.med, .pill.medium { background: rgba(232, 185, 65, 0.22); color: #8A6717; }
.pill.low { background: rgba(122, 181, 209, 0.22); color: var(--deep); }
.pill.success, .pill.published { background: rgba(69, 217, 197, 0.22); color: var(--deep); }
.pill.pending { background: var(--bg); color: var(--muted); }
.pill.failed, .pill.error { background: rgba(232, 101, 78, 0.18); color: #B6402F; }
.pill.connected { background: rgba(69, 217, 197, 0.22); color: var(--deep); }
.pill.missing { background: var(--bg); color: var(--muted); }

/* Flash */
.flash { padding: 12px 16px; border-radius: 8px; margin-bottom: 14px;
  font-size: 13px; border: 1px solid; }
.flash.success { background: rgba(69, 217, 197, 0.12);
  border-color: var(--teal); color: var(--deep); }
.flash.error { background: rgba(232, 101, 78, 0.10);
  border-color: var(--high); color: #8A2D1E; }
.flash.info { background: rgba(122, 181, 209, 0.10);
  border-color: var(--slate); color: var(--deep); }

/* AI unavailable notice. Sits above content the platform wrote without
   Claude. Warn colours, not error colours: falling back is not a failure,
   it just has to be said out loud. */
.ai-unavailable { border: 1px solid var(--warn-line); background: var(--warn-bg);
  color: var(--warn); border-radius: 9px; padding: 11px 13px;
  font-size: 12.5px; line-height: 1.6; margin-bottom: 14px; }
.ai-unavailable strong { font-weight: 800; }

/* Section header */
.section-head { display: flex; align-items: center; justify-content: space-between;
  margin: 24px 0 12px; }
.section-head h2 { font-size: 16px; font-weight: 800; color: var(--deep); }

/* URL */
.url { font-size: 11px; color: var(--muted); word-break: break-all;
  font-family: ui-monospace, monospace; }

/* Empty */
.empty { text-align: center; padding: 40px 20px; color: var(--muted); }
.empty h3 { font-size: 16px; color: var(--text); margin-bottom: 8px; }

/* Markdown content */
.md-content { background: var(--surface); border: 1px solid var(--border);
  border-radius: 12px; padding: 24px 32px; }
.md-content h1 { font-size: 22px; font-weight: 800; color: var(--deep);
  margin-top: 16px; margin-bottom: 8px; }
.md-content h2 { font-size: 17px; font-weight: 800; color: var(--deep);
  margin-top: 22px; margin-bottom: 6px; }
.md-content h3 { font-size: 14px; font-weight: 700; color: var(--deep);
  margin-top: 16px; margin-bottom: 4px; }
.md-content p { margin: 8px 0; }
.md-content ul, .md-content ol { margin: 8px 0 8px 22px; }
.md-content li { margin: 4px 0; }
.md-content code { background: var(--bg); padding: 2px 6px;
  border-radius: 4px; font-size: 12px; }
.md-content pre { background: var(--bg); padding: 12px; border-radius: 8px;
  overflow-x: auto; font-size: 12px; margin: 10px 0; }
.md-content blockquote { border-left: 3px solid var(--teal);
  padding-left: 14px; margin: 12px 0; color: var(--muted); font-style: italic; }

/* Loading */
.loading { display: none; align-items: center; gap: 8px;
  color: var(--muted); font-size: 13px; }
.loading.show { display: inline-flex; }
.spinner { width: 14px; height: 14px; border: 2px solid var(--border);
  border-top-color: var(--teal); border-radius: 50%;
  animation: spin 0.7s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

/* Misc */
hr { border: none; border-top: 1px solid var(--border); margin: 16px 0; }
.text-muted { color: var(--muted); }
.text-small { font-size: 12px; }
.mb-12 { margin-bottom: 12px; }
.mt-12 { margin-top: 12px; }
.gap-8 { gap: 8px; }
.flex { display: flex; align-items: center; }
.flex-between { display: flex; align-items: center; justify-content: space-between; }

/* Page header utility classes (used across clients_list, templates, etc.) */
.page-eyebrow { font-size: 11px; text-transform: uppercase; letter-spacing: 0.07em;
  color: var(--muted); font-weight: 600; margin-bottom: 2px; }
.page-title { font-size: 26px; font-weight: 800; letter-spacing: -0.02em;
  color: var(--deep); margin: 0; }

/* Chip — inline tag pill */
.chip { display: inline-flex; align-items: center; padding: 2px 8px;
  border-radius: 999px; border: 1px solid var(--border);
  background: var(--surface); font-size: 11px; font-weight: 600;
  color: var(--muted); white-space: nowrap; }

/* Blog engine — page header */
.page-header { margin-bottom: 24px; }
.page-header h1 { font-size: 26px; font-weight: 800; letter-spacing: -0.02em;
  color: var(--deep); margin-top: 6px; }
.back-link { font-size: 12px; color: var(--muted); text-decoration: none;
  display: inline-flex; align-items: center; gap: 4px; }
.back-link:hover { color: var(--teal-dark); }
.sub-line { font-size: 12px; color: var(--muted); margin-top: 4px;
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* Blog engine — content blocks */
.card-block { background: var(--surface); border: 1px solid var(--border);
  border-radius: 12px; padding: 20px; box-shadow: var(--shadow); margin-bottom: 14px; }
.block-eyebrow { font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--muted); margin-bottom: 8px; font-weight: 600; }

/* Blog engine — tables */
.table-wrap { overflow-x: auto; }
.ptable { width: 100%; border-collapse: collapse; font-size: 13px; }
.ptable th { text-align: left; padding: 8px 10px; background: var(--bg);
  border-bottom: 2px solid var(--border); color: var(--muted);
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; }
.ptable td { padding: 9px 10px; border-bottom: 1px solid var(--border); vertical-align: middle; }
.ptable tr:last-child td { border-bottom: none; }
.row-link { cursor: pointer; }
.row-link:hover td { background: var(--bg); }

/* Blog engine — status chip */
.status-chip { display: inline-block; padding: 2px 8px; border-radius: 999px;
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em;
  background: var(--bg); color: var(--muted); border: 1px solid var(--border); }
.status-chip.draft { background: rgba(122,181,209,0.18); color: var(--deep); border-color: transparent; }
.status-chip.published { background: rgba(69,217,197,0.18); color: var(--deep); border-color: transparent; }
.status-chip.pending { background: rgba(232,185,65,0.18); color: #8A6717; border-color: transparent; }
.status-chip.failed { background: rgba(232,101,78,0.18); color: #B6402F; border-color: transparent; }
[data-theme="dark"] .status-chip.draft { color: var(--teal); }
[data-theme="dark"] .status-chip.published { color: var(--teal); }
[data-theme="dark"] .status-chip.pending { color: var(--med); }
[data-theme="dark"] .status-chip.failed { color: var(--high); }

/* Blog engine — two-column draft layout */
.two-col { display: grid; grid-template-columns: 1fr 340px; gap: 20px; align-items: start; }
.col { min-width: 0; }
@media (max-width: 960px) { .two-col { grid-template-columns: 1fr; } }

/* Blog engine — KV metadata list */
.kv { display: grid; grid-template-columns: auto 1fr; gap: 6px 12px;
  font-size: 13px; align-items: baseline; }
.kv dt { color: var(--muted); font-size: 11px; text-transform: uppercase;
  letter-spacing: 0.05em; font-weight: 600; white-space: nowrap; }
.kv dd { color: var(--text); word-break: break-word; }

/* Blog engine — inline form row */
.row-form { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.field-narrow { width: 80px; }
/* On a phone the inline form fields collapse to a sliver and text wraps one
   character per line. Stack them full width instead. */
@media (max-width: 700px) {
  .row-form { flex-direction: column; align-items: stretch; }
  .row-form .field, .row-form .field-narrow { width: 100%; }
}

/* Utility text */
.muted { color: var(--muted); }
.small { font-size: 12px; }

/* ── UI Polish Additions ─────────────────────────────────── */

/* Ensure all .btn elements have identical sizing and no wrapping */
.btn { white-space: nowrap; min-height: 36px; }
.btn-sm { min-height: 28px; }

/* Form rows — fields on same horizontal line */
.form-row {
  display: flex;
  align-items: flex-end;
  gap: 12px;
  flex-wrap: nowrap;
}
.form-row .field { margin-bottom: 0; flex: 1; min-width: 0; }
.form-row .field label { white-space: nowrap; }
.form-row .btn { flex-shrink: 0; align-self: flex-end; }

/* Prevent label text wrapping in nav */
.sidebar .nav-item { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Table cell — no wrapping for status/action columns */
.td-nowrap { white-space: nowrap; }
.td-actions { white-space: nowrap; display: flex; gap: 6px; align-items: center; }

/* Consistent page header pattern */
.ph { display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 24px; gap: 16px; flex-wrap: wrap; border-bottom: 1px solid var(--border); padding-bottom: 16px; }
.ph h1 { font-size: 22px; font-weight: 800; color: var(--deep); margin: 0; white-space: nowrap; }
.ph .ph-actions { display: flex; gap: 8px; align-items: center; flex-shrink: 0; }

/* Section label */
.section-label { font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--muted); margin-bottom: 10px; }

/* Inline key-value pair */
.kv-inline { display: flex; align-items: center; gap: 8px; font-size: 13px; }
.kv-inline .k { color: var(--muted); white-space: nowrap; min-width: 120px; font-size: 12px; }
.kv-inline .v { color: var(--text); font-weight: 500; }

/* Input group (input + button on same line) */
.input-group { display: flex; gap: 0; }
.input-group input { border-radius: 8px 0 0 8px; flex: 1; min-width: 0; }
.input-group .btn { border-radius: 0 8px 8px 0; }

/* Chip row */
.chip-row { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }

/* Make flash messages not overflow */
.flash { word-break: break-word; }

/* Responsive table wrapper */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* Dark mode form fixes */
[data-theme="dark"] .form-row .field input,
[data-theme="dark"] .form-row .field select {
  background: var(--bg); color: var(--text);
}

/* ── Active client header in sidebar ──────────────────── */
.client-header {
  background: var(--deep);
  color: #fff;
  border-radius: 8px;
  padding: 10px 12px;
  margin: 8px 0 4px;
}
.client-header-name {
  font-size: 13px;
  font-weight: 800;
  color: #fff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.client-header-domain {
  font-size: 10px;
  color: rgba(255,255,255,0.55);
  margin-top: 2px;
  font-family: 'JetBrains Mono', monospace;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
[data-theme="dark"] .client-header { background: var(--teal); }
[data-theme="dark"] .client-header-name { color: #0A0A0A; }
[data-theme="dark"] .client-header-domain { color: rgba(10,10,10,0.6); }

/* Nav items under client header indent slightly */
.client-header + .nav-item,
.client-header ~ .nav-item {
  padding-left: 18px;
}

/* ══════════════════════════════════════════════════════════════════════
   SiteRISE row language.  Reference: docs/DESIGN_LANGUAGE.md

   Structure lifted from the operator's Grand Natural mockups, palette and
   faces left as SiteRISE's own. The property being protected is that you can
   read the state of a screen without reading a sentence.

   Everything here is prefixed sr- and nothing here touches an existing class,
   because .card / .pill / .btn-teal are load-bearing across thirty templates.
   Pages migrate to this vocabulary one at a time.
   ══════════════════════════════════════════════════════════════════════ */

/* ── Section header: icon + 20px title ───────────────────────────────── */
.sr-section {
  display: flex; align-items: center; gap: 12px;
  margin: 34px 0 14px; flex-wrap: wrap;
}
.sr-section:first-child { margin-top: 0; }
.sr-section-icon {
  width: 34px; height: 34px; flex: 0 0 34px;
  display: flex; align-items: center; justify-content: center;
  border: 2px solid var(--teal); border-radius: 9px;
  color: var(--teal);
}
.sr-section-icon svg { width: 19px; height: 19px; display: block; }
/* A numeral is a better icon than a glyph when the thing being said is order.
   Filled rather than outlined for two reasons. It borrows the mockups' one
   filled-green element, so pipeline steps separate from evidence sections at a
   glance. And --teal on --bg is 4.01:1, which is fine for a stroke icon under
   the 3:1 graphics rule but not fine for a numeral, which is text. Filled, the
   numeral is --on-accent on --accent-fill at 6.41:1. */
.sr-section-icon.num {
  font-family: "Archivo", system-ui, sans-serif;
  font-weight: 700; font-size: 15px; line-height: 1;
  letter-spacing: 0; border-color: var(--accent-fill);
  background: var(--accent-fill); color: var(--on-accent);
}
.sr-section-head { min-width: 0; flex: 1 1 260px; }
/* Section titles are ink, not green. The icon carries the brand colour. A page
   with eleven green headings has no emphasis left to spend. */
.sr-section h2 {
  font-size: 20px; font-weight: 700; letter-spacing: -0.02em;
  color: var(--text); margin: 0; line-height: 1.25;
}
.sr-section-note {
  font-size: 12px; color: var(--muted); margin-top: 3px; line-height: 1.45;
}
.sr-section-actions {
  margin-left: auto; display: flex; align-items: center;
  gap: 8px; flex-wrap: wrap;
}
.sr-section-actions form { margin: 0; }

/* ── Fact row: one fact, one bordered box, label / value / action ─────── */
.sr-facts { display: flex; flex-direction: column; gap: 8px; }
.sr-facts-2 {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: 8px; align-items: stretch;
}
@media (max-width: 1100px) { .sr-facts-2 { grid-template-columns: 1fr; } }

.sr-fact {
  display: grid;
  grid-template-columns: minmax(150px, 34%) 1fr auto;
  align-items: baseline;
  gap: 4px 20px;
  padding: 14px 18px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
}
.sr-fact-label {
  font-family: "Archivo", system-ui, sans-serif;
  font-weight: 600; font-size: 14px; color: var(--text);
  letter-spacing: -0.005em;
}
.sr-fact-value {
  font-size: 14px; color: var(--text); font-weight: 500;
  min-width: 0; word-break: break-word;
}
.sr-fact-note {
  grid-column: 2; font-size: 12px; color: var(--muted);
  margin-top: 2px; line-height: 1.45;
}
.sr-fact-action {
  grid-column: 3; grid-row: 1; justify-self: end; align-self: center;
  display: flex; align-items: center; gap: 8px; white-space: nowrap;
}
.sr-fact-action form { margin: 0; }
/* Metric variant. The only display size in the system besides the page title. */
.sr-fact-metric .sr-fact-value {
  font-family: "Archivo", system-ui, sans-serif;
  font-size: 26px; font-weight: 700; letter-spacing: -0.02em;
  line-height: 1.1;
}
.sr-fact-metric .sr-fact-value .unit {
  font-family: "Inter", system-ui, sans-serif;
  font-size: 13px; font-weight: 500; color: var(--muted);
  letter-spacing: 0;
}
/* Coloured left edge. A second, non-colour cue lives in the status pill. */
.sr-fact.is-yes { border-left: 3px solid var(--yes-fg); }
.sr-fact.is-no { border-left: 3px solid var(--no-fg); }

/* ── Item row: a list of comparable things ───────────────────────────── */
.sr-items {
  border: 1px solid var(--border); border-radius: 10px;
  background: var(--surface); overflow: hidden;
}
.sr-item {
  display: grid;
  grid-template-columns: minmax(170px, 30%) auto 1fr;
  align-items: center;
  gap: 8px 20px;
  padding: 12px 18px;
  border-bottom: 1px solid var(--border);
}
.sr-item:last-child { border-bottom: none; }
.sr-item-name {
  font-family: "Archivo", system-ui, sans-serif;
  font-weight: 600; font-size: 14px; color: var(--text);
}
.sr-item-control { display: flex; align-items: center; gap: 8px; }
.sr-item-control form { margin: 0; }
.sr-item-meta {
  font-size: 12px; color: var(--muted); text-align: right;
  justify-self: end; line-height: 1.45;
}
@media (max-width: 760px) {
  .sr-item { grid-template-columns: 1fr; }
  .sr-item-meta { text-align: left; justify-self: start; }
}

/* ── Status pill: colour + glyph + word, never colour alone ──────────── */
.sr-status {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 11px; border-radius: 8px;
  font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.06em; white-space: nowrap;
  border: 1px solid transparent;
}
/* The glyph is not decoration. About 8% of men cannot separate the two hues,
   so shape carries the same message the colour does. Do not remove it. */
.sr-status::before { font-weight: 800; font-size: 12px; line-height: 1; }
.sr-status.yes { background: var(--yes-bg); color: var(--yes-fg); border-color: var(--yes-fg); }
.sr-status.yes::before { content: "\2713"; }
.sr-status.no { background: var(--no-bg); color: var(--no-fg); border-color: var(--no-fg); }
.sr-status.no::before { content: "\2715"; }
.sr-status.na { background: var(--neutral-bg); color: var(--neutral-fg); border-color: var(--border); }
.sr-status.na::before { content: "\2013"; }

/* Tier P0 sits above .pill.high, which is a tint. Filled, so the top tier is
   distinguishable from the one below it by weight as well as by its label. */
.pill.p0 { background: var(--danger-fill); color: var(--on-danger); }

/* Red filled button: this thing is missing, set it up. Its label must differ
   from the green one — never the same words in a different colour. */
.btn-need { background: var(--danger-fill); color: var(--on-danger); }
.btn-need:hover { background: var(--high); color: var(--on-danger); }

/* ── Small uppercase link, lives inside the box it acts on ───────────── */
.sr-link {
  font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.07em; color: var(--teal-dark);
  text-decoration: none; white-space: nowrap;
  background: none; border: none; padding: 0; cursor: pointer;
  font-family: inherit;
}
.sr-link:hover { color: var(--teal-dark); text-decoration: underline; }
.sr-link[disabled] { color: var(--muted); cursor: not-allowed; text-decoration: none; }

/* ── Flat panel: holds a table, same edge as a fact box, no padding ──── */
.sr-panel {
  border: 1px solid var(--border); border-radius: 10px;
  background: var(--surface); overflow: hidden;
}
.sr-panel table { width: 100%; }
.sr-panel table th { background: var(--bg); }
.sr-panel > .sr-panel-foot {
  padding: 10px 18px; border-top: 1px solid var(--border);
  font-size: 12px; color: var(--muted); line-height: 1.5;
}
.sr-panel > .sr-panel-body { padding: 16px 18px; }

/* ── Negative finding. Red because it is a problem, worded because it is
      not readable as a colour alone. ───────────────────────────────────── */
.sr-alert {
  display: block; padding: 10px 14px; border-radius: 8px;
  font-size: 13px; margin-top: 8px;
  background: var(--no-bg); color: var(--no-fg);
  border: 1px solid var(--no-fg);
}
.sr-alert strong { font-weight: 700; }

/* ── Empty state ────────────────────────────────────────────────────── */
.sr-empty {
  padding: 18px; font-size: 13px; color: var(--muted); line-height: 1.5;
}

/* ── Delta figure. Sign is spelled out as well as coloured. ──────────── */
.sr-delta { font-weight: 700; white-space: nowrap; }
.sr-delta.up { color: var(--yes-fg); }
.sr-delta.down { color: var(--no-fg); }
.sr-delta.flat { color: var(--muted); }

/* Page bottom breathing room for row-language pages. */
.sr-page-end { height: 48px; }

/* ══════════════════════════════════════════════════════════════════════
   Catering. Appended, additive only. Nothing above this line is changed.

   Two audiences share one vocabulary. The venue's screens sit inside the
   platform chrome and reuse .page-head, .sr-section, .sr-panel and .btn-*.
   The booking agent's sheet is its own document with no sidebar, so the
   .cat-agent-* rules below are the only layout it has.

   No hex anywhere in this block. The six photo-fallback tones are existing
   palette tokens rather than six new colours, so they follow the theme
   without adding tokens nobody measured for contrast.
   ══════════════════════════════════════════════════════════════════════ */

.cat-lede { color: var(--muted); font-size: 13px; line-height: 1.6;
  max-width: 70ch; margin: 0 0 16px; }
.cat-meta { color: var(--muted); font-size: 12px; line-height: 1.5; }
.cat-strong { font-family: Archivo, system-ui, sans-serif; font-weight: 600;
  color: var(--text); text-decoration: none; }
a.cat-strong:hover { color: var(--teal-dark); text-decoration: underline; }
.cat-desc { font-size: 13px; line-height: 1.55; color: var(--text);
  margin: 6px 0; }
.cat-right { text-align: right; }
.cat-days { font-family: Archivo, system-ui, sans-serif; font-weight: 800;
  font-size: 20px; color: var(--text); }
.cat-warn-note { color: var(--warn); font-size: 12px; line-height: 1.5;
  margin-top: 4px; }
.cat-section { margin-top: 32px; }
.cat-spacer { height: 96px; }
.cat-voided { margin-top: 8px; }

/* ── Tables and panels ───────────────────────────────────────────────── */
.cat-panel { overflow-x: auto; }
.cat-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.cat-table th { text-align: left; font-size: 11px; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted);
  padding: 12px 16px; border-bottom: 1px solid var(--border); }
.cat-table td { padding: 14px 16px; border-bottom: 1px solid var(--border);
  vertical-align: top; }
.cat-table tr:last-child td { border-bottom: 0; }

/* ── Status tags. Words, never colour alone. ─────────────────────────── */
.cat-tag { display: inline-block; font-size: 11px; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.06em; padding: 3px 10px;
  border-radius: 999px; background: var(--neutral-bg); color: var(--neutral-fg); }
.cat-tag.cat-open, .cat-tag.cat-confirmed { background: var(--yes-bg); color: var(--yes-fg); }
.cat-tag.cat-locked { background: var(--warn-bg); color: var(--warn); }
.cat-tag.cat-cancelled { background: var(--no-bg); color: var(--no-fg); }

/* ── The countdown. The one thing on the page that has to be unmissable. ─ */
.cat-countdown { background: var(--surface); border: 1px solid var(--border);
  border-left: 3px solid var(--teal); border-radius: 12px;
  padding: 16px 20px; margin-bottom: 16px; }
.cat-countdown.is-locked { border-left-color: var(--warn-line); }
.cat-countdown-phrase { font-family: Archivo, system-ui, sans-serif;
  font-weight: 700; font-size: 16px; color: var(--text); margin-bottom: 4px; }

.cat-alert { background: var(--no-bg); color: var(--no-fg);
  border: 1px solid var(--no-fg); border-radius: 8px; padding: 12px 16px;
  font-size: 13px; line-height: 1.55; margin-bottom: 16px; }
.cat-alert .cat-meta { color: var(--no-fg); }
.cat-shotlist { margin: 8px 0 0 18px; padding: 0; font-size: 13px; }
.cat-shotlist li { margin-bottom: 4px; }

/* ── Forms ───────────────────────────────────────────────────────────── */
.cat-form { background: var(--surface); border: 1px solid var(--border);
  border-radius: 12px; padding: 16px 20px; margin-bottom: 16px; }
.cat-grid { display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  margin-bottom: 12px; }
.cat-field { display: block; }
.cat-field > span { display: block; font-size: 11px; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted);
  margin-bottom: 4px; }
.cat-form input, .cat-form select, .cat-form textarea,
.cat-guests input, .cat-inline input {
  width: 100%; box-sizing: border-box; font-family: inherit; font-size: 13px;
  padding: 8px 10px; border-radius: 8px; border: 1px solid var(--border);
  background: var(--bg); color: var(--text); }
.cat-form textarea { margin-bottom: 12px; resize: vertical; }
.cat-check { display: flex; gap: 8px; align-items: flex-start;
  font-size: 12px; color: var(--muted); line-height: 1.5; margin-bottom: 12px; }
.cat-check input { width: auto; margin-top: 2px; }
.cat-inline { display: inline-flex; gap: 6px; align-items: center; margin: 0; }
.cat-inline .cat-qty { width: 72px; }
.cat-two { display: grid; gap: 16px;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }

/* ── Change requests ─────────────────────────────────────────────────── */
.cat-change { display: flex; gap: 16px; flex-wrap: wrap;
  align-items: flex-start; justify-content: space-between;
  background: var(--surface); border: 1px solid var(--border);
  border-left: 3px solid var(--warn-line); border-radius: 12px;
  padding: 14px 18px; margin-bottom: 12px; }
.cat-change-body { min-width: 220px; flex: 1 1 320px; }
.cat-change-act { display: flex; gap: 8px; flex-wrap: wrap;
  align-items: center; margin: 0; }
.cat-change-act input { font-family: inherit; font-size: 13px; padding: 8px 10px;
  border-radius: 8px; border: 1px solid var(--border);
  background: var(--bg); color: var(--text); min-width: 200px; }

/* ── Totals block, venue side ────────────────────────────────────────── */
.cat-totals { background: var(--surface); border: 1px solid var(--border);
  border-radius: 12px; padding: 16px 20px; margin-top: 16px;
  max-width: 460px; }
.cat-total-row { display: flex; justify-content: space-between; gap: 16px;
  font-size: 13px; padding: 6px 0; color: var(--text); }
.cat-total-grand { border-top: 1px solid var(--border); margin-top: 6px;
  padding-top: 12px; font-family: Archivo, system-ui, sans-serif;
  font-weight: 800; font-size: 16px; }

/* ── Message thread ──────────────────────────────────────────────────── */
.cat-thread { background: var(--surface); border: 1px solid var(--border);
  border-radius: 12px; padding: 8px 0; margin-bottom: 12px; }
.cat-msg { padding: 12px 18px; border-bottom: 1px solid var(--border); }
.cat-msg:last-child { border-bottom: 0; }
.cat-msg-body { font-size: 13px; line-height: 1.6; white-space: pre-wrap;
  margin-top: 4px; color: var(--text); }
.cat-msg.is-internal { background: var(--warn-bg);
  border-left: 3px solid var(--warn-line); }
.cat-msg.is-agent { border-left: 3px solid var(--teal); }

/* ── Dish cards, shared by the menu admin and the agent's sheet ──────── */
.cat-cards { display: grid; gap: 16px;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
.cat-card { background: var(--surface); border: 1px solid var(--border);
  border-radius: 12px; overflow: hidden; display: flex; flex-direction: column; }
.cat-card.is-archived { opacity: 0.6; }
.cat-card-photo { aspect-ratio: 4 / 3; background: var(--bg-elevated); }
.cat-card-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cat-card-body { padding: 14px 16px; display: flex; flex-direction: column;
  gap: 6px; flex: 1; }
.cat-price { font-family: Archivo, system-ui, sans-serif; font-weight: 700;
  font-size: 16px; color: var(--text); }
.cat-line-total { margin-top: auto; padding-top: 8px;
  border-top: 1px solid var(--border); font-size: 12px; color: var(--muted); }
.cat-edit { margin-top: 8px; }
.cat-edit summary { font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--teal-dark); cursor: pointer; }
.cat-edit .cat-form { border: 0; padding: 12px 0 0; background: none; }

/* The typographic fallback for a dish nobody has photographed yet. A broken
   image frame reads as a broken site and a stock plate is a small lie printed
   next to a price, so it is initials on a tint keyed to the category. Six
   existing tokens, no new colours. */
.cat-fallback { width: 100%; height: 100%; display: flex; gap: 4px;
  flex-direction: column; align-items: center; justify-content: center;
  background: var(--bg-elevated); color: var(--muted); }
.cat-fallback span { font-family: Archivo, system-ui, sans-serif;
  font-weight: 800; font-size: 34px; letter-spacing: 0.04em; }
.cat-fallback em { font-style: normal; font-size: 11px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.06em; }
.cat-fallback.tone-clay { color: var(--high); }
.cat-fallback.tone-olive { color: var(--teal); }
.cat-fallback.tone-slate { color: var(--slate-dark); }
.cat-fallback.tone-plum { color: var(--deep-2); }
.cat-fallback.tone-sand { color: var(--med); }
.cat-fallback.tone-teal { color: var(--teal-dark); }

/* ── The booking agent's document ────────────────────────────────────── */
body.cat-agent { margin: 0; background: var(--bg); color: var(--text);
  font-family: Inter, system-ui, sans-serif; }
.cat-agent-wrap { max-width: 980px; margin: 0 auto; padding: 32px 24px 48px; }
.cat-guests { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 12px; padding: 14px 18px; margin-bottom: 16px; }
.cat-guests .cat-field { max-width: 200px; }
.cat-guests .cat-meta { flex: 1 1 100%; }

.cat-tabs { display: flex; gap: 8px; flex-wrap: wrap; margin: 20px 0 12px; }
.cat-tab { font-family: inherit; font-size: 12px; font-weight: 700;
  padding: 7px 14px; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--border); background: var(--surface);
  color: var(--muted); }
.cat-tab.is-on { border-color: var(--teal); color: var(--on-accent);
  background: var(--accent-fill); }

.cat-stepper { display: flex; gap: 6px; align-items: center; flex-wrap: wrap;
  margin: 4px 0 0; }
.cat-stepper .cat-qty { width: 68px; text-align: center; font-family: inherit;
  font-size: 13px; padding: 7px 6px; border-radius: 8px;
  border: 1px solid var(--border); background: var(--bg); color: var(--text); }
.cat-step { font-family: inherit; font-size: 16px; font-weight: 800;
  line-height: 1; width: 34px; height: 34px; cursor: pointer;
  border-radius: 8px; border: 1px solid var(--border);
  background: var(--surface); color: var(--text); }
.cat-step:hover { border-color: var(--teal); color: var(--deep); }

/* The running total. Fixed, always on screen, every component named. A cost
   revealed at the end is the biggest single cause of abandonment, and this
   number is a commitment rather than a checkout line. */
.cat-runningtotal { position: fixed; left: 0; right: 0; bottom: 0; z-index: 20;
  display: flex; gap: 20px; align-items: center; flex-wrap: wrap;
  justify-content: space-between;
  background: var(--surface); border-top: 2px solid var(--teal);
  box-shadow: var(--shadow); padding: 12px 24px; }
.cat-rt-parts { display: flex; gap: 18px; flex-wrap: wrap; font-size: 12px;
  color: var(--text); }
.cat-rt-parts em, .cat-rt-grand em { font-style: normal; display: block;
  font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--muted); }
.cat-rt-grand strong { font-family: Archivo, system-ui, sans-serif;
  font-weight: 800; font-size: 24px; color: var(--text); }
.cat-rt-note { position: fixed; left: 0; right: 0; bottom: 0; z-index: 21;
  background: var(--warn-bg); color: var(--warn); font-size: 12px;
  padding: 6px 24px; transform: translateY(-100%); }

@media (max-width: 640px) {
  .cat-agent-wrap { padding: 20px 14px 48px; }
  .cat-runningtotal { padding: 10px 14px; gap: 12px; }
  .cat-rt-parts { gap: 12px; font-size: 11px; }
}

/* ══════════════════════════════════════════════════════════════════════
   SMALL SCREENS
   ══════════════════════════════════════════════════════════════════════
   The layout above assumes a 240px sidebar column and a main area with room
   for wide tables. On a phone that produces a page you scroll sideways, which
   is the one thing a mobile layout must never do.

   Breakpoint is 900px, chosen to catch a tablet held upright (768 and 810 are
   the common widths) rather than only phones.

   Everything here is additive and scoped inside a media query. Nothing in the
   desktop layout changes, so a regression at full width is not possible from
   this block alone.
   ────────────────────────────────────────────────────────────────────── */

/* Phone chrome. Present in the markup on every page, shown only down here. */
.mobile-topbar { display: none; }
.nav-backdrop { display: none; }

@media (max-width: 900px) {

  /* ── Shell ─────────────────────────────────────────────────────────── */
  .app { grid-template-columns: 1fr; }

  /* The one rule that fixes most of this.
     A grid item defaults to min-width:auto, meaning it refuses to become
     narrower than its own content's minimum. So a single wide table inside
     .main pushed the whole 1fr column out to 896px on a 390px screen, and
     every block element inside it stretched to match. The page then scrolled
     sideways with nothing visibly at fault -- headings, paragraphs and cards
     all measured 868px wide and none of them had asked to.
     min-width:0 lets the column shrink and hands the overflow back to the
     element that actually caused it, where the table rules below can catch
     it. */
  .app > * { min-width: 0; }

  .mobile-topbar {
    display: flex; align-items: center; gap: 10px;
    position: fixed; top: 0; left: 0; right: 0; z-index: 60;
    height: 56px; padding: 0 12px;
    background: var(--surface); border-bottom: 1px solid var(--border);
  }
  .mobile-brand { display: flex; align-items: center; min-width: 0; }
  .mobile-brand img { max-height: 26px; width: auto; display: block;
    object-fit: contain; }
  .mobile-brand .logo-night { display: none; }
  [data-theme="dark"] .mobile-brand .logo-day { display: none; }
  [data-theme="dark"] .mobile-brand .logo-night { display: block; }

  /* 44px is the smallest target most people can hit reliably with a thumb.
     The bars are 2px so they stay crisp; the box around them is what gets
     tapped. */
  .navbtn {
    width: 44px; height: 44px; flex: 0 0 44px;
    display: flex; align-items: center; justify-content: center;
    background: transparent; border: 1px solid var(--border);
    border-radius: 8px; cursor: pointer; padding: 0;
  }
  .navbtn:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }
  .navbtn-bars { display: block; width: 18px; }
  .navbtn-bars i {
    display: block; height: 2px; background: var(--text); border-radius: 2px;
    transition: transform 0.18s, opacity 0.18s;
  }
  .navbtn-bars i + i { margin-top: 4px; }
  /* Turns into a cross while open, so the same control visibly closes it. */
  .nav-open .navbtn-bars i:nth-child(1) { transform: translateY(6px) rotate(45deg); }
  .nav-open .navbtn-bars i:nth-child(2) { opacity: 0; }
  .nav-open .navbtn-bars i:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

  /* ── The drawer ────────────────────────────────────────────────────── */
  .sidebar {
    position: fixed; top: 0; left: 0; bottom: 0; height: 100dvh;
    width: 280px; max-width: 84vw; z-index: 70;
    border-right: 1px solid var(--border);
    transform: translateX(-100%);
    transition: transform 0.22s ease;
    box-shadow: none;
    padding-top: 16px;
  }
  .nav-open .sidebar { transform: translateX(0); box-shadow: var(--shadow); }

  .nav-backdrop {
    display: block; position: fixed; inset: 0; z-index: 65;
    background: rgba(0, 0, 0, 0.45);
    opacity: 0; pointer-events: none; transition: opacity 0.22s ease;
  }
  .nav-open .nav-backdrop { opacity: 1; pointer-events: auto; }
  .nav-open body { overflow: hidden; }

  /* Nav rows get taller: 13px text in a 32px row is a hard target to hit
     while walking. */
  .sidebar .nav-item { padding: 11px 12px; font-size: 14px; }

  /* ── Main ──────────────────────────────────────────────────────────── */
  /* 56px topbar plus breathing room. */
  .main { padding: 72px 14px 64px; }

  /* A heading and its buttons side by side leaves about 90px for each on a
     phone. Stacked, both get the full width. */
  .page-head, .page-header {
    flex-direction: column; align-items: stretch; gap: 10px;
  }
  .page-head h1, .page-header h1 { font-size: 21px; }
  .page-head .actions { flex-wrap: wrap; }
  .page-head .actions .btn { flex: 1 1 auto; }

  /* Any grid still asking for more than one column gives up here. */
  .two-col, .field-row, .sr-facts-2 { grid-template-columns: 1fr; }

  /* A fact row is label / value / action across three columns, with the
     action set white-space:nowrap so its buttons never break mid-word. At
     390px that nowrap is what pushed the client page off the screen. Stacked,
     the action sits under the value and gets the full width. */
  .sr-fact { grid-template-columns: 1fr; }
  .sr-fact > * { min-width: 0; }
  .sr-fact-note, .sr-fact-action { grid-column: 1; }
  .sr-fact-action {
    grid-row: auto; justify-self: start; white-space: normal;
    flex-wrap: wrap; margin-top: 6px;
  }

  /* Form rows are laid out horizontally on desktop. Squashing four fields
     into 350px makes every one of them unusable, so they stack and each
     takes the full width. */
  .row-form, .form-row {
    flex-direction: column; align-items: stretch; flex-wrap: wrap;
  }
  .row-form .field, .form-row .field { width: 100%; min-width: 0; }
  .field-narrow input, .field-state input, .field-narrow, .field-state {
    max-width: none;
  }

  /* 16px is not a style choice. Safari zooms the whole page when a focused
     input has text smaller than this, and the zoom does not undo itself. */
  .field input, .field select, .field textarea,
  input[type="text"], input[type="email"], input[type="password"],
  input[type="number"], input[type="date"], input[type="search"],
  input[type="url"], select, textarea { font-size: 16px; }

  .btn { min-height: 40px; }

  /* A 13px checkbox is a target you miss with a thumb and hit with a
     fingernail. The default box does not scale with font-size, so it has to be
     set. Scoped to the phone layer: the desktop grids place these boxes over
     artwork at 13px on purpose and are untouched by this. */
  input[type="checkbox"], input[type="radio"] { width: 20px; height: 20px; }

  /* Horizontal strips of tabs, filters and chips. Each item is nowrap by
     design so a label never breaks mid-word, which on a phone means the strip
     is wider than the screen and takes the page with it. Wrapping onto a
     second line keeps every item readable and tappable; scrolling the strip
     sideways would hide filters with nothing to say they exist. */
  .hub-tabs, .tabs, .tab-bar, .filter-bar, .filters, .chips, .toolbar,
  .row-actions, .seg, .segmented {
    flex-wrap: wrap; overflow-x: visible; min-width: 0;
  }

  /* Preformatted blocks -- schema snippets, meta previews, API payloads --
     have no spaces to break on. They scroll inside their own box rather than
     stretching the page. */
  pre, code, .snippet, .codeblock { overflow-x: auto; max-width: 100%; }

  img, svg, video, canvas, iframe { max-width: 100%; height: auto; }

  /* Long values with no spaces -- domains, slugs, filenames, tokens, URLs --
     are the usual cause of a page that scrolls sideways on a phone.
     `anywhere` rather than `break-word` on purpose. They look identical, but
     only `anywhere` reduces the element's min-content width, and min-content
     is exactly what a grid or flex parent sizes itself from. With
     `break-word` a filename still reported itself as 539px wide and dragged
     its column out with it; the text wrapped and the layout did not. */
  body { overflow-wrap: anywhere; }
  .card, .card-block, .sr-panel { padding: 14px; }

  /* A fixed bar at the bottom-left and a drawer opening from the left are
     the same corner. Keep the toggle under the drawer. */
  .theme-toggle { bottom: 12px; left: 12px; z-index: 55; }
}

/* ══════════════════════════════════════════════════════════════════════
   TABLES INTO CARDS
   ══════════════════════════════════════════════════════════════════════
   A ten-column table cannot be made to fit 390px. Sideways scrolling hides
   columns off-screen with nothing to say they are there; hiding columns
   outright loses data. So each row becomes a small card and each cell gets
   its column heading in front of it.

   The heading text comes from data-label, which responsive-tables.js copies
   off each table's own thead at load. That is why this works on all 111
   pages without touching a single table by hand.

   Only tables the script has vouched for are converted. It marks those
   data-stack="on": a table whose cells do not line up with its headers is
   left alone rather than restacked into mislabelled nonsense.
   ────────────────────────────────────────────────────────────────────── */
@media (max-width: 900px) {
  table[data-stack="on"], table[data-stack="on"] tbody,
  table[data-stack="on"] tr, table[data-stack="on"] td { display: block; width: auto; }

  table[data-stack="on"] thead { display: none; }

  table[data-stack="on"] tr {
    border: 1px solid var(--border); border-radius: 10px;
    background: var(--surface); padding: 4px 12px; margin-bottom: 10px;
  }
  table[data-stack="on"] td {
    border: none; padding: 7px 0; text-align: left;
    display: grid; grid-template-columns: 40% 1fr; gap: 10px;
    align-items: baseline;
  }
  table[data-stack="on"] td + td { border-top: 1px solid var(--border); }

  table[data-stack="on"] td::before {
    content: attr(data-label);
    font-family: "JetBrains Mono", ui-monospace, monospace;
    font-size: 10px; font-weight: 700; text-transform: uppercase;
    letter-spacing: 0.08em; color: var(--muted);
    overflow-wrap: anywhere;
  }
  /* A cell whose heading was blank -- the actions column, usually -- would
     otherwise reserve 40% for an empty label. */
  table[data-stack="on"] td[data-label=""] { display: block; }
  table[data-stack="on"] td[data-label=""]::before { content: none; }

  table[data-stack="on"] td form { display: inline-block; }

  /* Anything the script declined stays a table, so it needs somewhere to go.
     A wrapper that scrolls is the honest fallback. */
  table[data-stack="off"] { display: block; overflow-x: auto; }
}

/* ── Carousel strip ──────────────────────────────────────────────────────
   A post with more than one piece of artwork. See ui/templates/_carousel.html
   for the markup and ui/static/carousel.js for the arrows.

   The track is a native scroll-snap row, one piece per slide, so swiping
   works on a phone with no script. The box has a fixed size chosen by the
   screen it sits on (--sr-car-w, --sr-car-h) and every piece is contained
   inside it, never cropped: a reviewer approving a set has to see all of each
   picture, whatever Instagram's crop later does to it.

   The arrows and the counter use --surface on --text, the pair this codebase
   already holds to 4.5:1 in both themes (see .rv-drop-in in review.html),
   because they sit over artwork of any colour. */
.sr-car{position:relative;width:var(--sr-car-w,100%);max-width:100%;
  background:#111;overflow:hidden}
.sr-car-track{display:flex;overflow-x:auto;scroll-snap-type:x mandatory;
  scroll-behavior:smooth;scrollbar-width:none;-webkit-overflow-scrolling:touch}
.sr-car-track::-webkit-scrollbar{display:none}
.sr-car-track:focus-visible{outline:2px solid var(--teal,#1F8A70);outline-offset:-2px}
.sr-car-slide{flex:0 0 100%;scroll-snap-align:start;display:flex;
  align-items:center;justify-content:center;height:var(--sr-car-h,auto)}
.sr-car-slide > a{display:flex;align-items:center;justify-content:center;
  width:100%;height:100%}
.sr-car-slide img,.sr-car-slide video{display:block;max-width:100%;
  max-height:var(--sr-car-h,none);width:auto;height:auto;object-fit:contain}
.sr-car-go{position:absolute;top:50%;transform:translateY(-50%);z-index:3;
  width:32px;height:32px;border-radius:50%;padding:0;cursor:pointer;
  font-size:20px;line-height:30px;text-align:center;
  background:var(--surface,#fff);color:var(--text,#222);
  border:1px solid var(--border,#e5e7eb)}
.sr-car-prev{left:6px}
.sr-car-next{right:6px}
.sr-car-go[disabled]{opacity:0;pointer-events:none}
.sr-car-count{position:absolute;right:8px;bottom:8px;z-index:3;line-height:1.4;
  font-size:12px;font-weight:650;padding:2px 8px;border-radius:999px;
  background:var(--surface,#fff);color:var(--text,#222);
  border:1px solid var(--border,#e5e7eb)}
/* No hover on a phone, so the arrows would be the only way to learn the strip
   moves. The counter says it, and a swipe does it. */
@media (hover:none){ .sr-car-go{display:none} }
