/*
 * Layout-editor canvas — WYSIWYG affordances on top of otherwise
 * preview-shaped block markup. The block BODY is pure Tabler
 * (`.card`, `.card-title`, `.layout-property`, etc.); only the
 * hover/selection/drag scaffolding lives here.
 */

/* Mobile viewport toggle: clamps the canvas card to phone width
   (~390px) so authors can see how a narrow layout reads without
   leaving the editor. Pure CSS — flip class via Stimulus
   `viewportValueChanged`. Desktop leaves the card full-width. */
.layout-editor__editpane--mobile > .card {
  max-width: 390px;
  margin-left: auto;
  margin-right: auto;
  transition: max-width 180ms ease;
}

.layout-editor__editpane > .card {
  transition: max-width 180ms ease;
}

/* Hide the canvas scrollbar — keep wheel/touchpad scrolling functional
   but visually clean. The scroller is the editPane / variablesPane
   div in form_layouts/show.html.erb (Tabler `overflow-auto`). */
[data-form-layout-editor-target="editPane"],
[data-form-layout-editor-target="variablesPane"] {
  scrollbar-width: none;
}
[data-form-layout-editor-target="editPane"]::-webkit-scrollbar,
[data-form-layout-editor-target="variablesPane"]::-webkit-scrollbar {
  display: none;
}

.layout-editor__block {
  position: relative;
  /* The chrome row floats to the LEFT of the block, outside the
     white card. The centre column's left padding (below) reserves
     room for it. */
  padding: 0.5rem 1.5rem;
  border-radius: var(--tblr-border-radius);
  outline: 1px dashed transparent;
  outline-offset: 2px;
  transition: outline-color 120ms ease;
}

/* Form inputs in the editor canvas are previews — not real
   submission targets. Disabling pointer events here stops the
   browser from focusing them, which means flatpickr (date),
   tom-select (chip combobox), and Bootstrap dropdowns all stay
   inert during authoring. Clicks on the input bubble up to the
   block wrapper, which still fires `selectBlock` — so the author
   can still click anywhere on the field to select the block.
   Contenteditable surfaces (section title, header, text markdown,
   tab title) aren't <input>/<select>/<textarea> elements, so they
   keep their authoring affordance. */
.layout-editor__block input,
.layout-editor__block select,
.layout-editor__block textarea {
  pointer-events: none;
}

/* On hover, subtly bold the EXISTING content rectangle of the
   block — no new outline around the row. Innermost-only via
   `:not(:has(...))` so a child question doesn't also bolden its
   parent section.
     - Question blocks: bolden the form-control border. Scoped to
       `[data-block-type="question"]` so a section hover doesn't
       cascade through to every child question's input.
     - Card-shaped blocks (section / tabs / image): bolden the
       card's EXISTING border via the `> .card` direct-child
       selector — same reason, won't reach grandchild blocks.
     - Header blocks: no rectangle to bolden, so nudge the
       heading's font-weight up a step instead. */
.layout-editor__block[data-block-type="question"]:hover:not(:has(.layout-editor__block:hover)) :is(input, textarea, select).form-control,
.layout-editor__block[data-block-type="question"]:hover:not(:has(.layout-editor__block:hover)) :is(input, textarea, select).form-select,
.layout-editor__block:hover:not(:has(.layout-editor__block:hover)) > .card {
  border-color: var(--tblr-secondary);
}

/* Text-content blocks with no border to bolden get a font-weight
   bump on hover instead. Header / text apply to both editors;
   page_break is form-only; property is case-only and only bumps
   plain-appearance (card-appearance gets the `> .card` border bump
   above). For property, both the label (first child div) and the
   value (`.fs-4`) bolden — `!important` is needed for the label
   because Bootstrap's `.fw-medium` utility ships with `!important`
   and would otherwise hold its 500 weight. */
.layout-editor__block[data-block-type="header"]:hover:not(:has(.layout-editor__block:hover)) :is(h1, h2, h3, h4, h5, h6),
.layout-editor__block[data-block-type="page_break"]:hover:not(:has(.layout-editor__block:hover)) .form-layout-page-break > span,
.layout-editor__block[data-block-type="text"]:hover:not(:has(.layout-editor__block:hover)) .layout-editor__markdown,
.layout-editor__block[data-block-type="property"]:hover:not(:has(.layout-editor__block:hover)) .layout-property:not(.layout-property--card) > div:first-child,
.layout-editor__block[data-block-type="property"]:hover:not(:has(.layout-editor__block:hover)) .layout-property:not(.layout-property--card) .fs-4 {
  font-weight: 700 !important;
}


