/* ---------------------------------------------------------------------------
 * Tiptap widget chrome — toolbar, format bar, editor surface.
 *
 * Served as a static stylesheet instead of being inlined into every fragment as
 * a <style> block. Two reasons:
 *   1. Only the editor path needs it. Anonymous readers were receiving ~1 KB of
 *      editor-only CSS (987 of their 1151 bytes) for controls they never see.
 *   2. It is infrastructure, not user-serviceable styling — keeping it out of the
 *      Java string constant means it can be edited without recompiling.
 *
 * User-provided CSS (Widget.css / WidgetType.css) is still injected separately as
 * an inline <style> block, so the cascade order is unchanged.
 * ------------------------------------------------------------------------- */

.tiptap-widget {
  position: relative;
}

.tiptap-widget.edit-mode {
  outline: 1px dotted #ff8c00;
}

/* ---- top bar: Edit toggle on the left, Save on the right ---------------- */

.tiptap-toolbar {
  display: flex;
  align-items: center;
  padding: 0.5rem 0;
  gap: 0.5rem;
}

.tiptap-toolbar-right {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.editor-toggle {
  padding: 0.4rem 1rem;
  background: #4a90d9;
  color: #fff;
  border: none;
  border-radius: 4px;
  font-size: 0.85rem;
  cursor: pointer;
}

.editor-toggle:hover {
  background: #357abd;
}

.save-btn {
  padding: 0.5rem 1.5rem;
  background: #4a90d9;
  color: #fff;
  border: none;
  border-radius: 4px;
  font-size: 0.9rem;
  cursor: pointer;
}

.save-btn:hover {
  background: #357abd;
}

.save-btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.save-feedback {
  font-size: 0.85rem;
  color: #666;
}

/* ---- reader prose + editor surface ------------------------------------- */

.tiptap-widget-prose {
  min-height: 1em;
}

.tiptap-widget-editor {
  display: none;
}

.tiptap-editor {
  min-height: 200px;
  padding: 0.75rem 1rem;
  border: 1px solid #ddd;
  border-radius: 0 0 4px 4px;
  background: #fff;
}

.tiptap-editor p {
  margin: 0 0 0.5em;
}

.tiptap-editor h1,
.tiptap-editor h2,
.tiptap-editor h3 {
  margin: 0.5em 0;
}

/* ---- format bar (edit mode only) --------------------------------------- */

.tiptap-formatbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.15rem;
  padding: 0.4rem 0.5rem;
  /* Editor chrome has to read as chrome. The page sits on #f0f2f5 and this bar
     used to be #f5f6f8 — 5 units apart, so it disappeared into the background.
     White plus a real border plus a soft shadow separates it on any background. */
  background: #fff;
  border: 1px solid #d5d8dd;
  border-bottom: none;
  border-radius: 4px 4px 0 0;
  box-shadow: 0 1px 3px rgba(15, 23, 42, 0.08);
  font-size: 0.85rem;
  line-height: 1;
}

.tiptap-formatbar select.fmt-select {
  height: 28px;
  padding: 0 0.35rem;
  border: 1px solid #ccc;
  border-radius: 3px;
  background: #fff;
  font-size: 0.8rem;
  color: #333;
  cursor: pointer;
}

.tiptap-formatbar select.fmt-select:focus {
  outline: 2px solid #4a90d9;
  outline-offset: -1px;
}

.fmt-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 28px;
  height: 28px;
  padding: 0 0.3rem;
  border: 1px solid transparent;
  border-radius: 3px;
  background: transparent;
  color: #333;
  font-size: 0.85rem;
  cursor: pointer;
}

.fmt-btn:hover {
  background: #e4e7ec;
}

.fmt-btn:focus-visible {
  outline: 2px solid #4a90d9;
  outline-offset: -1px;
}

.fmt-btn[data-active="true"] {
  background: #d6e4f5;
  border-color: #a9c6e8;
  color: #17456f;
}

.fmt-bold    { font-weight: 700; }
.fmt-italic  { font-style: italic; font-family: Georgia, serif; }
.fmt-underline { text-decoration: underline; }
.fmt-strike  { text-decoration: line-through; }

