/*
 * v0.48.6 — Public booking page styles.
 *
 * Tenant-agnostic. Brand color is set as a CSS variable on the body
 * from the studio's config (config.brand_color), so this stylesheet
 * has no hardcoded colors per studio. Same goes for fonts — the page
 * uses system fonts so there's nothing to load and the studio's
 * branding comes through the inline logo + brand color.
 */

:root {
  --brand: #B88E47;            /* overridden inline by app.jsx from config */
  --brand-tint: #B88E471a;
  --brand-strong: #b07832;
  --ink-900: #1a1a1a;
  --ink-700: #444;
  --ink-500: #888;
  --ink-400: #aaa;
  --ink-200: #ddd;
  --ink-100: #eee;
  --bg: #fafafa;
  --surface: #fff;
  --cream-50: #fdfaf3;
  /* Availability palette.
     Green means bookable, red means taken — the only two states a
     visitor is deciding between. Both are the modern desaturated
     versions rather than pure #0f0/#f00: on a warm cream page a
     saturated green reads as a system alert, and a saturated red as an
     error. These sit in the same tonal family as the studio's gold. */
  --available: #0E9F6E;
  --available-tint: #E9F8F1;
  --available-line: #93DCC0;
  --available-text: #076648;

  --booked: #E5484D;
  /*
   * The booked fill, and it has to be seen.
   *
   * This was #FDEDED — a pink two per cent off white. Against a grid
   * of pale green it read as an empty cell with a slightly warm cast,
   * so the one slot a visitor must not try to book was the least
   * visible thing on the calendar. Somebody scanning for a time saw
   * nothing wrong until they clicked it.
   *
   * Now a fill with real saturation, a solid border and dark text on
   * it. The strike-through stays, because the commonest colour
   * blindness is red-green and this grid runs on exactly that pair —
   * colour is the fast signal, not the only one.
   */
  --booked-tint: #F7B4B6;
  --booked-line: #C7282D;
  --booked-text: #6B0F13;

  /* Deliberately neutral, not red. A slot that has simply gone by is
     not a refusal — five past weekdays rendered in red would read as
     an error state across most of the calendar. */
  --past: #F5F4F1;
  --past-text: #B6B2AA;
  --warning: #d4922f;
  --radius: 4px;
  --shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--ink-900);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

#root { min-height: 100vh; }

/* ------- Loading state -------------------------------------------------- */
.loading {
  min-height: 60vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  color: var(--ink-500);
}
.loading-spinner {
  width: 28px; height: 28px;
  border: 2px solid var(--ink-200);
  border-top-color: var(--brand);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
  margin-bottom: 14px;
}
@keyframes spin { to { transform: rotate(360deg); } }
.loading-text { font-size: 13px; }

/* ------- Page shell ----------------------------------------------------- */
.page {
  max-width: 880px;
  margin: 0 auto;
  padding: 40px 24px 80px;
}

.studio-header {
  text-align: center;
  margin-bottom: 32px;
}
.studio-header img.logo {
  max-width: 240px;
  max-height: 100px;
  object-fit: contain;
  margin: 0 auto 16px;
  display: block;
}
.studio-header h1 {
  font-size: 28px;
  font-weight: 500;
  margin: 0 0 6px;
  letter-spacing: -0.01em;
}
.studio-header .tagline {
  color: var(--ink-500);
  font-size: 14px;
  margin: 0;
}

.headline-card {
  background: var(--surface);
  border: 1px solid var(--ink-100);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 28px 32px;
  margin-bottom: 24px;
}
.headline-card h2 {
  font-size: 20px;
  font-weight: 500;
  margin: 0 0 16px;
}
.headline-card .description {
  white-space: pre-wrap;
  color: var(--ink-700);
  font-size: 14px;
  line-height: 1.65;
}

/* ------- Week grid ------------------------------------------------------ */
.week-grid {
  background: var(--surface);
  border: 1px solid var(--ink-100);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 24px;
  overflow-x: auto;
}
.week-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 20px;
}
.week-nav button {
  background: var(--surface);
  border: 1px solid var(--ink-200);
  border-radius: var(--radius);
  padding: 8px 14px;
  font-size: 13px;
  cursor: pointer;
  color: var(--ink-700);
  transition: background 0.15s;
}
.week-nav button:hover:not(:disabled) {
  background: var(--cream-50);
}
.week-nav button:disabled {
  color: var(--ink-400);
  cursor: not-allowed;
}
.week-nav .week-range {
  font-weight: 500;
  color: var(--ink-700);
  font-size: 14px;
}

.grid-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 4px;
}
.grid-table th {
  font-weight: 500;
  color: var(--ink-700);
  font-size: 12px;
  padding: 8px 4px;
  text-align: center;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.grid-table th .day-num {
  display: block;
  font-size: 16px;
  color: var(--ink-900);
  margin-top: 2px;
}
.grid-table th.is-today .day-num {
  color: var(--brand);
  font-weight: 600;
}
.slot-cell {
  width: 14.28%;
  padding: 0;
}
.slot-btn {
  width: 100%;
  padding: 10px 4px;
  border: 1px solid var(--ink-200);
  border-radius: var(--radius);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  font-family: inherit;
  transition: transform 0.1s, box-shadow 0.15s;
}
.slot-btn.available {
  /* Tinted rather than filled. A whole column of solid green shouts;
     a soft field with a green edge still reads unmistakably as "these
     are the ones you can have", and leaves the filled state free to
     mean "you are about to pick this". */
  background: var(--available-tint);
  color: var(--available-text);
  border-color: var(--available-line);
  font-weight: 600;
}
.slot-btn.available:hover,
.slot-btn.available:focus-visible {
  background: var(--available);
  border-color: var(--available);
  color: #fff;
  transform: translateY(-1px);
  box-shadow: 0 4px 10px rgba(14, 159, 110, 0.25);
}
.slot-btn.booked {
  background: var(--booked-tint);
  color: var(--booked-text);
  /* A full-weight border, not a hairline. At one pixel in a pale pink
     the outline disappeared into the grid's own gaps. */
  border-color: var(--booked-line);
  border-width: 2px;
  font-weight: 600;
  cursor: not-allowed;
  /* Not colour alone: the commonest kind of colour blindness is
     red-green, which is precisely the pair this grid runs on. */
  text-decoration: line-through;
  text-decoration-thickness: 2px;
  text-decoration-color: var(--booked-text);
}
.slot-btn.past, .slot-btn.disabled {
  background: var(--past);
  color: var(--past-text);
  border-color: var(--past);
  cursor: not-allowed;
}
.slot-btn.unavailable {
  background: var(--cream-50);
  color: var(--ink-400);
  border-color: var(--ink-100);
  cursor: not-allowed;
}

/* ------- Modal (booking form) ------------------------------------------- */
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(20, 20, 20, 0.55);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 40px 16px;
  z-index: 100;
  overflow-y: auto;
}
.modal {
  background: var(--surface);
  border-radius: 8px;
  width: 100%;
  max-width: 480px;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
  overflow: hidden;
}
.modal-header {
  padding: 20px 24px;
  border-bottom: 1px solid var(--ink-100);
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.modal-header h3 {
  margin: 0;
  font-size: 17px;
  font-weight: 500;
}
.modal-header .close {
  background: transparent;
  border: 0;
  font-size: 22px;
  cursor: pointer;
  color: var(--ink-500);
  padding: 0 6px;
  line-height: 1;
}
.modal-body {
  padding: 24px;
}
.field {
  margin-bottom: 18px;
}
.field label {
  display: block;
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-700);
  margin-bottom: 6px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.field input,
.field textarea,
.field select {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--ink-200);
  border-radius: var(--radius);
  font-size: 14px;
  font-family: inherit;
  background: var(--surface);
}
.field textarea { min-height: 80px; resize: vertical; }
.field input:focus,
.field textarea:focus,
.field select:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 2px var(--brand-tint);
}
.field-hint {
  font-size: 11px;
  color: var(--ink-500);
  margin-top: 4px;
}
.field-required-mark {
  color: var(--brand);
  margin-left: 2px;
}
.modal-footer {
  padding: 16px 24px;
  border-top: 1px solid var(--ink-100);
  display: flex;
  gap: 10px;
  justify-content: flex-end;
  background: var(--cream-50);
}
.modal-footer button {
  padding: 10px 20px;
  border-radius: var(--radius);
  font-size: 13px;
  cursor: pointer;
  font-family: inherit;
  font-weight: 500;
  border: 1px solid var(--ink-200);
  background: var(--surface);
  color: var(--ink-700);
  transition: background 0.15s;
}
.modal-footer button.primary {
  background: var(--brand);
  border-color: var(--brand);
  color: #fff;
}
.modal-footer button.primary:hover:not(:disabled) {
  background: var(--brand-strong);
  border-color: var(--brand-strong);
}
.modal-footer button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.error-box {
  background: #fdecec;
  border: 1px solid #f5b8b8;
  color: #a02020;
  padding: 12px;
  border-radius: var(--radius);
  font-size: 13px;
  margin-bottom: 16px;
}

/* ------- Footer --------------------------------------------------------- */
.studio-footer {
  margin-top: 40px;
  padding-top: 24px;
  border-top: 1px solid var(--ink-100);
  text-align: center;
  color: var(--ink-500);
  font-size: 12px;
  line-height: 1.7;
}
.studio-footer a { color: var(--brand); text-decoration: none; }
.studio-footer a:hover { text-decoration: underline; }

/* ------- 404 / disabled ------------------------------------------------- */
.notfound {
  min-height: 60vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 24px;
}
.notfound h1 {
  font-size: 24px;
  font-weight: 500;
  margin: 0 0 8px;
  color: var(--ink-900);
}
.notfound p { color: var(--ink-500); max-width: 420px; }

/* ------- Mobile --------------------------------------------------------- */
@media (max-width: 700px) {
  .page { padding: 20px 12px 60px; }
  .headline-card { padding: 20px; }
  .week-grid { padding: 16px 12px; }
  .grid-table th { font-size: 10px; }
  .grid-table th .day-num { font-size: 14px; }
  .slot-btn { font-size: 11px; padding: 8px 2px; }
}

/* =========================================================================
 * v0.48.19 — Apple-minimal themes
 * =========================================================================
 *
 * The default styles above are a hold-over from v0.48.6 — strong green/red
 * available + booked colors. The new themes below override those via
 * `body[data-theme=...]` so the studio can switch their public page's
 * mood without code changes.
 *
 * Shared shape across all themes:
 *   - Larger border-radius (10px) on slots and the page card
 *   - Restrained color: open cells are pale, today is a small accent dot
 *     under the date number, booked cells use a muted fill
 *   - Inter / -apple-system body type, light weights
 *
 * Adding a new theme:
 *   1. Pick a key like `body[data-theme="my-theme"]`
 *   2. Override the variables below — `--surface`, `--bg`, `--available`,
 *      `--booked`, `--past`, `--brand` (today accent), text shades.
 *   3. Drop a thumbnail SVG into the picker in the admin app.
 */

body[data-theme] .slot-btn,
body[data-theme] .week-nav button,
body[data-theme] .field input,
body[data-theme] .field textarea,
body[data-theme] .field select,
body[data-theme] .modal-footer button,
body[data-theme] .headline-card,
body[data-theme] .week-grid,
body[data-theme] .modal {
  border-radius: 10px;
}

body[data-theme] .grid-table {
  border-spacing: 6px;
}

body[data-theme] .slot-btn {
  font-weight: 400;
  letter-spacing: 0.01em;
  border-width: 0.5px;
  padding: 12px 4px;
}

body[data-theme] .slot-btn.available:hover {
  transform: none;
  box-shadow: none;
  filter: brightness(0.97);
}

body[data-theme] .grid-table th.is-today .day-num {
  font-weight: 500;
  position: relative;
}

body[data-theme] .grid-table th.is-today .day-num::after {
  content: '';
  display: block;
  width: 4px; height: 4px;
  border-radius: 50%;
  background: var(--brand);
  position: absolute;
  left: 50%;
  bottom: -8px;
  transform: translateX(-50%);
}

/* ------- 1. Editorial (default — TL Portraitz brand) ------------------- */
body[data-theme="editorial"] {
  --bg: #FFFFFF;
  --surface: #FFFFFF;
  --cream-50: #FAFAFA;
  --ink-100: #ECE7DA;
  --ink-200: #E5DFCE;
  --available: #FFFFFF;
  --booked: #1A1A1A;
  --past: #FAFAFA;
  --past-text: #C9C4B8;
  --brand: #B88E47;
  --brand-tint: #B88E471a;
  --brand-strong: #9c773a;
}
/* The editorial theme used to repaint available slots cream with a gold
   edge, which is why the live page showed no green at all — the
   availability colours were being defined and then overridden. It now
   keeps them and only tunes what is around them. */
body[data-theme="editorial"] .slot-btn.unavailable {
  background: #FAFAFA;
  border-color: #F0EBDF;
  color: #C9C4B8;
}

