/* ============================================================================
   Floating design system (iteration 17) — the shared MD3 "expressive/floating"
   component vocabulary. Tonal surfaces + soft layered shadows + big radii +
   whitespace instead of borders. Defined once here, documented in /styleguide,
   composed by pages. Pages must NOT ship bespoke visual CSS for these.

   Token-driven: radii from --r-*, shadows from --elev-*, motion from --ease* ,
   color from --md-sys-color-*. No hardcoded values (scrim alpha aside).

   New classes coexist with the older system (.card / .tabs / .chip / .field)
   during the page-by-page migration (Q1=B); the superseded classes get deleted
   once every page migrates. Names that would collide with existing classes use
   a distinct form: .tabbar__tab (vs .tabs), .filled-field (vs .field),
   .fchip (vs .chip).
   ============================================================================ */

/* Many of these render as <a>; kill base.css's a:hover underline on all of
   them so links styled as controls never underline. */
.btn:hover,
.icon-btn:hover,
.tabbar__tab:hover,
.fchip:hover { text-decoration: none; }

/* ---- Floating card -------------------------------------------------------
   Borderless tonal card; lifts off the page via tone + soft shadow, never a
   border. The default surface for migrated content. */
.float-card {
  background: var(--md-sys-color-surface-container-lowest);
  border-radius: var(--r-md);
  box-shadow: var(--elev-1);
  padding: 1.75rem;
  margin-bottom: 1.25rem;
}
.float-card--flush { padding: 0.5rem; } /* tables sit nearly flush in the card */

/* ---- Buttons -------------------------------------------------------------
   Full-radius, soft shadow, active scale. Variants: filled / tonal / text. */