.fmt-sep {
  width: 1px;
  height: 18px;
  margin: 0 0.25rem;
  background: #ccc;
}

/* Alignment buttons draw three bars whose offsets show the alignment, so no
   glyph is needed and the active/hover colour follows currentColor. */
.fmt-align::before {
  content: "";
  display: block;
  width: 16px;
  height: 10px;
  background:
    linear-gradient(currentColor, currentColor) 0 0 / 16px 2px no-repeat,
    linear-gradient(currentColor, currentColor) 0 4px / 10px 2px no-repeat,
    linear-gradient(currentColor, currentColor) 0 8px / 16px 2px no-repeat;
}

.fmt-align[data-cmd="alignCenter"]::before {
  background:
    linear-gradient(currentColor, currentColor) 0 0 / 16px 2px no-repeat,
    linear-gradient(currentColor, currentColor) 3px 4px / 10px 2px no-repeat,
    linear-gradient(currentColor, currentColor) 0 8px / 16px 2px no-repeat;
}

.fmt-align[data-cmd="alignRight"]::before {
  background:
    linear-gradient(currentColor, currentColor) 0 0 / 16px 2px no-repeat,
    linear-gradient(currentColor, currentColor) 6px 4px / 10px 2px no-repeat,
    linear-gradient(currentColor, currentColor) 0 8px / 16px 2px no-repeat;
}

.fmt-align[data-cmd="alignJustify"]::before {
  background:
    linear-gradient(currentColor, currentColor) 0 0 / 16px 2px no-repeat,
    linear-gradient(currentColor, currentColor) 0 4px / 16px 2px no-repeat,
    linear-gradient(currentColor, currentColor) 0 8px / 16px 2px no-repeat;
}

/* ProseMirror needs a visible caret area even when the document is empty. */
.tiptap-editor .ProseMirror {
  min-height: 180px;
  outline: none;
}

.tiptap-editor .ProseMirror-focused {
  outline: none;
}

/* ---- colour popover ---------------------------------------------------- */
/* The colour control is a button plus a custom popover, not a <select>: a native
   select renders its DROPDOWN LIST with OS chrome, which no CSS can reach, so it
   always looks like an OS widget. The closed box was stylable; the open list was
   not. This is why colour is the one control that is not a select. */

.fmt-color {
  position: relative;
  display: inline-flex;
}

.fmt-color-btn {
  flex-direction: column;
  gap: 0;
  padding: 0 0.3rem 2px;
}

.fmt-color-glyph {
  font-weight: 700;
  font-size: 0.85rem;
  line-height: 1.05;
}

/* The bar under the A is the current colour. */
.fmt-color-current {
  display: block;
  width: 15px;
  height: 3px;
  border-radius: 1px;
  background: #111827;
}

.fmt-color-pop {
  position: absolute;
  top: calc(100% + 5px);
  left: 0;
  z-index: 30;
  display: grid;
  grid-template-columns: repeat(6, 20px);
  gap: 5px;
  padding: 8px;
  background: #fff;
  border: 1px solid #d5d8dd;
  border-radius: 7px;
  box-shadow: 0 8px 22px rgba(15, 23, 42, 0.18);
}

/* `hidden` has to beat `display: grid`. */
.fmt-color-pop[hidden] {
  display: none;
}

.fmt-swatch {
  width: 20px;
  height: 20px;
  padding: 0;
  border: 1px solid rgba(15, 23, 42, 0.14);
  border-radius: 5px;
  cursor: pointer;
  transition: transform 0.08s ease;
}

.fmt-swatch:hover {
  transform: scale(1.14);
}

.fmt-swatch[data-active="true"] {
  box-shadow: 0 0 0 2px #fff, 0 0 0 3.5px #4a90d9;
}

/* "Default" unsets the colour: a white chip with a red slash. */
.fmt-swatch-reset {
  position: relative;
  background: #fff;
}