.layout-editor__block--selected,
.layout-editor__block--selected:hover {
  outline-style: solid;
  /* Tinted primary instead of full saturation — the solid orange
     outline read as an alert state. 0.45 alpha keeps the selected
     block obvious without screaming for attention. */
  outline-color: rgba(var(--tblr-primary-rgb), 0.45);
  outline-width: 2px;
}

/* Flash a primary-tinted background on the block that was just
   inserted, so after a frame reload the user can see where it
   landed without scanning. The Stimulus controller adds this
   class on connect when sessionStorage carries a just-added id;
   the class self-removes on animationend. */
@keyframes layout-editor-block-just-added {
  from { background-color: rgba(var(--tblr-primary-rgb), 0.18); }
  to   { background-color: transparent; }
}

.layout-editor__block--just-added {
  animation: layout-editor-block-just-added 1.6s ease-out;
}

/* Centre the editpane card horizontally in the centre column so
   the form isn't biased to one side. The card is capped at 880px;
   on a wider centre column the natural left + right margins are
   equal, and the chrome row sits in the left margin. A small
   left padding keeps the card off the rail border on narrower
   viewports too. */
.layout-editor__centre {
  padding-left: 1rem;
}

.layout-editor__editpane {
  align-self: center;
}

/* Chrome group — Delete / Insert / Settings / Edit / Drag handle
   as a horizontal row of small icons floated to the LEFT of the
   block, OUTSIDE the white card (Tally-style). Hidden until the
   block is hovered (innermost only) or selected. Visual order,
   left → right (closest-to-content last): Delete · + · Settings ·
   Edit · Drag handle. Edit only appears for question blocks;
   Settings only when the type has inline-tweakable props. */
.layout-editor__chrome {
  position: absolute;
  /* Default: vertical-center on the block. For leaf blocks
     (question / header / text / page_break / image) this keeps
     the chrome reachable at any cursor Y inside the row. For
     CONTAINER blocks (section / tabs / question_grid) this
     would put the chrome deep inside the children's vertical
     range, so the per-type override below anchors them to the
     top instead. */
  top: 50%;
  transform: translateY(-50%);
  right: 100%;
  /* No `margin-right` — chrome touches the block, so the cursor
     moves from block → chrome without crossing a dead zone where
     neither is hovered. */
  display: inline-flex;
  align-items: center;
  gap: 0.125rem;
  /* Wrap the row in a single rounded white pill so it reads as
     one chrome group (matches the heading-level / case-property
     modal aesthetic). */
  padding: 0.25rem;
  background: var(--tblr-bg-surface);
  border: 1px solid var(--tblr-border-color);
  border-radius: var(--tblr-border-radius);
  box-shadow: var(--tblr-box-shadow-sm);
  z-index: 5;
  opacity: 0;
  visibility: hidden;
  /* Fade-out has a 250ms grace period so brief excursions through
     dead zones — moving from the input area up to the field label,
     across the 8px insert-gap between siblings, or back onto the
     chrome pill itself — don't flicker the buttons off. Visibility
     (which IS transitionable, unlike pointer-events) is held until
     after the fade so the buttons stay clickable for the full
     visible window; pointer-events:none would snap interactivity
     off the moment hover ended. Fade-in (in the :hover rule below)
     overrides with no delay so the buttons appear instantly. */
  transition: opacity 120ms ease 250ms,
              visibility 0s linear 370ms;
  white-space: nowrap;
}

/* Containers anchor their chrome to the top so the cursor can
   reach it without crossing any child block (which would flip
   the innermost-hover scope to the child and hide the parent's
   chrome mid-motion). */
.layout-editor__block[data-block-type="section"] > .layout-editor__chrome,
.layout-editor__block[data-block-type="tabs"] > .layout-editor__chrome,
.layout-editor__block[data-block-type="question_grid"] > .layout-editor__chrome {
  top: 0.5rem;
  transform: none;
}

.layout-editor__block:hover:not(:has(.layout-editor__block:hover)) > .layout-editor__chrome {
  opacity: 1;
  visibility: visible;
  /* Override the default fade-out delays — appearing on hover should
     be instant; the delays only apply on hover-OUT. */
  transition: opacity 120ms ease,
              visibility 0s linear;
}