.btn {
  font: inherit;
  font-size: 0.875rem;
  font-weight: 600;
  cursor: pointer;
  border: none;
  border-radius: var(--r-full);
  padding: 11px 22px;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  position: relative;
  overflow: hidden;
  text-decoration: none;
  transition: box-shadow var(--md-sys-motion-duration-short4) var(--ease),
              transform var(--md-sys-motion-duration-short2) var(--ease),
              background var(--md-sys-motion-duration-short4) var(--ease);
}
.btn .material-symbols-outlined,
.btn .material-symbols-rounded { font-size: 18px; z-index: 1; }
.btn > span:not([class*="material-symbols"]) { z-index: 1; }
.btn:active { transform: scale(0.97); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
.btn--filled { background: var(--md-sys-color-primary); color: var(--md-sys-color-on-primary); box-shadow: var(--elev-1); }
.btn--filled:hover { box-shadow: var(--elev-2); }
.btn--tonal { background: var(--md-sys-color-secondary-container); color: var(--md-sys-color-on-secondary-container); }
.btn--tonal:hover { box-shadow: var(--elev-1); }
.btn--text { background: transparent; color: var(--md-sys-color-primary); padding: 10px 14px; }

/* ---- Icon button + state layer -------------------------------------------
   Circular icon button. Add .state-layer (also usable on .btn / .fchip) for a
   currentColor hover/active ripple overlay. */
.icon-btn {
  width: 46px;
  height: 46px;
  border: none;
  background: transparent;
  border-radius: var(--r-full);
  display: grid;
  place-items: center;
  cursor: pointer;
  color: var(--md-sys-color-on-surface-variant);
  position: relative;
  overflow: hidden;
  text-decoration: none;
}
.icon-btn .material-symbols-outlined,
.icon-btn .material-symbols-rounded { font-size: 24px; z-index: 1; }
.icon-btn--sm { width: 40px; height: 40px; }
.icon-btn--sm .material-symbols-outlined,
.icon-btn--sm .material-symbols-rounded { font-size: 20px; }

.state-layer::after {
  content: "";
  position: absolute;
  inset: 0;
  background: currentColor;
  opacity: 0;
  transition: opacity var(--md-sys-motion-duration-short4) var(--ease);
  /* A ripple is decoration and must never be a click target. Without this the layer covers
     its own host's children, so a click on a nested control (e.g. the × inside a minimized
     SMS tray row) hit-tests as the HOST — event delegation then sees the row, not the button,
     and the wrong action fires. Hover/active still work: :hover matches the host, not ::after.
     The it-27.2 fix (position:relative; overflow:hidden on custom hosts) stopped the layer
     ESCAPING to blanket the page; this stops it swallowing what's inside. */
  pointer-events: none;
}
.state-layer:hover::after { opacity: var(--md-sys-state-hover-state-layer-opacity); }
.state-layer:active::after { opacity: var(--md-sys-state-pressed-state-layer-opacity); }

/* ---- Floating segmented tabs ---------------------------------------------
   A rounded pill bar with a filled pill (.tabbar__pill) that slides behind the
   active tab. The slide is driven by a small Alpine controller that sets the
   pill's left/width from the active tab's offset (Phase 3). */
.tabbar {
  display: inline-flex;
  position: relative;
  gap: 2px;
  padding: 6px;
  border-radius: var(--r-full);
  background: var(--md-sys-color-surface-container);
  box-shadow: var(--elev-1);
  max-width: 100%;
  overflow: auto;
}
.tabbar__pill {
  position: absolute;
  top: 6px;
  bottom: 6px;
  border-radius: var(--r-full);
  background: var(--md-sys-color-primary-container);
  box-shadow: var(--elev-1);
  transition: left var(--md-sys-motion-duration-medium4) var(--ease-emph),
              width var(--md-sys-motion-duration-medium4) var(--ease-emph);
  z-index: 0;
}
.tabbar__tab {
  border: none;
  background: transparent;
  cursor: pointer;
  position: relative;
  z-index: 1;
  white-space: nowrap;
  padding: 11px 20px;
  font: inherit;
  font-size: 0.875rem;
  font-weight: 600;
  border-radius: var(--r-full);
  color: var(--md-sys-color-on-surface-variant);
  display: flex;
  align-items: center;
  gap: 0.5rem;
  text-decoration: none;
  transition: color var(--md-sys-motion-duration-medium1) var(--ease);
}
.tabbar__tab .material-symbols-outlined,
.tabbar__tab .material-symbols-rounded { font-size: 18px; }
.tabbar__tab.is-active { color: var(--md-sys-color-on-primary-container); }
.tabbar__tab:not(.is-active):hover { color: var(--md-sys-color-on-surface); }
.tabbar__badge {
  min-width: 19px;
  height: 19px;
  padding: 0 5px;
  border-radius: var(--r-full);
  font-size: 11px;
  font-weight: 700;
  display: grid;
  place-items: center;
  background: var(--md-sys-color-surface-container-highest);
  color: var(--md-sys-color-on-surface-variant);
  transition: var(--md-sys-motion-duration-medium1) var(--ease);
}
.tabbar__tab.is-active .tabbar__badge { background: var(--md-sys-color-primary); color: var(--md-sys-color-on-primary); }

/* Static variant for server-navigation tabbars — one full page per tab, no JS-slid
   .tabbar__pill. The active tab carries its own fill instead. (Same approach the
   wireframe index tabs took; generalized here so settings + any page-nav tabbar can reuse it.) */
.tabbar--static .tabbar__tab.is-active { background: var(--md-sys-color-primary-container); }

/* ---- Floating chip -------------------------------------------------------
   Rounded tonal chip with an elevated selected state. Used for the lens
   "Viewing" selector + filters. (Distinct from chips.css's .chip tag pill.) */
.fchip {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font: inherit;
  font-size: 0.875rem;
  font-weight: 600;
  border-radius: var(--r-full);
  padding: 9px 16px;
  cursor: pointer;
  border: none;
  background: var(--md-sys-color-surface-container);
  color: var(--md-sys-color-on-surface-variant);
  text-decoration: none;
  position: relative;
  overflow: hidden;
  transition: background var(--md-sys-motion-duration-short4) var(--ease),
              box-shadow var(--md-sys-motion-duration-short4) var(--ease);
}
.fchip .material-symbols-outlined,
.fchip .material-symbols-rounded { font-size: 18px; }
.fchip.is-selected { background: var(--md-sys-color-primary-container); color: var(--md-sys-color-on-primary-container); box-shadow: var(--elev-1); }

/* ---- Count pill ----------------------------------------------------------
   Small tonal count badge sitting next to a heading. */
.count-pill {
  font-size: 0.8125rem;
  font-weight: 700;
  background: var(--md-sys-color-primary-container);
  color: var(--md-sys-color-on-primary-container);
  padding: 3px 12px;
  border-radius: var(--r-full);
}

/* ---- Pill search ---------------------------------------------------------
   Borderless tonal search input with a leading icon. */
.pill-search { position: relative; display: inline-flex; align-items: center; }
.pill-search .material-symbols-outlined,
.pill-search .material-symbols-rounded {
  position: absolute;
  left: 16px;
  font-size: 20px;
  color: var(--md-sys-color-on-surface-variant);
  pointer-events: none;
}
.pill-search input {
  font: inherit;
  font-size: 0.875rem;
  color: var(--md-sys-color-on-surface);
  background: var(--md-sys-color-surface-container);
  border: none;
  outline: none;
  border-radius: var(--r-full);
  padding: 12px 18px 12px 46px;
  width: 230px;
  transition: box-shadow var(--md-sys-motion-duration-short4) var(--ease),
              background var(--md-sys-motion-duration-short4) var(--ease);
}
.pill-search input::placeholder { color: var(--md-sys-color-on-surface-variant); }
.pill-search input:focus {
  box-shadow: var(--elev-1), 0 0 0 2px var(--md-sys-color-primary) inset;
  background: var(--md-sys-color-surface-container-lowest);
}

/* ---- Filled text field ---------------------------------------------------
   Tonal fill + bottom indicator + floating label. (Distinct from cards.css's
   read-only .field display block.) Requires the input to carry a
   placeholder=" " so :placeholder-shown drives the floating label. */
.filled-field { position: relative; }
.filled-field__input {
  width: 100%;
  font: inherit;
  font-size: 0.9375rem;
  color: var(--md-sys-color-on-surface);
  background: var(--md-sys-color-surface-container);
  border: none;
  border-bottom: 1px solid var(--md-sys-color-outline);
  border-radius: var(--r-xs) var(--r-xs) 0 0;
  padding: 23px 16px 9px;
  outline: none;
  transition: border-color var(--md-sys-motion-duration-short3) var(--ease),
              background var(--md-sys-motion-duration-short3) var(--ease);
}
.filled-field__input:focus {
  border-bottom: 2px solid var(--md-sys-color-primary);
  padding-bottom: 8px;
  background: var(--md-sys-color-surface-container-low);
}
.filled-field__label {
  position: absolute;
  left: 16px;
  top: 17px;
  font-size: 0.9375rem;
  color: var(--md-sys-color-on-surface-variant);
  pointer-events: none;
  transition: all var(--md-sys-motion-duration-short3) var(--ease);
}
.filled-field__input:focus + .filled-field__label,
.filled-field__input:not(:placeholder-shown) + .filled-field__label {
  top: 8px;
  font-size: 12px;
  color: var(--md-sys-color-primary);
}

/* ---- Borderless table treatment ------------------------------------------
   No dividers; rows get a soft rounded hover background. Drop inside a
   .float-card. .float-table__ico is the file-type icon cell. */
.float-table { width: 100%; border-collapse: collapse; }
.float-table thead th {
  text-align: left;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--md-sys-color-on-surface-variant);
  padding: 14px 16px;
  white-space: nowrap;
}
.float-table thead th:first-child { padding-left: 20px; }
.float-table thead th.is-actions { text-align: center; padding-right: 20px; }
.float-table tbody td { padding: 12px 16px; font-size: 0.875rem; vertical-align: middle; }
.float-table tbody td:first-child { padding-left: 12px; border-radius: var(--r-sm) 0 0 var(--r-sm); }
.float-table tbody td:last-child { padding-right: 12px; border-radius: 0 var(--r-sm) var(--r-sm) 0; }
.float-table tbody td.is-actions { text-align: center; }
.float-table tbody tr { transition: background var(--md-sys-motion-duration-short3) var(--ease); }
.float-table tbody tr:hover td { background: var(--md-sys-color-surface-container-low); }