.fmt-swatch-reset::after {
  content: "";
  position: absolute;
  left: -1px;
  right: -1px;
  top: 50%;
  height: 1.5px;
  background: #dc2626;
  transform: rotate(-45deg);
}

/* ---- colour legibility fixes ------------------------------------------- */

/* #1 Bold must not force a colour.
   prose.css puts a direct `color` on <strong>, which beats the colour inherited
   from the wrapping textStyle span — so bold text could never be coloured. Bold
   carries weight, not colour: it should inherit whatever colour the run has. This
   is what prose.css already does for strong inside h1-h4.
   Scoped to the widget so the shared prose.css is untouched. */
.tiptap-widget .prose strong,
.tiptap-widget .prose b {
  color: inherit;
}

/* #2 Selection tint.
   With no ::selection rule the browser paints its default solid blue with white
   text, so the colour you just applied is invisible until you deselect. A
   translucent tint keeps the text's own colour readable while selected.
   `color: inherit` holds the run's colour rather than forcing a selection colour.
   The `*::selection` arm matters: the coloured node is a descendant <span>, and
   the selection pseudo-element has to match it, not just the editor root. */
.tiptap-editor .ProseMirror::selection,
.tiptap-editor .ProseMirror *::selection {
  background: rgba(59, 130, 246, 0.22);
  color: inherit;
}

.tiptap-editor .ProseMirror::-moz-selection,
.tiptap-editor .ProseMirror *::-moz-selection {
  background: rgba(59, 130, 246, 0.22);
  color: inherit;
}

/* ---- tables ------------------------------------------------------------- */

/* The table GROUP is always in the markup; CSS decides whether it is shown.
   Context-sensitivity therefore costs no branch in buildFormatBar(). */
.tiptap-formatbar .fmt-table-group {
  display: none;
  align-items: center;
  gap: 0.15rem;
}

.tiptap-formatbar[data-in-table="true"] .fmt-table-group {
  display: inline-flex;
}

/* Editor-side table styling. The reader already styles tables via prose.css;
   the editor surface needs its own so a pasted table is readable while editing. */
.tiptap-editor .ProseMirror table {
  border-collapse: collapse;
  table-layout: fixed;
  width: 100%;
  margin: 0.5em 0;
  overflow: hidden;
}

.tiptap-editor .ProseMirror td,
.tiptap-editor .ProseMirror th {
  border: 1px solid #cbd2da;
  padding: 0.4em 0.6em;
  vertical-align: top;
  position: relative;
  min-width: 1em;
}

.tiptap-editor .ProseMirror th {
  background: #f2f4f7;
  font-weight: 600;
  text-align: left;
}

/* The cell holding the cursor is highlighted, which is how the user knows the
   table controls now apply to it. */
.tiptap-editor .ProseMirror .selectedCell::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(74, 144, 217, 0.16);
  pointer-events: none;
}

/* ProseMirror puts a gap cursor / column-resize handle in the table. */
.tiptap-editor .ProseMirror .column-resize-handle {
  position: absolute;
  right: -2px;
  top: 0;
  bottom: 0;
  width: 4px;
  background: #4a90d9;
  pointer-events: none;
}

/* ---- cell fill + border popovers --------------------------------------- */
/* Mirrors the text-colour popover: a button that opens a swatch grid. The
   table-only controls share the group's visibility rule. */

.fmt-fill, .fmt-border {
  position: relative;
  display: inline-flex;
}

.fmt-fill-btn, .fmt-border-btn {
  flex-direction: column;
  gap: 0;
  padding: 0 0.3rem 2px;
}

/* A little paint bucket / frame, drawn with borders so no glyph is needed. */
.fmt-fill-glyph {
  display: block;
  width: 14px;
  height: 11px;
  border: 1.5px solid currentColor;
  border-radius: 2px;
  background: #dbeafe;
}

.fmt-border-glyph {
  display: block;
  width: 14px;
  height: 11px;
  border: 2px solid currentColor;
  border-radius: 2px;
}

