@import url('/css/fonts.css');
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0;}
:root{
  --ink:#2c1810;--ink-light:#5c3d2e;
  --gold:#d8b85c;--gold-light:#e8d5a3;--gold-dim:rgba(201,168,76,0.6);
  --crimson:#8b1a1a;--crimson-light:#fcebeb;
  --parchment:#f0e8d0;--parchment-dark:#e0d0a8;
  --cream:#f5e8c8;
  --bg:#f9f7f4;--surface:#ffffff;
  --border:#e2ddd5;--border-dark:#c4bfb5;
  --text:#1a1410;--text-muted:#6b5f55;--text-light:#9e9088;
  --success:#0f6e56;--success-bg:#e1f5ee;
  --error:#a32d2d;--error-bg:#fcebeb;
  --warning:#8a5a00;--warning-bg:#fbf0d8;
  --info:#1f5066;--info-bg:#e6f0f4;
  --shadow:0 1px 3px rgba(0,0,0,0.08);
  --shadow-md:0 4px 16px rgba(0,0,0,0.12);
  --radius:8px;--radius-lg:12px;
  --font-display:'Cinzel',Georgia,serif;
}
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;background:#0a0806;background-image:url('/images/Campaignia_background.png');background-size:cover;background-position:center;background-attachment:fixed;color:var(--text);min-height:100vh;font-size:14px;line-height:1.5;}

.hidden{display:none!important;}
.flex{display:flex;}.items-center{align-items:center;}.justify-between{justify-content:space-between;}
.gap-1{gap:8px;}.gap-2{gap:16px;}.w-full{width:100%;}