.float-table__ico {
  width: 42px;
  height: 42px;
  border-radius: var(--r-sm);
  display: grid;
  place-items: center;
  flex-shrink: 0;
  background: var(--md-sys-color-primary-container);
  color: var(--md-sys-color-on-primary-container);
}
.float-table__ico--img { background: var(--md-sys-color-tertiary-container); color: var(--md-sys-color-on-tertiary-container); }
.float-table__ico--pdf { background: var(--md-sys-color-error-container); color: var(--md-sys-color-on-error-container); }
.float-table__ico .material-symbols-outlined,
.float-table__ico .material-symbols-rounded { font-size: 22px; }

/* ---- Snackbar ------------------------------------------------------------
   Bottom-center toast with an optional action button. Add .is-open to show
   (slides up). Used by the deferred-delete harness (Part C). Generic. */
.snackbar {
  position: fixed;
  left: 50%;
  bottom: 24px;
  /* Hidden state must clear the snackbar's own height PLUS the 24px bottom gap,
     or a short toast leaves a sliver peeking above the viewport edge. 140% of a
     small toast wasn't enough; calc(100% + 24px) drops it fully off-screen. */
  transform: translate(-50%, calc(100% + 24px));
  background: var(--md-sys-color-inverse-surface);
  color: var(--md-sys-color-inverse-on-surface);
  border-radius: var(--r-sm);
  padding: 15px 18px;
  display: flex;
  align-items: center;
  gap: 16px;
  font-size: 0.875rem;
  box-shadow: var(--elev-3);
  z-index: 60;
  transition: transform var(--md-sys-motion-duration-medium4) var(--ease-emph);
  max-width: 90vw;
}
.snackbar.is-open { transform: translate(-50%, 0); }
.snackbar__action {
  background: transparent;
  border: none;
  color: var(--md-sys-color-inverse-primary);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  padding: 4px 8px;
  border-radius: var(--r-xs);
}