/* ------- 2. Christmas Quiet ------------------------------------------- */
body[data-theme="christmas-quiet"] {
  --bg: #FBFAF7;
  --surface: #FFFFFF;
  --cream-50: #F7F4ED;
  --ink-100: #EAE6DD;
  --ink-200: #DCD4C2;
  --available: #EFF3EC;
  --booked: #F4ECEA;
  --past: #FAFAFA;
  --past-text: #B8B2A4;
  --brand: #C9402F;
  --brand-tint: #C9402F1a;
  --brand-strong: #a8341f;
}
body[data-theme="christmas-quiet"] .slot-btn.available { color: #2C5240; border-color: transparent; }
body[data-theme="christmas-quiet"] .slot-btn.booked { color: #7A2A2A; border-color: transparent; }

/* ------- 3. Christmas Evergreen ---------------------------------------- */
body[data-theme="christmas-evergreen"] {
  --bg: #F7F2E8;
  --surface: #FBF7EE;
  --cream-50: #EFE7D2;
  --ink-100: #E0D7C2;
  --ink-200: #C9BCA0;
  --ink-700: #1F3326;
  --ink-900: #1F3326;
  --ink-500: #7A8B6E;
  --available: #D8E4CC;
  --booked: #E8C8C8;
  --past: #EFE7D2;
  --past-text: #A89D85;
  --brand: #9C2F2F;
  --brand-tint: #9C2F2F1a;
  --brand-strong: #7A2424;
}
body[data-theme="christmas-evergreen"] .slot-btn.available { color: #2C5240; border-color: transparent; }
body[data-theme="christmas-evergreen"] .slot-btn.booked { color: #7A2A2A; border-color: transparent; }

/* ------- 4. Christmas Velvet (dark) ------------------------------------ */
body[data-theme="christmas-velvet"] {
  --bg: #0E2A1E;
  --surface: #143424;
  --cream-50: #0A1F16;
  --ink-100: #1A3A28;
  --ink-200: #2A5440;
  --ink-400: #6E8B7A;
  --ink-500: #8FA89A;
  --ink-700: #C8DDD0;
  --ink-900: #F5EBD8;
  --available: #1B4030;
  --booked: #5C2030;
  --past: #0A1F16;
  --past-text: #527060;
  --brand: #C8A047;
  --brand-tint: #C8A04733;
  --brand-strong: #b08a36;
}
body[data-theme="christmas-velvet"] .slot-btn.available { color: #C8DDD0; border-color: transparent; }
body[data-theme="christmas-velvet"] .slot-btn.booked { color: #E8C8C8; border-color: transparent; }
body[data-theme="christmas-velvet"] .slot-btn.unavailable { background: #0A1F16; color: #527060; border-color: #1A3A28; }
body[data-theme="christmas-velvet"] .modal-backdrop { background: rgba(0, 10, 6, 0.7); }
body[data-theme="christmas-velvet"] .field input,
body[data-theme="christmas-velvet"] .field textarea,
body[data-theme="christmas-velvet"] .field select {
  background: #0A1F16;
  color: #F5EBD8;
  border-color: #2A5440;
}

/* ------- 5. Spring Soft ------------------------------------------------ */
body[data-theme="spring-soft"] {
  --bg: #FFFFFF;
  --surface: #FFFFFF;
  --cream-50: #FAFAFA;
  --ink-100: #F1ECEE;
  --ink-200: #E5DCE0;
  --ink-500: #B8A8B0;
  --available: #FBF5F7;
  --booked: #F2E8EC;
  --past: #FAFAFA;
  --past-text: #C8BCC2;
  --brand: #D88A9C;
  --brand-tint: #D88A9C1a;
  --brand-strong: #b47284;
}
body[data-theme="spring-soft"] .slot-btn.available { color: #6C4252; border-color: transparent; }
body[data-theme="spring-soft"] .slot-btn.booked { color: #8B4F60; border-color: transparent; }

/* ------- 6. Sunset Warm ----------------------------------------------- */
body[data-theme="sunset-warm"] {
  --bg: #FDFAF7;
  --surface: #FFFFFF;
  --cream-50: #FAF1ED;
  --ink-100: #F2EDE6;
  --ink-200: #E5DACF;
  --ink-500: #C49880;
  --available: #FBF1EA;
  --booked: #F0DDD2;
  --past: #FAF6F1;
  --past-text: #C8B5A8;
  --brand: #D87856;
  --brand-tint: #D878561a;
  --brand-strong: #b35e3e;
}
body[data-theme="sunset-warm"] .slot-btn.available { color: #7A2E1F; border-color: transparent; }
body[data-theme="sunset-warm"] .slot-btn.booked { color: #8B3A24; border-color: transparent; }

/* ------- 7. Classic ---------------------------------------------------- */
/* v0.49.22 — Refined palette. The original neon green / fire-engine red
 * felt 2010-era spreadsheet. Swapped for sage emerald + warm terracotta
 * + steel blue accent — same green-means-go / red-means-taken cognitive
 * shortcut, but reads as a 2026-era booking page (Calendly, Cal.com,
 * Doodle aesthetic) instead of a Calengoo screenshot. */
body[data-theme="classic"] {
  --bg: #FFFFFF;
  --surface: #FFFFFF;
  --cream-50: #F5F5F5;
  --ink-100: #E8E8E8;
  --ink-200: #D8D8D8;
  --ink-500: #6F6F6F;
  --available: #5CAB7D;       /* sage emerald — was #2DBA4E neon */
  --available-tint: #5CAB7D1a;
  --booked: #D67B6E;          /* warm terracotta — was #E04848 fire engine */
  --booked-tint: #D67B6E1a;
  --past: #EFEFEF;
  --past-text: #B0B0B0;
  --brand: #5B8DBF;           /* steel blue — was #1E88E5 saturated material */
  --brand-tint: #5B8DBF1a;
  --brand-strong: #3F6F9C;
}
body[data-theme="classic"] .slot-btn { font-weight: 500; padding: 14px 4px; }
body[data-theme="classic"] .slot-btn.available { color: #FFFFFF; border-color: #5CAB7D; }
body[data-theme="classic"] .slot-btn.booked { color: #FFFFFF; border-color: #D67B6E; }
body[data-theme="classic"] .slot-btn.unavailable { background: #F4DEDB; color: #A8786E; border-color: transparent; }
body[data-theme="classic"] .grid-table th.is-today { color: #5B8DBF; }
body[data-theme="classic"] .grid-table th.is-today .day-num { color: #5B8DBF; font-weight: 500; }

/* ------- 8. Sanrio Pink (Hello Kitty inspired) ------------------------ */
body[data-theme="sanrio-pink"] {
  --bg: #FFF6F8;
  --surface: #FFFFFF;
  --cream-50: #FFE8EE;
  --ink-100: #FAD7DF;
  --ink-200: #F2BCC8;
  --ink-500: #C68497;
  --ink-700: #8E5168;
  --ink-900: #5A2C3E;
  --available: #FFD6E0;
  --booked: #FFB3C5;
  --past: #FFEDF1;
  --past-text: #D7B0BC;
  --brand: #FF6B8A;
  --brand-tint: #FF6B8A22;
  --brand-strong: #E04E70;
}
body[data-theme="sanrio-pink"] .slot-btn { padding: 14px 4px; }
body[data-theme="sanrio-pink"] .slot-btn.available { color: #8E5168; border-color: transparent; font-weight: 500; }
body[data-theme="sanrio-pink"] .slot-btn.booked { color: #5A2C3E; border-color: transparent; font-weight: 500; }
body[data-theme="sanrio-pink"] .studio-header h1 { color: #5A2C3E; }
body[data-theme="sanrio-pink"] .grid-table th.is-today .day-num::after {
  content: '♥';
  background: transparent;
  color: var(--brand);
  width: auto; height: auto;
  font-size: 11px;
  bottom: -14px;
}

/* ------- 9. Holiday Soft (warm Christmas, softer than Velvet) --------- */
body[data-theme="holiday-soft"] {
  --bg: #FFFAF3;
  --surface: #FFFFFF;
  --cream-50: #FFF4E8;
  --ink-100: #F2E5D2;
  --ink-200: #DEC9A8;
  --ink-500: #A08B6E;
  --ink-700: #6E523A;
  --ink-900: #3F2A1C;
  --available: #C8E0BE;
  --booked: #F5C5BE;
  --past: #FBF3E5;
  --past-text: #B8A98F;
  --brand: #C8806A;
  --brand-tint: #C8806A1a;
  --brand-strong: #A8624C;
}
body[data-theme="holiday-soft"] .slot-btn.available { color: #2F5230; border-color: transparent; font-weight: 500; }
body[data-theme="holiday-soft"] .slot-btn.booked { color: #7A3030; border-color: transparent; font-weight: 500; }

/* =========================================================================
   v0.49.0 — Client + Vendor portal scaffold styles.
   Used by /clients/ and /vendor/ — minimal centered-card layout for the
   sign-in flow and placeholder landing page. Real portal UI lands later.
   ========================================================================= */

.centered {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 32px 20px;
  background: var(--bg, #FAFAF7);
}
.card.narrow {
  max-width: 480px;
  width: 100%;
  background: var(--surface, #FFFFFF);
  padding: 40px 32px;
  border: 1px solid var(--ink-100, #ECEAE3);
  border-radius: 12px;
  text-align: center;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}
.card.narrow .title {
  font-family: 'Playfair Display', serif;
  font-size: 22px;
  font-weight: 500;
  letter-spacing: 0.04em;
  margin: 0;
  color: var(--ink-900, #2A2A2A);
}
.card.narrow .subtitle {
  font-size: 13px;
  color: var(--ink-500, #888880);
  margin-top: 8px;
  font-weight: 300;
  line-height: 1.5;
}
.card.narrow .body {
  font-size: 13px;
  color: var(--ink-700, #5A564D);
  line-height: 1.6;
  font-weight: 300;
  text-align: left;
  margin: 16px 0;
}
.card.narrow .fine {
  font-size: 11px;
  color: var(--ink-500, #888880);
  margin-top: 16px;
  font-weight: 300;
}
.card.narrow .err {
  font-size: 12px;
  color: var(--danger, #B8423B);
  margin-top: 12px;
  font-weight: 400;
}
.card.narrow .err-detail {
  font-family: 'SFMono-Regular', Menlo, monospace;
  font-size: 11px;
  background: rgba(184, 66, 59, 0.06);
  color: var(--ink-700, #5A564D);
  padding: 10px 12px;
  border-radius: 6px;
  white-space: pre-wrap;
  text-align: left;
  margin: 12px 0;
}
.card.narrow .btn-primary,
.card.narrow .btn-secondary {
  font-family: 'Montserrat', sans-serif;
  font-size: 12px;
  letter-spacing: 0.1em;
  padding: 12px 28px;
  border-radius: 6px;
  cursor: pointer;
  transition: all 0.2s ease;
  font-weight: 500;
}
.card.narrow .btn-primary {
  background: var(--brand, #C9A66B);
  color: #FFFFFF;
  border: 1px solid var(--brand, #C9A66B);
}
.card.narrow .btn-primary:hover { filter: brightness(0.95); }
.card.narrow .btn-primary:disabled { opacity: 0.5; cursor: wait; }
.card.narrow .btn-secondary {
  background: transparent;
  color: var(--ink-700, #5A564D);
  border: 1px solid var(--ink-200, #D8D3C5);
}
.card.narrow .btn-secondary:hover { background: var(--cream-50, #FAF7EE); }

/* =========================================================================
   v0.49.1 — Client portal real layout. Replaces the centered single-card
   shell with a full-page list view. Sign-in screens still use the Centered
   layout above.
   ========================================================================= */

.portal {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--bg, #FAFAF7);
}
.portal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 24px 32px;
  border-bottom: 1px solid var(--ink-100, #ECEAE3);
  background: var(--surface, #FFFFFF);
}
.portal-header .brand {
  font-family: 'Playfair Display', serif;
  font-size: 18px;
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--ink-900, #2A2A2A);
}
.portal-header .welcome {
  font-size: 12px;
  color: var(--ink-500, #888880);
  margin-top: 4px;
  font-weight: 300;
}
.btn-ghost {
  background: transparent;
  border: 1px solid transparent;
  color: var(--ink-500, #888880);
  padding: 8px 16px;
  font-size: 11px;
  letter-spacing: 0.1em;
  font-family: 'Montserrat', sans-serif;
  cursor: pointer;
  border-radius: 6px;
}
.btn-ghost:hover { color: var(--ink-900, #2A2A2A); border-color: var(--ink-100, #ECEAE3); }

.portal-main {
  flex: 1;
  max-width: 720px;
  width: 100%;
  margin: 0 auto;
  padding: 32px 20px;
}
.portal-footer {
  text-align: center;
  padding: 24px;
  font-size: 11px;
  color: var(--ink-500, #888880);
  border-top: 1px solid var(--ink-100, #ECEAE3);
}
.portal-footer a { color: inherit; }

.empty {
  text-align: center;
  padding: 60px 20px;
  color: var(--ink-500, #888880);
}
.empty h2 {
  font-family: 'Playfair Display', serif;
  font-size: 20px;
  font-weight: 500;
  margin: 0 0 12px;
  color: var(--ink-700, #5A564D);
}
.empty p { font-size: 13px; line-height: 1.6; max-width: 380px; margin: 0 auto; font-weight: 300; }

.booking-list { display: flex; flex-direction: column; gap: 20px; }

.booking-card {
  background: var(--surface, #FFFFFF);
  border: 1px solid var(--ink-100, #ECEAE3);
  border-radius: 12px;
  padding: 24px 28px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.03);
}
.booking-card-head {
  margin-bottom: 20px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--ink-100, #ECEAE3);
}
.session-when {
  font-family: 'Playfair Display', serif;
  font-size: 22px;
  font-weight: 500;
  margin: 0;
  color: var(--ink-900, #2A2A2A);
  letter-spacing: 0.01em;
}
.package-name {
  font-size: 12px;
  color: var(--ink-500, #888880);
  margin-top: 6px;
  letter-spacing: 0.04em;
  font-weight: 400;
}
.series-tag {
  display: inline-block;
  margin-top: 8px;
  padding: 3px 10px;
  border-radius: 99px;
  background: var(--brand-tint, rgba(201, 166, 107, 0.12));
  color: var(--brand-strong, #A28247);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  font-weight: 500;
  font-family: 'Montserrat', sans-serif;
}

/* Pipeline — 4 horizontal stages with dots + connectors. */
.pipeline {
  display: flex;
  align-items: flex-start;
  margin: 8px 0 20px;
  position: relative;
}
.pipeline-step {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 0 4px;
}
.pipeline-step .dot {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--ink-100, #ECEAE3);
  border: 2px solid var(--ink-100, #ECEAE3);
  z-index: 2;
  position: relative;
  transition: all 0.2s ease;
}
.pipeline-step.done .dot {
  background: var(--brand, #C9A66B);
  border-color: var(--brand, #C9A66B);
}
.pipeline-step.current .dot {
  background: var(--surface, #FFFFFF);
  border-color: var(--brand, #C9A66B);
  box-shadow: 0 0 0 4px var(--brand-tint, rgba(201, 166, 107, 0.18));
}
.pipeline-step.pending .dot {
  background: var(--surface, #FFFFFF);
  border-color: var(--ink-100, #ECEAE3);
}
.pipeline-step .step-label { margin-top: 8px; min-height: 38px; }
.pipeline-step .step-name {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.06em;
  color: var(--ink-700, #5A564D);
  font-family: 'Montserrat', sans-serif;
  text-transform: uppercase;
}
.pipeline-step.pending .step-name { color: var(--ink-500, #888880); }
.pipeline-step.current .step-name { color: var(--brand-strong, #A28247); }
.pipeline-step .step-sub {
  font-size: 10px;
  color: var(--ink-500, #888880);
  margin-top: 2px;
  font-weight: 300;
  line-height: 1.3;
}
.pipeline-step .connector {
  position: absolute;
  top: 7px;
  left: 50%;
  right: -50%;
  height: 2px;
  background: var(--ink-100, #ECEAE3);
  z-index: 1;
}
.pipeline-step.done + .pipeline-step .connector,
.pipeline-step.done .connector {
  background: var(--brand, #C9A66B);
}

/* Off-track banner: cancelled / no-show / rescheduled. */
.off-track {
  margin: 4px 0 20px;
  padding: 14px 18px;
  border-radius: 8px;
  border-left: 3px solid var(--ink-200, #D8D3C5);
  background: rgba(0, 0, 0, 0.02);
}
.off-track-cancelled { border-left-color: #B8423B; background: rgba(184, 66, 59, 0.05); }
.off-track-no_show   { border-left-color: #B8423B; background: rgba(184, 66, 59, 0.05); }
.off-track-rescheduled { border-left-color: #C9A66B; background: rgba(201, 166, 107, 0.06); }
.off-track-label {
  font-family: 'Montserrat', sans-serif;
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--ink-700, #5A564D);
}
.off-track-detail { font-size: 12px; line-height: 1.5; color: var(--ink-700, #5A564D); margin-top: 4px; font-weight: 300; }

/* Expected-delivery footer of the card. */
.expected {
  font-size: 12px;
  color: var(--ink-500, #888880);
  padding-top: 12px;
  border-top: 1px solid var(--ink-100, #ECEAE3);
  font-weight: 300;
}
.expected-label { color: var(--ink-700, #5A564D); font-weight: 500; }
.expected-value { color: var(--ink-700, #5A564D); }

/* Mobile tweaks — pipeline becomes vertical on narrow viewports. */
@media (max-width: 540px) {
  .portal-header { padding: 20px; }
  .booking-card { padding: 20px; }
  .session-when { font-size: 18px; }
  .pipeline { flex-direction: column; gap: 12px; }
  .pipeline-step { flex-direction: row; align-items: flex-start; text-align: left; gap: 12px; }
  .pipeline-step .dot { margin-top: 4px; }
  .pipeline-step .connector { display: none; }
  .pipeline-step .step-label { margin-top: 0; }
}

/* =========================================================================
   v0.49.3 — Client portal messaging thread.
   Below the booking list. Same card visual language; chat-bubble
   pattern with client right-aligned (gold) and studio left-aligned (cream).
   ========================================================================= */

.messages-card {
  margin-top: 28px;
  background: var(--surface, #FFFFFF);
  border: 1px solid var(--ink-100, #ECEAE3);
  border-radius: 12px;
  padding: 24px 24px 16px;
  display: flex;
  flex-direction: column;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.03);
}
.messages-card-head { margin-bottom: 16px; padding-bottom: 14px; border-bottom: 1px solid var(--ink-100, #ECEAE3); }
.messages-card-head h2 {
  font-family: 'Playfair Display', serif;
  font-size: 18px;
  font-weight: 500;
  margin: 0;
  color: var(--ink-900, #2A2A2A);
  letter-spacing: 0.02em;
}
.messages-card-head .muted {
  font-size: 12px;
  color: var(--ink-500, #888880);
  margin-top: 4px;
  font-weight: 300;
  line-height: 1.5;
}
.messages-scroll {
  max-height: 420px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 8px 4px;
  margin-bottom: 12px;
}
.empty-msg {
  text-align: center;
  font-size: 12px;
  color: var(--ink-500, #888880);
  padding: 32px 16px;
  font-weight: 300;
  line-height: 1.6;
}
.empty-msg strong { color: var(--ink-700, #5A564D); font-weight: 500; }
.msg-row { display: flex; }
.msg-mine { justify-content: flex-end; }
.msg-theirs { justify-content: flex-start; }
.msg-bubble {
  max-width: 78%;
  padding: 10px 14px;
  border-radius: 14px;
  font-size: 13px;
  line-height: 1.5;
  word-wrap: break-word;
}
.msg-mine .msg-bubble {
  background: var(--brand, #C9A66B);
  color: #FFFFFF;
}
.msg-theirs .msg-bubble {
  background: var(--cream-50, #FAF7EE);
  color: var(--ink-900, #2A2A2A);
  border: 1px solid var(--ink-100, #ECEAE3);
}
.msg-name {
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--ink-500, #888880);
  font-weight: 500;
  margin-bottom: 3px;
  font-family: 'Montserrat', sans-serif;
  text-transform: uppercase;
}
.msg-body { white-space: pre-wrap; }
.msg-time { font-size: 9px; margin-top: 4px; opacity: 0.7; }
.msg-mine .msg-time { text-align: right; }

.messages-card .composer {
  display: flex;
  gap: 10px;
  align-items: flex-end;
  padding-top: 12px;
  border-top: 1px solid var(--ink-100, #ECEAE3);
}
.messages-card .composer textarea {
  flex: 1;
  font-family: inherit;
  font-size: 13px;
  padding: 10px 12px;
  border: 1px solid var(--ink-100, #ECEAE3);
  border-radius: 8px;
  resize: vertical;
  min-height: 50px;
  background: var(--surface, #FFFFFF);
  color: var(--ink-900, #2A2A2A);
}
.messages-card .composer textarea:focus { outline: none; border-color: var(--brand, #C9A66B); }
.messages-card .composer .btn-primary {
  font-family: 'Montserrat', sans-serif;
  font-size: 12px;
  letter-spacing: 0.1em;
  padding: 12px 22px;
  border-radius: 6px;
  cursor: pointer;
  background: var(--brand, #C9A66B);
  color: #FFFFFF;
  border: 1px solid var(--brand, #C9A66B);
  font-weight: 500;
  white-space: nowrap;
}
.messages-card .composer .btn-primary:hover:not(:disabled) { filter: brightness(0.95); }
.messages-card .composer .btn-primary:disabled { opacity: 0.45; cursor: not-allowed; }
.messages-card .composer-hint {
  font-size: 10px;
  color: var(--ink-500, #888880);
  margin-top: 6px;
  font-weight: 300;
}
.messages-card .err {
  background: rgba(184, 66, 59, 0.08);
  color: var(--danger, #B8423B);
  font-size: 12px;
  padding: 8px 12px;
  border-radius: 6px;
  margin-bottom: 8px;
  font-weight: 400;
}

/* =========================================================================
   v0.49.5 — Vendor portal layout. Reuses .portal/.portal-main from
   v0.49.1 and adds the project-list grid + project-detail sections.
   ========================================================================= */

.vendor-list { display: grid; gap: 14px; }
.vendor-card {
  display: block;
  width: 100%;
  text-align: left;
  background: var(--surface, #FFFFFF);
  border: 1px solid var(--ink-100, #ECEAE3);
  border-radius: 10px;
  padding: 18px 20px;
  cursor: pointer;
  transition: all 0.15s ease;
  font: inherit;
  color: inherit;
}
.vendor-card:hover {
  border-color: var(--brand, #C9A66B);
  box-shadow: 0 2px 8px rgba(201, 166, 107, 0.12);
}
.vendor-card-name {
  font-family: 'Playfair Display', serif;
  font-size: 17px;
  font-weight: 500;
  color: var(--ink-900, #2A2A2A);
  margin-bottom: 6px;
}
.vendor-card-meta { display: flex; gap: 8px; flex-wrap: wrap; }
.meta-tag {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 99px;
  background: var(--cream-50, #FAF7EE);
  color: var(--ink-700, #5A564D);
  font-size: 10px;
  letter-spacing: 0.06em;
  font-family: 'Montserrat', sans-serif;
  font-weight: 500;
  border: 1px solid var(--ink-100, #ECEAE3);
}
.meta-tag.muted { color: var(--ink-500, #888880); }

.vendor-detail { display: flex; flex-direction: column; gap: 18px; }
.vendor-card-row {
  display: flex; gap: 32px; flex-wrap: wrap;
  background: var(--surface, #FFFFFF);
  border: 1px solid var(--ink-100, #ECEAE3);
  border-radius: 10px; padding: 18px 22px;
}
.vendor-card-pair { display: flex; flex-direction: column; gap: 4px; }
.kv-label {
  font-size: 9px; letter-spacing: 0.14em;
  color: var(--ink-500, #888880);
  font-family: 'Montserrat', sans-serif;
  text-transform: uppercase; font-weight: 500;
}
.kv-value {
  font-size: 14px; color: var(--ink-900, #2A2A2A);
  font-weight: 500; font-family: 'Playfair Display', serif;
}
.vendor-section {
  background: var(--surface, #FFFFFF);
  border: 1px solid var(--ink-100, #ECEAE3);
  border-radius: 10px; padding: 20px 22px;
}
.vendor-section h3 {
  font-family: 'Playfair Display', serif;
  font-size: 16px; font-weight: 500;
  color: var(--ink-900, #2A2A2A);
  margin: 0 0 10px; letter-spacing: 0.02em;
  display: flex; align-items: center; gap: 10px;
}
.sensitive-tag {
  font-size: 9px; letter-spacing: 0.14em;
  color: var(--danger, #B8423B);
  font-family: 'Montserrat', sans-serif;
  font-weight: 500;
  border: 1px solid var(--danger, #B8423B);
  border-radius: 99px; padding: 1px 7px;
  background: rgba(184, 66, 59, 0.06);
}
.vendor-section .brief-body {
  font-size: 14px; line-height: 1.7;
  color: var(--ink-700, #5A564D);
  font-weight: 300; white-space: pre-wrap; margin: 0;
}
.vendor-section .brief-empty { font-style: italic; }
.vendor-section .muted {
  color: var(--ink-500, #888880);
  font-size: 13px; line-height: 1.6; font-weight: 300;
}
.vendor-section-sensitive {
  background: rgba(184, 142, 71, 0.04);
  border-color: rgba(184, 142, 71, 0.4);
}
.vendor-section-upload .upload-bullets {
  font-size: 13px;
  color: var(--ink-700, #5A564D);
  margin: 8px 0 0;
  padding-left: 20px;
  line-height: 1.7;
  font-weight: 300;
}
.vendor-section-upload .upload-bullets li { margin-bottom: 4px; }
.vendor-section-upload .upload-bullets strong {
  color: var(--ink-900, #2A2A2A); font-weight: 500;
}

/* =========================================================================
   v0.49.5a — Vendor folder upload UI.
   Inside .vendor-section-upload. File-pick controls + per-file status list.
   ========================================================================= */

.vendor-section-upload .upload-controls {
  margin: 12px 0 6px;
}
.vendor-section-upload .upload-controls input[type=file] {
  font-family: inherit;
}
.vendor-section-upload .upload-summary {
  font-size: 12px;
  color: var(--ink-700, #5A564D);
  margin: 8px 0;
  font-weight: 400;
}
.vendor-section-upload .upload-summary strong {
  color: var(--ink-900, #2A2A2A);
}
.vendor-section-upload .upload-summary-done {
  color: var(--success, #4C8A5C);
  font-weight: 500;
}
.vendor-section-upload .upload-actions {
  display: flex;
  gap: 8px;
  margin: 12px 0;
}
.vendor-section-upload .upload-actions .btn-primary,
.vendor-section-upload .upload-actions .btn-secondary {
  font-family: 'Montserrat', sans-serif;
  font-size: 12px;
  letter-spacing: 0.1em;
  padding: 10px 22px;
  border-radius: 6px;
  cursor: pointer;
  font-weight: 500;
}
.vendor-section-upload .upload-actions .btn-primary {
  background: var(--brand, #C9A66B);
  color: #FFFFFF;
  border: 1px solid var(--brand, #C9A66B);
}
.vendor-section-upload .upload-actions .btn-primary:disabled {
  opacity: 0.5; cursor: wait;
}
.vendor-section-upload .upload-actions .btn-secondary {
  background: transparent;
  color: var(--ink-700, #5A564D);
  border: 1px solid var(--ink-200, #D8D3C5);
}
.vendor-section-upload .upload-file-list {
  list-style: none;
  padding: 0;
  margin: 8px 0 0;
  max-height: 320px;
  overflow-y: auto;
  border: 1px solid var(--ink-100, #ECEAE3);
  border-radius: 6px;
  background: var(--cream-50, #FAF7EE);
}
.vendor-section-upload .upload-file {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 6px 12px;
  font-size: 12px;
  border-bottom: 1px solid var(--ink-100, #ECEAE3);
}
.vendor-section-upload .upload-file:last-child { border-bottom: none; }
.vendor-section-upload .upload-file-name {
  font-family: monospace;
  color: var(--ink-700, #5A564D);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.vendor-section-upload .upload-file-status {
  font-size: 10px;
  letter-spacing: 0.06em;
  font-family: 'Montserrat', sans-serif;
  font-weight: 500;
  text-transform: uppercase;
  white-space: nowrap;
}
.vendor-section-upload .upload-file-queued .upload-file-status     { color: var(--ink-400, #A3A099); }
.vendor-section-upload .upload-file-uploading .upload-file-status  { color: var(--brand, #C9A66B); }
.vendor-section-upload .upload-file-done .upload-file-status       { color: var(--success, #4C8A5C); }
.vendor-section-upload .upload-file-error .upload-file-status      { color: var(--danger, #B8423B); text-transform: none; }
.vendor-section-upload .upload-file-more {
  text-align: center;
  font-style: italic;
  color: var(--ink-500, #888880);
  padding: 8px 12px;
}

/* =========================================================================
   v0.49.9 — Empty-state CTA on the client portal so brand-new prospects
   land directly on the public booking page. Anchor styled like the
   primary button, lives inside .portal-main .empty.
   ========================================================================= */
.portal-main .empty .btn-primary {
  display: inline-block;
  background: var(--brand, #C9A66B);
  color: #FFFFFF;
  border: 1px solid var(--brand, #C9A66B);
  padding: 12px 28px;
  border-radius: 6px;
  font-family: 'Montserrat', sans-serif;
  font-size: 12px;
  letter-spacing: 0.1em;
  font-weight: 500;
  text-decoration: none;
  transition: filter 0.15s ease;
}
.portal-main .empty .btn-primary:hover { filter: brightness(0.95); }

/* v0.49.11 — section heading on vendor portal list (Weddings / Projects) */
.vendor-section-heading {
  font-family: 'Montserrat', sans-serif;
  font-size: 10px;
  letter-spacing: 0.18em;
  color: var(--ink-500, #888880);
  text-transform: uppercase;
  font-weight: 500;
  margin: 8px 4px 12px;
}

/* ─────────────────────────────────────────────────────────────────────
   Estimates and invoices — /d/<token>
   ─────────────────────────────────────────────────────────────────────

   A document, not a page. The whole design job here is that a client
   opening this on a phone recognises it as the paperwork they were
   expecting, and can find the total and the due date without reading.

   So: one column, generous margins, the figures right-aligned in a
   column that lines up, and nothing competing with the total. The gold
   is the studio's, used once at the top; the only other colour is the
   green that says a thing is settled and the red that says it is not.
   ───────────────────────────────────────────────────────────────────── */

.doc-shell {
  min-height: 100vh;
  background: #f6f6f4;
  padding: 40px 16px 64px;
  display: flex;
  justify-content: center;
  align-items: flex-start;
}

.doc-card {
  width: 100%;
  max-width: 680px;
  background: #fff;
  border: 1px solid var(--ink-100);
  border-radius: 10px;
  padding: 36px 34px 30px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}

.doc-centered {
  text-align: center;
  padding: 60px 34px;
  color: var(--ink-500);
}

.doc-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
  padding-bottom: 22px;
  border-bottom: 1px solid var(--ink-100);
}

.doc-eyebrow {
  font-size: 12px;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: var(--brand);
  font-weight: 600;
}

.doc-title {
  margin: 6px 0 0;
  font-size: 26px;
  font-weight: 600;
  color: var(--ink-900);
}

.doc-subtitle {
  margin: 4px 0 0;
  color: var(--ink-500);
  font-size: 15px;
}

.doc-muted { color: var(--ink-500); }

.doc-state {
  flex-shrink: 0;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  padding: 6px 11px;
  border-radius: 4px;
  background: var(--ink-100);
  color: var(--ink-700);
}

.doc-state--paid,
.doc-state--accepted {
  background: var(--available-tint, #e9f8f1);
  color: var(--available-text, #076648);
}

.doc-state--due,
.doc-state--part_paid {
  background: #fdf3e3;
  color: #8a5b16;
}

.doc-state--declined,
.doc-state--expired,
.doc-state--void {
  background: var(--booked-tint, #fdeded);
  color: var(--booked-text, #a32126);
}

.doc-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 26px;
  padding: 20px 0;
  border-bottom: 1px solid var(--ink-100);
}

.doc-meta > div { display: flex; flex-direction: column; }

.doc-meta-label {
  font-size: 11px;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--ink-400);
  margin-bottom: 3px;
}

.doc-meta-value { font-size: 15px; color: var(--ink-900); }
.doc-meta-sub { font-size: 13px; color: var(--ink-500); }

.doc-lines {
  width: 100%;
  border-collapse: collapse;
  margin-top: 8px;
}

.doc-lines th {
  text-align: left;
  font-size: 11px;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--ink-400);
  font-weight: 500;
  padding: 14px 0 8px;
  border-bottom: 1px solid var(--ink-100);
}

.doc-lines td {
  padding: 13px 0;
  border-bottom: 1px solid var(--ink-100);
  vertical-align: top;
  font-size: 15px;
  color: var(--ink-900);
}

/* Figures line up because they are a column to be compared, not prose. */
.doc-lines .doc-num,
.doc-lines th.doc-num {
  text-align: right;
  white-space: nowrap;
  padding-left: 16px;
  font-variant-numeric: tabular-nums;
}

.doc-line-name { font-weight: 500; }
/* The inclusions arrive newline-separated — "14 hours of coverage\nTwo
   photographers\n…" — and HTML collapses those to spaces, so the guide's
   list rendered as one run-on sentence on the page a couple actually
   reads. The studio's own screen showed it correctly, which is why it
   looked fine everywhere it was checked. */
.doc-line-detail {
  font-size: 13px;
  color: var(--ink-500);
  margin-top: 3px;
  white-space: pre-line;
}

.doc-totals {
  margin: 18px 0 0;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 7px;
}

.doc-total-row {
  display: flex;
  justify-content: space-between;
  gap: 34px;
  min-width: 260px;
  font-size: 15px;
  color: var(--ink-700);
  font-variant-numeric: tabular-nums;
}

.doc-total-row--grand {
  font-size: 19px;
  font-weight: 600;
  color: var(--ink-900);
  padding-top: 9px;
  border-top: 1px solid var(--ink-100);
  min-width: 260px;
}

.doc-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 28px;
  padding-top: 24px;
  border-top: 1px solid var(--ink-100);
}

.doc-btn {
  flex: 1 1 160px;
  padding: 14px 20px;
  border-radius: 8px;
  border: 1px solid var(--ink-200);
  background: #fff;
  color: var(--ink-700);
  font-size: 15px;
  font-weight: 500;
  cursor: pointer;
}

.doc-btn:hover:not(:disabled) { border-color: var(--ink-400); }
.doc-btn:disabled { opacity: 0.55; cursor: default; }

.doc-btn--primary {
  background: var(--brand);
  border-color: var(--brand);
  color: #fff;
  font-weight: 600;
}

.doc-btn--primary:hover:not(:disabled) { background: var(--brand-strong); }

.doc-note {
  margin-top: 20px;
  padding: 14px 16px;
  border-radius: 8px;
  background: #faf9f6;
  border: 1px solid var(--ink-100);
  font-size: 14px;
  color: var(--ink-700);
  line-height: 1.5;
}

.doc-note--good {
  background: var(--available-tint, #e9f8f1);
  border-color: var(--available-line, #93dcc0);
  color: var(--available-text, #076648);
}

.doc-note--bad {
  background: var(--booked-tint, #fdeded);
  border-color: var(--booked-line, #f2b5b7);
  color: var(--booked-text, #a32126);
}

.doc-notes {
  margin-top: 22px;
  font-size: 14px;
  color: var(--ink-700);
  line-height: 1.6;
}

.doc-terms {
  margin-top: 18px;
  font-size: 12px;
  color: var(--ink-400);
  line-height: 1.6;
}

.doc-foot {
  margin-top: 30px;
  padding-top: 20px;
  border-top: 1px solid var(--ink-100);
  font-size: 12px;
  color: var(--ink-400);
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
}

@media (max-width: 560px) {
  .doc-shell { padding: 16px 10px 40px; }
  .doc-card { padding: 24px 20px 22px; border-radius: 8px; }
  .doc-title { font-size: 22px; }
  /* The totals block stops being a right-aligned column and becomes
     full-width rows — at this size an indented column of figures has
     nowhere to indent from. */
  .doc-total-row,
  .doc-total-row--grand { min-width: 0; width: 100%; }
}

/* A client printing the document should get the document, not the
   page it was displayed on.
 *
 * "Exactly like the web version" is only guaranteed if it *is* the web
 * version — so Download PDF prints this page rather than handing off
 * to a second renderer that would drift from it. What follows is the
 * difference between a screen and a sheet of paper, nothing more.
 */
@media print {
  @page { margin: 14mm; }

  html, body { background: #fff; }

  .doc-shell {
    background: #fff;
    padding: 0;
    /* Screens dim a dark block to save ink; on an invoice that turns
       the total into a grey smudge. */
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }

  .doc-card {
    border: none;
    box-shadow: none;
    max-width: none;
    padding: 0;
    border-radius: 0;
  }

  /* Controls are not part of the document. */
  .doc-actions,
  .doc-print,
  .doc-respond { display: none !important; }

  /* Keep a line's name and its detail together, and never orphan the
     totals onto a page of their own. */
  .doc-lines tr,
  .doc-totals,
  .doc-note { break-inside: avoid; page-break-inside: avoid; }

  a { text-decoration: none; color: inherit; }
}

/* ── The booking page's typefaces ────────────────────────────────────
   Fraunces and Karla, served from this origin.

   Self-hosted rather than linked from Google, because this page's CSP
   permits no third-party origin at all (`font-src 'self' data:`) and
   linking them would mean every visitor's browser announcing to a
   third party that they are on the studio's booking page. Downloading
   them once keeps both the typography and that guarantee.

   Both are variable fonts carrying a weight range in one file, so the
   three weights the design uses cost one request each rather than
   three. Latin subset, matching what Google serves; anything outside
   it falls through to the stacks named in --serif and --sans.

   Fraunces and Karla are licensed under the SIL Open Font License 1.1
   — see fonts/OFL.txt.
   ------------------------------------------------------------------ */
@font-face {
  font-family: 'Fraunces';
  font-style: normal;
  font-weight: 300 600;
  font-display: swap;
  src: url('fonts/fraunces.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Fraunces';
  font-style: italic;
  font-weight: 300 600;
  font-display: swap;
  src: url('fonts/fraunces-italic.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Karla';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('fonts/karla.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ===================================================================
   The booking page.

   Everything below is scoped to `.book`, because this stylesheet is
   shared: the client portal, the public invoice and the vendor pages
   are all served from it, and a bare `h1` rule here would restyle
   documents that have nothing to do with booking.

   ── On the typefaces ────────────────────────────────────────────────

   The design calls for Fraunces and Karla. This page's CSP allows no
   third-party origin at all — `font-src 'self' data:` — so those are
   named first and followed by real stacks rather than being fetched
   from Google. Iowan Old Style ships on Apple devices and Georgia
   everywhere else; both are old-style serifs and hold the intended
   character. To use the actual faces they have to be self-hosted, not
   linked.
   =================================================================== */

.book {
  --ink: #1B211C;
  --ink-soft: #4C5750;
  --paper: #F3F1EA;
  --paper-2: #FFFFFF;
  --garden: #5E7A52;
  --garden-deep: #3D5236;
  --garden-wash: #E7EDE1;
  --gold: #A8853C;
  --taken: #A84A3E;
  --taken-wash: #F6E5E2;
  --line: #DCD8CC;
  --serif: "Fraunces", "Iowan Old Style", "Palatino Linotype", Georgia, serif;
  --sans: "Karla", system-ui, -apple-system, "Segoe UI", sans-serif;

  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  /* Fraunces carries an optical-size axis: at display sizes it thins
     the hairlines and tightens the joins, which is most of why the
     headline reads the way it does in the design. */
  font-optical-sizing: auto;
  min-height: 100vh;
}

.book *, .book *::before, .book *::after { box-sizing: border-box; }
.book h1, .book h2, .book h3 {
  font-family: var(--serif);
  font-weight: 400;
  margin: 0;
  letter-spacing: -.01em;
  text-wrap: balance;
}
.book h1 { font-size: clamp(2.2rem, 6vw, 4.2rem); line-height: 1.04; }
.book h2 { font-size: clamp(1.5rem, 3.2vw, 2.1rem); line-height: 1.15; }
.book h3 { font-size: 1.15rem; line-height: 1.3; }
.book p { margin: 0 0 1em; }
.book a { color: inherit; }
.book :focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }

.book-wrap { max-width: 1200px; margin: 0 auto; padding: 0 24px; }

/* ── Header ──────────────────────────────────────────────────────── */
/* Sticky, so Book Now is reachable from the bottom of a long
   description rather than only from the top of the page. */
.book-header {
  padding: 16px 0;
  border-bottom: 1px solid var(--line);
  background: var(--paper);
  position: sticky;
  top: 0;
  z-index: 20;
}
.book-bar { display: flex; align-items: center; justify-content: space-between; gap: 20px; flex-wrap: wrap; }
.book-mark { display: flex; align-items: baseline; gap: 10px; }
.book-mark .tl { font-family: var(--serif); font-size: 1.5rem; color: var(--gold); letter-spacing: .02em; }
.book-mark .name { font-size: .82rem; letter-spacing: .32em; color: var(--ink-soft); text-transform: uppercase; }
/* The studio's mark, tall enough to read.
   34px was sized for a wordmark that is wider than it is tall. This
   logo is nearly square with "TIMELESS" arced under it, so the same
   height rendered it about 60px across and illegible — the one element
   on the page whose whole job is to be recognised. */
.book-mark img { height: 62px; width: auto; display: block; }
@media (max-width: 560px) {
  /* The header is tight on a phone and Book Now has to survive. */
  .book-mark img { height: 46px; }
}
.book-bar-right { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; justify-content: flex-end; }
.book-calls { display: flex; gap: 18px; font-size: .9rem; color: var(--ink-soft); flex-wrap: wrap; }
.book-calls a { text-decoration: none; border-bottom: 1px solid var(--line); padding-bottom: 2px; white-space: nowrap; }
.book-calls a:hover { border-color: var(--gold); }

/* ── Hero ────────────────────────────────────────────────────────── */
.book-hero { padding: 64px 0 56px; max-width: 780px; }
/* The eyebrow, when a headline follows it: quiet, and spaced a little
   so a short line like "Christmas 2026" does not read as a runt. */
.book-hero .season {
  font-size: .95rem;
  color: var(--garden);
  letter-spacing: .04em;
  margin-bottom: 18px;
}

/* The same line when it is carrying the hero on its own.
   Set in the display face at display size, because at .9rem it was a
   caption with nothing to caption. Slightly under the headline scale:
   it is a season, not a promise, and it should not shout louder than
   the sentence it would sit above if there were one. */
.book-hero h1.season-lead {
  font-size: clamp(2rem, 5vw, 3.4rem);
  line-height: 1.06;
  color: var(--garden-deep);
  letter-spacing: -.015em;
}
/* The emphasised word in the headline. The studio marks it with
   *asterisks* and it is set in the italic, in the deep green — the one
   place on the page where the accent lands on type. */
.book-hero h1 em { font-style: italic; color: var(--garden-deep); }
/* `.book .book-lede`, not `.book-lede`.
   The bare class is 0,1,0 and loses to `.book p` at 0,1,1, so the top
   margin was being zeroed and the description sat tight under the
   button above it. Third time this pairing has caught something in
   this file — any rule here that styles an element the base `.book`
   rules also style needs the extra class to win. */
.book .book-lede {
  font-size: 1.12rem;
  color: var(--ink-soft);
  max-width: 52ch;
  /* Follows the facts strip rather than a button now, so this is the
     ordinary gap between a rule and the paragraph under it. */
  margin: 30px 0 0;
  white-space: pre-wrap;
}
.book-facts {
  display: flex; flex-wrap: wrap; gap: 0; margin-top: 34px;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
}
.book-fact { padding: 16px 32px 16px 0; margin-right: 32px; border-right: 1px solid var(--line); }
.book-fact:last-child { border-right: 0; margin-right: 0; padding-right: 0; }
.book-fact .k { font-size: .82rem; color: var(--ink-soft); }
.book-fact .v { font-family: var(--serif); font-size: 1.35rem; }

/* `.book .book-cta`, not `.book-cta`. The plain class is 0,1,0 and
   loses to `.book a` at 0,1,1, so the white was being overwritten by
   the inherited ink and the button shipped with near-invisible text. */
.book .book-cta {
  display: inline-block; margin-top: 32px;
  background: var(--garden-deep); color: #fff; text-decoration: none;
  padding: 15px 30px; border-radius: 999px;
  font-weight: 700; font-size: .98rem; font-family: inherit;
  border: 0; cursor: pointer; transition: background .18s ease;
}
.book .book-cta:hover { background: var(--ink); color: #fff; }

/* ── The booking section ─────────────────────────────────────────── */
.book-section { padding: 56px 0; }
.book-section--open {
  background: var(--paper-2);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.book-section-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 20px; flex-wrap: wrap; margin-bottom: 26px;
}
.book-section-head p { color: var(--ink-soft); margin: 0; max-width: 48ch; }

.book-weeknav { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 10px; }
.book-weeknav .range { font-family: var(--serif); font-size: 1.3rem; }
.book-navbtns { display: flex; gap: 8px; }
.book-navbtns button {
  width: 40px; height: 40px; border-radius: 50%;
  border: 1px solid var(--line); background: #fff;
  font-size: 1.1rem; cursor: pointer; line-height: 1; color: var(--ink);
}
.book-navbtns button:hover:not(:disabled) { border-color: var(--garden); }
.book-navbtns button:disabled { color: #C9C5BB; cursor: not-allowed; }

/* The grid scrolls sideways on its own so the page body never does. */
.book-gridscroll { overflow-x: auto; padding-bottom: 6px; }
.book-grid { display: grid; grid-template-columns: repeat(7, minmax(118px, 1fr)); gap: 8px; min-width: 840px; }
.book-col { display: flex; flex-direction: column; gap: 6px; }
.book-colhead { text-align: center; padding: 6px 0 10px; border-bottom: 1px solid var(--line); margin-bottom: 4px; }
.book-colhead .d { font-size: .78rem; color: var(--ink-soft); }
.book-colhead .n { font-family: var(--serif); font-size: 1.35rem; line-height: 1.1; }
.book-colhead .left { font-size: .75rem; margin-top: 3px; color: var(--garden); }
.book-colhead .left.low { color: var(--taken); font-weight: 700; }
.book-colhead .left.none { color: #B9B4A9; }
.book-colhead.is-today .n { color: var(--gold); }

.book-slot {
  border: 1px solid var(--garden); background: var(--garden-wash); color: var(--garden-deep);
  border-radius: 9px; padding: 9px 4px; font: inherit; font-size: .88rem;
  text-align: center; cursor: pointer; font-variant-numeric: tabular-nums;
  transition: transform .12s ease, background .12s ease;
}
.book-slot:hover:not(:disabled) { background: #D8E3D0; transform: translateY(-1px); }
/* The hour and its meridiem. Side by side with a space at desktop
   width; stacked on a phone, where the column is 45px wide. */
.book-slot .m { margin-left: .28em; }
/* Booked and past are told apart on purpose: one is gone, the other
   never was, and a client scanning a week should not read an evening
   that closed at noon as somebody else's session. */
.book-slot.booked {
  border-color: #EBC9C3; background: var(--taken-wash); color: var(--taken);
  cursor: not-allowed; text-decoration: line-through; text-decoration-thickness: 1px;
}
.book-slot.past, .book-slot.closed, .book-slot.unavailable {
  border-color: var(--line); background: transparent; color: #C3C0B7; cursor: default;
}
/* The booking being rescheduled, in the reschedule dialog.
   Without this it renders as `booked` — struck through in the taken
   colour — and the person moving it reads their own session as
   somebody else's, which makes the whole calendar look full. */
.book-slot.mine {
  border-color: var(--brand, #b08d57);
  background: var(--brand-tint, #f5efea);
  color: var(--ink, #2b2b2b);
  cursor: default;
  font-weight: 600;
  box-shadow: inset 0 0 0 1px var(--brand, #b08d57);
}
.book-slot.mine::after {
  content: 'now';
  display: block;
  font-size: .6rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  opacity: .75;
}
.book-slot:disabled { transform: none; }

.book-legend {
  display: flex; gap: 20px; font-size: .84rem; color: var(--ink-soft);
  margin-top: 18px; flex-wrap: wrap; align-items: center;
}
.book-legend i { display: inline-block; width: 11px; height: 11px; border-radius: 3px; margin-right: 7px; vertical-align: -1px; }
.book-legend .free { background: var(--garden-wash); border: 1px solid var(--garden); }
.book-legend .gone { background: var(--taken-wash); border: 1px solid #EBC9C3; }

.book-empty { padding: 40px 10px; text-align: center; color: var(--ink-soft); }

/* ── Footer ──────────────────────────────────────────────────────── */
.book-footer { padding: 44px 0 60px; font-size: .92rem; color: var(--ink-soft); border-top: 1px solid var(--line); }
/* Centred, and stacked rather than pushed to the two edges.
   Space-between put the contacts hard against the right margin
   whenever no address was set, which is most of the time — an empty
   left column reads as a layout that has lost something. */
.book-foot {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 8px;
}
.book-footer a { text-decoration: none; border-bottom: 1px solid var(--line); }
.book-footer a:hover { border-color: var(--gold); }

/* The header's own Book Now: the same pill, sized to sit on a bar. */
.book .book-cta--sm { margin-top: 0; padding: 10px 20px; font-size: .9rem; }

/* The anchor sits under a sticky header, so scrolling to it must stop
   short of the bar rather than behind it. */
.book-section--open { scroll-margin-top: 76px; }

@media (max-width: 720px) {
  .book-hero { padding: 40px 0 44px; }
  .book-bar { gap: 10px; flex-wrap: nowrap; }
  .book-bar-right { gap: 12px; }
  /*
   * The contacts leave the header on a phone.
   *
   * Three rows — wordmark, two numbers, button — made the sticky bar
   * 156px, a fifth of the screen, held there for the whole visit. The
   * numbers are still in the footer, and the one thing worth that
   * space is the button.
   */
  .book-calls { display: none; }
}

@media (max-width: 560px) {
  .book { font-size: 16px; }
  .book-section { padding: 40px 0; }
  .book-wrap { padding: 0 14px; }

  /*
   * The whole week, on a phone, without scrolling sideways.
   *
   * The grid carried `min-width: 750px`, so a 327px screen showed
   * four and a half days and the rest lived behind a horizontal
   * scroll with nothing on screen to announce it — days a client
   * never swiped to did not exist for them.
   *
   * Seven columns fit if the label stacks: the hour on one line and
   * AM or PM under it, which keeps the column near 45px and keeps
   * the distinction between a 10am and a 10pm sitting, where an
   * abbreviated "10a" would have rested the whole thing on one
   * letter.
   */
  .book-grid { grid-template-columns: repeat(7, minmax(0, 1fr)); min-width: 0; gap: 4px; }
  /* All seven days fit here, so there is no sideways scrolling to
     protect — and a horizontal drag is free to mean "next week".
     `pan-y` hands vertical scrolling back to the browser while
     claiming the horizontal axis, which stops the swipe from being
     read as an attempt to scroll a page that does not move sideways. */
  .book-gridscroll { overflow-x: visible; touch-action: pan-y; }
  .book-col { gap: 4px; }

  .book-slot {
    padding: 7px 1px;
    font-size: .68rem;
    line-height: 1.15;
    border-radius: 7px;
    /* Two lines of type plus the padding, which also lands the target
       above the 44px a fingertip needs. */
    min-height: 46px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
  }
  .book-slot .t { font-size: .82rem; font-weight: 700; }
  .book-slot .m { margin-left: 0; font-size: .6rem; letter-spacing: .02em; opacity: .85; }
  /* The strike-through belongs on the hour, not across the stack. */
  .book-slot.booked { text-decoration: none; }
  .book-slot.booked .t { text-decoration: line-through; text-decoration-thickness: 1px; }

  .book-colhead { padding: 4px 0 7px; }
  .book-colhead .d { font-size: .62rem; }
  .book-colhead .n { font-size: 1.02rem; }
  .book-colhead .left { font-size: .58rem; margin-top: 1px; }

  /*
   * The facts stack instead of wrapping.
   *
   * As a wrapping flex row the third fact dropped to its own line and
   * left the second row's divider hanging beside an empty cell, which
   * reads as a layout that has broken rather than one that has
   * wrapped.
   */
  .book-facts { display: grid; grid-template-columns: 1fr; gap: 0; }
  .book-fact {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 16px;
    padding: 11px 0;
    margin: 0;
    border-right: 0;
    border-bottom: 1px solid var(--line);
  }
  .book-fact:last-child { border-bottom: 0; }
  .book-fact .v { font-size: 1.15rem; }
}
@media (prefers-reduced-motion: reduce) {
  .book * { transition: none !important; }
}

/* The full-gallery upgrade, stated in the hero rather than met for the
   first time inside the gallery. */
.book .book-upsell {
  margin: 18px 0 0;
  font-size: .95rem;
  color: var(--ink-soft);
  max-width: 46ch;
}

/* The month, shown on the first column of the week and wherever the
   month rolls over. Set smaller and in the accent so it reads as a
   label on the date rather than part of the number. */
.book-colhead .n .mo {
  font-family: var(--sans);
  font-size: .62rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--gold);
  display: block;
  line-height: 1.5;
}

/* "We filled this in from what you started earlier." — shown rather
   than restoring silently, because this is a public form and quietly
   putting a previous visitor's email into it is worse than asking. */
.draft-note {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 16px;
  padding: 10px 12px;
  border: 1px solid #DCD8CC;
  border-left: 3px solid #A8853C;
  border-radius: 0 6px 6px 0;
  background: #FAF7F0;
  font-size: .88rem;
  color: #4A4A4A;
}
.draft-note button {
  font: inherit;
  font-size: .82rem;
  padding: 5px 12px;
  border: 1px solid #C9C0A4;
  border-radius: 6px;
  background: #fff;
  color: #6B5A2E;
  cursor: pointer;
  white-space: nowrap;
}
.draft-note button:hover { background: #F3EEE1; }

/* ── The running total in the booking form ──────────────────────────
   Sits between the fields and the Book button, because that is where
   a party size stops being a number somebody typed and starts being
   what they owe. Quiet until the extras apply. */
/* "Sign in", beside Book Now in the sticky header.
   Deliberately quieter than the CTA: booking is what this page is for,
   and an account is a convenience for people who come back. */
.book-signin {
  font-size: .82rem;
  letter-spacing: .02em;
  color: var(--ink-soft, #6b6b6b);
  text-decoration: none;
  padding: 6px 10px;
  border-radius: 999px;
  border: 1px solid transparent;
  white-space: nowrap;
}
.book-signin:hover {
  color: var(--ink, #2b2b2b);
  border-color: var(--line, #e5e2da);
}
@media (max-width: 560px) {
  /* The header is tight on a phone and Book Now must survive. */
  .book-signin { display: none; }
}

.book-quote {
  margin-top: 20px;
  padding: 14px 16px;
  border: 1px solid var(--line, #e5e2da);
  border-radius: 10px;
  background: var(--brand-tint, #f7f4ef);
  font-size: 14px;
}
.book-quote-rows > div {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 2px 0;
  color: var(--ink-500, #6b6b6b);
  font-variant-numeric: tabular-nums;
}
.book-quote-total {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--line, #e5e2da);
  font-variant-numeric: tabular-nums;
}
.book-quote-total strong { font-size: 20px; font-weight: 600; }
.book-quote-note {
  margin-top: 8px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink-500, #6b6b6b);
}

/* ── The calendar, embedded in the manage-booking page ───────────────
 *
 * `.book` carries the booking page's design system *and* its page
 * chrome — a full-height paper background, a 17px body scale, a
 * min-height of the whole viewport. Inside a card on another page all
 * of that is wrong, but the palette is exactly what is wanted: this is
 * the same calendar, and it should read the same way.
 *
 * So the class comes along for the variables and this undoes the rest.
 */
.book--embedded {
  min-height: 0;
  background: transparent;
  font-size: 15px;
}

/* Seven days inside a card, rather than 840px behind a scrollbar.
 *
 * The full-width grid holds a 118px minimum per column, which is right
 * on a page 1200px wide and clips Sunday off the edge of a dialog. The
 * phone layout already solves this — stack the meridiem under the hour
 * and the column fits in a fraction of the width — so the embedded
 * calendar borrows it at every size. */
.book--embedded .book-gridscroll { overflow-x: visible; }
.book--embedded .book-grid {
  grid-template-columns: repeat(7, minmax(0, 1fr));
  min-width: 0;
  gap: 5px;
}
.book--embedded .book-col { gap: 5px; }
.book--embedded .book-slot {
  padding: 8px 2px;
  font-size: .74rem;
  line-height: 1.15;
  min-height: 44px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
}
.book--embedded .book-slot .m {
  margin-left: 0;
  font-size: .62rem;
  letter-spacing: .02em;
  opacity: .8;
}
.book--embedded .book-colhead { padding: 4px 0 8px; }
.book--embedded .book-colhead .n { font-size: 1.05rem; }
.book--embedded .book-colhead .d { font-size: .68rem; }
.book--embedded .book-colhead .left { font-size: .66rem; }
.book--embedded .book-weeknav { margin-bottom: 10px; }
.book--embedded .book-weeknav .range { font-size: 1.05rem; }

/* The legend, which is what makes the colours mean something.
 * Its swatches were empty boxes on this page for the same reason the
 * slots were colourless. */
.book--embedded .book-legend { margin-top: 12px; font-size: .78rem; }

/* ── How to pay the deposit ──────────────────────────────────────────
 *
 * Deliberately the loudest thing on the confirmation screen after the
 * confirmation itself. The studio's problem is not clients refusing to
 * pay — it is money arriving with nothing on it to say whose it is, and
 * that is fixed by one line of text the client has to notice and copy.
 */
.pay-panel {
  max-width: 640px;
  margin: 0 auto 20px;
  padding: 20px 22px;
  background: var(--surface, #fff);
  border: 1px solid var(--line, #e5e2da);
  border-left: 4px solid var(--brand, #b08d57);
  border-radius: 10px;
}
.pay-head h3 {
  margin: 0 0 4px;
  font-size: 18px;
  font-weight: 600;
}
.pay-head p { margin: 0 0 14px; font-size: 14px; color: #6b7280; }

.pay-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border: 1px solid var(--line, #e5e2da);
  border-radius: 8px;
  margin-bottom: 8px;
  background: #fff;
}
.pay-row-text { flex: 1; min-width: 0; }
.pay-label {
  font-size: 11px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: #8a8a8a;
}
.pay-value {
  font-size: 15px;
  margin-top: 2px;
  word-break: break-word;
  font-variant-numeric: tabular-nums;
}
.pay-hint { font-size: 12.5px; color: #6b7280; margin-top: 5px; line-height: 1.45; }

/* The reference. Everything else on this panel is a detail somebody
   can look up again; this is the one thing that cannot be recovered
   after the money has been sent. */
.pay-row--key {
  border-color: var(--brand, #b08d57);
  background: var(--brand-tint, #f7f2e9);
  border-width: 1.5px;
}
.pay-row--key .pay-label { color: var(--brand, #8a6d3b); font-weight: 700; }
.pay-row--key .pay-value { font-size: 16px; font-weight: 600; }

.pay-copy {
  flex: 0 0 auto;
  padding: 8px 14px;
  border: 1px solid var(--line, #d4d4d8);
  border-radius: 6px;
  background: #fff;
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  min-width: 74px;
}
.pay-copy:hover { border-color: var(--brand, #b08d57); color: var(--brand, #b08d57); }
.pay-foot { margin-top: 10px; font-size: 12.5px; color: #6b7280; }

/*
 * Two ways to settle an invoice, side by side.
 *
 * ── Why both are shown, and shown whole ─────────────────────────────
 *
 * A client choosing how to pay is comparing a fee against convenience,
 * and they can only do that if both prices are on screen at once.
 * Hiding the e-Transfer details behind the card option would make the
 * free choice the harder one to find, which is the opposite of what
 * either side wants.
 *
 * The card option is priced on its own button because that is the
 * number that will actually leave their account. An invoice saying
 * $316.25 and a checkout charging $325.74 is a surprise at the worst
 * possible moment, and surprises about money are what people remember
 * about a studio.
 */
.inv-pay {
  max-width: 640px;
  margin: 0 auto 20px;
}

.inv-pay-option {
  padding: 18px 20px;
  background: var(--surface, #fff);
  border: 1px solid var(--line, #e5e2da);
  border-radius: 10px;
}

/* The one that finishes here gets the studio's colour. */
.inv-pay--card {
  border-left: 4px solid var(--brand, #b08d57);
}

.inv-pay--etransfer {
  border-left: 4px solid var(--line, #e5e2da);
}

.inv-pay__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

.inv-pay__head h4 {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
}

.inv-pay__price {
  font-size: 18px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* The arithmetic, spelled out under the total it produces. */
.inv-pay__sum {
  margin: 4px 0 0;
  font-size: 13px;
  color: #6b7280;
  font-variant-numeric: tabular-nums;
}

.inv-pay__sum--free {
  color: #15803d;
  font-weight: 600;
}

.inv-pay__note {
  margin: 10px 0 0;
  font-size: 13px;
  line-height: 1.5;
  color: #6b7280;
}

.inv-pay__go {
  margin-top: 14px;
  width: 100%;
  padding: 13px 18px;
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  color: #fff;
  background: var(--brand, #b08d57);
  border: 1px solid var(--brand, #b08d57);
  border-radius: 8px;
  cursor: pointer;
}

.inv-pay__go:hover:not(:disabled) { filter: brightness(1.06); }
.inv-pay__go:disabled { opacity: .6; cursor: default; }

.inv-pay__go:focus-visible {
  outline: 2px solid var(--brand, #b08d57);
  outline-offset: 2px;
}

/* A rule with a word in it, so the two read as alternatives rather
   than as steps one and two. */
.inv-pay-or {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 14px 2px;
  color: #9ca3af;
  font-size: 12px;
  letter-spacing: .08em;
  text-transform: uppercase;
}

.inv-pay-or::before,
.inv-pay-or::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--line, #e5e2da);
}

/*
 * On a phone the price cannot sit beside the heading: "Pay now by
 * card" and "$325.74" together are wider than the screen, and the
 * flexbox answer is to squash the price into two lines. Stacked, both
 * stay readable and the price keeps its prominence.
 */
@media (max-width: 480px) {
  .inv-pay-option { padding: 16px; }

  .inv-pay__head {
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
  }

  .inv-pay__price { font-size: 20px; }
}

@media (max-width: 520px) {
  /* The button drops under the value rather than squeezing it, so a
     long address stays readable on a phone. */
  .pay-row { flex-wrap: wrap; }
  .pay-copy { width: 100%; }
}

/* Two short related fields on one line.
   The booking form opens in a dialog, often on a phone, where every
   extra row is another scroll between somebody deciding to book and
   being able to. First and last name are one question. */
.field-row {
  display: flex;
  gap: 12px;
}
.field-row > * {
  flex: 1 1 0;
  min-width: 0;
}
@media (max-width: 380px) {
  /* Below this the two boxes are narrower than the words that go in
     them, and one long field beats two cramped ones. */
  .field-row { display: block; }
}

/* ── The agreement, signed on the page it is read on ─────────────────
   The studio's Terms & Conditions used to be a PDF that was emailed,
   printed, signed by hand and photographed back. These style the same
   document as something a couple reads and signs where they read it —
   which is why the clauses are on the page rather than behind a link.
   A signature gathered over a link nobody opened is not worth having. */

.sign {
  margin-top: 30px;
  padding-top: 28px;
  border-top: 1px solid var(--ink-100);
}

.sign-title {
  margin: 0;
  font-size: 19px;
  font-weight: 600;
  color: var(--ink-900);
  letter-spacing: -0.01em;
}

.sign-intro {
  margin: 10px 0 0;
  color: var(--ink-700);
  font-size: 14px;
  line-height: 1.6;
}

.sign-group { margin-top: 22px; }

.sign-group-name {
  margin: 0 0 8px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--brand);
}

/* Folded by default, and openable one at a time.
   Ten clauses laid out in full is a wall a couple scrolls past to
   reach the button, which defeats the point of putting them here.
   Headings visible, bodies a tap away: the shape of the printed
   contract, which is also numbered and scannable. */
.sign-clause {
  border-bottom: 1px solid var(--ink-100);
}

.sign-clause > summary {
  display: flex;
  gap: 12px;
  align-items: baseline;
  padding: 11px 2px;
  cursor: pointer;
  list-style: none;
  font-size: 14.5px;
  color: var(--ink-900);
}

.sign-clause > summary::-webkit-details-marker { display: none; }

.sign-clause > summary::after {
  content: '+';
  margin-left: auto;
  color: var(--ink-400);
  font-size: 16px;
  line-height: 1;
}

.sign-clause[open] > summary::after { content: '−'; }

.sign-clause-no {
  font-variant-numeric: tabular-nums;
  color: var(--ink-400);
  font-size: 12px;
  min-width: 18px;
}

.sign-clause-head { font-weight: 500; }

.sign-clause > p {
  margin: 0 0 14px 30px;
  color: var(--ink-700);
  font-size: 14px;
  line-height: 1.65;
}

.sign-closing {
  margin: 22px 0 0;
  padding: 14px 16px;
  background: var(--cream-50);
  border-radius: 8px;
  color: var(--ink-700);
  font-size: 13.5px;
  line-height: 1.6;
}

.sign-form {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 22px;
  margin-top: 24px;
}

@media (max-width: 620px) {
  .sign-form { grid-template-columns: 1fr; }
}

.sign-party-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-500);
  margin-bottom: 10px;
}

.sign-optional {
  font-weight: 400;
  letter-spacing: 0.04em;
  text-transform: none;
  color: var(--ink-400);
}

.sign-field { display: block; margin-bottom: 12px; }

.sign-field > span {
  display: block;
  font-size: 12.5px;
  color: var(--ink-500);
  margin-bottom: 5px;
}

.sign-field input {
  width: 100%;
  padding: 11px 12px;
  border: 1px solid var(--ink-200);
  border-radius: 8px;
  font-size: 15px;
  color: var(--ink-900);
  background: #fff;
  box-sizing: border-box;
}

.sign-field input:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-tint);
}

/* The signature reads as one, without pretending to be handwriting a
   keyboard did not produce. */
.sign-signature {
  font-family: 'Fraunces', Georgia, serif;
  font-style: italic;
  font-size: 18px !important;
  letter-spacing: 0.01em;
}

.sign-hint {
  margin-top: -6px;
  font-size: 12.5px;
  color: #b45309;
  line-height: 1.5;
}

.sign-agree {
  display: flex;
  gap: 11px;
  align-items: flex-start;
  margin-top: 20px;
  padding: 14px 16px;
  background: var(--cream-50);
  border-radius: 8px;
  cursor: pointer;
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink-700);
}

.sign-agree input {
  margin-top: 2px;
  width: 17px;
  height: 17px;
  accent-color: var(--brand);
  flex: 0 0 auto;
}

.sign-submit {
  width: 100%;
  margin-top: 16px;
}

.sign-foot {
  margin: 12px 0 0;
  text-align: center;
  font-size: 12.5px;
  color: var(--ink-500);
  line-height: 1.5;
}

/* ── After it is signed ─────────────────────────────────────────── */

.signed { margin-top: 24px; }

.signed-names {
  display: grid;
  gap: 16px;
  margin-top: 18px;
  padding: 18px 20px;
  border: 1px solid var(--ink-100);
  border-radius: 10px;
}

.signed-label {
  display: block;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-500);
}

.signed-sig {
  display: block;
  margin-top: 4px;
  font-family: 'Fraunces', Georgia, serif;
  font-style: italic;
  font-size: 20px;
  color: var(--ink-900);
}

.signed-when {
  display: block;
  margin-top: 2px;
  font-size: 12.5px;
  color: var(--ink-500);
}

/* The one thing still outstanding. A signature alone reserves nothing —
   clause 01 — so the page that just took one says what does. */
.signed-next {
  margin-top: 18px;
  padding: 16px 18px;
  border: 1px solid var(--brand);
  border-radius: 10px;
  background: var(--brand-tint);
}

.signed-next-head {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--brand-strong);
  margin-bottom: 7px;
}

.signed-next p {
  margin: 0;
  font-size: 14px;
  line-height: 1.6;
  color: var(--ink-700);
  white-space: pre-line;
}

/* ── The editorial surfaces: the client portal and the invoice ────────

   One visual language for the two pages a client is sent a link to.
   Warm paper, near-black ink, oversized uppercase display type, and
   black panels used as emphasis rather than as a theme.

   Everything is scoped — `.tp` for the portal, `.doc` for the invoice —
   and every colour is a local token. The booking page shares this file
   and is not part of this: it keeps its own serif, its own green, and
   must not shift a pixel because these two changed.

   Manrope carries the display type at 700 with the tracking pulled in;
   Instrument Serif is kept for the few places a serif says it better.
   Both are self-hosted because the page's CSP allows no third-party
   font host — a Google Fonts link would be blocked silently and the
   whole thing would render in Georgia. */

@font-face {
  font-family: 'Instrument Serif';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/instrument-serif.woff2') format('woff2');
}

@font-face {
  font-family: 'Instrument Serif';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/instrument-serif-italic.woff2') format('woff2');
}

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('fonts/manrope.woff2') format('woff2');
}

.tp, .doc-shell {
  --ed-paper: #F2EEE8;
  --ed-card: #FFFFFF;
  --ed-ink: #111110;
  --ed-ink-soft: #4A4641;
  --ed-ink-mute: #8A837A;
  --ed-ink-faint: #B5AEA4;
  --ed-line: #E2DCD2;
  --ed-gold: #C9A24C;
  --ed-panel: #141210;
  /* Emphasis without inversion: a ground one step warmer than the
     paper, and a gold darkened until small uppercase type on that
     ground clears 4.5:1 — the display gold is for large text only. */
  --ed-live: #FAF6EF;
  --ed-live-line: #E4DACA;
  --ed-gold-ink: #96751F;
  --ed-on-panel: #F6F2EA;
  --ed-on-panel-mute: #A49C90;
  --ed-good: #2F7D57;
  --ed-r: 18px;
}

/* ── The portal ──────────────────────────────────────────────────── */

.tp {
  min-height: 100vh;
  background: var(--ed-paper);
  color: var(--ed-ink);
  font-family: 'Manrope', system-ui, -apple-system, sans-serif;
  padding: 0 22px 90px;
  -webkit-font-smoothing: antialiased;
}

.tp-top {
  max-width: 940px;
  margin: 0 auto;
  padding: 26px 0 40px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.tp-mark { display: flex; align-items: center; gap: 16px; }

/* Sized against the wordmark beside it rather than to a round number.
   The file used to carry its own transparent margin — the mark filled
   43% of its own canvas — so at any height it rendered small and
   floating, with the type next to it doing all the work. Cropped to the
   mark, this height is the mark. */
.tp-logo {
  height: 44px;
  width: auto;
  display: block;
}

.tp-wordmark {
  display: flex;
  flex-direction: column;
  line-height: 1.2;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ed-ink);
}

.tp-wordmark span:last-child { color: var(--ed-ink-mute); }

.tp-avatar {
  width: 34px;
  height: 34px;
  border-radius: 999px;
  background: var(--ed-panel);
  color: var(--ed-on-panel);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12.5px;
  font-weight: 700;
}

.tp-hero, .tp-gate {
  max-width: 940px;
  margin: 0 auto;
  padding: 10px 0 40px;
}

.tp-gate { padding: 70px 0; }

.tp-eyebrow {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ed-gold);
  margin-bottom: 16px;
}

/* The headline the reference is built around: set enormous, uppercase,
   tracked in, and allowed to run to two or three lines. */
.tp-display {
  margin: 0;
  font-family: 'Manrope', system-ui, sans-serif;
  font-weight: 700;
  font-size: clamp(27px, 3.6vw, 40px);
  line-height: 1.06;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  color: var(--ed-ink);
  max-width: 20ch;
}

.tp-gate .tp-display { max-width: 18ch; }

.tp-lede {
  margin: 20px 0 0;
  max-width: 46ch;
  font-size: 15.5px;
  line-height: 1.65;
  color: var(--ed-ink-soft);
}

.tp-lede a, .tp-aside a { color: var(--ed-ink); text-underline-offset: 3px; }

.tp-aside { margin: 24px 0 0; font-size: 13.5px; color: var(--ed-ink-mute); }

.tp-quiet {
  max-width: 940px;
  margin: 0 auto;
  padding: 90px 0;
  color: var(--ed-ink-mute);
  font-size: 14px;
}

/* ── The thread ──────────────────────────────────────────────────── */

.tp-thread {
  max-width: 940px;
  margin: 0 auto;
  position: relative;
  padding-left: 52px;
}

.tp-thread::before {
  content: '';
  position: absolute;
  left: 5px;
  top: 8px;
  bottom: 46px;
  width: 1px;
  background: var(--ed-line);
}

.tp-knot { position: relative; padding-bottom: 16px; }

.tp-dot {
  position: absolute;
  left: -50px;
  top: 30px;
  width: 5px;
  height: 5px;
  border-radius: 999px;
  background: var(--ed-ink-faint);
}

.tp-dot--live {
  left: -51px;
  top: 29px;
  width: 7px;
  height: 7px;
  background: var(--ed-gold);
  box-shadow: 0 0 0 5px rgba(201, 162, 76, 0.18);
}

.tp-era {
  position: relative;
  margin: 22px 0 14px -52px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.16em;
  color: var(--ed-ink-faint);
  font-variant-numeric: tabular-nums;
}

.tp-thread > .tp-era:first-child { margin-top: 0; }

/* ── Cards ───────────────────────────────────────────────────────── */

.tp-card {
  display: block;
  background: var(--ed-card);
  border: 1px solid var(--ed-line);
  border-radius: var(--ed-r);
  padding: 22px 24px;
  text-decoration: none;
  color: inherit;
}

/* The next sitting, emphasised without inverting it.
   This was a black block — the reference's move for the one thing it
   wants read first. But a slab that size sets the mood of the whole
   page, and the mood of a portrait studio is warm, not severe. The
   same emphasis comes from a gold rail and a warmer ground, and the
   page stays light. */
.tp-card--live {
  background: var(--ed-live);
  border-color: var(--ed-live-line);
  color: var(--ed-ink);
  border-left: 3px solid var(--ed-gold);
}

.tp-card--live .tp-card-title { color: var(--ed-ink); }
.tp-card--live .tp-card-sub { color: var(--ed-ink-mute); }
.tp-card--live .tp-label { color: var(--ed-gold-ink); }
.tp-card--live .tp-note {
  background: rgba(17, 17, 16, 0.04);
  color: var(--ed-ink-mute);
}

.tp-card--past {
  background: transparent;
  border-color: transparent;
  padding: 14px 0;
}

.tp-card--link { transition: transform 0.15s ease; }
.tp-card--link:hover { transform: translateX(2px); }

.tp-card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 24px;
}

.tp-label {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ed-ink-mute);
  margin-bottom: 10px;
}

.tp-label--gold { color: var(--ed-gold); }

.tp-card-title {
  margin: 0;
  font-family: 'Manrope', system-ui, sans-serif;
  font-weight: 700;
  font-size: clamp(18px, 1.9vw, 22px);
  line-height: 1.2;
  letter-spacing: -0.015em;
  text-transform: uppercase;
  color: var(--ed-ink);
}

.tp-card-sub {
  margin: 8px 0 0;
  font-size: 14px;
  line-height: 1.6;
  color: var(--ed-ink-soft);
}

.tp-card--past .tp-card-title { font-size: 19px; letter-spacing: -0.01em; }
.tp-card--past .tp-card-sub { color: var(--ed-ink-mute); font-size: 13.5px; }

.tp-more {
  display: inline-block;
  margin-top: 12px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ed-ink);
}

.tp-count {
  flex: 0 0 auto;
  text-align: center;
  padding: 14px 18px;
  border-radius: 16px;
  background: var(--ed-gold);
  color: var(--ed-panel);
}

.tp-count-n {
  display: block;
  font-size: 22px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.tp-count-l {
  display: block;
  margin-top: 4px;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  opacity: 0.75;
}

.tp-note {
  margin: 18px 0 0;
  padding: 14px 16px;
  background: var(--ed-paper);
  border-radius: 14px;
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--ed-ink-soft);
}

/* ── Buttons: pills, as the reference has them ───────────────────── */

.tp-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 22px; }

.tp-btn {
  display: inline-block;
  padding: 11px 20px;
  border-radius: 999px;
  border: 1px solid var(--ed-ink);
  background: transparent;
  color: var(--ed-ink);
  font-family: inherit;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}

.tp-btn:hover { background: var(--ed-ink); color: var(--ed-card); }

.tp-btn--gold {
  background: var(--ed-ink);
  border-color: var(--ed-ink);
  color: var(--ed-card);
}

.tp-btn--gold:hover { background: var(--ed-gold); border-color: var(--ed-gold); color: var(--ed-panel); }

/* On the live card the buttons keep the page's own polarity — it is a
   light card now, so nothing needs to flip. */
.tp-card--live .tp-btn--gold {
  background: var(--ed-gold);
  border-color: var(--ed-gold);
  color: var(--ed-panel);
}

.tp-card--live .tp-btn--gold:hover {
  background: var(--ed-ink);
  border-color: var(--ed-ink);
  color: var(--ed-card);
}

.tp-btn--sm { padding: 10px 18px; font-size: 10.5px; }

.tp-gate .tp-btn { margin-top: 30px; }

/* ── Quiet business ──────────────────────────────────────────────── */

.tp-business {
  max-width: 940px;
  margin: 0 auto 40px;
  padding: 24px 26px 20px;
  background: var(--ed-card);
  border: 1px solid var(--ed-line);
  border-radius: var(--ed-r);
}

.tp-business-lede {
  margin: 0 0 18px;
  font-size: 14px;
  line-height: 1.6;
  color: var(--ed-ink-soft);
  max-width: 52ch;
}

.tp-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  padding: 16px 0;
  border-top: 1px solid var(--ed-line);
}

.tp-row-name {
  font-size: 15.5px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ed-ink);
}

.tp-row-sub { margin-top: 4px; font-size: 13px; color: var(--ed-ink-mute); }

.tp-pay {
  margin: 16px 0 0;
  padding: 14px 16px;
  background: var(--ed-paper);
  border-radius: 14px;
  font-size: 13px;
  line-height: 1.6;
  color: var(--ed-ink-soft);
  white-space: pre-line;
}

.tp-business-foot {
  margin: 16px 0 0;
  font-size: 13px;
  line-height: 1.6;
  color: var(--ed-ink-mute);
}

/* ── Passing it on ───────────────────────────────────────────────── */

/* Also once a black block. Two of them on one page read as a dark
   page with light gaps, which is the opposite of the intent — and an
   invitation to recommend us should be warm, not funereal. */
.tp-pass {
  max-width: 940px;
  margin: 52px auto 0;
  padding: 28px 28px 26px;
  background: var(--ed-live);
  border: 1px solid var(--ed-live-line);
  border-radius: var(--ed-r);
  color: var(--ed-ink);
}

.tp-pass .tp-label--gold { color: var(--ed-gold-ink); }

.tp-pass-title {
  margin: 6px 0 0;
  font-family: 'Manrope', system-ui, sans-serif;
  font-weight: 700;
  font-size: clamp(19px, 2.1vw, 25px);
  line-height: 1.18;
  letter-spacing: -0.015em;
  text-transform: uppercase;
  color: var(--ed-ink);
  max-width: 20ch;
}

.tp-pass-note {
  margin-top: 20px;
  padding: 16px 18px;
  background: var(--ed-card);
  border: 1px solid var(--ed-live-line);
  border-radius: 14px;
  font-size: 14px;
  line-height: 1.6;
  color: var(--ed-ink-mute);
  word-break: break-word;
}

.tp-pass .tp-btn--gold {
  background: var(--ed-gold);
  border-color: var(--ed-gold);
  color: var(--ed-panel);
}

.tp-pass .tp-btn--gold:hover {
  background: var(--ed-ink);
  border-color: var(--ed-ink);
  color: var(--ed-card);
}

.tp-foot {
  max-width: 940px;
  margin: 60px auto 0;
  padding-top: 26px;
  border-top: 1px solid var(--ed-line);
  text-align: center;
  font-size: 12px;
  line-height: 1.9;
  color: var(--ed-ink-mute);
}

.tp-foot a { color: var(--ed-ink-mute); }

@media (max-width: 600px) {
  .tp { padding: 0 16px 64px; }
  .tp-thread { padding-left: 32px; }
  .tp-thread::before { left: 3px; }
  .tp-dot { left: -31px; }
  .tp-dot--live { left: -32px; }
  .tp-era { margin-left: -32px; }
  .tp-card { padding: 22px 20px; }
  .tp-card-head { flex-direction: column; gap: 16px; }
  .tp-count { align-self: flex-start; display: flex; align-items: baseline; gap: 10px; padding: 10px 16px; }
  .tp-count-n { font-size: 24px; }
  .tp-count-l { margin-top: 0; }
  .tp-pass { padding: 26px 22px; }
  .tp-logo { height: 42px; }
  .tp-wordmark { font-size: 12px; }
}

/* ── The invoice, in the same language as the portal ──────────────────

   These override the plainer rules earlier in this file rather than
   replacing them, so the document keeps every state and every element
   it had and only changes how it reads. Appended deliberately: the
   cascade does the work, and the earlier block stays as the record of
   what this page was.

   Same system as `.tp` — warm paper, near-black ink, uppercase display
   type, one black block for the figure that matters. A client who
   opens their invoice and then their portal should not feel they have
   arrived at two different studios. */

.doc-shell {
  background: var(--ed-paper);
  font-family: 'Manrope', system-ui, -apple-system, sans-serif;
  padding: 46px 20px 80px;
  color: var(--ed-ink);
}

.doc-card {
  max-width: 760px;
  background: var(--ed-card);
  border: 1px solid var(--ed-line);
  border-radius: var(--ed-r);
  padding: 32px 34px 28px;
  box-shadow: none;
}

.doc-centered { padding: 70px 40px; }

.doc-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
  padding-bottom: 26px;
  border-bottom: 1px solid var(--ed-line);
}

.doc-eyebrow {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ed-gold);
}

.doc-title {
  margin: 10px 0 0;
  font-family: 'Manrope', system-ui, sans-serif;
  font-weight: 700;
  font-size: clamp(23px, 2.8vw, 30px);
  line-height: 1.1;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  color: var(--ed-ink);
}

.doc-subtitle {
  margin: 10px 0 0;
  font-size: 15px;
  color: var(--ed-ink-soft);
}

.doc-state {
  flex: 0 0 auto;
  padding: 8px 15px;
  border-radius: 999px;
  border: 1px solid var(--ed-line);
  background: transparent;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ed-ink-mute);
}

.doc-state--paid,
.doc-state--accepted {
  border-color: var(--ed-good);
  color: var(--ed-good);
  background: transparent;
}

.doc-state--due,
.doc-state--part_paid {
  border-color: var(--ed-gold);
  color: #8a6c26;
  background: transparent;
}

.doc-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 32px;
  padding: 24px 0;
  border-bottom: 1px solid var(--ed-line);
}

.doc-meta-label {
  display: block;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ed-ink-mute);
  margin-bottom: 6px;
}

.doc-meta-value {
  display: block;
  font-size: 15.5px;
  font-weight: 600;
  color: var(--ed-ink);
}

.doc-meta-sub {
  display: block;
  font-size: 13px;
  color: var(--ed-ink-mute);
  margin-top: 3px;
}

.doc-lines { margin-top: 8px; }

.doc-lines th {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ed-ink-mute);
  border-bottom: 1px solid var(--ed-line);
  padding: 16px 0 10px;
}

.doc-lines td {
  border-bottom: 1px solid var(--ed-line);
  padding: 18px 0;
  color: var(--ed-ink-soft);
}

.doc-line-name {
  font-size: 15.5px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ed-ink);
}

.doc-line-detail {
  font-size: 13.5px;
  color: var(--ed-ink-mute);
  margin-top: 6px;
  line-height: 1.6;
  white-space: pre-line;
}

/* The totals as the one black block on the page — the same move the
   portal makes for the next sitting. It is the figure the client came
   to read. */
/* The totals: a narrow block sitting under its own column, not a band
   across the page. Full width it read as a banner — the heaviest thing
   on a document whose job is to be quietly legible — and it left the
   figures floating a long way from the amounts they total. */
.doc-totals {
  margin: 22px 0 0;
  margin-left: auto;
  width: 100%;
  max-width: 320px;
  padding: 16px 18px;
  background: var(--ed-panel);
  border-radius: 14px;
  align-items: stretch;
  gap: 8px;
}

.doc-total-row {
  min-width: 0;
  font-size: 13px;
  color: var(--ed-on-panel-mute);
}

.doc-total-row--grand {
  font-size: 16px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ed-on-panel);
  padding-top: 10px;
  margin-top: 2px;
  border-top: 1px solid rgba(246, 242, 234, 0.18);
  min-width: 0;
}

.doc-btn {
  padding: 11px 20px;
  border-radius: 999px;
  border: 1px solid var(--ed-ink);
  background: transparent;
  color: var(--ed-ink);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  transition: background 0.15s ease, color 0.15s ease;
}

.doc-btn:hover:not(:disabled) { background: var(--ed-ink); color: var(--ed-card); border-color: var(--ed-ink); }

.doc-btn--primary {
  background: var(--ed-ink);
  border-color: var(--ed-ink);
  color: var(--ed-card);
}

.doc-btn--primary:hover:not(:disabled) {
  background: var(--ed-gold);
  border-color: var(--ed-gold);
  color: var(--ed-panel);
}

/* The state pill and the download button share the header's right
   edge; the pill keeps its size, the button sits under it on narrow
   screens rather than squeezing the pill. */
.doc-head-right {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
}

/* `.doc-btn` carries `flex: 1 1 160px` for the actions row, where 160px
   is a width. This column is the same declaration read as a height, and
   with a 999px radius the download button renders as an ellipse. */
.doc-head-right .doc-btn { flex: 0 0 auto; }

.doc-btn--ghost {
  padding: 8px 16px;
  font-size: 10.5px;
  border-color: var(--ed-line);
  color: var(--ed-ink-mute);
}

.doc-btn--ghost:hover:not(:disabled) {
  background: transparent;
  border-color: var(--ed-ink);
  color: var(--ed-ink);
}

.doc-note {
  border-radius: 16px;
  padding: 16px 18px;
  font-size: 14px;
  line-height: 1.6;
  background: var(--ed-paper);
  color: var(--ed-ink-soft);
  border: 1px solid var(--ed-line);
}

.doc-note--good {
  background: #EEF6F1;
  border-color: #CBE3D6;
  color: #245F41;
}

.doc-notes, .doc-terms { color: var(--ed-ink-mute); }

.doc-foot {
  margin-top: 34px;
  padding-top: 24px;
  border-top: 1px solid var(--ed-line);
  color: var(--ed-ink-mute);
  font-size: 12.5px;
}

/* ── The agreement, in the same hand ─────────────────────────────── */

.sign { border-top-color: var(--ed-line); }

.sign-title {
  font-family: 'Manrope', system-ui, sans-serif;
  font-weight: 700;
  font-size: 22px;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  color: var(--ed-ink);
}

.sign-group-name { color: var(--ed-gold); letter-spacing: 0.16em; }

.sign-clause { border-bottom-color: var(--ed-line); }
.sign-clause > summary { color: var(--ed-ink); }
.sign-clause-no { color: var(--ed-ink-faint); }
.sign-clause > p { color: var(--ed-ink-soft); }

.sign-closing, .sign-agree {
  background: var(--ed-paper);
  border: 1px solid var(--ed-line);
  border-radius: 16px;
  color: var(--ed-ink-soft);
}

.sign-field input {
  border-radius: 12px;
  border-color: var(--ed-line);
  color: var(--ed-ink);
}

.sign-field input:focus {
  border-color: var(--ed-ink);
  box-shadow: 0 0 0 3px rgba(17, 17, 16, 0.08);
}

.sign-party-label { color: var(--ed-ink-mute); letter-spacing: 0.16em; }

.signed-names { border-color: var(--ed-line); border-radius: 18px; }
.signed-label { color: var(--ed-ink-mute); letter-spacing: 0.16em; }
.signed-sig { color: var(--ed-ink); }
.signed-when { color: var(--ed-ink-mute); }

.signed-next {
  border-color: var(--ed-line);
  background: var(--ed-paper);
  border-radius: 18px;
}

.signed-next-head { color: var(--ed-gold); letter-spacing: 0.16em; }
.signed-next p { color: var(--ed-ink-soft); }

@media (max-width: 600px) {
  .doc-card { padding: 26px 22px 24px; border-radius: 18px; }
  .doc-shell { padding: 26px 14px 60px; }
  .doc-head { flex-direction: column; gap: 14px; }

  /*
   * The state and the download, side by side.
   *
   * On a desk these stack at the right-hand edge, under each other and
   * beside the title. On a phone the header itself becomes a column,
   * so that stacking compounds: the pill took a line, the button took
   * another, and the two of them pushed the invoice down half a screen
   * to say what one line says.
   *
   * A row here, wrapping only if a long state label forces it.
   */
  .doc-head-right {
    flex-direction: row;
    align-items: center;
    align-self: stretch;
    flex-wrap: wrap;
    gap: 10px;
  }
}

/* ── Managing a booking, in the same hand ─────────────────────────────

   The page a client reaches from the link in their confirmation: their
   sitting, moving it, giving it away, calling it off.

   Scoped to `.page--ed` rather than `.page`, which the booking page
   also uses. The two share a shell and must not share a look: the
   booking page is the studio's shopfront and keeps its serif and its
   green, while this is a client's own record and belongs with the
   portal and the invoice. */

.page--ed {
  --ed-paper: #F2EEE8;
  --ed-card: #FFFFFF;
  --ed-ink: #111110;
  --ed-ink-soft: #4A4641;
  --ed-ink-mute: #8A837A;
  --ed-line: #E2DCD2;
  --ed-gold: #C9A24C;
  --ed-panel: #141210;
  /* Emphasis without inversion: a ground one step warmer than the
     paper, and a gold darkened until small uppercase type on that
     ground clears 4.5:1 — the display gold is for large text only. */
  --ed-live: #FAF6EF;
  --ed-live-line: #E4DACA;
  --ed-gold-ink: #96751F;
  --ed-on-panel: #F6F2EA;
  --ed-on-panel-mute: #A49C90;
  --ed-r: 24px;

  background: var(--ed-paper);
  font-family: 'Manrope', system-ui, -apple-system, sans-serif;
  color: var(--ed-ink);
  min-height: 100vh;
  padding: 46px 20px 80px;
  max-width: 860px;
  margin: 0 auto;
}

.page--ed .studio-header { text-align: left; margin-bottom: 28px; }

/* Under the title, not down the middle of the page. It reads as a
   byline: who the studio thinks you are, in the page's own muted ink
   rather than the booking page's grey. `!important` because the
   component carries its centring inline. */
.page--ed .signed-in {
  justify-content: flex-start !important;
  margin-top: 10px !important;
  font-size: 12.5px !important;
  color: var(--ed-ink-mute) !important;
  letter-spacing: 0.01em;
}

.page--ed .signed-in strong { color: var(--ed-ink) !important; font-weight: 600; }
.page--ed .signed-in a { color: #A8842F !important; }

.page--ed .studio-header h1 {
  font-family: 'Manrope', system-ui, sans-serif;
  font-weight: 700;
  font-size: clamp(22px, 2.6vw, 28px);
  line-height: 1.14;
  letter-spacing: -0.015em;
  text-transform: uppercase;
  color: var(--ed-ink);
  margin: 0;
}

.page--ed .studio-header img.logo {
  max-height: 64px;
  margin: 0 0 20px;
}

/* The card the whole page lives in. */
.page--ed .headline-card {
  background: var(--ed-card);
  border: 1px solid var(--ed-line);
  border-radius: var(--ed-r);
  padding: 26px 28px;
  box-shadow: none;
  margin-bottom: 18px;
}

.page--ed .headline-card h2 {
  font-family: 'Manrope', system-ui, sans-serif;
  font-weight: 700;
  font-size: 24px;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  color: var(--ed-ink);
}

.page--ed .description,
.page--ed .headline-card p { color: var(--ed-ink-soft); }

/* The facts panel is light. It carries most of the card, and a black
   slab that size stops being emphasis and becomes the mood — the
   invoice can afford one because its block is small and sits under a
   page of white. */
.page--ed .facts {
  background: #FBF9F6;
  border: 1px solid var(--ed-line);
  border-radius: 14px;
  padding: 18px 20px;
  color: var(--ed-ink);
}

.page--ed .facts .muted,
.page--ed .facts dt { color: var(--ed-ink-mute); }

.page--ed .facts a { color: #A8842F; }

/* Pills, as everywhere else in this language. */
.page--ed button,
.page--ed .btn,
.page--ed a.btn {
  border-radius: 999px;
  font-family: inherit;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 14px 24px;
}

.page--ed .btn-primary,
.page--ed button.primary {
  background: var(--ed-ink);
  border-color: var(--ed-ink);
  color: var(--ed-card);
}

.page--ed .btn-primary:hover,
.page--ed button.primary:hover {
  background: var(--ed-gold);
  border-color: var(--ed-gold);
  color: var(--ed-panel);
}

.page--ed .btn-secondary,
.page--ed button.secondary {
  background: transparent;
  border: 1px solid var(--ed-ink);
  color: var(--ed-ink);
}

.page--ed .btn-secondary:hover,
.page--ed button.secondary:hover { background: var(--ed-ink); color: var(--ed-card); }

.page--ed .link,
.page--ed a { color: var(--ed-ink); }

.page--ed .foot,
.page--ed footer {
  margin-top: 34px;
  padding-top: 24px;
  border-top: 1px solid var(--ed-line);
  color: var(--ed-ink-mute);
  font-size: 12.5px;
}

/* The reschedule calendar keeps its green-and-red availability palette:
   that is information, not decoration, and the one thing on this page
   a client reads by colour. Only its frame joins the new language. */
.page--ed .book--embedded {
  border-radius: 18px;
  border-color: var(--ed-line);
  background: var(--ed-card);
}

@media (max-width: 600px) {
  .page--ed { padding: 26px 14px 60px; }
  .page--ed .headline-card { padding: 22px 20px; border-radius: 18px; }
}

/* ── Lightbox ───────────────────────────────────────────────────────
 *
 * Used by the manage-booking page for moving, passing on and
 * cancelling. Full-screen on a phone, a centred panel from 640px up:
 * a 90%-width dialog on a 390px screen leaves 20px gutters that hold
 * nothing, and the slot picker inside wants every pixel of width.
 */
.lb-backdrop {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(17, 17, 16, 0.42);
  backdrop-filter: blur(2px);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 0;
  overscroll-behavior: contain;
}

.lb-panel {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-height: 100%;
  /* Small viewport units: on iOS the address bar otherwise eats the
     bottom of the panel, taking the confirm button with it. */
  max-height: 100dvh;
  background: var(--ed-card, #fff);
  color: var(--ed-ink, #111110);
  border-radius: 20px 20px 0 0;
  box-shadow: 0 -8px 40px rgba(17, 17, 16, 0.18);
  animation: lb-rise 0.22s ease-out;
}

@keyframes lb-rise {
  from { transform: translateY(16px); opacity: 0; }
  to   { transform: translateY(0);    opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .lb-panel { animation: none; }
}

.lb-head {
  flex: 0 0 auto;
  display: flex;
  align-items: flex-start;
  gap: 16px;
  padding: 18px 20px 14px;
  border-bottom: 1px solid var(--ed-line, #E2DCD2);
}

.lb-title {
  margin: 0;
  flex: 1;
  font-family: 'Manrope', system-ui, sans-serif;
  font-size: 15px;
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  color: var(--ed-ink, #111110);
}

.lb-close {
  flex: 0 0 auto;
  /* 40px: a thumb target, not a 16px glyph. */
  width: 40px;
  height: 40px;
  margin: -8px -8px 0 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 999px;
  background: transparent;
  color: var(--ed-ink-mute, #8A837A);
  font-size: 26px;
  line-height: 1;
  cursor: pointer;
}

.lb-close:hover:not(:disabled) { background: rgba(17, 17, 16, 0.06); color: var(--ed-ink, #111110); }
.lb-close:disabled { opacity: 0.4; cursor: default; }

.lb-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  /* Clear of the home indicator on a phone. */
  padding-bottom: env(safe-area-inset-bottom, 0px);
}

/* The panel is the card, so the content inside it must not draw a
   second one. */
.lb-body { padding: 0; }

.lb-body .headline-card,
.lb-body .book {
  margin: 0;
  padding: 18px 20px 22px;
  max-width: none;
  background: transparent;
  border: none;
  border-radius: 0;
  box-shadow: none;
}

.lb-body .headline-card + .headline-card { padding-top: 0; }

@media (min-width: 640px) {
  .lb-backdrop { align-items: center; padding: 24px; }

  .lb-panel {
    width: min(560px, 100%);
    max-height: min(88dvh, 900px);
    border-radius: 20px;
    box-shadow: 0 24px 60px rgba(17, 17, 16, 0.24);
  }

  .lb-panel--wide { width: min(820px, 100%); }

  .lb-head { padding: 20px 26px 16px; }
  .lb-title { font-size: 16px; }
  .lb-body .headline-card,
  .lb-body .book { padding: 20px 26px 26px; }
}

/* ── Phones ──────────────────────────────────────────────────────────
 *
 * The client-facing pages — the portal, the invoice, and the page for
 * managing a booking — are opened on a phone far more often than on a
 * desk. What follows is the small-screen half of those three designs:
 * it changes size and flow, never the language.
 */

/* A row of choices becomes a column. The buttons carry inline
   `flex: 1` and `min-width`, which is why these need `!important`:
   two 180px minimums fit a 375px screen by three pixels, and what a
   client actually sees is every label broken across two lines. */
.btn-row {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
}

@media (max-width: 560px) {
  .btn-row > button,
  .btn-row > a {
    flex: 1 1 100% !important;
    min-width: 0 !important;
  }
}

@media (max-width: 480px) {
  /* Facts: the label column was wrapping "People in the photos" onto
     two lines and taking the value with it. */
  .page--ed .fact-row,
  .page--ed .facts .fact-row {
    font-size: 14px;
    gap: 12px;
  }

  .page--ed .headline-card > h1,
  .page--ed .studio-header h1 { font-size: 26px; }

  /* The invoice's line table: quantity is the first thing to go when
     every line is a single item anyway. */
  .doc-lines th:nth-child(2),
  .doc-lines td:nth-child(2) { display: none; }

  .doc-lines th,
  .doc-lines td { padding: 14px 0; }

  .doc-line-name { font-size: 14.5px; }
  .doc-line-detail { font-size: 12.5px; }

  /* The totals block stops being a narrow right-hand column when there
     is no width to sit beside. */
  .doc-totals { max-width: none; }

  /*
   * The meta pairs read as two columns rather than one long ladder.
   * At 32px of gap on a 390px screen "Payment due" wrapped under
   * "Issued" and the two dates lost their relationship to each other.
   */
  .doc-meta {
    gap: 16px 20px;
    padding: 18px 0;
  }

  .doc-meta > * { flex: 1 1 42%; min-width: 0; }
  .doc-meta-value { font-size: 14.5px; }
  .doc-meta-sub { font-size: 12px; }

  /* The studio's name is the largest thing on the page and does not
     need to be; the invoice number above it is what identifies this
     document. */
  .doc-title { font-size: 24px; }
  .doc-subtitle { font-size: 14px; margin-top: 6px; }
  .doc-card { padding: 22px 18px 20px; }

  .doc-state { font-size: 10px; padding: 7px 13px; }
  .doc-btn--ghost { padding: 7px 13px; }

  /* Actions go full width: on a phone a row of pills each sized to its
     own label is a ragged edge, and the primary one is easy to miss. */
  .doc-actions { flex-direction: column; align-items: stretch; }
  .doc-actions .doc-btn { flex: 0 0 auto; width: 100%; }

  /* The portal's day count: a chip beside the title on a desk, a quiet
     line of its own here rather than a gold slab. */
  .tp-count {
    padding: 8px 14px;
    border-radius: 12px;
  }

  .tp-count-n { font-size: 19px; }

  .tp-card-actions { gap: 10px; }
  .tp-card-actions .tp-btn { flex: 1 1 auto; text-align: center; }
}

/* Touch targets. A 34px pill is comfortable with a mouse and small
   under a thumb; 44px is the platform minimum on both iOS and Android.
 *
 * Keyed on width as well as pointer type, because `pointer: coarse`
 * misses a narrow desktop window and a phone that has not yet been
 * told it is one — and a button too small to hit reliably is a worse
 * outcome than one four pixels taller than it needed to be. */
@media (pointer: coarse), (max-width: 560px) {
  .tp-btn,
  .doc-btn,
  .btn-row > button,
  .btn-row > a { min-height: 44px; }

  .lb-close { width: 44px; height: 44px; }
}

/* ── Choosing how to pay ──────────────────────────────────────────────
 *
 * Two full-width rows, stacked, not two cards side by side.
 *
 * Side by side was the first attempt and it failed on the only width
 * that matters: inside a 520px dialog, two columns each holding a
 * sentence and a price left four words to a line and made a two-line
 * heading out of "Confirmed straight away". Stacked, the text has the
 * whole width and the two prices sit in a column where they can
 * actually be compared, which is the comparison being made.
 */
.pay-choice {
  margin-top: 18px;
}

.pay-choice-head {
  font-size: 13px;
  font-weight: 600;
  color: #111110;
  margin-bottom: 8px;
}

.pay-choice-options {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.pay-option {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  text-align: left;
  padding: 13px 15px;
  border: 1px solid #E2DCD2;
  border-radius: 8px;
  background: #FFFDFB;
  cursor: pointer;
  font: inherit;
  /* Colour only. A card that grows on selection shifts the one below
     it under the pointer. */
  transition: border-color .12s ease, background-color .12s ease;
}

.pay-option:hover { border-color: #C9BFAE; }

.pay-option.is-on {
  border-color: #8A6A3B;
  background: #FBF7F0;
  /* A ring rather than a thicker border, so the row does not change
     height and nudge its neighbour when picked. */
  box-shadow: 0 0 0 1px #8A6A3B;
}

.pay-option-mark {
  font-size: 12px;
  color: #8A6A3B;
  flex: 0 0 auto;
}

.pay-option-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1 1 auto;
  min-width: 0;
}

.pay-option-lead {
  font-size: 14px;
  font-weight: 600;
  color: #111110;
  line-height: 1.3;
}

.pay-option-detail {
  font-size: 12px;
  color: #6B6558;
  line-height: 1.45;
}

.pay-option-money {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 1px;
  flex: 0 0 auto;
  /* Tabular figures so the two amounts line up under each other. */
  font-variant-numeric: tabular-nums;
}

.pay-option-money strong {
  font-size: 16px;
  color: #111110;
  line-height: 1.2;
}

.pay-option-note {
  font-size: 11px;
  color: #6B6558;
  white-space: nowrap;
}

@media (max-width: 420px) {
  /*
   * ── Why the fee note is no longer hidden here ─────────────────────
   *
   * It used to be `display: none` on a narrow phone, on the reasoning
   * that the price was the part being compared. But the price is
   * exactly what cannot be compared without it: one option shows a
   * larger number than the other, and the line explaining why was the
   * thing being dropped. On a phone — where most of these bookings are
   * made — the client saw two prices and no reason for the difference.
   *
   * So the row stacks instead. The money moves under the text and
   * aligns left with it, which gives the note a full line to sit on
   * rather than a corner to be squeezed out of.
   */
  .pay-option {
    align-items: flex-start;
    flex-wrap: wrap;
  }

  .pay-option-text { flex: 1 1 100%; }

  .pay-option-money {
    flex: 1 1 100%;
    flex-direction: row;
    align-items: baseline;
    justify-content: flex-start;
    gap: 8px;
    /* Clears the radio mark so the amount lines up with the text
       above it rather than with the edge of the card. */
    padding-left: 24px;
  }

  .pay-option-note {
    /* Wraps now, because it has a whole line and something worth
       saying: "$25 deposit + $0.75 card fee (3%)". */
    white-space: normal;
    font-size: 11.5px;
  }

  .pay-option-detail { font-size: 11.5px; }
}

/* Coming back from the card page. */
.pay-return {
  margin: 0 0 16px;
  padding: 12px 14px;
  border-radius: 8px;
  border: 1px solid #E2DCD2;
  background: #FBF9F6;
  font-size: 13.5px;
  line-height: 1.5;
  color: #111110;
}

.pay-return.is-paid {
  border-color: #7FA37F;
  background: #F3F8F3;
}

/* ── Buying a session as a gift ──────────────────────────────────────
 *
 * A disclosure inside the booking form, not a second page. Closed, it
 * is one line somebody buying for themselves reads past; open, it is
 * the only part of the form with a tinted ground, because from that
 * point on the questions are about a different person and the change
 * of subject should be visible rather than inferred.
 *
 * The tint is the studio's own gold at very low opacity rather than a
 * new colour: this is a warmer, more personal path through the same
 * form, and introducing a second accent to say so would make it look
 * like a different product.
 */
.gift-panel {
  margin: 4px 0 18px;
  border: 1px solid var(--ink-200);
  border-radius: var(--radius);
  background: var(--surface);
  transition: border-color 0.15s ease, background 0.15s ease;
}
.gift-panel--on {
  border-color: var(--brand);
  background: var(--cream-50);
}

.gift-toggle {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 12px 14px;
  cursor: pointer;
}
.gift-toggle input {
  /* Nudged down so it sits on the first line of text rather than
     floating above the two-line label. */
  margin-top: 2px;
  width: 16px;
  height: 16px;
  flex: none;
  accent-color: var(--brand);
  cursor: pointer;
}
.gift-toggle-text {
  display: block;
  line-height: 1.35;
}
.gift-toggle-text strong {
  display: block;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink-900);
}
.gift-toggle-text em {
  display: block;
  font-style: normal;
  font-size: 12px;
  color: var(--ink-500);
  margin-top: 2px;
}

.gift-body {
  padding: 0 14px 14px;
  border-top: 1px solid var(--brand-tint);
  margin-top: 2px;
}
.gift-note {
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--ink-700);
  background: var(--brand-tint);
  border-radius: var(--radius);
  padding: 9px 11px;
  margin: 12px 0 16px;
}

/* The gallery choice: two plain options, neither dressed as the better
   one. Same shape as the payment chooser so the form has one idea of
   what "pick one of these" looks like. */
.gift-gallery {
  margin: 4px 0 16px;
}
.gift-gallery-head {
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-700);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 8px;
}
.gift-opt {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 10px 12px;
  border: 1px solid var(--ink-200);
  border-radius: var(--radius);
  background: var(--surface);
  cursor: pointer;
  margin-bottom: 8px;
}
.gift-opt:last-child { margin-bottom: 0; }
.gift-opt--on {
  border-color: var(--brand);
  box-shadow: 0 0 0 2px var(--brand-tint);
}
.gift-opt input {
  margin-top: 2px;
  width: 16px;
  height: 16px;
  flex: none;
  accent-color: var(--brand);
  cursor: pointer;
}
.gift-opt span { display: block; line-height: 1.35; }
.gift-opt strong {
  display: block;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink-900);
}
.gift-opt em {
  display: block;
  font-style: normal;
  font-size: 12px;
  color: var(--ink-500);
  margin-top: 2px;
}

/* What happens next, in the order it happens. Ticks rather than
   bullets: every line is a reassurance, and a bullet list of
   reassurances reads as terms and conditions. */
.gift-steps {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--brand-tint);
  padding-top: 12px;
}
.gift-steps li {
  position: relative;
  padding-left: 20px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink-700);
  margin-bottom: 5px;
}
.gift-steps li:last-child { margin-bottom: 0; }
.gift-steps li::before {
  content: '✓';
  position: absolute;
  left: 0;
  top: 0;
  color: var(--brand);
  font-size: 12px;
  font-weight: 700;
}

/* The total's own note, when the session is a present. Gold rather
   than grey: on a gift it is not small print, it is the answer to
   "will they find out what I spent?" */
.book-quote-note--gift {
  color: var(--brand-strong);
  font-weight: 500;
}

/* ── The recipient's page ────────────────────────────────────────────
 *
 * Warmer than the cancellation page it sits beside, and deliberately
 * so: that page is somebody administering a booking, this one is
 * somebody finding out they have been given something. The gold is
 * used at full strength here — on the eyebrow, the quote rule and the
 * one button — where elsewhere it is a tint.
 *
 * There is no destructive action on this page, so nothing needs the
 * quiet-red treatment the cancel page uses. One button, and it is the
 * friendly one.
 */
.gift-eyebrow {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--brand-strong);
  margin-bottom: 10px;
}

/* The buyer's own words. Set in their voice, not the studio's: a note
   folded into the surrounding copy stops reading as something a friend
   wrote. */
.gift-quote {
  margin: 18px 0;
  padding: 2px 0 2px 16px;
  border-left: 3px solid var(--brand);
  font-size: 15px;
  line-height: 1.55;
  font-style: italic;
  color: var(--ink-900);
}

.gift-facts {
  margin: 20px 0 0;
  padding: 0;
  border-top: 1px solid var(--ink-100);
}
.gift-facts > div {
  display: flex;
  gap: 16px;
  padding: 11px 0;
  border-bottom: 1px solid var(--ink-100);
}
.gift-facts dt {
  flex: none;
  width: 108px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-500);
  padding-top: 2px;
}
.gift-facts dd {
  margin: 0;
  flex: 1;
  font-size: 14.5px;
  line-height: 1.5;
  color: var(--ink-900);
}
.gift-facts a { color: var(--brand-strong); }

/* The reassurance, stated plainly. It is the sentence that answers
   what somebody handed a present actually wonders. */
.gift-paid {
  margin-top: 18px;
  padding: 11px 13px;
  background: var(--brand-tint);
  border-radius: var(--radius);
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--ink-900);
}

.gift-btn {
  padding: 11px 20px;
  background: var(--brand);
  color: #fff;
  border: 1px solid var(--brand);
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  cursor: pointer;
}
.gift-btn:hover { background: var(--brand-strong); border-color: var(--brand-strong); }
.gift-btn:disabled { opacity: 0.55; cursor: default; }
.gift-btn--ghost {
  background: transparent;
  color: var(--ink-900);
  border-color: var(--ink-900);
}
.gift-btn--ghost:hover { background: var(--ink-100); border-color: var(--ink-900); }

.gift-remaining {
  display: block;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink-500);
  margin-top: 8px;
}

.gift-error {
  margin: 12px 0;
  padding: 12px;
  background: var(--booked-tint);
  border: 1px solid var(--booked-line, #fecaca);
  border-radius: var(--radius);
  color: #991b1b;
  font-size: 14px;
}

.gift-moved {
  margin: 14px 0 4px;
  padding: 11px 13px;
  background: var(--available-tint);
  border: 1px solid var(--available-line);
  border-radius: var(--radius);
  color: var(--available-text);
  font-size: 13.5px;
}

@media (max-width: 480px) {
  /* The label column costs more than it earns on a phone: at this
     width "Who it is for" wraps to three lines beside a value that
     fits on one. */
  .gift-facts > div { display: block; }
  .gift-facts dt { width: auto; margin-bottom: 3px; }
}