/* Hover-bridge pseudo-elements — invisible (debug-tinted in dev)
   strips on the block's left and right that EXTEND the block's
   :hover region into the side gutters where the chrome pills live.
   The chrome is vertically-centered (`top: 50%`), so the gutter
   areas above/below it aren't naturally part of any element's hover
   region; the cursor moving from the block's content up to the
   chrome (or down past it) crosses dead space and the chrome fades
   off. Pseudo-elements are owned by the block, so hovering them
   keeps `.layout-editor__block:hover` true and the chrome stays
   on. Z-index 4 keeps them BELOW the chrome (z=5) so chrome buttons
   remain on top and clickable. */
.layout-editor__block::before,
.layout-editor__block::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  z-index: 4;
}

.layout-editor__block::before {
  right: 100%;
  width: 3rem; /* left gutter — drag handle pill is small */
}

.layout-editor__block::after {
  left: 100%;
  width: 5rem; /* right gutter — edit + delete pill is wider */
}

/* Right-side chrome (edit + delete). Mirror of the left chrome:
   same pill, same hover-reveal lifecycle, anchored to the block's
   right edge instead of the left. */
.layout-editor__chrome--right {
  right: auto;
  left: 100%;
}

.layout-editor__chrome-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  padding: 0;
  border: 0;
  border-radius: var(--tblr-border-radius);
  background: transparent;
  color: var(--tblr-secondary);
  text-decoration: none;
  cursor: pointer;
  transition: background-color 120ms ease, color 120ms ease;
}

.layout-editor__chrome-btn:hover {
  background-color: var(--tblr-bg-surface-tertiary);
  color: var(--tblr-body-color);
}

.layout-editor__chrome-btn--danger:hover {
  background-color: var(--tblr-danger-lt);
  color: var(--tblr-danger);
}

/* Chrome: tiny pills sitting in the block's left / right gutters.
   Hidden until the block is hovered or selected; vertically
   centered against the block's content so tall blocks (sections,
   grids) put the handle near the middle, short blocks near the top
   — same `top: 0.25rem` for both to keep the layout predictable. */
/* Drag handle inside the chrome row keeps the grab cursor +
   active state — sortable.js binds on `.drag-handle`. */
.layout-editor__chrome .drag-handle { cursor: grab; }
.layout-editor__chrome .drag-handle:active { cursor: grabbing; }

/* Section title rendered as a real heading — contenteditable, with a
   muted placeholder when empty. */
.layout-editor__title {
  outline: none;
  min-width: 2rem;
}

.layout-editor__title:empty::before,
.layout-editor__heading:empty::before,
.layout-editor__markdown:empty::before {
  content: attr(data-placeholder);
  color: var(--tblr-text-muted);
  font-weight: inherit;
  font-style: italic;
  pointer-events: none;
}

.layout-editor__title:focus,
.layout-editor__heading:focus,
.layout-editor__markdown:focus {
  box-shadow: 0 0 0 2px var(--tblr-primary-lt);
  border-radius: var(--tblr-border-radius-sm);
}

/* Multi-line text block contenteditable — preserves newlines as
   entered so markdown paragraph breaks round-trip correctly. */
.layout-editor__markdown {
  outline: none;
  white-space: pre-wrap;
  min-height: 1.5em;
}

/* Children drop zones — the dashed rectangle that says "drop inside". */
.layout-editor__children {
  min-height: 3rem;
  border: 1px dashed var(--tblr-border-color);
  border-radius: var(--tblr-border-radius);
  padding: 0.5rem;
}

.layout-editor__children:empty,
.layout-editor__children:has(> [data-empty-placeholder]:only-child) {
  background: var(--tblr-bg-surface-secondary);
}

/* Shared grid container used by both the preview (`_property_grid`)
   and the editor grid branch (`_editor_block.html.erb`). N columns
   pinned via --grid-col on each child; on narrow containers the
   grid collapses to 1 column through a container query and the
   per-cell pinning is dropped.
   `container-type` lives on the outer `.layout-grid-container`
   rather than `.layout-grid` itself because an element cannot
   query its own size — the @container rule has to target an
   element *inside* the container. Using a dedicated wrapper also
   scopes the inline-size container to exactly this grid, so a
   nested grid inside a narrow section collapses based on its own
   parent's width. */
.layout-grid-container {
  container-type: inline-size;
}

.layout-grid {
  display: grid;
  grid-template-columns: repeat(var(--grid-columns, 2), 1fr);
  gap: 1rem;
}