/* ---- Pending-delete row (undoable.js) ------------------------------------
   The row stays in the DOM, dimmed + struck, during the undo window and until
   River confirms. Generic — any undoable list row gets this while deleting. */
.is-deleting {
  opacity: 0.5;
  text-decoration: line-through;
  pointer-events: none;
  transition: opacity var(--md-sys-motion-duration-short4) var(--ease);
}
.is-deleting td { color: var(--md-sys-color-on-surface-variant); }

/* ---- Dialog + scrim (shared modal) --------------------------------------
   A centered modal on a blurred scrim. Always in the DOM; toggle .is-open on
   the .scrim (Alpine :class) so it can transition in/out. Sub-parts:
   __header / __title / __close (an .icon-btn) / __body (scrolls) / __actions.
   .dialog--wide for big content (e.g. the icon picker grid).

   NOTE: iteration-17's contact-show upload dialog still ships its own
   page-scoped .scrim/.dialog in contact-show.css (toggled via .open). Folding
   it onto this shared component is a tracked TODO; until then both exist and
   the page-scoped copy wins on the contact show page. */
.scrim {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  backdrop-filter: blur(2px);
  z-index: 50;
  display: grid;
  place-items: center;
  padding: 1.5rem;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--md-sys-motion-duration-medium2) var(--ease);
}
.scrim.is-open { opacity: 1; pointer-events: auto; }