/* BUTTONS */
.btn{padding:8px 16px;border-radius:var(--radius);font-size:13px;font-weight:500;cursor:pointer;border:1px solid rgba(201,168,76,0.3);background:rgba(30,20,10,0.7);color:var(--gold-light);white-space:nowrap;transition:all 0.15s;display:inline-flex;align-items:center;gap:6px;}
.btn:hover{background:rgba(201,168,76,0.15);border-color:rgba(201,168,76,0.6);color:var(--gold);}
.btn:active{transform:scale(0.98);}
.btn:disabled{opacity:0.5;cursor:not-allowed;transform:none;}
.btn-primary{background:var(--gold);color:var(--ink);border-color:var(--gold);font-weight:600;}
.btn-primary:hover{background:var(--gold-light);border-color:var(--gold-light);color:var(--ink);}
.btn-danger{background:var(--crimson);color:#fff;border-color:var(--crimson);}
.btn-danger:hover{background:#6f1414;color:#fff;border-color:#6f1414;}
.lib-remove-btn{background:rgba(139,26,26,0.45);color:#f0c4c4;border-color:rgba(240,149,149,0.4);}
.lib-remove-btn:hover{background:#6e1a1a;color:#ffe5e5;border-color:#a83232;}
.btn-sm{padding:5px 10px;font-size:12px;}
.btn-lg{padding:12px 24px;font-size:14px;}
.btn-ghost{border-color:transparent;background:transparent;}
.btn-ghost:hover{background:rgba(201,168,76,0.1);}

/* FORMS */
.form-group{margin-bottom:16px;}
.form-label{display:block;font-size:11px;font-weight:600;color:rgba(201,168,76,0.6);text-transform:uppercase;letter-spacing:0.06em;margin-bottom:5px;}
.form-input{width:100%;padding:9px 12px;border:1px solid rgba(201,168,76,0.2);border-radius:var(--radius);font-size:13px;background:rgba(20,12,4,0.75);color:var(--parchment);font-family:inherit;transition:border-color 0.15s;}
.form-input:focus{outline:none;border-color:rgba(201,168,76,0.6);}
.form-input::placeholder{color:rgba(201,168,76,0.55);}
.form-textarea{width:100%;padding:9px 12px;border:1px solid rgba(201,168,76,0.2);border-radius:var(--radius);font-size:13px;background:rgba(20,12,4,0.75);color:var(--parchment);font-family:inherit;min-height:120px;resize:vertical;transition:border-color 0.15s;}
.form-textarea:focus{outline:none;border-color:rgba(201,168,76,0.6);}
.form-textarea::placeholder{color:rgba(201,168,76,0.55);}
.form-hint{font-size:11px;color:var(--text-muted);margin-top:4px;}
/* v3.0.725 -- TD-524. The cost note inside a button. Two steps down from the button label and
   slightly dimmed, so the price reads as a footnote rather than as part of the action. em
   rather than px: buttons in this product are not all one size, and a fixed pixel value would
   look right on the one it was measured against and wrong on the next. */
.btn-cost{font-size:0.8em;opacity:0.75;font-weight:400;}
/* Hint/instruction text: readable on light panels by default; .panel-dark for dark. */
.panel-dark .form-hint{color:rgba(232,213,163,0.72);}

/* ALERTS */
.alert{padding:10px 14px;border-radius:var(--radius);font-size:13px;margin-bottom:16px;}
/* ===== STATUS MESSAGE CONTRAST CONTRACT =====
   Alerts live on WHITE panels (modals, settings sections), so the DEFAULT is the
   light-surface treatment: dark readable text on a light tint, pulled from the
   semantic tokens. For an alert on a dark/transparent panel, add .panel-dark to an
   ancestor for the light-on-dark variant. NEVER hand-color a status message --
   use these classes so contrast is guaranteed everywhere. */
.alert-error{background:var(--error-bg);border:1px solid rgba(163,45,45,0.35);color:#3a1010;}
.alert-success{background:var(--success-bg);border:1px solid rgba(15,110,86,0.35);color:#0d2a20;}
.alert-warning{background:var(--warning-bg);border:1px solid rgba(138,90,0,0.35);color:#3a2600;}
.alert-info{background:var(--info-bg);border:1px solid rgba(31,80,102,0.35);color:#0f2a35;}
.panel-dark .alert-error{background:rgba(139,26,26,0.35);border-color:rgba(240,149,149,0.4);color:#f09595;}
.panel-dark .alert-success{background:rgba(15,110,86,0.3);border-color:rgba(134,212,186,0.4);color:#86d4ba;}
.panel-dark .alert-warning{background:rgba(120,80,0,0.32);border-color:rgba(230,190,120,0.4);color:#e6be78;}
/* v3.0.628 -- the warning text colour as a token, so the Optimize busy line and the alert it
   belongs with cannot drift to two different oranges. Same value as the rule above. */
:root{--warn-text:#e6be78;}
.panel-dark .alert-info{background:rgba(40,60,80,0.4);border-color:rgba(150,180,200,0.4);color:#a8c4d6;}
/* v3.0.493 -- A WARNING IN THE OPTIMIZE PROGRESS LIST SHOULD LOOK LIKE ONE.
   The stale-layout notice was written into #optimize-progress as an ordinary bullet,
   identical to every other line, so it read as one more progress step and was never
   noticed. Ian: "make it look a little more like a warning... look at how we do other
   warnings in the CSS and mimic that."
   These are the .panel-dark .alert-warning colours above, verbatim. They are restated
   rather than inherited because #optimize-progress is not inside a .panel-dark ancestor,
   and a style that depends on where it happens to sit is a style that stops working when
   the box moves. */
.opt-line-warn{display:block;background:rgba(120,80,0,0.32);border:1px solid rgba(230,190,120,0.4);color:#e6be78;border-radius:var(--radius);padding:8px 12px;margin:8px 0;line-height:1.5;}
.opt-line-warn strong{color:#f0d9a8;}

/* ===== APP HEADER ===== */
.app-header{
  background:var(--ink);
  padding:0 1.5rem;
  display:flex;
  align-items:center;
  gap:12px;
  height:44px;
  position:relative;
  z-index:50;
  border-bottom:1px solid rgba(201,168,76,0.15);
  box-shadow:0 2px 12px rgba(0,0,0,0.4);
}
.app-logo{
  height:44px;
  display:flex;align-items:center;gap:10px;
  flex-shrink:0;
}
.app-logo-text{
  font-family:var(--font-display);
  font-size:18px;
  font-weight:700;
  color:var(--gold);
  letter-spacing:4px;
  white-space:nowrap;
}
.app-logo img{height:32px;width:auto;object-fit:contain;display:block;}
.app-name{
  font-family:var(--font-display);
  font-size:20px;
  font-weight:700;
  color:var(--gold);
  letter-spacing:3px;
  text-shadow:0 0 20px rgba(201,168,76,0.3);
}
.app-header-right{margin-left:auto;display:flex;align-items:center;gap:10px;}
.app-header-right > .btn,.app-header-right > #token-balance-label{height:30px;}
.app-header-right > .btn{font-size:13px;font-weight:400;}

/* USER MENU */
.user-menu-wrap{position:relative;}
.user-menu-btn{display:flex;align-items:center;gap:8px;padding:0 10px;height:30px;border-radius:var(--radius);cursor:pointer;border:1px solid rgba(201,168,76,0.3);background:rgba(30,20,10,0.7);color:var(--gold-light);font-size:13px;font-weight:400;transition:all 0.15s;}
.user-menu-btn:hover{background:rgba(201,168,76,0.15);border-color:rgba(201,168,76,0.6);}
.user-avatar{width:22px;height:22px;border-radius:50%;background:var(--gold);color:var(--ink);font-size:11px;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-family:var(--font-display);}
.user-menu-dropdown{position:absolute;top:calc(100% + 8px);right:0;background:var(--ink);border:1px solid rgba(201,168,76,0.2);border-radius:var(--radius-lg);box-shadow:0 8px 24px rgba(0,0,0,0.5);min-width:190px;overflow:hidden;display:none;z-index:100;}
.user-menu-dropdown.open{display:block;}
.user-menu-item{padding:10px 14px;font-size:13px;color:var(--gold-light);cursor:pointer;display:flex;align-items:center;gap:8px;transition:background 0.1s;}
.user-menu-item:hover{background:rgba(201,168,76,0.1);}
.user-menu-item.danger{color:#f09595;}
.user-menu-item.danger:hover{background:rgba(139,26,26,0.3);}
.user-menu-divider{height:1px;background:rgba(201,168,76,0.15);margin:4px 0;}
.user-menu-header{padding:10px 14px;font-size:11px;color:rgba(201,168,76,0.5);border-bottom:1px solid rgba(201,168,76,0.15);}

/* BREADCRUMB */
.breadcrumb{display:flex;align-items:center;gap:6px;font-size:12px;color:rgba(201,168,76,0.6);flex-wrap:wrap;flex:1 1 auto;min-width:0;}
.breadcrumb-link{color:var(--gold);cursor:pointer;font-weight:500;}
.breadcrumb-link:hover{text-decoration:underline;}
.breadcrumb-sep{color:rgba(201,168,76,0.3);font-size:11px;}
.breadcrumb-current{color:var(--gold-light);font-weight:500;}

/* LAYOUT */
.app-layout{display:flex;min-height:calc(100vh - 44px - 35px);}
.sidebar{width:220px;background:rgba(20,15,8,0.82);border-right:1px solid rgba(201,168,76,0.15);flex-shrink:0;padding:12px 0;backdrop-filter:blur(8px);}
.sidebar-section{margin-bottom:4px;}
.sidebar-label{font-size:10px;font-weight:700;color:var(--text-light);text-transform:uppercase;letter-spacing:0.1em;padding:0 14px;margin-bottom:3px;margin-top:10px;font-family:var(--font-display);}
.sidebar-item{padding:8px 14px;font-size:13px;color:var(--text-muted);cursor:pointer;display:flex;align-items:center;gap:8px;border-left:2px solid transparent;transition:all 0.12s;}
.sidebar-item:hover{background:var(--bg);color:var(--text);}
.sidebar-item.active{background:var(--crimson-light);color:var(--crimson);border-left-color:var(--crimson);font-weight:500;}
.sidebar-divider{height:1px;background:var(--border);margin:8px 14px;}
.main-content{flex:1;overflow-y:auto;max-width:100%;}

/* ===== CAMPAIGNS VIEW — epic background ===== */
#view-campaigns{
  min-height:calc(100vh - 44px - 35px);
  background:#0a0806;
  background-image:url('/images/Campaignia_background.png');
  background-size:cover;
  background-position:center;
  position:relative;
  padding:2rem 1.5rem;
}
#view-campaigns::before{
  content:'';
  position:absolute;
  inset:0;
  background:rgba(0,0,0,0.55);
  pointer-events:none;
}
#view-campaigns .page-header,
#view-campaigns .campaigns-grid{
  position:relative;
  z-index:1;
}
#view-campaigns .page-title{
  font-family:var(--font-display);
  color:var(--gold);
  font-size:26px;
  letter-spacing:2px;
  text-shadow:0 2px 8px rgba(0,0,0,0.6);
}
#view-campaigns .page-subtitle{
  color:rgba(201,168,76,0.6);
  font-size:13px;
}

/* PAGE */
.page-header{display:flex;align-items:flex-start;justify-content:space-between;margin-bottom:20px;gap:12px;}
.page-title{font-size:20px;font-weight:600;color:var(--text);letter-spacing:-0.3px;}
.page-subtitle{font-size:13px;color:var(--text-muted);margin-top:2px;}
.main-content > div:not(#view-campaigns){padding:1.5rem;}

/* Glass dark panels for all views except campaigns */
#view-sessions,#view-characters,#view-novel,
#view-session-detail,#view-settings{
  background:rgba(12,8,4,0.72);
  min-height:calc(100vh - 44px - 35px);
  backdrop-filter:blur(10px);
}
#view-sessions .page-title,#view-characters .page-title,
#view-novel .page-title,#view-session-detail .page-title,
#view-settings .page-title{
  color:var(--gold);font-family:var(--font-display);letter-spacing:1px;
}
#view-sessions .page-subtitle,#view-characters .page-subtitle,
#view-novel .page-subtitle,#view-session-detail .page-subtitle,
#view-settings .page-subtitle{color:rgba(201,168,76,0.5);}

/* Make cards and surfaces slightly translucent */
.session-item{background:rgba(30,22,12,0.85);border-color:rgba(201,168,76,0.15);color:var(--gold-light);}
.session-item:hover{border-color:var(--gold);background:rgba(50,35,15,0.92);box-shadow:0 0 0 1px rgba(201,168,76,0.3);}
.session-name{color:var(--gold-light);}
.session-date{color:rgba(201,168,76,0.45);}
.session-num{background:rgba(201,168,76,0.1);border-color:rgba(201,168,76,0.2);color:var(--gold);}
.char-card{background:rgba(25,18,10,0.85);border-color:rgba(201,168,76,0.15);}
.char-card:hover{border-color:rgba(201,168,76,0.4);}
.char-name{color:var(--gold-light);}
.char-player{color:rgba(201,168,76,0.5);}
.char-desc{color:rgba(201,168,76,0.45);}
.char-badge{background:rgba(201,168,76,0.08);border-color:rgba(201,168,76,0.15);color:rgba(201,168,76,0.6);}
.char-btn{background:rgba(201,168,76,0.08);border-color:rgba(201,168,76,0.2);color:rgba(201,168,76,0.6);}
.char-btn:hover{background:rgba(201,168,76,0.2);color:var(--gold);}
.char-btn-delete:hover{background:rgba(139,26,26,0.4);color:#f09595;border-color:rgba(240,149,149,0.4);}
.add-char-card{background:rgba(20,14,6,0.6);border-color:rgba(201,168,76,0.2);color:rgba(201,168,76,0.4);}
.add-char-card:hover{border-color:rgba(201,168,76,0.5);color:var(--gold);background:rgba(40,28,10,0.7);}
.moment-card{background:rgba(25,18,10,0.85);border-color:rgba(201,168,76,0.15);}
.moment-img{background:rgba(15,10,5,0.6);border-bottom-color:rgba(201,168,76,0.1);}
.moment-title{color:var(--gold-light);}
.moment-desc{color:rgba(201,168,76,0.5);}
.moment-prompt{color:rgba(201,168,76,0.35);border-top-color:rgba(201,168,76,0.1);}
.settings-section{background:rgba(25,18,10,0.85);border-color:rgba(201,168,76,0.15);}
.settings-section-title{color:var(--gold-light);}
.settings-section-desc{color:rgba(201,168,76,0.5);}
.form-label{color:rgba(201,168,76,0.6);}
.form-input{background:rgba(15,10,5,0.7);border-color:rgba(201,168,76,0.2);color:var(--gold-light);}
.form-input:focus{border-color:rgba(201,168,76,0.6);}
.form-input::placeholder{color:rgba(201,168,76,0.55);}
.form-textarea{background:rgba(15,10,5,0.7);border-color:rgba(201,168,76,0.2);color:var(--gold-light);}
.form-textarea:focus{border-color:rgba(201,168,76,0.6);}
.form-textarea::placeholder{color:rgba(201,168,76,0.55);}
.form-hint{color:var(--text-muted);}
.transcript-box{background:rgba(15,10,5,0.7);border-color:rgba(201,168,76,0.2);color:var(--parchment);}
.transcript-box:focus{border-color:rgba(201,168,76,0.5);}
.transcript-box::placeholder{color:rgba(201,168,76,0.55);}

/* Session Notes tab — transcript + notes side by side */
.notes-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;}
@media (max-width:860px){.notes-grid{grid-template-columns:1fr;}}

/* Review tab — storyboard outline (intro → panel → bridge → panel → ... → outro) */
.review-actionbar{display:flex;align-items:center;justify-content:space-between;
  gap:14px;flex-wrap:wrap;margin-bottom:14px;}

/* Narrative blocks — the through-line. Soft, italic, indented. */
.review-nar{padding:10px 14px;margin:6px 0;border-left:3px solid rgba(201,168,76,0.4);
  background:rgba(201,168,76,0.05);border-radius:0 var(--radius) var(--radius) 0;}
.review-nar-open,.review-nar-close{border-left-color:var(--gold);
  background:rgba(201,168,76,0.08);}
.review-nar-label{font-size:10px;font-weight:700;color:var(--gold);
  text-transform:uppercase;letter-spacing:0.1em;margin-bottom:4px;}
.review-nar-text{font-size:13px;color:var(--parchment);font-style:italic;line-height:1.5;}
/* Pass 1 — per-gap narrative steering. The head row carries the label + the
   Outline + Direction pills, clustered left like the panel rows. */
.review-nar-head{display:flex;align-items:center;gap:10px;margin-bottom:4px;}
.review-nar-head .review-nar-label{margin-bottom:0;}
.gen-bar-label{font-size:10px;font-weight:700;letter-spacing:0.05em;text-transform:uppercase;color:var(--gold-dim);margin-bottom:3px;}
.review-actions-inline{display:inline-flex;gap:6px;align-items:center;}
.review-nar-head .review-actions-inline{margin-left:auto;}
.row-menu.review-row-menu{display:none;}
@media (max-width:640px){
  .review-actions-inline{display:none;}
  .row-menu.review-row-menu{display:inline-block;}
  .review-nar-head .review-row-menu{margin-left:auto;}
}
.review-nar-empty{color:rgba(201,168,76,0.45);font-style:italic;}
.review-dir-btn{padding:3px 8px;font-size:10px;background:rgba(0,0,0,0.5);
  color:rgba(201,168,76,0.85);border:1px solid rgba(201,168,76,0.3);border-radius:6px;
  cursor:pointer;white-space:nowrap;line-height:1.3;transition:background 0.15s,color 0.15s,border-color 0.15s;}
.review-dir-btn:hover{background:rgba(0,0,0,0.8);color:var(--gold);border-color:var(--gold);}
.review-dir-btn.is-on{color:var(--gold);border-color:var(--gold);background:rgba(201,168,76,0.12);}

/* Panel blocks — quick reference points along the narrative. */
.review-panel{border:1px solid rgba(201,168,76,0.18);border-radius:var(--radius);
  padding:10px 14px;margin:6px 0;background:rgba(20,12,4,0.4);}
.review-panel-head{display:flex;align-items:center;gap:10px;margin-bottom:4px;}
.review-panel-num{display:inline-flex;align-items:center;justify-content:center;
  width:22px;height:22px;border-radius:50%;background:var(--crimson-light);
  color:var(--crimson);font-size:11px;font-weight:700;flex-shrink:0;}
.review-panel-title{font-family:var(--font-display);font-size:14px;color:var(--parchment);}
.review-snippet{font-size:12px;color:var(--text-muted);margin-bottom:4px;}
.review-row{font-size:12px;margin-top:3px;display:flex;flex-wrap:wrap;
  align-items:center;gap:5px;}
.review-label{color:var(--text-light);font-weight:600;text-transform:uppercase;
  letter-spacing:0.05em;font-size:10px;}
.review-chip{display:inline-block;padding:2px 8px;border-radius:10px;font-size:11px;
  background:rgba(201,168,76,0.12);color:var(--gold);}
.review-chip-asset{background:rgba(122,140,184,0.14);color:#9caedb;}
.review-none{font-size:11px;color:var(--text-light);font-style:italic;}
/* Pass 2 — editable per-panel casting */
.review-chip{position:relative;}
.review-chip-x{margin-left:5px;border:none;background:transparent;color:inherit;
  opacity:0.55;cursor:pointer;font-size:12px;line-height:1;padding:0;}
.review-chip-x:hover{opacity:1;color:var(--crimson);}
.review-add-select{font-size:11px;padding:2px 6px;border-radius:8px;cursor:pointer;
  background:rgba(0,0,0,0.4);color:rgba(201,168,76,0.85);
  border:1px dashed rgba(201,168,76,0.35);}
.review-add-select:hover{border-color:var(--gold);color:var(--gold);}
.review-cast-badge{margin-left:auto;font-size:9px;font-weight:700;text-transform:uppercase;
  letter-spacing:0.08em;padding:2px 7px;border-radius:8px;
  background:rgba(122,140,184,0.14);color:#9caedb;}
.review-cast-badge.is-custom{background:rgba(201,168,76,0.16);color:var(--gold);}
.review-reset-btn{font-size:10px;padding:2px 7px;border-radius:8px;cursor:pointer;
  background:transparent;border:1px solid rgba(201,168,76,0.3);color:rgba(201,168,76,0.75);}
.review-reset-btn:hover{border-color:var(--gold);color:var(--gold);}
.review-change-mark{font-size:11px;color:var(--gold);font-style:italic;
  margin:2px 0 4px;opacity:0.85;}

/* Asset modal — image drop zone */
.asset-drop{border:2px dashed rgba(201,168,76,0.3);border-radius:var(--radius);
  min-height:140px;display:flex;align-items:center;justify-content:center;
  cursor:pointer;transition:all 0.15s;background:rgba(20,12,4,0.4);overflow:hidden;}
.asset-drop:hover{border-color:rgba(201,168,76,0.55);}
.asset-drop.drag-over{border-color:var(--gold);background:rgba(201,168,76,0.1);}
.asset-drop-empty{display:flex;flex-direction:column;align-items:center;gap:6px;
  color:rgba(201,168,76,0.45);font-size:13px;}
.asset-drop-preview{max-width:100%;max-height:240px;border-radius:6px;display:block;}

/* Session detail header — name left, date pushed right, one row */
.session-detail-head{display:flex;align-items:center;justify-content:space-between;
  gap:16px;flex-wrap:wrap;margin-bottom:6px;}
.session-detail-date{max-width:150px;font-size:10px;padding:0 8px;}
.main-content > div#view-session-detail{padding-top:0.75rem;}
.main-content > div#view-novel{padding-top:0.375rem;}
#view-novel .page-header{margin-bottom:4px;}
#view-novel .sess-tab{padding-top:5px;padding-bottom:5px;}
#view-session-detail .sess-tab{padding-top:5px;padding-bottom:5px;}
#view-session-detail .sess-tabs{margin-bottom:8px;}
.sdh-left{display:flex;align-items:center;gap:10px;flex-wrap:nowrap;}
.sdh-titlewrap{display:flex;flex-direction:column;gap:3px;min-width:0;}
.sdh-title-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:0;}
.sdh-date-display{font-size:0.5em;font-weight:400;color:var(--text-light);white-space:nowrap;}
.camp-edit-input.sdh-date-edit{min-width:0;width:auto;font-size:0.7em;padding:2px 6px;}
.sdh-right{display:flex;align-items:center;gap:8px;margin-left:auto;flex-wrap:wrap;}
.session-detail-head .form-input{height:30px;box-sizing:border-box;padding-top:0;padding-bottom:0;width:auto;flex:0 0 auto;}
.session-detail-head .btn{height:30px;box-sizing:border-box;}
.session-detail-head .session-access-chip{height:30px;box-sizing:border-box;display:inline-flex;align-items:center;}

/* Notes toolbar — find/replace (left) + Generate Story (right), one row */
.notes-toolbar{display:flex;align-items:center;justify-content:space-between;
  gap:12px;flex-wrap:wrap;margin-bottom:8px;}
.fr-tools{display:flex;align-items:center;gap:6px;flex-wrap:nowrap;}
.fr-input{height:30px;padding:2px 8px;font-size:12px;
  min-width:90px;max-width:150px;}
.fr-result{font-size:11px;color:var(--gold);white-space:nowrap;}
.btn-xs{padding:4px 10px;font-size:12px;line-height:1.2;white-space:nowrap;}
.sess-tab{color:rgba(201,168,76,0.45);}
.sess-tab:hover{color:var(--gold);}
.sess-tab.active{color:var(--gold);border-bottom-color:var(--gold);}
.sess-tabs{border-bottom-color:rgba(201,168,76,0.15);}
.sidebar-item{color:rgba(201,168,76,0.5);}
.sidebar-item:hover{background:rgba(201,168,76,0.08);color:var(--gold);}
.sidebar-item.active{background:rgba(201,168,76,0.12);color:var(--gold);border-left-color:var(--gold);}
.sidebar-label{color:rgba(201,168,76,0.35);}
.sidebar-divider{background:rgba(201,168,76,0.12);}
.novel-session-block{background:rgba(25,18,10,0.85);border-color:rgba(201,168,76,0.15);}
.novel-session-header{background:rgba(15,10,5,0.6);border-bottom-color:rgba(201,168,76,0.1);}
.novel-session-title{color:var(--gold-light);}
.novel-session-date{color:rgba(201,168,76,0.45);}
.novel-moment-row{border-bottom-color:rgba(201,168,76,0.1);}
.novel-moment-title{color:var(--gold-light);}
.novel-moment-desc{color:rgba(201,168,76,0.5);}
.key-input-field{background:rgba(15,10,5,0.7);border-color:rgba(201,168,76,0.2);color:var(--gold-light);}
.progress-bar{background:rgba(201,168,76,0.1);}
.progress-fill{background:var(--gold);}
.progress-msg{color:rgba(201,168,76,0.6);}
.chip{background:rgba(25,18,10,0.7);border-color:rgba(201,168,76,0.2);color:rgba(201,168,76,0.5);}
.chip:hover{border-color:rgba(201,168,76,0.4);color:var(--gold);}
.chip.sel{background:rgba(201,168,76,0.15);color:var(--gold);border-color:rgba(201,168,76,0.5);}
.storyboard-header{color:rgba(201,168,76,0.5);}
.empty-state h3{color:var(--gold-light);}
.empty-state p{color:rgba(201,168,76,0.45);}
.modal{background:rgba(18,12,6,0.95);border:1px solid rgba(201,168,76,0.2);}
.modal-title{color:var(--gold);}
.modal-close{color:rgba(201,168,76,0.5);}
.modal-footer{border-top-color:rgba(201,168,76,0.15);}
.image-upload-area{background:rgba(15,10,5,0.6);border-color:rgba(201,168,76,0.2);}
.image-upload-area:hover{border-color:rgba(201,168,76,0.5);}


/* CAMPAIGN CARDS */
.campaigns-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:14px;}
.campaign-card{
  background:rgba(20,15,8,0.85);
  border:1px solid rgba(201,168,76,0.25);
  border-radius:var(--radius-lg);
  display:flex;
  flex-direction:column;
  cursor:pointer;
  transition:all 0.2s;
  box-shadow:0 4px 16px rgba(0,0,0,0.4);
  backdrop-filter:blur(4px);
}
.campaign-card:hover{
  border-color:rgba(201,168,76,0.6);
  box-shadow:0 6px 24px rgba(0,0,0,0.5),0 0 0 1px rgba(201,168,76,0.3);
  transform:translateY(-2px);
}
.campaign-card-icon{width:40px;height:40px;margin-bottom:10px;}
.campaign-card-icon img{width:40px;height:40px;object-fit:contain;}
.campaign-card-name{font-size:14px;font-weight:600;color:var(--gold);margin-bottom:4px;font-family:var(--font-display);letter-spacing:0.5px;}
.campaign-card-desc{font-size:12px;color:rgba(201,168,76,0.55);line-height:1.5;margin-bottom:10px;}
.campaign-card-meta{font-size:11px;color:rgba(201,168,76,0.35);}
.add-campaign-card{
  background:rgba(20,15,8,0.6);
  border:1px dashed rgba(201,168,76,0.25);
  border-radius:var(--radius-lg);
  padding:16px;
  cursor:pointer;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:8px;min-height:130px;
  color:rgba(201,168,76,0.4);
  font-size:13px;
  transition:all 0.2s;
  backdrop-filter:blur(4px);
}
.add-campaign-card:hover{border-color:rgba(201,168,76,0.5);color:var(--gold);}
.add-campaign-card .plus{font-size:28px;line-height:1;}

/* SESSIONS */
.sessions-list{display:flex;flex-direction:column;gap:10px;}
.session-item{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-lg);padding:4px 16px;cursor:pointer;transition:all 0.15s;display:flex;align-items:center;justify-content:space-between;}
.session-item:hover{border-color:var(--crimson);box-shadow:var(--shadow);}
.session-item-left{display:flex;align-items:center;gap:12px;}
.session-num{width:32px;height:32px;background:var(--bg);border:1px solid var(--border);border-radius:6px;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:600;color:var(--text-muted);flex-shrink:0;}
.session-name{font-size:13px;font-weight:500;color:var(--text);}
.session-date{font-size:11px;color:var(--text-muted);margin-top:1px;}
.session-badge{font-size:10px;padding:2px 7px;border-radius:99px;background:var(--success-bg);color:var(--success);font-weight:500;}
.session-badge.empty{background:var(--bg);color:var(--text-light);border:1px solid var(--border);}
/* SESSION CARDS (campaign list + publish list) */
.session-card-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:14px;}
.session-card{background:rgba(25,18,10,0.85);border:1px solid rgba(201,168,76,0.15);border-radius:var(--radius-lg);overflow:hidden;cursor:pointer;transition:border-color 0.15s,box-shadow 0.15s;display:flex;flex-direction:column;}
.session-card:hover{border-color:var(--gold);box-shadow:0 0 0 1px rgba(201,168,76,0.3);}
.session-card-img{width:100%;aspect-ratio:16/10;object-fit:cover;display:block;background:rgba(15,10,5,0.5);}
/* v3.0.663 -- TD-457. A DRAWN TITLE IS CONTAINED, NEVER CROPPED. cover is right for a scene and
   takes equal bites out of both ends of lettering. The box keeps its shape so the grid stays even;
   only the fit changes, and a little padding keeps the glyphs off the edges. */
.session-card-img-title{object-fit:contain;padding:6px;}
.session-card-img-empty{display:flex;align-items:center;justify-content:center;font-size:30px;opacity:0.25;}
.session-card-body{padding:10px 12px;display:flex;flex-direction:column;gap:4px;}
.session-card-title{font-size:13px;font-weight:600;color:var(--gold-light);line-height:1.3;}
.session-card-date{font-size:11px;color:rgba(201,168,76,0.45);}
.session-card-fork{font-size:10px;color:rgba(201,168,76,0.55);font-style:italic;}
.session-card-meta{display:flex;align-items:center;gap:8px;margin-top:4px;}
.session-card-pills{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-top:2px;}
.session-card-actions{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-top:6px;flex-wrap:wrap;}
.session-card-include{font-size:11px;color:rgba(201,168,76,0.55);display:inline-flex;align-items:center;gap:4px;cursor:pointer;}
.session-card-open{font-size:11px;color:#8fb8e0;cursor:pointer;text-decoration:underline;}
.session-card-publish{cursor:default;}
.session-card-publish:hover{border-color:rgba(201,168,76,0.3);box-shadow:none;}

/* CHARACTERS */
.char-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:12px;}
.char-card{border:1px solid var(--border);border-radius:var(--radius-lg);padding:12px;background:var(--surface);box-shadow:var(--shadow);}
.char-card-header{display:flex;align-items:flex-start;justify-content:space-between;margin-bottom:8px;}
.char-avatar{width:48px;height:48px;border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0;overflow:hidden;border:2px solid var(--border);}
.char-actions{display:flex;gap:4px;}
.char-btn{padding:3px 8px;font-size:10px;border-radius:4px;cursor:pointer;border:1px solid var(--border);background:var(--bg);color:var(--text-muted);}
.char-btn:hover{background:var(--border);}
.char-btn-delete:hover{background:var(--error-bg);color:var(--error);border-color:#f09595;}
.char-name{font-size:13px;font-weight:600;color:var(--text);margin-bottom:2px;}
.char-player{font-size:11px;color:var(--text-muted);margin-bottom:3px;}
.char-desc{font-size:11px;color:var(--text-muted);line-height:1.5;}
.char-badge{display:inline-block;font-size:10px;padding:2px 6px;border-radius:4px;margin-top:6px;background:var(--bg);color:var(--text-muted);border:1px solid var(--border);}
.add-char-card{border:1px dashed var(--border-dark);border-radius:var(--radius-lg);padding:12px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;cursor:pointer;min-height:120px;color:var(--text-muted);font-size:12px;transition:all 0.15s;}
.add-char-card:hover{border-color:var(--crimson);color:var(--crimson);}

/* SESSION TABS */
.sess-tabs{display:flex;border-bottom:1px solid var(--border);margin-bottom:20px;}
.sess-tab{padding:10px 18px;font-size:13px;color:var(--gold);cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-1px;white-space:nowrap;transition:color 0.15s;}
.sess-tab:hover{color:var(--gold-light);}
.sess-tab.active{color:var(--gold);border-bottom-color:var(--gold);font-weight:600;}

/* STORYBOARD */
.storyboard-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;}
.moments-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:16px;align-items:start;}
.moment-card{border:1px solid var(--border);border-radius:var(--radius-lg);overflow:hidden;background:var(--surface);box-shadow:var(--shadow);}
.moment-img{width:100%;aspect-ratio:4/3;background:var(--bg);display:flex;align-items:center;justify-content:center;padding:12px;border-bottom:1px solid var(--border);}
.moment-img-inner{font-size:11px;color:var(--text-muted);text-align:center;line-height:1.5;}
.moment-body{padding:10px;}
.moment-num{font-size:10px;color:var(--text-muted);margin-bottom:3px;}
.moment-title{font-size:12px;font-weight:600;color:var(--text);margin-bottom:4px;line-height:1.4;}
/* v3.0.507 -- the storyboard title is an INPUT when you can edit it, and it should not announce
   itself as a form field until you touch it. Transparent until hover/focus, same type as the
   read-only span so nothing shifts when it swaps in. */
/* v3.0.509 -- SITS ON THE SAME ROW AS "Opening" / "Panel 1". Ian: "it's now taking up a lot more
   vertical space. Can you put the title edit on the same row... then pull the rest of what's below
   it up tighter underneath it?"
   The row is already display:flex with flex-wrap:wrap, so the v3.0.507 width:100% was forcing the
   input onto a line of its own and adding a whole row of height to every panel. flex:1 lets it take
   the room left beside the panel number instead, and min-width:0 lets it actually shrink -- without
   it a flex item refuses to go below its content width and wraps anyway. */
.moment-title-edit{flex:1 1 auto;min-width:0;background:transparent;border:1px solid transparent;
  border-radius:4px;padding:1px 5px;margin:0 0 0 -5px;font-family:inherit;font-size:12px;
  font-weight:600;color:var(--text);line-height:1.35;transition:background 0.15s,border-color 0.15s;}
.moment-title-edit:hover{background:rgba(201,168,76,0.07);border-color:rgba(201,168,76,0.25);}
.moment-title-edit:focus{outline:none;background:rgba(201,168,76,0.10);border-color:rgba(201,168,76,0.55);}
.moment-title-edit::placeholder{color:rgba(245,232,200,0.35);font-weight:400;font-style:italic;}
.moment-desc{font-size:11px;color:var(--text-muted);line-height:1.5;}
.moment-prompt{font-size:10px;color:var(--text-muted);line-height:1.4;margin-top:6px;padding-top:6px;border-top:1px solid var(--border);font-style:italic;}
.moment-type{display:inline-block;font-size:10px;padding:2px 6px;border-radius:4px;margin-top:5px;font-weight:500;}
.type-combat{background:#fcebeb;color:#a32d2d;}
.type-drama{background:#eeedfe;color:#3c3489;}
.type-discovery{background:#e1f5ee;color:#0f6e56;}
.type-humor{background:#faeeda;color:#854f0b;}

/* TRANSCRIPT */
.transcript-box{width:100%;min-height:220px;border:1px solid var(--border);border-radius:var(--radius);padding:12px;font-size:13px;line-height:1.7;color:var(--text);background:var(--surface);resize:vertical;font-family:inherit;}
.transcript-box:focus{outline:none;border-color:var(--crimson);}

/* STYLE CHIPS */
.style-row{display:flex;gap:7px;flex-wrap:wrap;margin:10px 0 16px;}
.chip{padding:5px 13px;border-radius:99px;font-size:12px;border:1px solid var(--border);cursor:pointer;color:var(--text-muted);background:var(--surface);transition:all 0.12s;}
.chip:hover{border-color:var(--border-dark);color:var(--text);}
.chip.sel{background:var(--ink);color:var(--gold);border-color:var(--ink);}

/* PROGRESS */
.progress-wrap{margin-top:14px;display:none;}
.progress-bar{height:4px;background:var(--border);border-radius:2px;overflow:hidden;margin-bottom:8px;}
.progress-fill{height:100%;background:var(--crimson);border-radius:2px;width:0%;transition:width 0.3s;}
/* v3.0.701 -- TD-489. THE SHEEN, AND WHY IT IS PART OF THE FIX RATHER THAN A FLOURISH.
   creepBar can never arrive, but past ~99% each step moves the bar less than a pixel -- so a reader
   watching the last stretch of a long render would see a bar that has, as far as they can tell,
   stopped. The maths and the screen have to agree. This runs on every .progress-fill including the
   Before/After bar, which Ian deliberately left parked at 45% because the real page counter takes
   over there: a parked bar that still looks alive is exactly right for that one too.
   SCREEN ONLY. Nothing here goes near the print path -- these are app chrome, never rendered into
   a PDF -- so the standing rule about gradients through the flatten does not apply. */
.progress-fill{position:relative;overflow:hidden;}
.progress-fill::after{content:"";position:absolute;top:0;bottom:0;width:40%;pointer-events:none;
  background:linear-gradient(90deg,rgba(255,255,255,0) 0%,rgba(255,255,255,0.30) 50%,rgba(255,255,255,0) 100%);
  animation:pf-sheen 2.2s linear infinite;}
@keyframes pf-sheen{0%{left:-40%;}100%{left:100%;}}
@media (prefers-reduced-motion: reduce){.progress-fill::after{animation:none;display:none;}}
.progress-msg{font-size:12px;color:var(--text-muted);text-align:center;}

/* MODAL */
.modal-overlay{position:fixed;inset:0;background:rgba(0,0,0,0.6);z-index:100;display:flex;align-items:center;justify-content:center;padding:16px;}
.modal{background:var(--surface);border-radius:var(--radius-lg);padding:24px;width:100%;max-width:500px;box-shadow:var(--shadow-md);max-height:90vh;overflow-y:auto;}
.modal-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:20px;}
.modal-title{font-size:16px;font-weight:600;color:var(--text);font-family:var(--font-display);letter-spacing:0.5px;}
.modal-close{background:none;border:none;cursor:pointer;font-size:20px;color:var(--text-muted);padding:0;line-height:1;}
.modal-close:hover{color:var(--text);}
.modal-footer{display:flex;gap:8px;justify-content:flex-end;margin-top:20px;padding-top:16px;border-top:1px solid var(--border);}

/* IMAGE UPLOAD */
.image-upload-area{border:1px dashed var(--border-dark);border-radius:var(--radius);padding:16px;text-align:center;cursor:pointer;background:var(--bg);transition:border-color 0.15s;}
.image-upload-area:hover{border-color:var(--crimson);}
.image-preview{width:80px;height:80px;border-radius:50%;object-fit:cover;display:none;margin:10px auto 0;border:2px solid var(--border);}

/* EMPTY STATES */
.empty-state{text-align:center;padding:3rem 1rem;color:var(--text-muted);}
.empty-state-icon{font-size:48px;margin-bottom:12px;}
.empty-state h3{font-size:16px;font-weight:600;color:var(--text);margin-bottom:6px;font-family:var(--font-display);}
.empty-state p{font-size:13px;margin-bottom:16px;max-width:300px;margin-left:auto;margin-right:auto;}

/* SETTINGS */
.settings-section{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-lg);padding:20px;margin-bottom:16px;}
.settings-section-title{font-size:14px;font-weight:600;color:var(--text);margin-bottom:4px;font-family:var(--font-display);letter-spacing:0.5px;}
.settings-section-desc{font-size:12px;color:var(--text-muted);margin-bottom:16px;}
.key-input-field{width:100%;padding:9px 12px;border:1px solid var(--border);border-radius:var(--radius);font-size:12px;background:var(--surface);color:var(--text);font-family:monospace;}
.key-input-field:focus{outline:none;border-color:var(--crimson);}

/* NOVEL */
.novel-session-block{border:1px solid var(--border);border-radius:var(--radius-lg);margin-bottom:14px;overflow:hidden;background:var(--surface);}
.novel-session-header{padding:12px 16px;background:var(--bg);border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;}
.novel-session-title{font-size:13px;font-weight:600;color:var(--text);font-family:var(--font-display);letter-spacing:0.5px;}
.novel-session-date{font-size:11px;color:var(--text-muted);}
.novel-session-moments{padding:12px 16px;}
.novel-moment-row{display:flex;align-items:flex-start;gap:10px;padding:8px 0;border-bottom:1px solid var(--border);}
.novel-moment-row:last-child{border-bottom:none;}
.novel-moment-num{width:24px;height:24px;border-radius:4px;background:var(--ink);color:var(--gold);font-size:10px;font-weight:600;display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-top:1px;}
.novel-moment-title{font-size:12px;font-weight:600;color:var(--text);}
.novel-moment-desc{font-size:11px;color:var(--text-muted);margin-top:2px;line-height:1.5;}
.novel-empty{text-align:center;padding:12px;font-size:12px;color:var(--text-light);font-style:italic;}
.novel-cover{padding:2rem 1.5rem;background:var(--ink);text-align:center;color:var(--gold);}
.novel-cover-eyebrow{font-size:10px;opacity:0.5;letter-spacing:0.12em;text-transform:uppercase;margin-bottom:10px;font-family:var(--font-display);}
.novel-cover-title{font-size:22px;font-weight:700;margin-bottom:4px;font-family:var(--font-display);letter-spacing:1px;}
.novel-cover-divider{width:40px;height:1px;background:var(--gold);margin:12px auto;opacity:0.4;}
.novel-cover-sub{font-size:12px;opacity:0.6;}
.novel-grid{display:grid;grid-template-columns:1fr 1fr;gap:2px;background:#222;padding:2px;}
.novel-panel{background:var(--bg);aspect-ratio:4/3;display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden;}
.novel-panel.wide{grid-column:1/-1;aspect-ratio:16/7;}
.novel-panel-inner{font-size:11px;color:var(--text-muted);text-align:center;padding:8px;line-height:1.5;}
.novel-caption{position:absolute;bottom:0;left:0;right:0;background:rgba(0,0,0,0.75);color:white;font-size:9px;padding:4px 7px;line-height:1.4;}
.novel-chapter-header{background:var(--ink);color:var(--gold);padding:8px 12px;font-size:11px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;font-family:var(--font-display);}

/* IMAGE GENERATION */
.moment-img-generated{width:100%;aspect-ratio:4/3;object-fit:cover;display:block;}
.moment-img-shimmer{
  width:100%;aspect-ratio:4/3;
  background:linear-gradient(90deg,rgba(201,168,76,0.05) 25%,rgba(201,168,76,0.12) 50%,rgba(201,168,76,0.05) 75%);
  background-size:200% 100%;
  animation:shimmer 1.5s infinite;
  display:flex;align-items:center;justify-content:center;
}
@keyframes shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}
.moment-img-shimmer-text{font-size:11px;color:rgba(201,168,76,0.5);text-align:center;}
/* Non-destructive busy overlay — sits ON TOP of the existing panel image
   during regeneration, dimming the original instead of replacing it. The
   user's current image stays in the DOM, so a refusal / failure leaves
   their work intact when the overlay is removed. */
.moment-img-busy-overlay{
  position:absolute;inset:0;
  background:rgba(12,8,4,0.55);
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;
  z-index:2;pointer-events:none;
  border-radius:inherit;
}
.moment-img-busy-spinner{
  width:28px;height:28px;border-radius:50%;
  border:3px solid rgba(201,168,76,0.2);
  border-top-color:#c9a84c;
  animation:momentBusySpin 0.9s linear infinite;
}
.moment-img-busy-label{
  font-size:11px;color:#c9a84c;letter-spacing:1px;text-transform:uppercase;
  font-family:var(--font-display);text-shadow:0 1px 4px rgba(0,0,0,0.6);
}
.moment-img-busy-sublabel{
  font-size:11px;color:rgba(201,168,76,0.8);
  max-width:80%;text-align:center;line-height:1.4;
  text-shadow:0 1px 4px rgba(0,0,0,0.6);
  margin-top:2px;
}
/* Error-overlay variant — uses the same overlay slot as the busy overlay
   but with a warm red tone, an error icon, and a dismiss (X) corner button.
   Used for in-panel error display on single-image regen (so users don't
   miss errors when scrolled deep in a long storyboard). */
.moment-img-error-overlay{
  position:absolute;inset:0;
  background:rgba(40,12,8,0.85);
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;
  z-index:3;
  border-radius:inherit;
  padding:14px;
}
.moment-img-error-icon{
  font-size:22px;color:#f0a090;line-height:1;
}
.moment-img-error-message{
  font-size:12px;color:#f5d8d0;
  max-width:90%;text-align:center;line-height:1.45;
  text-shadow:0 1px 4px rgba(0,0,0,0.6);
}
.moment-img-error-dismiss{
  position:absolute;top:6px;right:8px;
  background:none;border:none;cursor:pointer;
  color:rgba(245,216,208,0.7);font-size:18px;line-height:1;
  padding:2px 6px;border-radius:4px;
}
.moment-img-error-dismiss:hover{color:#f5d8d0;background:rgba(0,0,0,0.3);}
@keyframes momentBusySpin{to{transform:rotate(360deg);}}
.moment-card{position:relative;}
.moment-regen-btn{
  position:absolute;top:6px;right:6px;
  padding:3px 8px;font-size:10px;
  background:rgba(0,0,0,0.6);color:rgba(201,168,76,0.8);
  border:1px solid rgba(201,168,76,0.3);border-radius:6px;
  cursor:pointer;opacity:0;transition:opacity 0.15s;
}
.moment-card:hover .moment-regen-btn{opacity:1;}
.moment-regen-btn:hover{background:rgba(0,0,0,0.8);color:var(--gold);}
.moment-lock-btn{position:absolute;top:8px;left:8px;z-index:3;padding:3px 8px;font-size:10px;background:rgba(0,0,0,0.6);color:rgba(201,168,76,0.8);border:1px solid rgba(201,168,76,0.3);border-radius:6px;cursor:pointer;opacity:0.85;transition:opacity 0.15s;}
.moment-lock-btn:hover{opacity:1;background:rgba(0,0,0,0.8);color:var(--gold);}
.moment-lock-btn.is-locked{color:var(--gold);border-color:var(--gold);opacity:1;}
.moment-lock-btn.is-static{cursor:default;}
.moment-regen-btn[disabled]{cursor:not-allowed;color:rgba(201,168,76,0.3);}

/* DRAG AND DROP */
.image-upload-area{transition:all 0.2s;cursor:pointer;}
.image-upload-area.drag-over{
  border-color:var(--gold) !important;
  background:rgba(201,168,76,0.1) !important;
  transform:scale(1.02);
  box-shadow:0 0 0 3px rgba(201,168,76,0.2);
}
.char-card-drop{
  position:relative;
  transition:all 0.2s;
}
.char-card-drop.drag-over{
  border-color:var(--gold) !important;
  background:rgba(201,168,76,0.12) !important;
  transform:scale(1.02);
  box-shadow:0 0 0 3px rgba(201,168,76,0.25);
}
.char-card-drop.drag-over::after{
  content:'Drop portrait here';
  position:absolute;
  inset:0;
  background:rgba(12,8,4,0.75);
  display:flex;
  align-items:center;
  justify-content:center;
  color:var(--gold);
  font-size:12px;
  font-weight:600;
  border-radius:var(--radius-lg);
  letter-spacing:0.5px;
}

/* LIGHTBOX */
.lightbox-overlay{
  position:fixed;inset:0;
  background:rgba(0,0,0,0.92);
  z-index:999;
  display:flex;align-items:center;justify-content:center;
  padding:20px;
  cursor:zoom-out;
  animation:lightbox-in 0.2s ease;
}
@keyframes lightbox-in{from{opacity:0}to{opacity:1}}
.lightbox-img{
  max-width:90vw;max-height:88vh;
  object-fit:contain;
  border-radius:var(--radius-lg);
  box-shadow:0 8px 60px rgba(0,0,0,0.8),0 0 0 1px rgba(201,168,76,0.2);
  animation:lightbox-scale 0.2s ease;
}
@keyframes lightbox-scale{from{transform:scale(0.92)}to{transform:scale(1)}}
.lightbox-caption{
  position:fixed;bottom:24px;left:50%;transform:translateX(-50%);
  background:rgba(18,12,6,0.9);
  color:var(--gold-light);
  padding:8px 20px;border-radius:99px;
  font-size:13px;
  border:1px solid rgba(201,168,76,0.2);
  white-space:nowrap;
  max-width:80vw;overflow:hidden;text-overflow:ellipsis;
}
.lightbox-close{
  position:fixed;top:20px;right:20px;
  width:36px;height:36px;
  background:rgba(18,12,6,0.9);
  border:1px solid rgba(201,168,76,0.2);
  border-radius:50%;
  color:var(--gold);font-size:18px;
  display:flex;align-items:center;justify-content:center;
  cursor:pointer;
}
.lightbox-close:hover{background:rgba(201,168,76,0.15);}

/* Make images show zoom cursor */
.moment-img-generated{cursor:zoom-in;}
.char-avatar img{cursor:zoom-in;object-position:center top;}

/* CHARACTER IMAGE SLOTS */
.image-slot{
  position:relative;
  display:flex;align-items:center;justify-content:center;
  min-height:80px;transition:all 0.2s;cursor:pointer;
}
.image-slot.drag-over{
  border-color:var(--gold) !important;
  background:rgba(201,168,76,0.12) !important;
  transform:scale(1.02);
}
.slot-preview{
  width:100%;height:100%;object-fit:cover;
  border-radius:6px;max-height:120px;
  cursor:zoom-in;
}
/* Per-type image treatment — each shot type frames differently.
   Portrait: fill, crop from the top so faces are safe.
   Full body: show the WHOLE figure (contain), taller frame, no crop.
   Action: fill, centered — dynamic compositions read fine cropped.
   Other: catch-all reference, show all of it. */
.slot-preview.slot-portrait{
  object-fit:cover;object-position:center top;max-height:220px;
}
.slot-preview.slot-fullbody{
  object-fit:contain;max-height:340px;background:rgba(12,8,4,0.55);
}
.slot-preview.slot-action{
  object-fit:cover;object-position:center;max-height:220px;
}
.slot-preview.slot-other{
  object-fit:contain;max-height:220px;background:rgba(12,8,4,0.55);
}
.image-slot.slot-tall{ min-height:220px; }
.image-slot.slot-xtall{ min-height:340px; }
.slot-placeholder{
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  text-align:center;pointer-events:none;
}
.slot-preview.hidden{display:none;}

/* Character card image grid */
.char-img-grid{
  display:grid;grid-template-columns:1fr 1fr;
  gap:3px;margin-top:8px;border-radius:6px;overflow:hidden;
}
.char-img-thumb{
  aspect-ratio:1;object-fit:cover;object-position:center top;
  width:100%;cursor:zoom-in;
  transition:opacity 0.15s;
}
.char-img-thumb:hover{opacity:0.85;}

/* NARRATIVE EDITOR */
.narrative-section {
  margin-bottom: 20px;
  border: 1px solid rgba(201,168,76,0.15);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: rgba(20,12,4,0.6);
}
.narrative-section-header {
  padding: 8px 14px;
  background: rgba(201,168,76,0.08);
  border-bottom: 1px solid rgba(201,168,76,0.12);
  font-family: var(--font-display);
  font-size: 11px;
  color: rgba(201,168,76,0.6);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  display: flex;
  align-items: center;
  gap: 8px;
}
.narrative-section-img {
  width: 48px;
  height: 36px;
  object-fit: cover;
  border-radius: 3px;
  flex-shrink: 0;
}
.narrative-textarea {
  width: 100%;
  padding: 12px 14px;
  background: transparent;
  border: none;
  color: var(--parchment);
  font-family: 'Georgia', serif;
  font-size: 13px;
  line-height: 1.7;
  resize: vertical;
  min-height: 80px;
  font-style: italic;
}
.narrative-textarea:focus {
  outline: none;
  background: rgba(201,168,76,0.03);
}
.narrative-textarea::placeholder {
  color: rgba(201,168,76,0.55);
  font-style: italic;
}

/* NEW STORYBOARD LAYOUT WITH INLINE NARRATIVE */
.storyboard-panel {
  border: 1px solid rgba(201,168,76,0.15);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: rgba(20,12,4,0.7);
  position: relative;
  break-inside: avoid;
}
.storyboard-panel-img {
  position: relative;
  width: 100%;
}
.storyboard-panel-img .moment-img-generated {
  width: 100%;
  aspect-ratio: 4 / 3;
  max-height: 420px;
  object-fit: contain;
  display: block;
  cursor: zoom-in;
  background: rgba(10,6,3,0.85);
}
.storyboard-panel-img .moment-img-placeholder {
  width: 100%;
  min-height: 200px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: rgba(15,10,5,0.5);
  padding: 24px;
}
.storyboard-panel-meta {
  /* v3.0.509 -- tightened: the title now shares this row, so the block no longer needs the
     breathing room it had when the title was its own line. row-gap kept small so the meta list
     sits close under the title rather than floating. */
  padding: 7px 14px 6px;
  display: flex;
  align-items: center;
  gap: 2px 10px;
  border-top: 1px solid rgba(201,168,76,0.1);
  flex-wrap: wrap;
}
/* v3.0.509 -- ROW ONE: panel number, title, options button. ROW TWO: the meta list.
   `order` is what makes that work. In DOM order the meta list sits BEFORE the options button, so
   giving the list flex-basis:100% would wrap it AND push the button onto a third line -- worse than
   what we started with. Ordering the list last keeps the button up on row one where it belongs. */
.storyboard-panel-meta .moment-num{flex:0 0 auto;margin-bottom:0;order:0;}
.storyboard-panel-meta .moment-opts-btn{order:0;}
.storyboard-panel-meta .moment-meta-list{order:1;flex:1 1 100%;margin-top:0;}
.moment-prompt-text {
  padding: 8px 14px;
  font-size: 10px;
  color: rgba(201,168,76,0.3);
  font-style: italic;
  line-height: 1.5;
  border-top: 1px solid rgba(201,168,76,0.08);
}

/* NARRATIVE BLOCKS */
.narrative-block {
  margin: 12px 0;
  border: 1px solid rgba(201,168,76,0.12);
  border-radius: var(--radius);
  overflow: hidden;
  background: rgba(12,8,4,0.5);
}
.narrative-block-header {
  padding: 6px 12px;
  background: rgba(201,168,76,0.06);
  border-bottom: 1px solid rgba(201,168,76,0.1);
  font-size: 10px;
  color: rgba(201,168,76,0.45);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.narrative-regen-btn {
  padding: 2px 8px;
  font-size: 10px;
  background: rgba(201,168,76,0.08);
  border: 1px solid rgba(201,168,76,0.2);
  border-radius: 4px;
  color: rgba(201,168,76,0.6);
  cursor: pointer;
  transition: all 0.15s;
}
.narrative-regen-btn:hover {
  background: rgba(201,168,76,0.15);
  color: var(--gold);
}
.narrative-inline-box {
  width: 100%;
  padding: 14px 16px;
  background: transparent;
  border: none;
  color: var(--parchment);
  font-family: 'Georgia', serif;
  font-size: 13px;
  line-height: 1.8;
  resize: vertical;
  min-height: 120px;
  font-style: italic;
  field-sizing: content;
  overflow-y: hidden;
}
.narrative-inline-box:focus {
  outline: none;
  background: rgba(201,168,76,0.03);
}
.narrative-inline-box::placeholder {
  color: rgba(201,168,76,0.55);
  font-style: italic;
}

/* NARRATIVE SAVE BUTTON */
.narrative-save-btn {
  padding: 2px 8px;
  font-size: 10px;
  background: rgba(15,110,86,0.2);
  border: 1px solid rgba(134,212,186,0.3);
  border-radius: 4px;
  color: rgba(134,212,186,0.7);
  cursor: pointer;
  transition: all 0.15s;
}
.narrative-save-btn:hover {
  background: rgba(15,110,86,0.35);
  color: #86d4ba;
}

/* Publish tab - no top padding */
#session-tab-export {
  padding-top: 0 !important;
  margin-top: 0 !important;
}

/* Storyboard panel caption - gold parchment */
.storyboard-panel-meta {
  color: var(--gold-light) !important;
}
.storyboard-panel-meta .moment-num {
  color: rgba(201,168,76,0.6) !important;
}
.storyboard-panel-meta .moment-title {
  color: var(--gold-light) !important;
}
.moment-prompt-text {
  color: rgba(201,168,76,0.35) !important;
}

/* Fix regenerate button visibility on storyboard panels */
.storyboard-panel .moment-regen-btn {
  opacity: 0;
  position: absolute;
  top: 8px;
  right: 8px;
}
.storyboard-panel:hover .moment-regen-btn {
  opacity: 1;
}

/* Narrative blocks span full width in storyboard grid */
.moments-grid .narrative-block {
  grid-column: 1 / -1;
}

/* Storyboard layout - alternating narrative and image panels */
#moments-grid {
  display: block;
}
.panels-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
  align-items: start;
}
@media (max-width: 700px) {
  .panels-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 480px) {
  .panels-grid { grid-template-columns: 1fr; }
}
/* Narrative panels sit in the grid same as image panels */
.narrative-panel {
  border: 1px solid rgba(201,168,76,0.12);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: rgba(12,8,4,0.5);
  display: flex;
  flex-direction: column;
}
.narrative-panel .narrative-inline-box {
  flex: 1;
  min-height: 160px;
}

/* TIER WATERMARK */
.watermarked {
  position: relative;
}
.watermarked::after {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22280%22%20height%3D%22170%22%3E%3Ctext%20x%3D%22140%22%20y%3D%2292%22%20fill%3D%22%23ffffff%22%20fill-opacity%3D%220.40%22%20font-family%3D%22Georgia%2Cserif%22%20font-size%3D%2219%22%20font-weight%3D%22700%22%20letter-spacing%3D%223%22%20text-anchor%3D%22middle%22%20transform%3D%22rotate%28-30%20140%2092%29%22%3ECAMPAIGNIA%20TRIAL%3C%2Ftext%3E%3C%2Fsvg%3E");
  background-repeat: repeat;
  background-position: center;
  pointer-events: none;
}

/* ACCOUNT PAGE PLAN CARDS */
.plan-card-current {
  box-shadow: 0 0 0 2px var(--gold);
}

/* Trial expiry banner */
.trial-expired-banner {
  background: rgba(139,26,26,0.4);
  border: 1px solid rgba(240,149,149,0.4);
  color: #f09595;
  padding: 12px 16px;
  border-radius: var(--radius);
  font-size:13px;
  margin-bottom: 16px;
  text-align: center;
}

/* Platinum prompt editing on storyboard panels */
.moment-prompt-wrap { position: relative; }
.moment-prompt-edit-btn {
  margin-top: 6px;
  background: transparent;
  border: 1px solid rgba(201,168,76,0.35);
  color: rgba(201,168,76,0.7);
  font-size: 11px;
  padding: 3px 10px;
  border-radius: 6px;
  cursor: pointer;
  font-family: var(--font-display);
}
.moment-prompt-edit-btn:hover {
  background: rgba(201,168,76,0.12);
  color: var(--gold);
}
/* Pass 1 — the Direction "prompt" block under each Storyboard narrative panel
   reuses the image panel's .moment-prompt-* styling; these align the Edit
   button under the text and mute the empty (no-direction) state. */
.narr-dir-wrap .moment-prompt-edit-btn { margin-left: 14px; margin-bottom: 8px; }
.narr-dir-empty { opacity: 0.8; }
.moment-prompt-editor {
  width: 100%;
  min-height: 90px;
  background: rgba(12,8,4,0.6);
  border: 1px solid rgba(201,168,76,0.35);
  border-radius: 6px;
  color: var(--gold-light);
  font-size: 12px;
  line-height: 1.5;
  padding: 8px 10px;
  font-family: inherit;
  resize: vertical;
  box-sizing: border-box;
}
.moment-prompt-actions {
  display: flex;
  gap: 8px;
  margin-top: 8px;
}

/* Canonical character prompt — Edit Character modal */
.char-prompt-text {
  font-size: 13px;
  line-height: 1.6;
  color: var(--parchment);
  background: rgba(20,12,4,0.75);
  border: 1px solid rgba(201,168,76,0.2);
  border-radius: var(--radius);
  padding: 10px 12px;
}
.char-prompt-empty {
  font-size: 13px;
  font-style: italic;
  color: var(--text);
  background: rgba(20,12,4,0.4);
  border: 1px dashed rgba(201,168,76,0.2);
  border-radius: var(--radius);
  padding: 10px 12px;
}
.char-prompt-editor {
  width: 100%;
  min-height: 110px;
  padding: 9px 12px;
  border: 1px solid rgba(201,168,76,0.2);
  border-radius: var(--radius);
  font-size: 13px;
  line-height: 1.6;
  background: rgba(20,12,4,0.75);
  color: var(--parchment);
  font-family: inherit;
  resize: vertical;
  box-sizing: border-box;
  transition: border-color 0.15s;
}
.char-prompt-editor:focus {
  outline: none;
  border-color: rgba(201,168,76,0.6);
}
/* v3.0.860 -- TD-724. The message line under the prompt field. Every failure path
   for this character writes here, in view, rather than at the top of a modal the
   reader has scrolled away from. */
.char-prompt-msg {
  margin: 6px 0 0; padding: 8px 10px; border-radius: 6px; font-size: 12px; line-height: 1.45;
  background: rgba(150,40,20,0.10); border: 1px solid rgba(150,40,20,0.35); color: #7a2412;
}
.char-prompt-msg.hidden { display: none; }
.char-prompt-hint { margin: 4px 0 0; font-size: 11px; color: var(--text-muted); }
/* The line beside the picture, saying in words what the lit button says in colour. */
.char-ref-stale {
  margin: 0 0 6px; padding: 7px 10px; border-radius: 6px; font-size: 12px; line-height: 1.4;
  background: rgba(201,168,76,0.16); border: 1px solid rgba(201,168,76,0.5); color: #6b4d0e;
}
.char-prompt-actions {
  display: flex;
  gap: 8px;
  margin-top: 10px;
}

/* Reference image shown under the Rebuild button in the Edit modal */
.char-ref-image {
  margin-top: 14px;
}
.char-ref-label {
  font-size: 11px;
  letter-spacing: 0.5px;
  color: var(--text-light);
  margin-bottom: 6px;
}
.char-ref-image img {
  max-width: 320px;
  width: 100%;
  border-radius: var(--radius);
  border: 1px solid rgba(201,168,76,0.18);
  cursor: zoom-in;
  display: block;
}
.char-ref-imgwrap {
  position: relative;
  display: inline-block;
}
.char-ref-archive {
  position: absolute;
  top: 8px;
  left: 8px;
  z-index: 3;
}
/* Regenerate/Retouch/Archive pills on the character reference image. .panel-pill
   defaults to opacity:0 and is revealed by the storyboard hover rule, which does
   not apply inside the character modal — so reveal them on the wrapper's hover. */
.char-ref-imgwrap:hover .panel-pill { opacity: 1; }
.sc-review-imgwrap:hover .panel-pill { opacity: 1; }
.archives-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 16px;
}
.archive-card {
  background: rgba(0,0,0,0.25);
  border: 1px solid rgba(201,168,76,0.18);
  border-radius: var(--radius);
  overflow: hidden;
}
.archive-thumb img {
  width: 100%;
  display: block;
  cursor: zoom-in;
}
.archive-meta {
  padding: 10px 12px 12px;
}
.archive-title {
  font-weight: 600;
  color: var(--gold);
  font-size: 13px;
  word-break: break-word;
}
.archive-sub {
  font-size: 11px;
  color: var(--text-light);
  margin-top: 3px;
}
.archive-del {
  margin-top: 10px;
}
.archive-thumb { position: relative; }
.archive-prompt-btn {
  position: absolute; bottom: 8px; right: 8px; opacity: 0;
  padding: 3px 8px; font-size: 11px; background: rgba(0,0,0,0.7);
  color: rgba(201,168,76,0.9); border: 1px solid rgba(201,168,76,0.4);
  border-radius: 6px; cursor: pointer; transition: opacity .15s;
}
.archive-card:hover .archive-prompt-btn { opacity: 1; }
.archive-prompt-btn:hover { background: rgba(0,0,0,0.9); color: var(--gold); }
.archive-del-thumb{position:absolute;top:6px;right:6px;padding:3px 8px;font-size:10px;background:rgba(0,0,0,0.6);color:rgba(201,168,76,0.8);border:1px solid rgba(201,168,76,0.3);border-radius:6px;cursor:pointer;opacity:0;transition:opacity 0.15s;}
.archive-card:hover .archive-del-thumb{opacity:1;}
.archive-del-thumb:hover{background:rgba(0,0,0,0.85);color:var(--gold);}
.archive-asset-thumb{position:absolute;top:6px;left:6px;padding:3px 8px;font-size:10px;background:rgba(0,0,0,0.6);color:rgba(201,168,76,0.8);border:1px solid rgba(201,168,76,0.3);border-radius:6px;cursor:pointer;opacity:0;transition:opacity 0.15s;}
.archive-card:hover .archive-asset-thumb{opacity:1;}
.archive-asset-thumb:hover{background:rgba(0,0,0,0.85);color:var(--gold);}
/* Ask Claudia: floating opener (pill) + persistent white side-panel chat */
.corner-fabs{display:flex;gap:8px;align-items:center;flex:0 0 auto;margin-left:auto;position:relative;}
.help-fab{padding:8px 14px;font-size:13px;font-weight:600;background:rgba(0,0,0,0.72);color:rgba(201,168,76,0.95);border:1px solid rgba(201,168,76,0.45);border-radius:8px;cursor:pointer;box-shadow:0 4px 14px rgba(0,0,0,0.4);transition:background 0.15s,color 0.15s,border-color 0.15s;white-space:nowrap;line-height:1.2;}
.help-fab:hover{background:rgba(0,0,0,0.92);color:var(--gold);border-color:var(--gold);}
@keyframes helpNudge{0%{box-shadow:0 0 0 0 rgba(216,184,92,0.6);}60%{box-shadow:0 0 0 7px rgba(216,184,92,0);}100%{box-shadow:0 0 0 0 rgba(216,184,92,0);}}
.help-nudge{animation:helpNudge 1.7s ease-out 3;}
@media (prefers-reduced-motion: reduce){.help-nudge{animation:none;}}
.help-panel{position:fixed;top:0;right:0;bottom:0;width:360px;max-width:92vw;z-index:96;background:#ffffff;color:#1f2430;display:flex;flex-direction:column;box-shadow:-8px 0 28px rgba(0,0,0,0.35);}
.help-panel-head{display:flex;align-items:center;justify-content:space-between;padding:14px 16px;border-bottom:1px solid #e6e1d6;background:#faf8f3;}
.help-panel-title{font-family:var(--font-display);font-size:17px;color:#7a5c12;}
.help-close{background:none;border:none;font-size:24px;line-height:1;color:#6b7280;cursor:pointer;padding:0 4px;}
.help-close:hover{color:#1f2430;}
.help-panel-body{flex:1;min-height:0;display:flex;flex-direction:column;}
.help-thread{flex:1;min-height:0;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:10px;}
.help-msg{font-size:14px;line-height:1.5;padding:9px 12px;border-radius:10px;max-width:88%;white-space:pre-wrap;word-wrap:break-word;}
.help-msg-bot{align-self:flex-start;background:#f1eee6;color:#2a2f3a;border:1px solid #e2dccb;}
.help-msg-user{align-self:flex-end;background:#e8eef7;color:#1f2a3a;border:1px solid #d2deef;}
.help-msg-pending{opacity:0.7;font-style:italic;}
.help-msg-error{align-self:flex-start;background:#fbecea;color:#b4452f;border:1px solid #f0cfc8;}
.help-compose{display:flex;gap:8px;align-items:flex-end;padding:12px 16px;border-top:1px solid #e6e1d6;background:#faf8f3;}
.help-input{flex:1;box-sizing:border-box;min-height:44px;max-height:140px;padding:10px 12px;font-size:14px;font-family:inherit;color:#1f2430;background:#ffffff;border:1px solid #cfd4dc;border-radius:8px;resize:vertical;line-height:1.4;}
.help-input:focus{outline:none;border-color:var(--gold);box-shadow:0 0 0 2px rgba(201,168,76,0.25);}
@media (max-width:480px){.help-fab{padding:10px 16px;font-size:14px;}.tour-replay{padding:10px 16px;font-size:14px;}.help-panel{width:100%;max-width:100%;}}
.archive-row { display: flex; justify-content: space-between; gap: 10px; font-size: 11px; margin-top: 3px; }
.archive-row span { color: var(--gold-dim); }
.archive-row b { color: var(--gold-light); font-weight: 600; text-align: right; word-break: break-word; }
.archives-filters { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.archive-filter { padding: 6px 10px; background: rgba(0,0,0,0.3); color: var(--gold-light); border: 1px solid rgba(201,168,76,0.25); border-radius: 8px; font-size: 12px; }
.archive-filter option { background: #1b1b20; color: var(--gold-light); }
.archive-filter-search { min-width: 150px; }
.archive-clear { cursor: pointer; }
.moment-replace-btn{position:absolute;top:6px;right:6px;padding:3px 8px;font-size:10px;background:rgba(0,0,0,0.6);color:rgba(201,168,76,0.8);border:1px solid rgba(201,168,76,0.3);border-radius:6px;cursor:pointer;opacity:0;transition:opacity 0.15s;}
.moment-card:hover .moment-replace-btn{opacity:1;}
.moment-replace-btn:hover{background:rgba(0,0,0,0.8);color:var(--gold);}
.moment-replace-btn[disabled]{cursor:not-allowed;color:rgba(201,168,76,0.3);}
.storyboard-panel .moment-replace-btn{position:absolute;top:38px;right:8px;opacity:0;}
.storyboard-panel:hover .moment-replace-btn{opacity:1;}
body.role-player.can-edit-fork .moment-replace-btn{display:inline-block !important;}
.panel-img-actions{position:absolute;top:8px;left:8px;right:8px;z-index:3;display:flex;flex-wrap:wrap;gap:6px;}
.panel-pill{padding:3px 7px;font-size:10px;background:rgba(0,0,0,0.6);color:rgba(201,168,76,0.85);border:1px solid rgba(201,168,76,0.3);border-radius:6px;cursor:pointer;opacity:0;transition:opacity 0.15s,background 0.15s,color 0.15s;white-space:nowrap;line-height:1.3;}
.storyboard-panel:hover .panel-pill{opacity:1;}
.storyboard-panel.tour-show-pills .panel-pill,.storyboard-panel.tour-show-pills .moment-replace-btn{opacity:1;}
.panel-pill:hover{background:rgba(0,0,0,0.85);color:var(--gold);}
.panel-pill[disabled]{cursor:not-allowed;color:rgba(201,168,76,0.3);}
.panel-pill.is-on{opacity:1;color:var(--gold);border-color:var(--gold);}
.panel-pill.is-static{cursor:default;}
/* v3.0.860 -- TD-724. The prompt changed and this picture has not caught up.
   opacity is FORCED because .panel-pill is invisible until its wrapper is hovered,
   and a signal you have to go looking for is not a signal. */
.panel-pill.is-stale{opacity:1 !important;color:#2a1a06;background:var(--gold);border-color:var(--gold);font-weight:600;}
.panel-pill.is-stale:hover{background:#e0c169;color:#2a1a06;}
body.role-player.can-edit-fork .panel-pill{display:inline-flex !important;}
.modal.archive-picker{max-width:880px;background:#241810;border:1px solid rgba(201,168,76,0.25);}
#style-picker-modal .modal{overflow-y:visible;max-height:92vh;display:flex;flex-direction:column;}
.modal.archive-picker .modal-title{color:var(--gold-light);}
.modal.archive-picker .modal-close{color:var(--gold-dim);}
.modal.archive-picker .modal-close:hover{color:var(--gold-light);}
.archive-picker-filters{display:flex;flex-wrap:wrap;gap:8px;}
.archive-picker-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:12px;margin-top:14px;max-height:60vh;overflow-y:auto;}
.archive-pick-item{border:1px solid rgba(201,168,76,0.2);border-radius:8px;overflow:hidden;background:rgba(0,0,0,0.25);display:flex;flex-direction:column;}
/* v3.0.670 -- TD-474. THE PICTURE AT ITS OWN SHAPE.
   Was: a fixed 130px box with object-fit:cover, so a tower, a panorama and a square all arrived as
   the same rectangle with the difference cropped off -- and the shape was the one thing the reader
   was trying to judge. Ian: "show the pictures at their existing aspect ratio... small, but their
   correct aspect ratio and not cropped."

   width:auto WITH max-width AND max-height is what keeps it undistorted. width:100% + max-height
   clamps the height while the width stays full, which squashes a tall image -- the shape lie again,
   in a new form. Letting both axes be auto and capping both is the only combination where the
   browser is free to honour the aspect. */
.archive-pick-shot{position:relative;display:flex;align-items:center;justify-content:center;
  min-height:96px;max-height:190px;padding:6px;background:rgba(0,0,0,0.35);}
.archive-pick-imgwrap{position:relative;display:inline-block;line-height:0;}
/* v3.0.672 -- TD-475. The tester badge reads as a state, not a button: it is the one badge in this
   row that says something is switched OFF (billing), so it borrows the warning palette the Support
   Access panel uses rather than the gold everything else here wears. */
/* v3.0.676 -- a lone Copper user keeps the copper-orange the retired badge used, so the warning
   still reads as a warning rather than as an ordinary plan label. */
.tier-badge-lone{border-color:rgba(184,115,51,0.7)!important;color:#fff!important;
  background:#b87333!important;font-weight:700!important;}
.tier-badge-tester{border-color:rgba(251,146,60,0.55)!important;color:#fb923c!important;
  background:rgba(194,65,12,0.12)!important;}
.archive-pick-item img{max-width:100%;max-height:178px;width:auto;height:auto;display:block;
  cursor:pointer;border-radius:4px;}
/* v3.0.671 -- TD-474. The part of the picture the cover keeps. Faint on purpose: it is a guide over
   the artwork, not a frame around it, and it has to stay readable on both a dark and a bright
   image -- hence the white line with a dark companion shadow rather than one colour that
   disappears against half the archive. Positioned in percentages by pickCropOverlay, which is why
   it starts hidden: an unpositioned box would flash across the whole thumbnail on first paint. */
.archive-pick-crop{position:absolute;display:none;box-sizing:border-box;pointer-events:none;
  border:1px dashed rgba(255,255,255,0.85);box-shadow:0 0 0 1px rgba(0,0,0,0.45);border-radius:2px;}
/* The shape in a word, because "is that tall enough for a cover" should not be a squint. */
.archive-pick-shape{position:absolute;left:6px;bottom:6px;padding:2px 7px;border-radius:10px;
  font-size:10px;font-weight:600;letter-spacing:0.03em;color:#1a1410;background:rgba(201,168,76,0.92);
  pointer-events:none;}
/* The image already on the book. The old cover picker explained "click the highlighted image to
   remove it" in a footnote; the button now says so itself. */
.archive-pick-item.is-current{border-color:var(--gold);box-shadow:0 0 0 2px rgba(201,168,76,0.35);}
.archive-pick-item.is-current .archive-pick-use{background:transparent;color:var(--gold-light);
  border:1px solid rgba(201,168,76,0.5);}
.archive-pick-cap{padding:6px 8px;font-size:11px;color:var(--gold-dim);line-height:1.35;flex:1;}
.archive-pick-cap b{color:var(--gold-light);font-weight:600;}
.archive-pick-use{margin:0 8px 8px;padding:5px;font-size:11px;cursor:pointer;background:var(--gold);color:#1a1410;border:none;border-radius:6px;font-weight:600;}
.archive-pick-use:hover{filter:brightness(1.08);}
.archive-pick-empty{padding:24px;text-align:center;color:var(--gold-dim);font-size:13px;}
.archive-pick-note{grid-column:1/-1;padding:8px 10px;margin-bottom:2px;border-radius:6px;background:rgba(201,168,76,0.10);border:1px solid rgba(201,168,76,0.25);color:var(--gold-light);font-size:12px;}
#view-archives .page-header { margin-bottom: 10px; }
#view-archives .page-subtitle { margin-top: 2px; }
.prompt-modal-box { max-width: 640px; width: 90%; max-height: 80vh; overflow: auto; background: #1b1b20; border: 1px solid rgba(201,168,76,0.3); border-radius: 12px; padding: 20px 22px; }
.prompt-modal-title { color: var(--gold); font-weight: 700; font-size: 15px; margin-bottom: 10px; }
.prompt-modal-text { white-space: pre-wrap; color: var(--parchment); font-size: 13px; line-height: 1.5; }

/* NPC checkbox row + badge */
.form-checkbox-row {
  display: flex;
  align-items: center;
  gap: 9px;
  cursor: pointer;
  font-size: 13px;
  color: var(--gold-light);
}
.form-checkbox-row input[type="checkbox"] {
  width: 16px;
  height: 16px;
  accent-color: var(--gold);
  cursor: pointer;
  flex-shrink: 0;
}
.char-badge-npc {
  background: rgba(90,70,140,0.35);
  color: #c9bdf0;
  border: 1px solid rgba(160,140,210,0.4);
  margin-left: 5px;
}

/* Session Characters tab */
.sc-card {
  border: 1px solid rgba(201,168,76,0.2);
  border-radius: var(--radius-lg);
  background: rgba(20,12,4,0.5);
  padding: 14px;
  margin-bottom: 12px;
}

/* Stage 3: change-detected badge */
.sc-change-badge {
  margin: 10px 0 4px;
  padding: 8px 12px;
  background: rgba(224,122,106,0.12);
  border: 1px solid rgba(224,122,106,0.4);
  border-radius: var(--radius);
  color: #e8a99c;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.sc-change-badge:hover {
  background: rgba(224,122,106,0.2);
}

/* Accepted-change badge — quieter, greenish, re-opens the review screen */
.sc-change-badge-accepted {
  background: rgba(122,184,116,0.12);
  border-color: rgba(122,184,116,0.4);
  color: #9ccb94;
}
.sc-change-badge-accepted:hover {
  background: rgba(122,184,116,0.2);
}

/* Stage 3: change review screen */
.sc-review-title {
  font-weight: 600;
  color: #e8a99c;
  font-size: 14px;
}
.sc-review-name {
  font-family: var(--font-display);
  font-size: 18px;
  color: var(--gold);
  margin: 4px 0;
}
.sc-review-detected {
  font-size: 13px;
  color: var(--text-light);
  margin-bottom: 10px;
}
.sc-review-label {
  display: block;
  font-size: 12px;
  color: var(--text-light);
  margin-bottom: 4px;
}
.sc-review-retouch {
  display: flex;
  gap: 8px;
  margin: 6px 0;
}
.sc-review-row {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  align-items: flex-start;
  margin: 6px 0 8px;
}
.sc-review-row .sc-review-imgwrap {
  flex: 0 0 auto;
  width: 280px;
  max-width: 100%;
  margin: 0;
}
.sc-review-side {
  flex: 1 1 220px;
  min-width: 0;
}
.sc-review-side .sc-review-label {
  margin-top: 0;
}

.sc-review-imgwrap {
  margin-top: 12px;
  position: relative;
}
.sc-review-archive {
  position: absolute;
  top: 8px;
  left: 8px;
  z-index: 3;
}
.sc-review-img {
  max-width: 280px;
  width: 100%;
  border-radius: var(--radius);
  border: 1px solid rgba(201,168,76,0.18);
  display: block;
}
.sc-review-img-empty {
  padding: 30px;
  text-align: center;
  color: var(--text-light);
  font-size: 13px;
  background: rgba(20,12,4,0.5);
}
.sc-review-msg {
  font-size: 13px;
  color: var(--gold);
  margin: 4px 0;
  min-height: 16px;
}

.sc-card-head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 10px;
}
.sc-thumb {
  width: 52px;
  height: 52px;
  border-radius: 8px;
  object-fit: cover;
  object-position: center top;
  border: 1px solid rgba(201,168,76,0.25);
  flex-shrink: 0;
}
.sc-thumb-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  background: rgba(201,168,76,0.08);
  color: rgba(201,168,76,0.4);
}
.sc-card-id { flex: 1; }
.sc-card-name {
  font-family: var(--font-display);
  font-size: 15px;
  color: var(--gold-light);
}
.sc-card-cls {
  font-size: 12px;
  color: var(--text-light);
  margin-top: 2px;
}
.sc-card-prompt {
  font-size: 13px;
  line-height: 1.6;
  color: var(--parchment);
  background: rgba(20,12,4,0.75);
  border: 1px solid rgba(201,168,76,0.15);
  border-radius: var(--radius);
  padding: 10px 12px;
}

/* Phase 3 — hide DM-only UI elements when the current user is a player.
   The body.role-player class is toggled by applyRoleVisibility() in
   app.js whenever a campaign view is rendered. Elements marked
   .dm-only (statically in HTML or rendered dynamically in JS) get
   hidden via display:none. !important is used because some inline
   styles or layout containers may set display via JS otherwise. */
body.role-player .dm-only { display: none !important; }
/* Phase 4 Step 3 — when a player is viewing THEIR OWN version, the
   per-panel edit + regenerate controls become available again. Higher
   specificity than the rule above so it wins. */
body.role-player.can-edit-fork .moment-prompt-edit-btn,
body.role-player.can-edit-fork .moment-regen-btn { display: inline-block !important; }
/* Phase 4 Step 3c — a player on their OWN version gets the batch image
   generators too: "Generate Storyboard Images" (Review tab) and
   "Generate all images" (Storyboard tab). Backend infers their fork. */
body.role-player.can-edit-fork #review-generate-btn,
body.role-player.can-edit-fork #generate-all-btn,
body.role-player.can-edit-fork #review-narr-style-btn,
body.role-player.can-edit-fork #sb-narr-style-btn,
body.role-player.can-edit-fork #sb-generate-narr-btn,
body.role-player.can-edit-fork #review-art-style-btn,
body.role-player.can-edit-fork #sb-art-style-btn { display: inline-block !important; }
body.role-player.can-edit-fork #extract-btn { display: inline-block !important; }
/* Viewing a fork you do not own (e.g. DM looking at a player version) is read-only. */
body.viewing-foreign-fork #extract-btn,
body.viewing-foreign-fork #review-generate-btn,
body.viewing-foreign-fork #generate-all-btn,
body.viewing-foreign-fork #review-narr-style-btn,
body.viewing-foreign-fork #sb-narr-style-btn,
body.viewing-foreign-fork #review-art-style-btn,
body.viewing-foreign-fork #sb-art-style-btn,
body.viewing-foreign-fork #sb-generate-narr-btn,
body.viewing-foreign-fork .moment-regen-btn,
body.viewing-foreign-fork .moment-prompt-edit-btn { display: none !important; }
/* Publish/Preview is a READ capability — a player may preview any version
   they can see (canonical, their own, or another player's Ready version). */
body.role-player #stab-export { display: block !important; }

/* ============================================================
   PHASE 3 DEPLOY 2 — Members tab styles
   ============================================================ */

/* A single row in the members list or pending-invites list. Flex layout:
   icon | identity block | meta block | actions menu. */
.member-row, .invite-row {
  display: flex;
  align-items: center;
  gap: 12px;
  background: rgba(20,12,4,0.75);
  border: 1px solid rgba(201,168,76,0.15);
  border-radius: var(--radius);
  padding: 10px 14px;
  position: relative;
}

.member-row-icon, .invite-row-icon {
  font-size: 24px;
  width: 32px;
  text-align: center;
  flex-shrink: 0;
}

.member-row-headshot {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  object-fit: cover;
  object-position: center top;
  display: block;
  border: 1px solid rgba(201,168,76,0.3);
}

.member-row-body, .invite-row-body {
  flex: 1;
  min-width: 0;
}

.member-row-name {
  font-size: 15px;
  color: var(--gold);
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.member-row-email, .invite-row-email {
  font-size: 13px;
  color: var(--gold);
  opacity: 0.85;
  margin-top: 3px;
}

.member-row-meta, .invite-row-meta {
  font-size: 12px;
  color: var(--gold);
  opacity: 0.9;
  margin-top: 4px;
}

.member-row-actions, .invite-row-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

/* Role badges — DM crown, Player */
.role-badge {
  display: inline-block;
  padding: 2px 8px;
  font-size: 11px;
  font-family: var(--font-display);
  border-radius: 10px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.role-badge-dm {
  background: rgba(201,168,76,0.18);
  color: var(--gold);
  border: 1px solid rgba(201,168,76,0.35);
}
.role-badge-player {
  background: rgba(80,140,200,0.18);
  color: #94c0e8;
  border: 1px solid rgba(120,170,210,0.35);
}

/* Ellipsis menu (⋯) — opens a small dropdown for destructive actions */
.row-menu {
  position: relative;
  display: inline-block;
}
.row-menu-btn {
  background: transparent;
  border: 1px solid rgba(201,168,76,0.2);
  color: var(--text);
  width: 30px;
  height: 30px;
  border-radius: 6px;
  cursor: pointer;
  font-size: 16px;
  line-height: 1;
}
.row-menu-btn:hover {
  background: rgba(201,168,76,0.1);
  border-color: rgba(201,168,76,0.4);
}
.row-menu-dropdown {
  position: absolute;
  right: 0;
  top: 34px;
  background: rgba(20,12,4,0.98);
  border: 1px solid rgba(201,168,76,0.3);
  border-radius: 6px;
  padding: 4px 0;
  min-width: 130px;
  z-index: 100;
  box-shadow: 0 6px 18px rgba(0,0,0,0.5);
  display: none;
}
.row-menu-dropdown.open { display: block; }
/* Session tiles are short and clip overflow, so their menu opens upward. */
.session-card .row-menu-dropdown { top: auto; bottom: 34px; left: 0; right: auto; }
.row-menu-item {
  display: block;
  padding: 8px 14px;
  background: transparent;
  border: none;
  color: var(--gold-light);
  font-size: 13px;
  text-align: left;
  cursor: pointer;
  width: 100%;
}
.row-menu-item:hover { background: rgba(201,168,76,0.12); }
.row-menu-item-danger { color: #f5b0a8; }
.row-menu-item-danger:hover { background: rgba(180,40,30,0.18); }

/* Pending-invite specific touches */
.invite-row-expired-tag {
  display: inline-block;
  font-size: 11px;
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 2px 8px;
  border-radius: 10px;
  background: rgba(180,60,40,0.18);
  color: #f5b0a8;
  border: 1px solid rgba(220,120,100,0.35);
  margin-left: 8px;
}

/* Character-tab badges (Phase 3 Deploy 2) */
.char-owner-badge {
  display: inline-block;
  font-size: 11px;
  padding: 2px 8px;
  border-radius: 10px;
  background: rgba(80,140,200,0.18);
  color: #94c0e8;
  border: 1px solid rgba(120,170,210,0.35);
  margin-top: 4px;
}
.char-pending-badge {
  display: inline-block;
  font-size: 11px;
  padding: 2px 8px;
  border-radius: 10px;
  background: rgba(201,168,76,0.15);
  color: var(--gold);
  border: 1px dashed rgba(201,168,76,0.4);
  margin-top: 4px;
}

/* ============================================================
   PHASE 3 DEPLOY 3 — Session access status chip + select
   ============================================================ */

.session-access-status-select {
  display: inline-block;
  padding: 6px 10px;
  font-size: 13px;
  font-family: var(--font-display);
  vertical-align: middle;
  cursor: pointer;
}

.session-access-chip {
  display: inline-block;
  padding: 4px 12px;
  font-size: 12px;
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  border-radius: 12px;
  vertical-align: middle;
}
.session-access-chip-draft {
  background: rgba(110,100,85,0.55);
  color: #f5e8c8;
  border: 1px solid rgba(200,190,170,0.45);
}
.session-access-chip-ready {
  background: rgba(80,160,100,0.35);
  color: #d4f0d8;
  border: 1px solid rgba(140,200,160,0.55);
}

/* Lock indicator on Characters tab when campaign is locked (player POV).
   Subtle banner above the character grid. */
.campaign-lock-banner {
  background: rgba(40,30,15,0.85);
  border: 1px solid rgba(201,168,76,0.3);
  border-left: 4px solid var(--gold);
  border-radius: var(--radius);
  padding: 12px 16px;
  margin: 14px 0;
  color: var(--gold);
  font-size: 13px;
  line-height: 1.5;
}
.campaign-lock-banner-icon {
  display: inline-block;
  margin-right: 8px;
  font-size: 16px;
}

/* ============================================================
   Session list — Draft/Ready as badges + thumbnail + ⋯ menu
   ============================================================ */

/* Reuse .session-badge styling for the access-status pill on the list.
   Ready inherits the default green-success treatment. Draft uses a
   muted gold tint so it harmonizes with Chronicle's palette without
   competing with the Ready or "Has transcript" badges. */
.session-badge.session-badge-draft {
  background: rgba(201,168,76,0.18);
  color: var(--gold);
  border: 1px solid rgba(201,168,76,0.35);
}

/* Thumbnail to the left of the session name. Square, slightly rounded.
   Hidden when no image is available (the JS only renders it when
   first_image_url is set). */
.session-thumb {
  width: 56px;
  height: 56px;
  border-radius: 6px;
  object-fit: cover;
  flex-shrink: 0;
  border: 1px solid rgba(201,168,76,0.2);
  background: rgba(20,12,4,0.6);
}

/* The .session-item-left already uses flex layout for name + date
   (defined earlier in this file). Thumbnail slots into that flex row. */

/* Lock + Archive grouped top-left; Regenerate stays top-right */
.panel-img-tl{position:absolute;top:8px;left:8px;z-index:3;display:flex;gap:6px;align-items:flex-start;}
.panel-img-tl .moment-lock-btn,
.panel-img-tl .moment-archive-btn{position:static;top:auto;left:auto;right:auto;}
.moment-archive-btn{padding:2px 5px;font-size:10px;line-height:1;background:rgba(0,0,0,0.6);color:rgba(201,168,76,0.8);border:1px solid rgba(201,168,76,0.3);border-radius:6px;cursor:pointer;opacity:0.85;transition:opacity 0.15s;}
.moment-archive-btn:hover{opacity:1;background:rgba(0,0,0,0.8);color:var(--gold);}
.moment-archive-btn.is-archived{color:var(--gold);border-color:var(--gold);opacity:1;}
.moment-archive-btn img{display:block;height:20px;width:auto;}

/* Style picker (Narrative Styles; Art Styles join in Step 3) — a card grid
   inside the shared modal. Mirrors .archive-picker-grid so it reflows to a
   single column and scrolls internally on narrow / mobile screens. */
.style-card-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px;max-height:62vh;overflow-y:auto;padding:2px;}
/* Verbosity segmented control (Narrative Style Modal). Mobile-friendly: fills width, taps big. */
.verbosity-seg{display:flex;gap:0;border:1px solid var(--gold-dim);border-radius:6px;overflow:hidden;max-width:180px;}
.vseg-btn{flex:1 1 0;padding:5px 4px;background:transparent;color:var(--gold-dim);border:0;border-right:1px solid var(--gold-dim);font:inherit;font-size:11px;cursor:pointer;transition:background .12s,color .12s;}
.vseg-btn:last-child{border-right:0;}
.vseg-btn:hover{background:rgba(212,175,110,0.10);}
.vseg-btn.is-on{background:var(--gold);color:#1a1410;font-weight:600;}

.style-card{background:rgba(20,12,4,0.6);border:1px solid rgba(201,168,76,0.18);border-radius:10px;padding:12px 13px;cursor:pointer;transition:border-color .12s, background .12s;}
.style-card:hover{border-color:rgba(201,168,76,0.5);background:rgba(30,18,6,0.7);}
.style-card.is-selected{border-color:var(--gold);background:rgba(201,168,76,0.12);}
.style-card.is-locked{opacity:.5;cursor:not-allowed;pointer-events:none;}
.style-card.is-locked .style-card-current{color:#fff;background:#6b7280;}
.style-card-name{font-family:var(--font-display);font-size:14px;color:var(--gold-light);font-weight:600;margin-bottom:5px;display:flex;align-items:center;gap:6px;flex-wrap:wrap;}
.style-card-current{font-size:10px;color:var(--gold);background:rgba(201,168,76,0.18);border-radius:4px;padding:1px 6px;font-weight:600;}
.style-card-desc{font-size:12px;color:rgba(230,210,160,0.82);line-height:1.45;margin-bottom:7px;}
.style-card-eg{font-size:11.5px;color:rgba(201,168,76,0.62);font-style:italic;line-height:1.45;border-left:2px solid rgba(201,168,76,0.25);padding-left:8px;}

/* Per-panel style labels: art style on the image caption, narrative style in
   the narrative block header. Both reflect the style ACTUALLY used (moments.style
   stamped at generation; narrative_style_used stamped on the narrative write). */
.moment-artstyle{font-size:10px;color:rgba(201,168,76,0.5);margin-top:5px;}
.narr-block-style{text-transform:none;letter-spacing:0;color:rgba(201,168,76,0.7);font-weight:600;margin-left:6px;}

/* Campaign cover art on the list chip: faded, bordered, filling most of the
   card behind the text. Content is lifted above the cover layer. */
.campaign-card{position:relative;overflow:hidden;}
.campaign-card-cover{position:absolute;inset:8px;border-radius:8px;background-size:cover;background-position:center;opacity:0.43;border:1px solid rgba(201,168,76,0.45);pointer-events:none;z-index:0;}
.campaign-card-icon,.campaign-card-name,.campaign-card-desc,.campaign-card-meta{position:relative;z-index:1;}
/* Inline campaign name/description editing on the Sessions header */
.camp-edit-pencil{background:none;border:none;color:rgba(201,168,76,0.5);cursor:pointer;font-size:0.55em;padding:2px 5px;line-height:1;border-radius:4px;}
.camp-edit-pencil:hover{color:var(--gold);background:rgba(201,168,76,0.12);}
.camp-edit-input{font-family:inherit;font-size:0.85em;background:rgba(12,8,4,0.75);border:1px solid rgba(201,168,76,0.45);border-radius:5px;color:var(--gold);padding:2px 8px;min-width:220px;max-width:100%;}
.camp-edit-textarea{display:block;width:100%;max-width:560px;font-family:var(--font-body,inherit);font-size:12px;line-height:1.5;background:rgba(12,8,4,0.75);border:1px solid rgba(201,168,76,0.45);border-radius:5px;color:rgba(230,210,160,0.92);padding:5px 8px;min-height:46px;margin-top:5px;resize:vertical;}
/* Archive 'Make cover' control (DM-only) */
.archive-cover-toggle{display:inline-flex;align-items:center;gap:4px;font-size:10px;color:rgba(201,168,76,0.72);cursor:pointer;user-select:none;}
.archive-cover-toggle input{margin:0;width:13px;height:13px;accent-color:var(--gold);cursor:pointer;}
.archive-actions{display:flex;align-items:center;gap:8px;margin-top:8px;}
.archive-actions .archive-del{margin-left:auto;}

/* Admin Settings: tabs + Tiers config */
.settings-tabs {
  display: flex;
  gap: 4px;
  margin-bottom: 18px;
  border-bottom: 1px solid rgba(201,168,76,0.2);
}
.settings-tab {
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--gold);
  font-size: 14px;
  font-weight: 600;
  padding: 8px 16px;
  cursor: pointer;
  font-family: inherit;
}
.settings-tab:hover { color: var(--gold-light); }
.settings-tab.active {
  color: var(--gold);
  border-bottom-color: var(--gold);
}
.tier-config-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 12px;
  margin-top: 10px;
}

/* Admin Settings: Stats cards */
.stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 14px;
}
.stat-card {
  background: rgba(201,168,76,0.06);
  border: 1px solid rgba(201,168,76,0.2);
  border-radius: 10px;
  padding: 18px 16px;
}
.stat-value {
  font-size: 30px;
  font-weight: 700;
  color: var(--gold);
  line-height: 1.1;
}
.stat-label {
  font-size: 13px;
  color: var(--text-light);
  margin-top: 6px;
}

/* Campaign card settings menu (SM only) + settings toggle */
.campaign-card-menu-btn {
  background: none;
  border: none;
  color: rgba(201,168,76,0.55);
  font-size: 20px;
  line-height: 1;
  padding: 2px 10px;
  min-height: 30px;
  border-radius: 6px;
  cursor: pointer;
}
.campaign-card-menu-btn:hover {
  color: var(--gold);
  background: rgba(201,168,76,0.12);
}
.cs-toggle {
  display: flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  font-size: 14px;
}
.cs-toggle input[type="checkbox"] {
  width: 18px;
  height: 18px;
  cursor: pointer;
  flex: 0 0 auto;
}

/* Admin Dashboard: compact read-only stat rows */
.stats-list { max-width: 520px; }
.stat-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  padding: 7px 0;
  border-bottom: 1px solid rgba(201,168,76,0.12);
  font-size: 14px;
}
.stat-name { color: var(--text-light); }
.stat-num {
  color: var(--gold);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.stats-subhead {
  margin: 20px 0 4px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--gold-dim, rgba(201,168,76,0.7));
}

/* Admin Dashboard: Trends charts */
.trend-block { margin-bottom: 26px; max-width: 760px; }
.trend-title { font-size: 14px; font-weight: 700; color: var(--parchment); margin-bottom: 8px; }
.trend-svg { width: 100%; height: auto; display: block; }
.trend-axis { fill: var(--gold-dim, rgba(201,168,76,0.7)); font-size: 10px; }
.trend-empty { color: var(--text-light); font-size: 13px; padding: 16px 0; }
.trend-legend { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 6px; }
.trend-leg { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-light); }
.trend-swatch { width: 12px; height: 12px; border-radius: 3px; display: inline-block; }

.storyboard-panel-meta .moment-meta-list{font-size:11px;color:rgba(201,168,76,0.55) !important;}

/* Order Printed Copy tab -- readable blue-grey placeholders on dark fields */
#novel-tab-order input::placeholder,
#novel-tab-order textarea::placeholder { color:#9fb0c4; opacity:1; }

/* ============================================================
   PANEL TEXT CONTRAST STANDARD
   The app sits on a dark background image. Panels are one of two kinds:
     - LIGHT panels (white surface): DARK text + light inputs.
     - DARK / transparent panels: the gold/light base text above.
   The light redesign migrated panel backgrounds + titles but left
   .form-label / .form-input on their original dark-theme colors, so
   forms on white panels rendered gold-on-white. These rules pin form
   controls inside the known light panels (.settings-section, .modal) to
   dark text. For any NEW panel, add .panel-light or .panel-dark instead
   of restyling per field, so contrast stays consistent automatically.
   ============================================================ */
.settings-section .form-label,
.modal .form-label,
.panel-light .form-label { color: var(--text-muted); }

.settings-section .form-input,
.modal .form-input,
.panel-light .form-input {
  background: var(--surface);
  color: var(--text);
  border-color: var(--border);
}
.settings-section .form-input:focus,
.modal .form-input:focus,
.panel-light .form-input:focus { border-color: var(--gold); }
.settings-section .form-input::placeholder,
.modal .form-input::placeholder,
.panel-light .form-input::placeholder { color: var(--text-light); }

/* Explicit dark/transparent panel: keep the gold/light treatment even
   when nested inside a light panel. */
.panel-dark .form-label { color: rgba(201,168,76,0.6); }
.panel-dark .form-input {
  background: rgba(15,10,5,0.7);
  color: var(--gold-light);
  border-color: rgba(201,168,76,0.2);
}
.panel-dark .form-input::placeholder { color: rgba(201,168,76,0.55); }

/* ============================================================
   PANEL TEXT STANDARDIZATION
   The page backdrop is dark (Chronicle image). Two panel styles:
   - WHITE panels (.settings-section, default): opaque white -> DARK text.
   - TRANSPARENT panels (.panel-dark): reveal the dark backdrop -> GOLD/LIGHT
     text. Add .panel-dark to any panel that should go transparent.
   Scoped so they never recolor forms elsewhere (auth, modals, cards).
   ============================================================ */
.settings-section .form-label{color:var(--text-muted);}
.settings-section .form-input{background:#fff;border-color:var(--border);color:var(--text);}
.settings-section .form-input::placeholder{color:var(--text-light);}
.settings-section .form-input:focus{border-color:var(--gold);}
/* v3.0.803 -- TD-613. THE SAME FAULT THIS BLOCK EXISTS FOR, ON A CLASS IT DID NOT KNOW ABOUT.
   Ian, on the admin Orders tab: "the lettering its yellow on white which is really hard to read."
   .ord-lbl is rgba(245,232,200,0.75) -- cream, correct on the DARK Order tab it was written for,
   and the admin filters reuse it inside a WHITE .settings-section. Cream on white.
   The comment above this block already prescribes the fix: pin the control inside the known light
   panels rather than restyling per field. So these two lines sit beside the .form-label ones they
   copy, and the dark Order tab is untouched -- its labels live in .ord-col / .ord-sec, not in a
   .settings-section, which was checked rather than assumed. */
.settings-section .ord-lbl,
.modal .ord-lbl,
.panel-light .ord-lbl { color: var(--text-muted); }
.settings-section .ord-lbl-dim,
.modal .ord-lbl-dim,
.panel-light .ord-lbl-dim { color: var(--text-light); }

/* v3.0.803 -- TD-613. THE ORDERS GRID, STRIPED.
   Ian: "have every other row alternate a background color, make the other color more of an
   off white/cream, make sure the lettering is darker... see if you can find something in the CSS
   that is already proven to work."
   IT IS THE THEME'S OWN TWO SURFACES. --surface is #ffffff and --bg is #f9f7f4, the off-white the
   whole light palette already sits on. Nothing new is invented, nothing can drift from the theme,
   and if the palette ever changes the stripes follow it.
   v3.0.790 moved these rows OFF an inline dark panel for exactly that reason (TD-590: a dark box
   painted under light-theme text). The layout moves out of an inline cssText for the same reason
   plus one more -- :nth-child cannot be expressed inline at all. */
.adm-ord-row { display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  padding:9px 10px; border-top:1px solid var(--border); color:var(--text); }
.adm-ord-row:nth-child(even) { background: var(--bg); }
.adm-ord-row:first-child { border-top:none; }
/* The order number was var(--gold) -- #d8b85c on white, which is 1.9:1 against the panel. It is
   the identifier you scan the column for, so it takes the darkest text the palette has: 17.1:1.
   ONE CLASS FOR BOTH ADMIN LISTS. The Promo tab's Existing codes list carried the identical inline
   gold -- it is the list v3.0.790 deliberately modelled the Orders rows on, so it had the same bug
   and nobody had looked at it yet. Two classes for one concept is how the two drift apart, which is
   the fault this file keeps recording. */
.adm-row-id { min-width:110px; font-weight:700; color:var(--text); }
/* Transparent/dark panel variant -- must follow the light rules to win the cascade. */
.panel-dark{background:rgba(20,14,6,0.5);border-color:rgba(201,168,76,0.18);}
.panel-dark .settings-section-title{color:var(--gold-light);}
.panel-dark .settings-section-desc{color:rgba(201,168,76,0.6);}
.panel-dark .form-label{color:rgba(201,168,76,0.78);}
.panel-dark .form-input{background:rgba(15,10,5,0.6);border-color:rgba(201,168,76,0.28);color:var(--gold-light);}
.panel-dark .form-input::placeholder{color:rgba(201,168,76,0.5);}

/* ===== STAGING ENVIRONMENT INDICATOR ===== */
html.staging-env .app-header{
  background:#ff6a00;
  border-bottom:1px solid #7a2e00;
  box-shadow:0 2px 12px rgba(0,0,0,0.35);
}
html.staging-env .app-logo-text{color:#2b1400;}

/* Storyboard moment prominence control */
.moment-prom{display:inline-flex;align-items:center;gap:6px;font-size:11px;color:rgba(201,168,76,0.7);}
.moment-prom-select{background:rgba(20,12,4,0.9);color:var(--gold);border:1px solid rgba(201,168,76,0.35);border-radius:5px;font-size:11px;padding:2px 6px;font-family:inherit;cursor:pointer;}
.moment-prom-static{opacity:0.85;}

/* Storyboard per-moment options panel (... button) */
.moment-opts-btn{background:rgba(20,12,4,0.85);color:var(--gold);border:1px solid rgba(201,168,76,0.35);border-radius:6px;font-size:14px;line-height:1;padding:2px 10px;cursor:pointer;font-weight:700;letter-spacing:1px;margin-left:auto;}
.moment-opts-btn:hover{background:rgba(201,168,76,0.18);border-color:rgba(201,168,76,0.6);}
.moment-options{padding:10px 14px;border-top:1px dashed rgba(201,168,76,0.2);background:rgba(10,8,5,0.45);}
.moment-opts-inner{display:flex;flex-direction:column;gap:8px;}
.moment-opts-casthead{display:flex;align-items:center;gap:8px;}
.moment-options .review-row{display:flex;align-items:center;gap:6px;flex-wrap:wrap;font-size:11px;}
.moment-opts-hint{font-size:10px;color:rgba(201,168,76,0.45);font-style:italic;}
.moment-opts-note{font-size:11px;color:rgba(201,168,76,0.55);font-style:italic;}
.moment-opts-loading{font-size:11px;color:rgba(201,168,76,0.5);}

/* Session establishing image (auto-generated title card) at the top of the
   session detail page. Compact thumbnail + label; click the thumb to enlarge. */
.session-establishing{display:flex;align-items:center;gap:14px;margin:10px 0 6px;padding:10px 12px;background:rgba(20,12,4,0.5);border:1px solid rgba(201,168,76,0.18);border-radius:10px;}
.session-establishing-thumb{width:112px;height:63px;object-fit:cover;border-radius:8px;border:1px solid rgba(201,168,76,0.3);display:block;flex:0 0 auto;}
.session-establishing-meta{display:flex;flex-direction:column;gap:3px;min-width:0;}
.session-establishing-label{font-size:13px;color:var(--gold);font-weight:600;letter-spacing:0.3px;}
.session-establishing-hint{font-size:11px;color:rgba(201,168,76,0.55);line-height:1.35;}
@media (max-width:640px){.session-establishing-thumb{width:92px;height:52px;}}

/* ============================================================
   MOBILE -- Phase 3: the app on a phone.
   Header fits, content stacks, and the storyboard image actions
   (Edit prompt / Regenerate / Retouch / Replace / Lock / Archive)
   work on touch. Additive only -- desktop is unchanged.
   ============================================================ */

/* Storyboard + reference-image action pills are hover-revealed (opacity:0
   until :hover) and overlaid on the image. On NARROW screens (phones / small
   tablets) reveal them and lay the toolbar out BELOW the image instead.
   Gated on WIDTH, NOT on hover/pointer media features: touchscreen laptops and
   desktops misreport (hover:none / pointer:coarse) even with a mouse attached,
   which wrongly pushed the buttons below the image on big screens. A real
   desktop/laptop viewport is far wider than 820px, so it always keeps the
   on-image overlay. .dm-only stays display:none for view-only players, so this
   never exposes edit controls to someone who cannot edit. */
@media (max-width: 820px) {
  .panel-img-actions { position: static; margin: 6px 0 2px; padding: 0 6px; }
  .panel-pill { opacity: 1 !important; padding: 7px 11px; font-size: 12px; }
  .char-ref-imgwrap .panel-pill,
  .sc-review-imgwrap .panel-pill { opacity: 1 !important; padding: 6px 10px; font-size: 12px; }
  .storyboard-panel .moment-regen-btn,
  .storyboard-panel .moment-replace-btn { opacity: 1 !important; }
}

/* Phone layout: compact header, stacked page headers, tighter padding. */
@media (max-width: 640px) {
  .app-header { padding: 0 0.75rem; gap: 8px; }
  .app-logo-text { display: none; }            /* keep just the logo mark */
  /* v3.0.796 -- TD-600. THE BUILD STAMP NEVER APPEARS ON A PHONE, admin or not. Ian: it takes up
     too much space on mobile. It lives inside .app-logo beside the wordmark, and the wordmark
     itself is already hidden one line up -- the header has no room to spare.
     !important is LOAD-BEARING: the /version handler in app.html reveals the stamp with
     `el.style.display = 'inline'`, an INLINE style, which a plain rule cannot beat. */
  #build-stamp { display: none !important; }
  #user-name { display: none; }                /* avatar-only menu button */
  #header-campaigns-btn { display: none; }      /* still reachable via the breadcrumb */
  .app-header-right { gap: 7px; }
  .app-header-right > .btn,
  .app-header-right > #token-balance-label { height: 28px; }
  #trial-badge { font-size: 10px; padding: 3px 6px; }

  .breadcrumb { padding: 4px 0.9rem; }
  .main-content > div:not(#view-campaigns) { padding: 1rem; }
  .page-header { flex-direction: column; align-items: stretch; gap: 10px; }

  /* Section nav scrolls horizontally instead of overflowing. */
  #campaign-subnav { overflow-x: auto; -webkit-overflow-scrolling: touch; white-space: nowrap; }

  .modal { padding: 18px; }
}

/* Phase 3 follow-up: button/toolbar rows wrap on a phone instead of running
   off-screen (campaign section nav, find/replace tools, preview header). */
@media (max-width: 640px) {
  .page-header .flex { flex-wrap: wrap; }
  .fr-tools { flex-wrap: wrap; }
  .notes-toolbar { flex-wrap: wrap; }
}

/* Phase 3 follow-up #2: the session/novel TAB BARS were a no-wrap flex row, so
   on a phone the later tabs (e.g. Preview) ran off-screen. Squeeze the tabs and
   let them wrap, left-aligned. Also compact the preview-tab control buttons. */
@media (max-width: 640px) {
  .sess-tabs { flex-wrap: wrap; }
  .sess-tab { padding: 7px 11px; font-size: 12px; }
  #session-tab-export > div:first-child,
  #novel-tab-preview > div:first-child { justify-content: flex-start; }
  #session-tab-export .btn,
  #novel-tab-preview .btn { padding: 5px 9px; font-size: 11.5px; }
}

/* v3.0.798 -- TD-608. The "this is not the book on screen" notice, on the publish card and on
   the order review. GOLD, NOT RED: it is information about a legitimate choice, not a failure --
   v3.0.783 (TD-583) is the note about everything in this app having been rendered in the failure
   colour by default. Nothing it appears beside is ever disabled. */
.book-mismatch { margin: 10px 0; padding: 10px 12px; border: 1px solid rgba(201,168,76,0.45);
  border-left-width: 3px; border-radius: 8px; background: rgba(201,168,76,0.08);
  color: rgba(245,232,200,0.92); font-size: 12px; line-height: 1.45; }
.book-mismatch b { color: var(--gold); font-weight: 600; }
.book-mismatch ul { margin: 6px 0 6px 18px; padding: 0; }
.book-mismatch li { margin: 2px 0; }
.book-mismatch .bm-foot { opacity: 0.85; }
/* v3.0.799 -- TD-609. On the order button's row it is a flex ITEM, not a block: without a basis
   it claimed a full line of its own and pushed the button up away from it. 260px matches the
   240px #print-place-msg beside it closely enough to wrap at the same point. */
.ord-actions .book-mismatch { flex: 1 1 260px; min-width: 0; margin: 0; }

/* ===== Pre-Publish Prep panel (Preview & Export tab) ===== */
.novel-preview-layout { display:flex; gap:0; align-items:flex-start; }
.novel-preview-col { order:3; flex:1 1 0; min-width:0; }
.novel-prep-panel { order:1; flex:0 0 37%; min-width:300px; background:rgba(20,12,4,0.5); border:1px solid rgba(201,168,76,0.2); border-radius:var(--radius-lg); padding:16px; position:sticky; top:12px; max-height:calc(100vh - 24px); overflow-y:auto; }
.prep-h { font-family:var(--font-display); font-size:15px; color:var(--gold); margin-bottom:12px; letter-spacing:0.04em; }
.prep-field-label { color:rgba(201,168,76,0.9); font-size:12px; margin:0 0 6px; }
.prep-input { width:100%; background:rgba(20,12,4,0.85); color:var(--gold); border:1px solid rgba(201,168,76,0.3); border-radius:8px; padding:8px 10px; font-size:14px; font-family:inherit; box-sizing:border-box; margin-bottom:14px; }
/* v3.0.627 -- two short dropdowns side by side. Ian: they are small and take up too much vertical
   space. min-width:0 is the load-bearing part: without it a flex item refuses to shrink below its
   content, so the longest option string would push the pair wider than the panel instead of the
   select clipping as it already does at full width. Stacks again under 640px, where two selects on
   one row would each be too narrow to read. */
.prep-field-pair { display:flex; gap:12px; align-items:flex-start; }
.prep-field-pair .prep-field-col { flex:1 1 0; min-width:0; }
@media (max-width:640px){ .prep-field-pair { display:block; } }
.prep-textarea { width:100%; min-height:64px; background:rgba(20,12,4,0.85); color:var(--gold); border:1px solid rgba(201,168,76,0.3); border-radius:8px; padding:8px 10px; font-size:13px; font-family:inherit; resize:vertical; box-sizing:border-box; margin-bottom:12px; }
.prep-attest { display:flex; gap:8px; align-items:flex-start; font-size:12px; color:#cbb994; line-height:1.4; margin:0 0 14px; cursor:pointer; }
.prep-attest input { margin-top:2px; flex-shrink:0; }
.prep-hint { color:rgba(240,232,208,0.5); font-size:11px; margin-top:10px; }
.prep-acc { border:1px solid rgba(201,168,76,0.18); border-radius:var(--radius); margin-bottom:10px; overflow:hidden; background:rgba(20,12,4,0.3); }
.prep-acc-head { width:100%; display:flex; align-items:center; justify-content:space-between; gap:6px; padding:5px 10px; border-radius:var(--radius); font-size:12px; font-weight:600; cursor:pointer; border:1px solid var(--gold); background:var(--gold); color:var(--ink); white-space:nowrap; transition:all 0.15s; }
.prep-acc-head:hover { background:var(--gold-light); border-color:var(--gold-light); color:var(--ink); }
.prep-acc-head:active { transform:scale(0.98); }
.prep-acc-chev { transition:transform 0.15s ease; font-size:11px; opacity:0.7; }
.prep-acc.open .prep-acc-chev { transform:rotate(180deg); }
.prep-acc-body { padding:12px; }
#novel-splitter { order:2; flex:0 0 14px; align-self:stretch; cursor:col-resize; display:flex; align-items:center; justify-content:center; }
#novel-splitter .novel-splitter-bar { width:3px; height:60px; background:rgba(216,184,92,0.5); border-radius:2px; transition:background 0.12s; }
#novel-splitter:hover .novel-splitter-bar { background:rgba(216,184,92,0.85); }
.pcl-fields { display:flex; flex-direction:column; gap:10px; }
/* v3.0.613 -- TWO COLUMNS, FIXED. Ian, 2026-08-10, looking at the panel: "little weird... can you
   do two columns with the check boxes in them? Needs to be a little more orderly."
   auto-fit was the disorder. repeat(auto-fit, minmax(150px, 1fr)) lets the COLUMN COUNT follow
   the panel width, and the Prep panel is user-draggable, so the same eight controls reflowed
   into two, three or four columns and a long label like Include table of contents wrapped
   wherever it happened to land. Two columns is a decision rather than a consequence.
   minmax(0, 1fr) rather than 1fr: a grid track will not shrink below its content by default, so
   a long label would push the column wider than half and break the alignment it is here to fix. */
.pcl-toggles { display:grid; grid-template-columns:repeat(2, minmax(0, 1fr)); gap:6px 14px; align-items:start; }
@media (max-width: 900px) { .novel-preview-layout { flex-direction:column; } .novel-prep-panel { flex:1 1 auto; width:100%; min-width:0; position:static; max-height:none; } #novel-splitter { display:none; } }

/* =================================================================================================
   v3.0.600 -- THE PUBLISH PAGE FITS THE SCREEN. Ian, 2026-08-10: "can you make the publish page as
   big as their screen allows and not have an outer scrollbar, then just let the inner scroll bars
   work... I think we might still need it in cases when the session tab runs long."

   MEASURED FIRST. On a 798px viewport the document wanted 1022 -- 224px of overshoot. It was never
   content length; it was three literals each overshooting a little and stacking:
     .novel-prep-panel      max-height:calc(100vh - 24px)   -- but it STARTS ~200px down the page,
                                                               so its bottom sat that far below the
                                                               fold. This is the runaway scrollbar.
     #novel-preview-iframe  height:90vh (inline)            -- 90vh + chrome + headers > 100vh
     resizeNovelPreviewIframe()                             -- copied the PREP PANEL's height onto
                                                               the iframe (measured: both 774)
   Each is a guess at a screen size, written where a derivation belonged.

   THE FIX IS NOT overflow:hidden. Nothing here suppresses the outer scrollbar; the page simply
   stops exceeding the viewport, so the bar has nothing to scroll. That distinction is the whole
   design: if a tab genuinely needs more room -- a short laptop, 125 percent display scaling, a long
   Prep column -- the outer bar returns and everything stays reachable. Clipping would have turned
   every measurement error into an invisible, unclickable button.

   THE HEIGHT COMES FROM --app-chrome, MEASURED IN JS from .app-header and .breadcrumb-row, because
   neither has a height rule and the 44px/35px in .app-layout is itself a pair. Fallback 79px keeps
   today's behaviour if the variable never arrives.

   SCOPED TO min-width:901px, matching the breakpoint above, where the layout already stacks and the
   prep panel is already released. Fixed-height panes on a phone are hostile.
   ================================================================================================= */

/* Fallbacks for every width -- these are what a narrow screen keeps. */
#novel-preview-frame{ min-height:520px; }
#novel-preview-iframe, #novel-preview-loading{ height:75vh; }

@media (min-width:901px){
  #view-novel{
    display:flex; flex-direction:column;
    height:calc(100vh - var(--app-chrome, 79px));
    /* the glass rule sets min-height:calc(100vh - 44px - 35px); if the measured chrome is TALLER
       than 79px that min-height would win and reintroduce the overshoot it is meant to remove. */
    min-height:0;
  }
  #view-novel > .page-header,
  #view-novel > .sess-tabs{ flex:0 0 auto; }

  /* Only the Prep & Preview tab is height-managed in this build. The other three panes keep their
     default flex sizing, so if one is taller than the view it overflows and the page scrolls -- the
     same behaviour they have today, deliberately unchanged until each is looked at on its own. */
  #view-novel > #novel-tab-preview{ flex:1 1 auto; min-height:0; display:flex; flex-direction:column; }
  #novel-tab-preview > .novel-preview-layout{ flex:1 1 auto; min-height:0; align-items:stretch; }

  /* Sticky was solving the problem that the panel could run off the page. At full row height there
     is nothing to stick to and nothing to run off. */
  .novel-preview-layout > .novel-prep-panel{ position:static; top:auto; max-height:none; min-height:0; }
  .novel-preview-layout > .novel-preview-col{ display:flex; flex-direction:column; min-height:0; }
  .novel-preview-col > #novel-preview-frame{ flex:1 1 auto; min-height:0; }
  #novel-preview-frame > #novel-preview-iframe,
  #novel-preview-frame > #novel-preview-loading{ height:100%; }

  /* ---------------------------------------------------------------------------------------------
     v3.0.601 -- THE OPTIMIZE TAB, same treatment. Ian: "I still get the far right scroll bar when I
     go to the Optimize tab." It carried FOUR more literals -- three panes at height:680px and a page
     rail at 660px -- which is 680 plus roughly 230px of headers and tabs, so it overflows anything
     under about a 910px viewport. Ian's is 798.

     Every one of those inline heights is gone; the panes are flex children that take what is left.
     #finalize-left loses its overflow:hidden and scrolls on its own, because once the row is bounded
     the controls column is the one thing here that can genuinely be taller than the window.

     The two page rails are stretched to the row height rather than given one, so they stay level with
     the panes they index no matter what height the row settles at -- the old 680/660 pairing meant
     the After rail was 20px shorter than its own pane for no reason anybody recorded.
     --------------------------------------------------------------------------------------------- */
  #view-novel > #novel-tab-finalize{ flex:1 1 auto; min-height:0; display:flex; flex-direction:column; }
  #novel-tab-finalize > #finalize-split{ flex:1 1 auto; min-height:0; }
  #finalize-left{ overflow-y:auto; min-height:0; }
  #finalize-right{ min-height:0; }
  #finalize-right > #finalize-pdf-panes{ flex:1 1 auto; min-height:0; }
  #finalize-before-wrap{ display:flex; flex-direction:column; min-height:0; }
  #finalize-after-wrap{ display:flex; flex-direction:column; min-height:0; }
  #finalize-before-scroll{ flex:1 1 auto; min-height:0; }
  #finalize-page-nav-wrap{ min-height:0; }
  #finalize-page-nav{ flex:1 1 auto; min-height:0; }
  #finalize-after-row{ flex:1 1 auto; min-height:0; align-items:stretch; }
  #finalize-after-scroll{ min-height:0; }
  #finalize-after-body{ min-height:0; }
  #finalize-after-nav-wrap{ min-height:0; }
  #finalize-after-nav{ flex:1 1 auto; min-height:0; }
}

/* v3.0.607 -- THE ROW MUST STRETCH ITS PANES, AND SAYING SO IN THE STYLESHEET WAS NOT ENOUGH.
   v3.0.601 set align-items:stretch on #finalize-after-row from here. The element carried
   align-items:flex-start as an INLINE style, and an inline declaration beats a stylesheet rule
   without !important -- so the rule was present, correct, and dead. The panes went on sizing to
   their content, which for the After pane is a stack of 49 rendered canvases, and the whole Optimize
   tab scrolled instead of the pane. Ian saw it as the page spine and the left column scrolling away.
   The inline declaration is gone and the alignment is set per breakpoint below, so there is now ONE
   place that decides it.
   THE GENERAL LESSON, and it is worth more than the fix: a CSS rule can be present and overridden,
   which reads exactly like a rule that is absent, except that grepping for it finds it. The apply
   script now sweeps every property these rules set against the inline style of the same element. */
@media (min-width:901px){
  #finalize-after-row{ align-items:stretch; }
}
@media (max-width:900px){
  #finalize-after-row{ align-items:flex-start; }
}

/* =================================================================================================
   v3.0.608 -- THE OPTIMIZE SCROLLBARS. Ian, 2026-08-10: "we don't need the horizontal scroll bar at
   the bottom of the page count panel, just make the panel wide enough. And can you make the vertical
   scroll bar on the page count panel prettier and thinner... a lot thinner."

   WHY THE HORIZONTAL BAR WAS THERE, and it was arithmetic rather than styling: a page number is
   font-size:10px with padding:0 5px, so three digits need about 28px. The spine wrapper was
   flex:0 0 36px and a default scrollbar takes about 15 of those. 28 + 15 does not fit in 36, so the
   numbers overflowed sideways and the browser added a horizontal bar. Widening alone would have
   worked; thinning alone would have worked; doing both leaves real slack instead of a near miss --
   which is what the 36 was.

   44px = 28 for three digits (the print page cap is 250, so three is the maximum) + 6 for the thin
   bar + 2 for the borders, leaving 8px of slack. overflow-x:hidden is a belt, not the mechanism:
   with the width right there is nothing to clip, and if a font ever renders wider a clipped digit is
   better than the bar Ian asked to remove.

   The thumb is the app gold at low alpha with a transparent track, so the bar reads as part of the
   panel rather than as chrome sitting on it. scrollbar-width / scrollbar-color are the standard
   properties (Firefox, and Chrome 121+); the ::-webkit- rules cover older Chrome. Both are declared
   because either alone leaves one of the two browsers on the default bar.
   ================================================================================================= */
#finalize-after-nav, #finalize-page-nav{
  overflow-x:hidden;
  scrollbar-width:thin;
  scrollbar-color:rgba(201,168,76,0.40) transparent;
}
#finalize-after-nav::-webkit-scrollbar, #finalize-page-nav::-webkit-scrollbar{ width:6px; }
#finalize-after-nav::-webkit-scrollbar-track, #finalize-page-nav::-webkit-scrollbar-track{ background:transparent; }
#finalize-after-nav::-webkit-scrollbar-thumb, #finalize-page-nav::-webkit-scrollbar-thumb{
  background:rgba(201,168,76,0.35); border-radius:3px;
}
#finalize-after-nav::-webkit-scrollbar-thumb:hover, #finalize-page-nav::-webkit-scrollbar-thumb:hover{
  background:rgba(201,168,76,0.65);
}

/* The two book panes get the same treatment a little wider -- these are long documents and the bar
   is a position indicator you actually read, where the spine bar is only a way to reach a number. */
#finalize-after-scroll, #finalize-before-scroll{
  scrollbar-width:thin;
  scrollbar-color:rgba(201,168,76,0.40) transparent;
}
#finalize-after-scroll::-webkit-scrollbar, #finalize-before-scroll::-webkit-scrollbar{ width:9px; }
#finalize-after-scroll::-webkit-scrollbar-track, #finalize-before-scroll::-webkit-scrollbar-track{
  background:rgba(201,168,76,0.06); border-radius:5px;
}
#finalize-after-scroll::-webkit-scrollbar-thumb, #finalize-before-scroll::-webkit-scrollbar-thumb{
  background:rgba(201,168,76,0.38); border-radius:5px; border:2px solid transparent; background-clip:padding-box;
}
#finalize-after-scroll::-webkit-scrollbar-thumb:hover, #finalize-before-scroll::-webkit-scrollbar-thumb:hover{
  background:rgba(201,168,76,0.7); background-clip:padding-box;
}

/* Narrow screens keep a definite height, because below 901px the view is not a bounded column and a
   flex:1 pane would collapse to nothing. These are the old numbers, kept only where they still
   describe something true. */
@media (max-width:900px){
  #finalize-before-scroll, #finalize-after-scroll, #finalize-after-body{ height:680px; }
  #finalize-page-nav{ height:680px; }
  #finalize-after-nav{ height:660px; }
  #finalize-left{ overflow:hidden; }
}

/* ===== Pre-Publish image picker (slots + modal) ===== */
.prep-img-slots { display:flex; gap:8px; margin:0 0 14px; }
.prep-img-slot { flex:1 1 0; min-width:0; text-align:center; }
/* v3.0.479a -- the three Prep & Preview slots match the BOOK, not a square. The trim is
   8.5in x 11in (services/printing/renderPdf.js), so 8.5/11 shows the cover at the shape it
   actually prints -- a square box cropped the preview and made a portrait cover look wrong
   before anyone opened it. Scoped to .prep-img-slots on purpose: .prep-img-thumb is shared
   with the campaign-settings thumb (app.html #cs-image-thumb, sized inside a 96px box) and
   with the picker grid, and neither of those is a book cover. */
.prep-img-slots .prep-img-thumb { aspect-ratio:8.5/11; }
/* v3.0.679 -- TD-480. The campaign-settings thumbnail cropped the same way, and at 96px a centre
   crop of a title card is unreadable -- Ian's screenshot shows it reading "OMAL". */
.prep-img-thumb { background-size:contain!important; background-repeat:no-repeat!important;
  background-position:center!important; }
.prep-img-thumb { width:100%; aspect-ratio:1/1; border:1px dashed rgba(201,168,76,0.4); border-radius:8px; background-size:cover; background-position:center; background-color:rgba(20,12,4,0.6); cursor:pointer; display:flex; align-items:center; justify-content:center; transition:border-color 0.15s; }
.prep-img-thumb:hover { border-color:var(--gold); }
.prep-img-thumb.has-img { border-style:solid; border-color:rgba(201,168,76,0.5); }
.prep-thumb-plus { color:rgba(201,168,76,0.5); font-size:22px; line-height:1; }
/* v3.0.698 -- TD-497. THE COVER AND BACK TILES SHOW THE CROP.
   Ian, 2026-08-18: "the cover tiles don't show what they are going to look like. When you select
   them you can see how it's going to crop them to fit in the Archive picker... but they don't show
   that way here."
   The rule above letterboxes EVERY thumbnail, which is right for the campaign-settings thumb and
   for the title-page tile (that one prints whole), and wrong for the two that print cropped. The
   anchor classes mirror pdf.js COVER_PLACE exactly and are chosen in app.js from the SAME helper
   the picker outline uses, so tile, picker and print cannot drift. Note the inversion is real:
   a title at the BOTTOM anchors the picture to the TOP. */
.prep-img-slots .prep-img-thumb.pt-crop { background-size:cover!important; background-repeat:no-repeat!important; }
.prep-img-slots .prep-img-thumb.pt-crop.pt-pos-top { background-position:center top!important; }
.prep-img-slots .prep-img-thumb.pt-crop.pt-pos-center { background-position:center center!important; }
.prep-img-slots .prep-img-thumb.pt-crop.pt-pos-bottom { background-position:center bottom!important; }
/* The Remove pill. Same family as the regen / retouch / archive pills on other images, smaller,
   and only ever rendered when the tile actually has a picture. */
.prep-thumb-x { position:absolute; top:3px; right:3px; z-index:2; width:18px; height:18px; padding:0;
  line-height:16px; text-align:center; font-size:12px; font-family:inherit; border-radius:50%;
  border:1px solid rgba(201,168,76,0.55); background:rgba(12,7,3,0.82); color:rgba(240,217,138,0.9);
  cursor:pointer; opacity:0.75; transition:opacity 0.15s, background 0.15s; }
.prep-thumb-x:hover { opacity:1; background:rgba(120,30,20,0.9); border-color:var(--gold); }
.prep-img-slots .prep-img-thumb { position:relative; }
.prep-img-cap { font-size:11px; color:rgba(201,168,76,0.8); margin-top:4px; }
.prep-img-modal { position:fixed; inset:0; z-index:100001; background:rgba(8,5,2,0.72); display:flex; align-items:center; justify-content:center; padding:20px; }
.prep-img-modal-box { background:#16100a; border:1px solid rgba(201,168,76,0.35); border-radius:12px; box-shadow:0 18px 50px rgba(0,0,0,0.5); width:100%; max-width:680px; max-height:85vh; display:flex; flex-direction:column; padding:18px; }
.prep-img-modal-head { display:flex; align-items:center; justify-content:space-between; margin-bottom:12px; }
.prep-img-modal-title { font-family:var(--font-display); font-size:16px; color:var(--gold); }
.prep-img-modal-x { background:none; border:none; color:rgba(201,168,76,0.8); font-size:24px; line-height:1; cursor:pointer; padding:0 4px; }
.prep-img-grid { display:grid; grid-template-columns:repeat(auto-fill, minmax(120px, 1fr)); gap:10px; overflow-y:auto; padding:2px; }
.prep-img-pick { aspect-ratio:1/1; border:2px solid rgba(201,168,76,0.2); border-radius:8px; background-size:cover; background-position:center; background-color:rgba(20,12,4,0.6); cursor:pointer; padding:0; transition:border-color 0.15s; }
.prep-img-pick:hover { border-color:var(--gold); }
/* v3.0.482 -- the cover PICKER matches the cover SLOTS (8.5in x 11in trim), so a reader sees
   the shape each image will actually be before choosing it -- a square tile cropped the top
   and bottom and hid exactly the part a cover uses. Scoped to .prep-img-grid-cover: the same
   grid and tile classes are reused by openCampaignImagePicker and _buildCastPicker, and a
   campaign image and a character portrait are not book covers.
   background-position moves to `top` with the taller box: a cover's title is at the top, and
   centring a landscape source in a portrait tile pushes it out of view. */
.prep-img-grid-cover .prep-img-pick { aspect-ratio:8.5/11; background-position:center top; }
.prep-img-grid-cover .prep-img-pick-named .prep-img-pick-img { aspect-ratio:8.5/11; }
.prep-img-pick.selected { border-color:var(--gold); box-shadow:0 0 0 2px rgba(201,168,76,0.4); }
.prep-img-empty { color:rgba(240,232,208,0.6); font-size:13px; padding:20px; text-align:center; }
.prep-img-modal-foot { margin-top:12px; font-size:11px; color:rgba(240,232,208,0.5); }

.prep-divider { height:1px; background:rgba(201,168,76,0.2); margin:4px 0 14px; }

.prep-color-input { width:36px; height:26px; padding:0; border:1px solid rgba(201,168,76,0.3); border-radius:4px; background:transparent; cursor:pointer; flex:0 0 auto; }

/* ===== Guided Tours ===== */
.tour-overlay{position:fixed;inset:0;z-index:9998;}
.tour-overlay.hidden{display:none;}
.tour-backdrop{position:fixed;inset:0;background:transparent;pointer-events:auto;}
.tour-overlay.no-target .tour-backdrop{background:rgba(0,0,0,0.6);}
.tour-highlight{position:fixed;border-radius:10px;border:2px solid var(--gold);box-shadow:0 0 0 9999px rgba(0,0,0,0.6);pointer-events:none;transition:top .18s ease,left .18s ease,width .18s ease,height .18s ease;}
.tour-overlay.no-target .tour-highlight{display:none;}
.tour-tip{position:fixed;z-index:10000;max-width:330px;background:var(--surface);color:var(--text);border:1px solid var(--border);border-radius:var(--radius-lg);box-shadow:var(--shadow-md);padding:16px 18px;pointer-events:auto;}
.tour-tip-title{font-family:var(--font-display);font-size:17px;font-weight:700;color:var(--ink);margin:0 0 6px;}
.tour-tip-text{font-size:14px;line-height:1.5;color:var(--text-muted);margin:0 0 14px;}
.tour-tip-foot{display:flex;align-items:center;justify-content:space-between;gap:12px;}
.tour-tip-count{font-size:12px;color:var(--text-light);}
.tour-tip-btns{display:flex;gap:8px;}
.tour-btn{font:inherit;font-size:13px;border-radius:var(--radius);padding:7px 14px;cursor:pointer;border:1px solid var(--border-dark);background:var(--surface);color:var(--text);}
.tour-btn-skip{background:transparent;border-color:transparent;color:var(--text-light);}
.tour-btn-next{background:var(--gold);border-color:var(--gold);color:#fff;font-weight:600;}
.tour-replay{padding:8px 14px;font-size:13px;font-weight:600;background:rgba(0,0,0,0.72);color:rgba(201,168,76,0.95);border:1px solid rgba(201,168,76,0.45);border-radius:8px;cursor:pointer;box-shadow:0 4px 14px rgba(0,0,0,0.4);transition:background 0.15s,color 0.15s,border-color 0.15s;white-space:nowrap;line-height:1.2;}
.tour-replay:hover{background:rgba(0,0,0,0.92);color:var(--gold);border-color:var(--gold);}
@media (max-width:640px){
  .tour-tip{left:12px!important;right:12px!important;max-width:none;bottom:84px!important;top:auto!important;}
}

/* ===== Asset Library cards (panel-sized image, name below, storyboard-style overlay buttons) ===== */
.asset-grid{grid-template-columns:repeat(auto-fill,minmax(240px,1fr));}
.asset-card{border:1px solid rgba(201,168,76,0.15);border-radius:var(--radius-lg);overflow:hidden;background:rgba(20,12,4,0.7);position:relative;}
.asset-card-img{position:relative;width:100%;aspect-ratio:1/1;background:rgba(20,12,4,0.5);}
.asset-card-photo{width:100%;height:100%;object-fit:cover;object-position:center top;display:block;cursor:zoom-in;}
.asset-photo-empty{display:flex;align-items:center;justify-content:center;font-size:48px;color:rgba(201,168,76,0.4);cursor:default;}
.asset-card-meta{padding:10px 12px;}
.asset-card-name{font-weight:600;color:var(--gold-light);font-size:14px;line-height:1.3;word-break:break-word;}
.asset-card-cls{font-size:12px;color:rgba(201,168,76,0.6);margin-top:2px;}
.asset-grid .add-char-card{min-height:240px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;cursor:pointer;border-radius:var(--radius-lg);}
/* Asset pills are always visible (storyboard pills default to opacity:0 and rely
   on a .storyboard-panel hover rule that does not apply here). Keep them overlaid
   even on mobile, where the shared rule would otherwise push them below the image. */
.asset-card .panel-pill{opacity:1;}
.asset-card .panel-img-actions{position:absolute;top:8px;left:8px;right:8px;}

/* ===== Cast picker (image+name tiles) and its '+ Add' buttons ===== */
.review-add-btn{font-size:11px;padding:2px 8px;border-radius:8px;cursor:pointer;background:rgba(0,0,0,0.4);color:rgba(201,168,76,0.85);border:1px dashed rgba(201,168,76,0.35);}
.review-add-btn:hover{border-color:var(--gold);color:var(--gold);}
.prep-img-pick-named{display:flex;flex-direction:column;gap:0;border:none;background:transparent;cursor:pointer;padding:0;}
.prep-img-pick-named .prep-img-pick-img{aspect-ratio:1/1;border:2px solid rgba(201,168,76,0.2);border-radius:8px;background-size:cover;background-position:center top;background-color:rgba(20,12,4,0.6);display:flex;align-items:center;justify-content:center;font-size:34px;color:rgba(201,168,76,0.4);transition:border-color 0.15s;}
.prep-img-pick-named:hover .prep-img-pick-img{border-color:var(--gold);}
.prep-img-pick-named .prep-img-cap{text-align:center;line-height:1.25;}

/* ===== Breadcrumb band holds the corner actions (Tour / Ask). Desktop: two pills.
   Mobile (<=480px): collapse into a kebab menu so they don't stretch the row. ===== */
.breadcrumb-row{display:flex;align-items:center;gap:8px;padding:4px 1.5rem;background:rgba(20,15,8,0.75);border-bottom:1px solid rgba(201,168,76,0.15);backdrop-filter:blur(8px);position:relative;z-index:60;}
.corner-kebab{display:none;align-items:center;justify-content:center;padding:6px 11px;font-size:18px;line-height:1;background:rgba(0,0,0,0.72);color:rgba(201,168,76,0.95);border:1px solid rgba(201,168,76,0.45);border-radius:8px;cursor:pointer;}

.corner-menu{display:none;position:absolute;top:100%;right:0;margin-top:6px;background:var(--ink);border:1px solid rgba(201,168,76,0.3);border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,0.5);z-index:96;min-width:130px;overflow:hidden;}
.corner-menu.open{display:block;}
.corner-menu-item{display:block;width:100%;text-align:left;padding:10px 14px;background:transparent;border:none;color:var(--gold-light);font-size:14px;cursor:pointer;white-space:nowrap;}
.corner-menu-item:hover{background:rgba(201,168,76,0.12);}
@media (max-width:480px){.corner-fabs .tour-replay,.corner-fabs .help-fab{display:none;}.corner-kebab{display:inline-flex;}}

/* TF-18: persistent show/hide eye on password inputs (see js/pw-eye.js) */
.pw-eye-wrap{position:relative;display:block;width:100%;}
.pw-eye-wrap > input{width:100%;padding-right:40px;}
.pw-eye-btn{position:absolute;top:0;bottom:0;right:6px;margin:auto 0;height:30px;width:30px;display:flex;align-items:center;justify-content:center;padding:0;background:none;border:none;border-radius:6px;cursor:pointer;color:rgba(201,168,76,0.7);}
.pw-eye-btn:hover{color:var(--gold);background:rgba(201,168,76,0.12);}
.pw-eye-btn svg{display:block;}
/* only one eye: suppress the native Edge/IE reveal control */
input[type="password"]::-ms-reveal,input[type="password"]::-ms-clear{display:none;}

/* Campaign card "Details" button - styled like the moment-panel pills, but
   always visible (pills default to opacity:0 for storyboard hover-reveal). */
.campaign-details-btn{padding:4px 11px;font-size:11px;background:rgba(0,0,0,0.6);color:rgba(201,168,76,0.85);border:1px solid rgba(201,168,76,0.3);border-radius:6px;cursor:pointer;line-height:1.3;white-space:nowrap;transition:background 0.15s,color 0.15s;}
.campaign-details-btn:hover{background:rgba(0,0,0,0.85);color:var(--gold);}
.camp-lore-count{font-size:11px;color:rgba(201,168,76,0.6);text-align:right;margin-top:4px;}
.camp-lore-edit-wrap{margin-top:12px;}
.camp-lore-edit-label{display:block;font-family:var(--font-display);font-size:12px;color:var(--gold);margin-bottom:4px;}

/* Campaign cards now match the session-card layout: image band on top, solid
   body below (no more faded background image behind the text). */
/* v3.0.679 -- TD-480. THE WHOLE PICTURE, NOT THE MIDDLE OF IT. Ian: "I thought we made it so the
   images always fit on the tile?" v3.0.670 gave the PICKER true aspect; the tile it feeds still
   cropped to 16:10, so a portrait title card lost its top and bottom -- the reader chose a
   picture they could see whole and got a slice of it. contain letterboxes a tall image against
   the tile background, which is the honest trade: empty space beside the picture, rather than
   picture missing off the edges. */
.campaign-card-img{width:100%;aspect-ratio:16/10;object-fit:contain;display:block;background:rgba(15,10,5,0.5);}
.campaign-card-img-empty{display:flex;align-items:center;justify-content:center;background:rgba(15,10,5,0.5);}
.campaign-card-img-empty img{width:56px;height:56px;object-fit:contain;opacity:0.7;}
.campaign-card-body{padding:12px 14px;display:flex;flex-direction:column;gap:6px;}
.campaign-card-footer{display:flex;align-items:center;justify-content:space-between;margin-top:6px;}

/* Prep & Preview toolbar: centre the page navigation over the preview panel instead of letting
   space-between shove it to the right edge (the 'Open in New Tab' button beside it is hidden, so
   the pager was landing hard right). Absolute centring only on wider screens; below that it wraps
   normally in the flex row so it can never overlap the zoom controls on mobile. */
.nprev-toolbar { position: relative; }
@media (min-width: 820px) {
  .nprev-toolbar > #novel-pager { position: absolute; left: 50%; transform: translateX(-50%); margin: 0; }
}

/* ==================== Order & Publish tab ====================
   Two bordered cards: the print order (two columns on desktop -- what-to-print + format on the
   left, ship-to on the right) and Publish to the Library beneath it. Mobile-first: .ord-col uses
   flex-basis with wrap, so the columns collapse to a single stack on narrow screens with no media
   query needed, and every field is width:100% inside its column. */
/* Three columns need real room. 1040px was sized for two and left a wide dead strip on the right;
   1600px lets all three breathe while still capping line length on an ultrawide monitor. */
.ord-wrap { max-width: 1600px; }
.ord-card { border: 1px solid rgba(201,168,76,0.35); border-radius: var(--radius); background: rgba(20,12,4,0.35); padding: 16px; margin-bottom: 16px; }
.ord-card-title { font-family: var(--font-display); color: var(--gold); font-size: 15px; text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 4px; }
.ord-card-sub { color: rgba(245,232,200,0.6); font-size: 12px; margin-bottom: 14px; line-height: 1.4; }
.ord-cols { display: flex; gap: 18px; flex-wrap: wrap; align-items: flex-start; }
.ord-col { flex: 1 1 320px; min-width: 0; }
.ord-sec { margin-bottom: 6px; color: var(--gold); font-size: 12px; font-family: var(--font-display); letter-spacing: 0.05em; }
.ord-sec-gap { margin-top: 16px; }
/* v3.0.785 -- TD-586. The parenthetical is an aside, so it is sized like one.
   It inherited the label's 12px, which made "Interior printing (premium colour recommended)"
   wrap onto a second line -- and a wrapped label pushes its <select> down while the field beside
   it stays put, so the two controls in the FORMAT row stopped lining up. Introduced by v3.0.784,
   which lengthened that label from "Color". */
.ord-lbl { display: block; font-size: 12px; color: rgba(245,232,200,0.75); margin-bottom: 3px; }
.ord-lbl-dim { color: rgba(245,232,200,0.4); font-size: 10px; }
/* v3.0.785 -- TD-586. AND THE CONTROLS ALIGN EVEN WHEN A LABEL DOES WRAP.
   Making the text smaller fixes the case in front of us and not the general one: any narrower
   viewport, any longer wording, any translation, and it wraps again. Bottom-aligning the fields
   means a label that grows grows UPWARD and the controls stay on one line regardless.
   Scoped to its own class rather than added to .ord-row, because the Paper row carries a hint
   BELOW its select -- bottom-aligning that one would drag its neighbours down to meet the hint,
   trading this bug for a worse one. */
.ord-row-align-controls { align-items: flex-end; }
.ord-in { width: 100%; background: rgba(20,12,4,0.8); color: var(--cream); border: 1px solid rgba(201,168,76,0.3); border-radius: var(--radius); padding: 8px 10px; font-size: 13px; margin-bottom: 10px; }
.ord-in-ro { opacity: 0.7; cursor: not-allowed; background: rgba(20,12,4,0.55); border-color: rgba(201,168,76,0.25); }
.ord-row { display: flex; gap: 10px; flex-wrap: wrap; }
.ord-row > * { min-width: 0; }
.ord-f1 { flex: 1 1 150px; }
.ord-f2 { flex: 1 1 110px; }
.ord-f3 { flex: 1 1 88px; }
.ord-hint { font-size: 10px; color: rgba(245,232,200,0.5); margin: -6px 0 10px; line-height: 1.3; }
.ord-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.ord-divider { border-top: 1px solid rgba(201,168,76,0.2); margin: 16px 0 14px; }
@media (max-width: 700px) {
  .ord-card { padding: 12px; }
  .ord-cols { gap: 0; }
}

/* The 'New page per session' toggle only means anything when Session dividers is on, so it must sit
   directly beneath it. .pcl-toggles is an auto-fit grid, which otherwise pushes the dependent toggle
   into the next CELL (the adjacent column), where its indent made it read as a child of the toggle
   above. Span the full row and stack the pair, matching the Layout modal. */
.pcl-toggle-pair { grid-column: 1 / -1; display: flex; flex-direction: column; gap: 6px; }
/* v3.0.612 -- Ian: put the character-page pair opposite the session-divider pair, since both
   carry a sub-checkbox and so stand the same height.
   THE WRAPPER SPANS THE ROW AND LAYS THE PAIRS OUT ITSELF. Putting them next to each other in
   .pcl-toggles would not have held: that grid is auto-fit, so the number of controls per line
   follows the panel width and the two pairs would sit opposite at some widths and split across
   rows at others. auto-fit here too, so they drop to one column when the panel is genuinely
   too narrow instead of squeezing the labels.
   AND THE SPAN ABOVE HAS TO BE UNDONE INSIDE IT: .pcl-toggle-pair carries grid-column:1/-1 for
   when it sits directly in .pcl-toggles, and left alone that makes each pair span the WRAPPER
   too -- which stacks them again and looks exactly like the rule never applied. */
/* The wrapper spans both columns and repeats the SAME two-column track and the SAME gap, so its
   two pairs line up exactly with the single controls above and below them. A different gap or a
   different track here would put the second pair a few pixels off the column it sits under,
   which is the kind of near-miss that reads as sloppy without being obviously wrong. */
.pcl-pair-row { grid-column: 1 / -1; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px 14px; align-items: start; }
.pcl-pair-row > .pcl-toggle-pair { grid-column: auto; }
/* Below 901px the Prep panel is already released to full width and stacked under the preview,
   where two columns of checkboxes squeeze the labels rather than tidy them. */
@media (max-width:900px){
  .pcl-toggles, .pcl-pair-row { grid-template-columns: 1fr; }
}

/* Order & Publish: the print order and the Library publish are SIBLING cards, each with its own
   border -- publishing is not part of the order, so it must not sit inside the order's frame.
   The order card takes the greater share; both collapse to a single stack on narrow screens. */
/* Three columns across the page: what-to-print/format, the address, then publishing. The first two
   live inside the order card (one border); publishing is its own card beside them. Basis values are
   what drive the collapse order on the way down: publishing drops under the order card first, then
   the address drops under what-to-print. */
.ord-col-main { flex: 5 1 640px; }
.ord-col-side { flex: 2 1 330px; }
.ord-cols-inner { gap: 16px; }
.ord-cols-inner > .ord-col { flex: 1 1 250px; }

/* ==================== Optimize tab on small screens ====================
   Two page-previews side by side is meaningless on a phone -- each pane ends up a couple of
   hundred pixels wide and you cannot judge a layout at that size. So below 900px the whole tab
   becomes a single column: the controls first, then Before, then After, each full width. The
   drag splitter is hidden (there is nothing to drag when nothing is side by side) and the
   vertical page rail turns into a horizontal strip between the two panes, so tapping a page
   still jumps both. Heights move from a fixed 680px to a share of the viewport so the panes fit
   the screen the reader actually has. !important is needed throughout: the sizes are inline
   styles on the elements, and inline beats a stylesheet rule. */
@media (max-width: 900px) {
  #finalize-split { flex-direction: column; }
  #finalize-left { flex: 1 1 auto !important; width: 100%; padding-right: 0 !important; margin-bottom: 14px; }
  #finalize-splitter { display: none !important; }
  #finalize-right { padding-left: 0 !important; min-width: 0 !important; }
  #finalize-pdf-panes { flex-direction: column; gap: 12px !important; }
  #finalize-before-wrap, #finalize-after-wrap { flex: 1 1 auto !important; width: 100%; }
  #finalize-before-scroll, #finalize-after-scroll, #finalize-after-body { height: 62vh !important; }
  /* page rail: vertical column -> horizontal scroll strip */
  #finalize-page-nav-wrap { flex: 1 1 auto !important; width: 100%; flex-direction: row !important; align-items: center; gap: 8px; }
  #finalize-page-nav { flex-direction: row !important; height: auto !important; max-height: none !important;
                       width: 100%; overflow-x: auto; overflow-y: hidden; padding: 4px 0; gap: 4px !important; }
  #finalize-page-nav > * { flex: 0 0 auto; }
}
@media (max-width: 560px) {
  #finalize-before-scroll, #finalize-after-scroll, #finalize-after-body { height: 55vh !important; }
}

/* v3.0.596 -- SESSION NAV ARROWS ON A PHONE. Ian: "make them smaller or gone on phones... they push
   the tab row down. They are fine on a tablet."
   GONE, not smaller. The tab row is a wrap-enabled flex row, so on a narrow screen the arrows do not
   shrink -- they take a whole line of their own and shove the tabs down, which is the complaint.
   Making them smaller would delay that, not prevent it. 640px is the breakpoint this stylesheet
   already uses for phone layouts, so it stays one number rather than a new one.
   The Sessions list is one tap away and still navigates, so nothing is unreachable without them. */
@media (max-width:640px){ #sess-nav-prev, #sess-nav-next { display:none; } }

/* v3.0.597 -- BACK TO TOP. Ian, 2026-08-10: "small, semi transparent and always float at the
   bottom... almost like a chat button."
   ALWAYS ON, which is why `display` is flex here and JS never touches it. v3.0.596 shipped it
   `display:none` and let a scroll handler reveal it past 400px; that handler was watching
   `.main-content.scrollTop`, which is structurally always 0 because the box has no bounded height,
   so the button never appeared on any screen. The threshold is gone rather than repaired -- an
   always-present control needs no state, and state is what was wrong.
   BOTTOM RIGHT, which is where a reader's hand already is on a wide screen and where this control
   is conventional. Deliberately NOT bottom-centre: centre sits over the middle of the content, and
   this app puts its primary buttons there (Generate Images, Continue to secure payment).
   The help FAB lives in the HEADER, not down here, so there is nothing to collide with.
   z-index sits below the impersonation banner (99999) and below modals (100000+): a support banner
   or an open modal must never be covered by a scroll affordance. */
.to-top-btn{
  position:fixed; right:22px; bottom:22px; z-index:90;
  width:34px; height:34px; border-radius:50%;
  background:rgba(20,15,8,0.55); color:rgba(201,168,76,0.9);
  border:1px solid rgba(201,168,76,0.35);
  font-size:15px; line-height:1; cursor:pointer;
  display:flex; align-items:center; justify-content:center;
  box-shadow:0 4px 14px rgba(0,0,0,0.35);
  backdrop-filter:blur(3px);
  transition:opacity 0.15s, background 0.15s, border-color 0.15s;
  opacity:0.4;
}
.to-top-btn:hover{ opacity:1; background:rgba(20,15,8,0.85); border-color:var(--gold); }
/* Hidden on phones entirely, per Ian. A phone has its own way back to the top and the button would
   sit over content that is already tight. */
@media (max-width:640px){ .to-top-btn{ display:none !important; } }

/* v3.0.617 -- TITLE BUILDER (TD-357), stage one. Nothing here is novel: the readonly rows borrow the
   prep-input surface so the title reads as a value rather than an empty field, and the result box is
   a checkerboard so a TRANSPARENT result is visibly transparent. That last one is the whole point of
   the panel -- on a plain dark background a white rectangle and a clean cut look identical, which is
   precisely the failure the alpha cut exists to prevent. */
.prep-title-style-row { display:flex; align-items:center; justify-content:space-between; gap:10px; margin:0 0 6px; }
/* v3.0.632 -- the swatch sits on the row with the SELECT now, not the row with the label, so the
   auto-margin rule that held it beside the label came out with it. The select carries the bottom
   margin for the pair; leaving it on both would double the gap under this control. */
.prep-style-color-row { display:flex; align-items:center; gap:8px; margin:0 0 14px; }
.prep-style-color-row .prep-input { flex:1 1 auto; min-width:0; margin-bottom:0; }
.prep-style-color-row .prep-color-input { flex:0 0 auto; }
.tb-readonly { background:rgba(20,12,4,0.85); border:1px solid rgba(201,168,76,0.3); border-radius:8px;
  padding:8px 10px; font-size:14px; color:var(--gold); min-height:38px; display:flex; align-items:center; }
.tb-ref-row { display:flex; gap:8px; align-items:center; }
.tb-ref-row .form-input { flex:1 1 auto; min-width:0; }
.tb-ref-thumb img { max-height:70px; border-radius:6px; border:1px solid rgba(201,168,76,0.3); margin-top:8px; }
/* v3.0.622 -- Title Builder reference drop zone. Deliberately the same shape, border and drag-over
   treatment as .asset-drop, because it is the same gesture doing the same job. */
.tb-drop { border:2px dashed rgba(201,168,76,0.3); border-radius:var(--radius); padding:12px; cursor:pointer;
  min-height:96px; display:flex; align-items:center; justify-content:center; transition:border-color 0.15s, background 0.15s; }
.tb-drop:hover { border-color:rgba(201,168,76,0.55); }
.tb-drop.drag-over { border-color:var(--gold); background:rgba(201,168,76,0.1); }
/* v3.0.629 -- the drop zone now carries pills, so it needs to be a positioned ancestor and to
   reveal them the way every other pill host does. Fifth hover rule; the pattern is per-wrapper. */
.tb-drop { position:relative; }
.tb-drop:hover .panel-pill { opacity:1; }
.tb-drop .panel-img-actions { justify-content:flex-end; }   /* v3.0.633 -- position from the shared rule */
/* Generate above the result. The row is right-aligned so the button lands where the eye already
   goes after typing the description. */
.tb-gen-row { display:flex; justify-content:flex-end; margin:10px 0 0; }
/* The empty-state Replace is always visible -- there is no artwork for it to obscure, and a
   hover-only control on an empty box is a control nobody finds. */
.panel-pill.is-static-shown { opacity:1; }
.tb-drop-empty { display:flex; flex-direction:column; align-items:center; gap:6px; color:var(--text-muted); font-size:12px; text-align:center; }
.tb-drop-preview { max-width:100%; max-height:120px; border-radius:6px; display:block; }
.tb-ref-row { margin-top:8px; }
/* THE FOURTH HOVER RULE. .panel-pill is opacity:0 and each wrapper reveals its own; the three others
   are .storyboard-panel, .char-ref-imgwrap and .sc-review-imgwrap. Without this line every pill on a
   built title is present, wired, and permanently invisible. */
/* v3.0.633 -- one rule for this class. overflow:hidden came off with the reason for it: the busy
   overlay moved to .tb-result in v3.0.629, and clipping the wrapper was what made a pill sitting
   at top:0 look shorn. */
.tb-imgwrap { position:relative; display:inline-block; max-width:100%; border-radius:6px; }
.tb-imgwrap:hover .panel-pill { opacity:1; }
.tb-imgwrap img { max-width:100%; height:auto; display:block; }
/* v3.0.624 -- ITEM 4. Ian: "Push the buttons closer to the top of the generated image Just like the
   Storyboard Panels." .panel-img-actions is top:8px against its wrapper, but .tb-result is a CENTRED
   flex box with 10px of padding and a 120px min-height, so on a short wide title the pills sit well
   inside the picture rather than on its edge. Tightened to 3px here only -- the storyboard keeps its
   own 8px, because a full panel is tall enough that 8px still reads as the top corner. */
/* v3.0.633 -- THE OVERRIDES COME OFF. Ian: "I want the buttons on the images on the title builder
   to look like these buttons... the button tops are cut off." Both halves of that were mine:
   v3.0.628 squared the top corners and pushed the row to top:0, which reads as sheared, and shrank
   the pills to 9px so they did not match the storyboard ones beside them.

   There is now NO title-builder pill styling at all -- .panel-pill and .panel-img-actions apply
   unmodified, exactly as they do on a storyboard panel. Chasing "closer to the top edge" with a
   local override was the wrong move; the shared rule was already right. */
/* Warnings, not blocks: gold rather than red, because nothing here is broken and nothing is refused.
   v3.0.625 -- .prep-title-note dropped with the element it styled; leaving the rule behind is the
   kind of text that outlives what it describes. */
.tb-warn { border:1px solid rgba(201,168,76,0.45); background:rgba(201,168,76,0.08);
  color:var(--gold-light); border-radius:8px; padding:8px 10px; font-size:12px; line-height:1.4; margin-top:8px; }
.tb-result { margin-top:6px; border:1px solid rgba(201,168,76,0.25); border-radius:8px; min-height:120px;
  display:flex; align-items:center; justify-content:center; padding:10px;
  background-color:#2a2018;
  background-image:linear-gradient(45deg, rgba(255,255,255,0.05) 25%, transparent 25%, transparent 75%, rgba(255,255,255,0.05) 75%),
                   linear-gradient(45deg, rgba(255,255,255,0.05) 25%, transparent 25%, transparent 75%, rgba(255,255,255,0.05) 75%);
  background-size:16px 16px; background-position:0 0, 8px 8px; }
.tb-result img { max-width:100%; height:auto; display:block; }
.tb-empty { color:var(--text-muted); font-size:13px; text-align:center;
  display:flex; flex-direction:column; align-items:center; gap:10px; }   /* v3.0.629 -- holds the Replace pill */

/* v3.0.816 -- TD-645. Regenerate in Art Style: the bar above the character
   list, the per-card strip, and the staleness warning. Additive only -- no
   existing rule is touched, so the tab is unchanged until a reference is
   actually restyled. */
.sc-style-bar { display:flex; flex-wrap:wrap; align-items:center; gap:10px; margin-bottom:14px; }
.sc-style-warning { border:1px solid rgba(201,168,76,0.45); background:rgba(201,168,76,0.08);
  color:#c9a84c; border-radius:6px; padding:8px 10px; font-size:12px; margin-bottom:14px; line-height:1.45; }
.sc-style-strip:empty { display:none; }
.sc-style-row { display:flex; flex-wrap:wrap; align-items:center; gap:6px; margin-top:8px; }
.sc-style-chip { font-size:11px; padding:2px 8px; border-radius:10px; border:1px solid rgba(255,255,255,0.18);
  color:rgba(255,255,255,0.72); white-space:normal; }
.sc-style-chip-draft { border-color:rgba(201,168,76,0.6); color:#c9a84c; }
.sc-style-chip-stale { border-color:rgba(224,122,95,0.65); color:#e07a5f; }
.sc-style-retouch { display:flex; flex-wrap:wrap; gap:6px; margin-top:6px; }
.sc-style-retouch .form-input { flex:1 1 200px; font-size:12px; padding:5px 8px; }