.fmt-fill-pop, .fmt-border-pop {
  position: absolute;
  top: calc(100% + 5px);
  right: 0;
  z-index: 30;
  padding: 8px;
  background: #fff;
  border: 1px solid #d5d8dd;
  border-radius: 7px;
  box-shadow: 0 8px 22px rgba(15, 23, 42, 0.18);
}

.fmt-fill-pop {
  display: grid;
  grid-template-columns: repeat(6, 20px);
  gap: 5px;
}

.fmt-fill-pop[hidden], .fmt-border-pop[hidden] {
  display: none;
}

.fmt-fill-swatch {
  width: 20px;
  height: 20px;
  padding: 0;
  border: 1px solid rgba(15, 23, 42, 0.2);
  border-radius: 5px;
  cursor: pointer;
  transition: transform 0.08s ease;
}

.fmt-fill-swatch:hover { transform: scale(1.14); }

.fmt-fill-swatch[data-active="true"],
.fmt-border-width[data-active="true"],
.fmt-border-colour[data-active="true"] {
  box-shadow: 0 0 0 2px #fff, 0 0 0 3.5px #4a90d9;
}

/* Border sizes: the swatch itself grows with the weight, so the choice is
   legible without reading the tooltip. */
.fmt-border-pop {
  display: flex;
  flex-direction: column;
  gap: 7px;
}

.fmt-border-row {
  display: flex;
  align-items: center;
  gap: 5px;
}

.fmt-border-width {
  width: 20px;
  min-height: 3px;
  padding: 0;
  border: none;
  border-radius: 2px;
  background: #374151;
  cursor: pointer;
}