.dialog {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  width: 100%;
  max-width: 480px;
  max-height: 90vh;
  padding: 1.75rem;
  background: var(--md-sys-color-surface-container-high);
  border-radius: var(--r-lg);
  box-shadow: var(--elev-3);
  opacity: 0;
  transform: scale(0.9) translateY(8px);
  transition: transform var(--md-sys-motion-duration-medium3) var(--ease-emph),
              opacity var(--md-sys-motion-duration-medium1) var(--ease);
}
.scrim.is-open .dialog { opacity: 1; transform: none; }
/* Wide variant for grid/list content (icon picker): wider + a fixed tall body
   so __body scrolls instead of the whole dialog growing. */
.dialog--wide { max-width: 800px; height: min(640px, 90vh); }

.dialog__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-shrink: 0;
}
.dialog__title {
  margin: 0;
  font-size: 1.375rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  gap: 0.75rem;
}
.dialog__title .material-symbols-outlined,
.dialog__title .material-symbols-rounded { color: var(--md-sys-color-primary); }
.dialog__sub { margin: 0; color: var(--md-sys-color-on-surface-variant); font-size: 0.875rem; }
.dialog__body { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
/* For a dialog whose body contains a dropdown that escapes its own box — an .autocomplete list, a
 * menu — the scroll container CLIPS it: the list is absolutely positioned, and an ancestor with
 * overflow other than `visible` is its clipping boundary. The list appears to vanish, or (worse)
 * turns the dialog into a scrollbox that hides it below the fold.
 *
 * Such a body must not scroll. The dropdowns carry their own max-height + scroll, and the dialog's
 * own max-height still bounds the whole thing. */
.dialog__body--visible { overflow: visible; }
.dialog__actions { display: flex; justify-content: flex-end; gap: 0.5rem; flex-shrink: 0; margin-top: 0.5rem; }

/* ── Option-picker cards (.start-cards / .start-card) ─────────────────────────
   A shared "choose one of N options" card grid built on the floating system:
   surface tone + shadow, no borders; lift on hover; plum-tinted surface + ring
   when selected (.is-active). Works as <button> or <a>. Used by the email Start
   wizard and the A2P brand picker. */
.start-cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
}
/* Two-up variant for a 2-option picker (avoids an empty third column). */
.start-cards--two { grid-template-columns: repeat(2, 1fr); }
.start-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 0.5rem;
  padding: 1.75rem 1.25rem;
  min-height: 190px;
  border: none;
  border-radius: var(--r-md);
  background: var(--md-sys-color-surface-container-lowest);
  color: var(--md-sys-color-on-surface);
  text-decoration: none;
  cursor: pointer;
  box-shadow: var(--elev-1);
  transition: box-shadow 0.2s ease, transform 0.2s ease, background-color 0.15s ease;
}
.start-card:not(.start-card--disabled):hover {
  box-shadow: var(--elev-2);
  transform: translateY(-3px);
}
.start-card.is-active {
  background: var(--md-sys-color-primary-container);
  color: var(--md-sys-color-on-primary-container, var(--md-sys-color-on-surface));
  box-shadow: 0 0 0 2px var(--md-sys-color-primary), var(--elev-2);
}
.start-card__icon { font-size: 44px; color: var(--md-sys-color-primary); }
.start-card.is-active .start-card__icon { color: var(--md-sys-color-primary); }
.start-card__name { font-weight: 700; font-size: 1.05rem; }
.start-card__desc { font-size: 0.82rem; color: var(--md-sys-color-on-surface-variant); }
.start-card.is-active .start-card__desc { color: inherit; opacity: 0.85; }
.start-card__soon { margin-top: 0.4rem; }
.start-card--disabled {
  cursor: not-allowed;
  opacity: 0.5;
  box-shadow: var(--elev-1);
}
.start-card--disabled .start-card__icon { color: var(--md-sys-color-on-surface-variant); }

@media (max-width: 720px) {
  .start-cards { grid-template-columns: 1fr; }
}

/* Generic spinner rotation (e.g. a material-symbols progress_activity glyph with
   `animation: spin 1s linear infinite`). */
@keyframes spin { to { transform: rotate(360deg); } }