.layout-grid > .layout-grid__cell {
  grid-column: var(--grid-col, auto);
}

@container (max-width: 500px) {
  .layout-grid {
    grid-template-columns: 1fr;
  }
  .layout-grid > .layout-grid__cell {
    grid-column: auto;
  }
}

.layout-editor__slot {
  /* Slot = grid child = SortableJS drop target. Shares the dashed
     zone style; the layout itself comes from `.layout-grid`. */
  min-height: 4rem;
}

/* Tabs — editable strip + panes. Only the pane with `data-active` is
   visible; clicking a tab head toggles this attribute via JS. */
.layout-editor__tab-head .nav-link {
  padding: 0.375rem 0.5rem;
}

.layout-editor__tab-head .layout-editor__tab-title {
  outline: none;
  min-width: 3rem;
}

.layout-editor__tab-head .layout-editor__tab-title:empty::before {
  content: attr(data-placeholder);
  color: var(--tblr-text-muted);
}

.layout-editor__tab-head .btn-icon {
  padding: 0;
  width: 1.25rem;
  height: 1.25rem;
  opacity: 0;
  transition: opacity 120ms ease;
}

.layout-editor__tab-head:hover .btn-icon,
.layout-editor__tab-head--active .btn-icon {
  opacity: 1;
}

.layout-editor__tab-pane {
  display: none;
}

.layout-editor__tab-pane[data-active] {
  display: block;
}

/* Property block — card appearance. Mirrors Tabler's "card with
   primary light background" preview: pale primary-tinted fill,
   a subtle primary-tinted border all around (replaces the earlier
   4px left stripe, which read as an alert callout), and the label
   recoloured to the primary so the block still reads as emphasised
   against plain siblings. Used on both the detail page and the edit
   canvas (preview partial is shared). */
.layout-property--card {
  background: var(--tblr-primary-lt);
  border: 1px solid color-mix(in srgb, var(--tblr-primary) 18%, transparent);
}
.layout-property--card .card-body > div:first-child {
  color: var(--tblr-primary);
}

/* Sibling insertion drop-line — the SortableJS `ghostClass` element
   (placeholder at the drop target) is flattened into a 3-px primary
   bar so the user sees exactly where the dragged block will land. We
   hide the ghost's descendants so only the outline shows. */
.layout-editor__drop-ghost {
  height: 3px !important;
  min-height: 3px !important;
  padding: 0 !important;
  margin: 0.25rem 0 !important;
  background: var(--tblr-primary);
  border-radius: 2px;
  outline: none;
  overflow: hidden;
  opacity: 1;
}

.layout-editor__drop-ghost > * {
  display: none !important;
}

/* The element currently being dragged (distinct from the ghost). A
   subtle dim so it reads as "in flight" without being hidden. */
.layout-editor__dragging {
  opacity: 0.4;
}

/* Horizontal drop-line for tab strip reordering. */
.layout-editor__drop-ghost-tab {
  width: 3px !important;
  min-width: 3px !important;
  padding: 0 !important;
  margin: 0 0.125rem !important;
  background: var(--tblr-primary);
  border-radius: 2px;
  outline: none;
  overflow: hidden;
  opacity: 1;
}

.layout-editor__drop-ghost-tab > * {
  display: none !important;
}

/* Form-layout: `question` blocks render the form's actual renderer
   partial as a preview, which means the rendered input would otherwise
   capture focus and draw its own orange focus ring on top of the
   block-selection outline. The editor canvas is preview-only — never
   a real fill-in surface — so make every form control inside a block
   non-interactive. Clicks pass through to the block wrapper (so
   selection still works), and the focus ring + focus border are
   suppressed so the user only ever sees the block's selection
   indicator. No-op on the case-layout side which never renders inputs. */
.layout-editor__block .form-control,
.layout-editor__block .form-select,
.layout-editor__block .form-check-input,
.layout-editor__block textarea,
.layout-editor__block input,
.layout-editor__block select {
  pointer-events: none;
}
.layout-editor__block .form-control:focus,
.layout-editor__block .form-select:focus,
.layout-editor__block textarea:focus,
.layout-editor__block input:focus,
.layout-editor__block select:focus {
  outline: none;
  box-shadow: none;
  border-color: var(--tblr-border-color);
}