.fmt-border-width:hover { background: #4a90d9; }

.fmt-border-colour {
  width: 20px;
  height: 20px;
  padding: 0;
  border: 1px solid rgba(15, 23, 42, 0.2);
  border-radius: 5px;
  cursor: pointer;
}

/* The separator that belongs to the table group must hide with it, or an empty
   divider dangles in the bar when the cursor is not in a table. */
.tiptap-formatbar .fmt-table-sep { display: none; }
.tiptap-formatbar[data-in-table="true"] .fmt-table-sep { display: inline-block; }

/* Give a filled cell a readable default text colour only when it is light.
   (Deliberately NOT forcing a colour: the user may want white text on a dark
   fill, so that stays their choice via the text-colour control.) */
.tiptap-editor .ProseMirror td[style*="background-color"],
.tiptap-editor .ProseMirror th[style*="background-color"] {
  position: relative;
}

/* ---- #273: the tiptap formatting controls sit above the document ---------- */
/* editor.js moves the active widget's `.tiptap-formatbar` into this container,
   which it inserts ABOVE `body > header`. The result reads as: commands, header,
   article, footer — the author's "Word ribbon" model, where the whole page is the
   document and the controls sit above it. The bar is MOVED, never cloned, so the
   widget's own listeners and its `editorInstance` closure keep working.

   On a page whose root widget is the layout composer the container is the header
   wrapper's FIRST CHILD, so it renders above the header's blue bar and is pinned
   by the header's own single sticky behaviour. There it deliberately does NOT set
   `position: sticky`: two sticky elements sharing `top: 0` pin to the same offset
   and overlap, which is exactly what happened when this was a sibling placed
   before the header.

   A `layout` root is NOT a precondition, and this comment used to claim the
   container was always pinned. It is not: several live pages have no
   `body > header` at all (`creative-layout-demo-page`, `demo-weather`, both of
   which can host tiptap children), and editor.js puts the container as the body's
   first child there. That path has no header sticky behaviour to ride on, so it
   pins itself — see `body > .tiptap-shell-formatbar` below. */
.tiptap-shell-formatbar {
  box-sizing: border-box;
  width: 100%;
}
.tiptap-shell-formatbar[hidden] {
  display: none;
}
/* The fallback path. When there is no `body > header`, editor.js makes the
   container the body's first child instead, and nothing else pins it — without
   this rule the controls would sit at the top of the document and scroll away,
   which is the opposite of the requirement. The selector is deliberately a child
   of body: on the normal path the container is `body > header > .tiptap-shell-
   formatbar`, which this cannot match, so the two paths never fight. */
body > .tiptap-shell-formatbar {
  position: sticky;
  top: 0;
  z-index: 20;
}
/* Empty means no editor is active. An empty strip above the blue bar would be
   dead space in the pinned chrome for every reader. */
.tiptap-shell-formatbar:empty {
  display: none;
}

/* Inside the header the bar is page chrome, not a widget's surface: full width
   and square, with the widget-side top rounding dropped. */
.tiptap-shell-formatbar > .tiptap-formatbar {
  border-radius: 0;
  /* The side edges run to the viewport edge, so they are dropped; the top and
     bottom edges are what make the bar readable against the header, and dropping
     the top edge (as an earlier version did) left the bar with NO border at all. */
  border-left: none;
  border-right: none;
  border-top: 1px solid #d5d8dd;
  border-bottom: 1px solid #d5d8dd;
  padding: 0.4rem 0.9rem;
  background: #fff;
  box-shadow: 0 1px 3px rgba(15, 23, 42, 0.1);
}

/* A widget that is editing but does NOT hold the header keeps its own bar in
   place — its commands act on it and nothing else reaches them. A widget that is
   not editing never shows a bar. */
.tiptap-widget-editor > .tiptap-formatbar {
  display: none;
}
.tiptap-widget.edit-mode .tiptap-widget-editor > .tiptap-formatbar {
  display: flex;
}

/* ---- #273 option (b): one scrolling row on narrow viewports --------------- */
/* At 375px the bar wrapped to THREE rows, making the pinned chrome 208px — 31.2%
   of the viewport. A single row that scrolls keeps the pinned chrome at the same
   41px desktop gets, with every control one swipe away.

   `flex-shrink: 0` on the children is NOT what makes the row overflow — it already
   does, by 544px, because the controls total ~890px. Its job is to stop individual
   controls being squashed and clipped by the flex algorithm: measured without it,
   the table control compresses 41px -> 28px and one button is clipped to 26px
   while its content wants 28px. With it, every control keeps its natural width. */
@media (max-width: 640px) {
  .tiptap-formatbar {
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
  }
  .tiptap-formatbar > * {
    flex-shrink: 0;
  }
  /* A scrolling row clips its overflow, which would cut off the popovers — they
     are absolutely-positioned children of the bar. editor.js sets data-pop-open
     while one is open, and the row stops clipping for that moment. */
  .tiptap-formatbar[data-pop-open] {
    overflow: visible;
  }
}

/* ---- #273: the edit-mode metadata strip ---------------------------------- */
/* A child of <body>, after <footer>, so it is the last thing in the document.
   Sticky-bottom rather than fixed: `position: fixed` takes no space in flow, so
   the strip would cover the last lines of the article the moment it appeared and
   there would be no way to scroll clear of it. Sticky keeps its flow space, so it
   can be scrolled to and never hides content underneath.

   It exists only while a tiptap is in edit mode, so a reader never sees it. This
   is NOT the tenant's footer — that stays authored content and stays non-sticky. */
.tiptap-edit-metadata {
  position: sticky;
  bottom: 0;
  z-index: 20;
  box-sizing: border-box;
  width: 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem 1.25rem;
  padding: 0.5rem 0.9rem;
  /* Same surface as the controls bar: they are the same kind of thing. */
  background: #fff;
  border-top: 1px solid #d5d8dd;
  box-shadow: 0 -1px 3px rgba(15, 23, 42, 0.1);
  font-size: 0.8rem;
  line-height: 1.3;
  color: #333;
}
.tiptap-edit-metadata[hidden] {
  display: none;
}
.tiptap-edit-metadata-item {
  display: inline-flex;
  align-items: baseline;
  gap: 0.35rem;
  white-space: nowrap;
}
.tiptap-edit-metadata-label {
  color: #6b7280;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  font-size: 0.68rem;
}
.tiptap-edit-metadata-value {
  color: #111827;
  font-weight: 600;
}
.tiptap-edit-metadata-slug .tiptap-edit-metadata-value {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-weight: 500;
}