/* Form renderer partials (rating, decimal, etc.) use
   .btn-outline-primary for segmented inputs — orange is correct
   in the live form but competes with the editor's primary-orange
   selection chrome inside .layout-editor__block. Neutralise it so
   the canvas reads as a subdued preview. Editor-side controls (the
   gear-pill dropdowns, the appearance tab) use .btn-outline-secondary
   explicitly so they're unaffected. */
.layout-editor__block .btn-outline-primary,
.layout-editor__block .btn-check:checked + .btn-outline-primary,
.layout-editor__block .btn-outline-primary.active,
.layout-editor__block .btn-outline-primary:hover {
  color: var(--tblr-body-color);
  background-color: transparent;
  border-color: var(--tblr-border-color);
}

/* Editor shell widths — promoted from inline `style="..."` so DESIGN.md
   and the rest of the editor pick the spacing scale up consistently.
   Settings rail: same flex-basis the legacy sidebar carried inline.
   Inspector: matches the floating-toolbar / question modal width.
   Tweak both here so a future re-skin doesn't have to walk view files.
   Both classes follow DESIGN.md §4.37's `layout-editor__*` namespace
   for editor-only chrome. */
.layout-editor__settings-rail {
  flex-basis: 240px;
}

.layout-editor__inspector {
  width: 360px;
}

/* Suppress the focus ring on the tabpanel hosts entirely. The
   panes carry tabindex="0" per the ARIA tabpanel pattern, which
   makes any click inside them focus the pane root and paint a wide
   blue ring around the whole canvas — distracting and non-
   actionable. Tabler paints the ring via `box-shadow`, not
   `outline`, so both are cleared. The pane is a container, not an
   interactive control; screen-reader users still get the panel via
   ARIA. */
#form-editor-edit-pane,
#form-editor-edit-pane:focus,
#form-editor-variables-pane,
#form-editor-variables-pane:focus {
  outline: none !important;
  box-shadow: none !important;
}

/* Inspector card-action cluster pull — utility class for the
   inline `margin-inline-end: 1.2rem` used on the action clusters
   above the inspector cards (Settings dropdown, Validation +,
   Display logic +, Options pencil). Bootstrap's spacer scale
   jumps from `me-2` (0.5rem) → `me-3` (1rem); the in-between value
   that visually aligns the cluster with the chrome's drag-handle
   centre lives here so the markup doesn't need an inline `style`.
   Documented in DESIGN.md as the only project-specific spacing
   utility added by the inspector-card-modals refactor. */
.inspector-action-cluster-pull {
  margin-inline-end: 1.2rem;
}

/* ---------- inspector outline (TOC) ----------
   The outline is a draggable mirror of the canvas, but it must read
   visually as a flat list-group-flush. We wrap each row in an <li>
   inside a nested <ol> so SortableJS can reorder across nesting
   levels — these rules strip the list chrome and normalise each
   row's borders so adjacent buttons read as a single thin separator,
   identical to the pre-drag look. */
.layout-outline__root,
.layout-outline__children {
  list-style: none;
  padding-left: 0;
  margin: 0;
}
.layout-outline__item {
  display: block;
}
.layout-outline__item > .list-group-item {
  border-left: 0;
  border-right: 0;
  border-top: 0;
  border-radius: 0;
  cursor: pointer;
}
/* Strips the bottom border on the bottommost row in document order
   (the deepest last-leaf at the end of the tree) to match the
   `.list-group-flush > .list-group-item:last-child { border-bottom: 0 }`
   contract that our nested <li> wrapper otherwise breaks. The class
   is applied by `form-layout-editor-controller#outlineRootTargetConnected`
   after each render. */
.layout-outline__item--last > .list-group-item {
  border-bottom: 0;
}
.layout-outline__drop-ghost > .list-group-item {
  opacity: 0.5;
  background: var(--tblr-primary-lt);
}
.layout-outline__dragging > .list-group-item {
  background: var(--tblr-primary-lt);
}
/* Label/key swap driven by `outline_display_mode_controller`. The
   default state (no class on the root) shows labels — matches the
   server-rendered HTML which ships `.layout-outline--show-labels`.
   Only question rows carry both spans; non-question rows render
   only `.layout-outline__label`, so they're unaffected by keys mode. */
.layout-outline__key { display: none; }
.layout-outline--show-keys .layout-outline__item[data-block-type="question"] > .list-group-item > .layout-outline__label {
  display: none;
}
.layout-outline--show-keys .layout-outline__item[data-block-type="question"] > .list-group-item > .layout-outline__key {
  display: inline;
}
