/* ─────────────────────────────────────────────────────────────────────────────
   Tables app.

   Everything is expressed through F7's theme variables (--f7-text-color,
   --f7-theme-color, --f7-page-bg-color …) so the app follows the room's colour and
   the user's light/dark choice without a second palette to maintain.

   Two house rules honoured throughout:
     • f7-icons font sizes are set INLINE at each call site, never here — a class
       does not win against the icon font's own rule.
     • Buttons hug their content (width:auto). No fixed or full-width buttons.

   Naming: `tg-` is the grid engine's own surface (it writes those class names in
   plain JS and F7 never touches them); `rt-` is the app chrome around it.
   ───────────────────────────────────────────────────────────────────────────── */

:root {
    --tg-row-h: 34px;
    --tg-header-h: 34px;
    /*
       A DATA SURFACE IS WHITE. `--f7-page-bg-color` is #e2e2e2 — the app's grey page —
       and building the grid on it made every cell, header and bar the same flat grey:
       nothing read as a surface you could write on. The surface is now
       `--f7-list-bg-color` (#fff light, #222 dark), and the grey page is what the
       surface sits ON, which is where the contrast comes from.
    */
    --tg-surface: var(--f7-list-bg-color, #fff);
    /* Two weights of line, as in the reference: a hairline between cells, a slightly
       firmer one under a header or between bands. One weight everywhere reads as a
       wireframe. */
    --tg-line: color-mix(in srgb, var(--f7-text-color) 7%, transparent);
    --tg-border: color-mix(in srgb, var(--f7-text-color) 11%, transparent);
    --tg-border-strong: color-mix(in srgb, var(--f7-text-color) 20%, transparent);
    --tg-cell-bg: var(--tg-surface);
    /* Barely there — a header is distinguished by weight and a firmer rule, not by
       being a different colour. */
    --tg-head-bg: color-mix(in srgb, var(--f7-text-color) 3%, var(--tg-surface));
    --tg-hover: color-mix(in srgb, var(--f7-text-color) 4%, transparent);
    --tg-sel-bg: color-mix(in srgb, var(--f7-theme-color) 8%, transparent);
    --tg-muted: color-mix(in srgb, var(--f7-text-color) 55%, transparent);
    --tg-radius: 10px;
}

/*
   THE ONE TOKEN THIS FILE CANNOT ASSUME.

   --f7-list-bg-color (#fff light, #222 dark) is defined in app/css/app/app.css, and
   form.qik.mt does not load app.css — the form host loads the F7 bundle, core.min.css
   and THIS file, which is the whole point of keeping the runner's rules here. So in
   dark mode --tg-surface fell through to its own fallback, #fff, and the form's sheet
   came out white on a black page.

   The fallback is named per theme instead. Where app.css IS loaded its value wins in
   both, because this only ever supplies the second argument to the var() above.
   Everything else in that block is built from --f7-text-color and --f7-theme-color,
   which the Framework7 bundle defines and flips on its own.
*/
.dark { --tg-surface: var(--f7-list-bg-color, #222); }


/* The form builder's navbar is `navbar-modern`: it floats over the page with a 12px
   inset rather than occupying space above it. So the first row inside page-content has
   to clear it, or it is simply hidden — which is what happened to the builder's own
   Build / Settings / Responses strip. Expressed from the same numbers app.css uses to
   place the navbar, so the two cannot drift. */
.popup-room-table-form > .page-content.rt-fb-content {
    padding: calc(var(--f7-navbar-height, 44px) + 12px) 0 0;
}
.ios .popup-room-table-form > .page-content.rt-fb-content {
    padding-top: calc(var(--f7-navbar-height, 44px) + 20px);
}

/* ── the app shell ──────────────────────────────────────────────────────── */

.room-tables {
    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: 0;
    background: var(--tg-surface);
}

/* Table tabs — the "base" bar across the top, as in the reference. */
.rt-tabs {
    display: flex;
    align-items: stretch;
    gap: 2px;
    /* Airtable's spacing: the tab strip is inset from the edge, and each tab carries
       its own padding rather than the strip padding everything at once. */
    padding: 0 10px;
    border-bottom: 1px solid var(--tg-border);
    background: var(--tg-head-bg);
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    flex: 0 0 auto;
    min-height: 40px;
}
.rt-tabs::-webkit-scrollbar { display: none; }

.rt-tab {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 0 12px;
    height: 40px;
    font-size: 13.5px;
    font-weight: 500;
    color: var(--tg-muted);
    white-space: nowrap;
    cursor: pointer;
    border-bottom: 2px solid transparent;
    transition: color .15s, background .15s;
    flex: 0 0 auto;
    border-radius: 8px 8px 0 0;
}
.rt-tab:hover { background: var(--tg-hover); color: var(--f7-text-color); }
.rt-tab.rt-tab-on {
    color: var(--f7-theme-color);
    border-bottom-color: var(--f7-theme-color);
    background: var(--tg-cell-bg);
}
.rt-tab-count {
    font-size: 11px;
    padding: 1px 6px;
    border-radius: 8px;
    background: color-mix(in srgb, var(--f7-text-color) 9%, transparent);
    font-variant-numeric: tabular-nums;
}
.rt-tab-on .rt-tab-count { background: color-mix(in srgb, var(--f7-theme-color) 16%, transparent); }
/* The open tab is also the table's menu; the caret is the only thing that says so. */
.rt-tab-caret { opacity: .6; margin-left: -2px; }
.rt-tab-on:hover .rt-tab-caret { opacity: 1; }

.rt-tab-add {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 40px;
    color: var(--tg-muted);
    cursor: pointer;
    flex: 0 0 auto;
}
.rt-tab-add:hover { color: var(--f7-theme-color); }

/* The floating close button — no navbar, per the fullscreen popup shell. */
/*
   THE WAY OUT SITS AT THE LEFT, BEFORE THE TABS.
   It used to float top-RIGHT, where it covered whatever the view bar's right-hand end
   happened to be (the Sort chip, at panel widths). Back belongs where back always is,
   and reading order puts it before the thing it leaves.

   Still absolutely positioned rather than a real first child of the tab strip: the
   strip is rendered by <room-table-list> and this button by the popup shell around it,
   so they are not siblings. The strip reserves a gutter for it instead — see
   .rt-has-close below.
*/
.rt-close {
    position: absolute;
    top: 6px;
    z-index: 20;
    width: 34px;
    height: 34px;
    border-radius: 9px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: color-mix(in srgb, var(--f7-page-bg-color) 88%, transparent);
    backdrop-filter: blur(8px);
    color: var(--tg-muted);
    cursor: pointer;
    transition: background .15s, color .15s;
}
/*
   Which side, by device:
     desktop  X, top-RIGHT      — a window you dismiss
     mobile   chevron, top-LEFT — a screen you come back from
   The shell picks the class; the gutter above matches it. Getting that pairing wrong is
   what put the button on top of the Sort chip.
*/
.rt-close-left  { left: 8px;  right: auto; }
.rt-close-right { right: 8px; left: auto; }
.rt-close:hover { background: var(--tg-hover); color: var(--f7-text-color); }

/* ── the view bar ───────────────────────────────────────────────────────── */

.rt-viewbar {
    display: flex;
    align-items: center;
    gap: 2px;
    padding: 7px 10px;
    border-bottom: 1px solid var(--tg-line);
    background: var(--tg-surface);
    flex: 0 0 auto;
    overflow-x: auto;
    scrollbar-width: none;
    min-height: 44px;
}
.rt-viewbar::-webkit-scrollbar { display: none; }

.rt-vb {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    height: 28px;
    /* 8px, not 10px: the controls are a related group, so they sit closer together
       than the bar's own inset. Wider padding made the bar read as five separate
       buttons instead of one toolbar. */
    padding: 0 8px;
    border-radius: 6px;
    font-size: 13px;
    font-weight: 500;
    color: var(--f7-text-color);
    background: transparent;
    border: 1px solid transparent;
    cursor: pointer;
    white-space: nowrap;
    width: auto;
    transition: background .12s, border-color .12s, color .12s;
}
.rt-vb:hover { background: var(--tg-hover); }
/* An ACTIVE control is tinted, so "3 hidden fields" and "Filter" read as state
   rather than as buttons — the same signal the reference uses. */
.rt-vb.rt-vb-on {
    /* Tinted fill, no outline: the reference marks an active control by colour alone,
       and the extra border made "on" look like a selected form field. */
    background: color-mix(in srgb, var(--f7-theme-color) 11%, transparent);
    color: color-mix(in srgb, var(--f7-theme-color) 88%, var(--f7-text-color));
    border-color: transparent;
}
.rt-vb-sep { width: 1px; height: 18px; background: var(--tg-border); margin: 0 6px; flex: 0 0 auto; }
.rt-vb-grow { flex: 1 1 auto; min-width: 4px; }

.rt-vb-count {
    font-size: 11px;
    font-variant-numeric: tabular-nums;
    padding: 0 5px;
    border-radius: 7px;
    background: color-mix(in srgb, var(--f7-theme-color) 20%, transparent);
}

/* The overlay chip a member sees: their filter is theirs, and says so. */
.rt-overlay-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 30px;
    padding: 0 8px 0 10px;
    border-radius: 8px;
    font-size: 12.5px;
    background: color-mix(in srgb, var(--f7-theme-color) 12%, transparent);
    color: var(--f7-theme-color);
    white-space: nowrap;
}
.rt-overlay-chip b { font-weight: 600; }
.rt-overlay-reset {
    display: inline-flex;
    align-items: center;
    padding: 0 4px;
    opacity: .75;
    cursor: pointer;
}
.rt-overlay-reset:hover { opacity: 1; }

.rt-count {
    font-size: 12px;
    color: var(--tg-muted);
    font-variant-numeric: tabular-nums;
    padding: 0 8px;
    white-space: nowrap;
}

/* ── the grid ───────────────────────────────────────────────────────────── */

.rt-body { position: relative; flex: 1 1 auto; min-height: 0; }

/*
   The engine adds `tg-root` TO the element it is handed, and absolute+inset:0 is right
   for a standalone host: it fills a positioned parent of known size, which is how the
   test harnesses mount it.
*/
/*
   THE GRID IS NOT PROSE, so the mouse does not select text in it.

   Dragging across cells draws a CELL range. With the browser's own text selection left
   on, the same drag painted the row numbers and cell contents blue underneath the range
   ring — two selections disagreeing about what was selected. Copy does not depend on it
   either: copySelection() builds TSV from the cell range and writes it with the
   clipboard API, so nothing is lost by turning it off.

   The editor is the exception, and has to be: a cell you are typing in is prose, and
   selecting a word inside it must work as it does anywhere else.
*/
.tg-root {
    position: absolute;
    inset: 0;
    overflow: hidden;
    -webkit-user-select: none;
    user-select: none;
}
.tg-editor, .tg-editor * {
    -webkit-user-select: text;
    user-select: text;
}

/*
   But in the app that host IS `.rt-body` — a flex child that already has its own box.
   Absolute positioning tore it out of the column and inset:0 stretched it over the
   whole popup, so the column headers drew on top of the tab bar and the view bar. In
   flow here, and still positioned, so the engine's own absolute pieces (header,
   selection overlay, editor) keep anchoring to it.
*/
.rt-body.tg-root { position: relative; inset: auto; height: auto; }

.tg-scroll {
    position: absolute;
    inset: 0;
    overflow: auto;
    outline: none;
    /* The one scroller. Frozen columns are sticky inside it, so there is no second
       pane to keep in sync. */
    overscroll-behavior: contain;
}

.tg-header {
    position: sticky;
    top: 0;
    z-index: 4;
    height: var(--tg-header-h);
    background: var(--tg-head-bg);
    border-bottom: 1px solid var(--tg-border-strong);
}
.tg-hrow { display: flex; height: var(--tg-header-h); }

.tg-canvas { position: relative; }

.tg-row {
    position: absolute;
    top: 0;
    left: 0;
    display: flex;
    /* Bounds layout and paint per row. At a 10,000-row virtual height this is the
       difference between a smooth scroll and a stuttering one. */
    contain: layout style paint;
    content-visibility: auto;
    contain-intrinsic-size: auto var(--tg-row-h);
}
.tg-row-row:hover .tg-cell { background: var(--tg-hover); }
.tg-row-sel .tg-cell { background: var(--tg-sel-bg); }

.tg-cells { display: flex; height: 100%; }

.tg-cell {
    flex: 0 0 auto;
    height: 100%;
    box-sizing: border-box;
    padding: 0 8px;
    display: flex;
    align-items: center;
    gap: 4px;
    overflow: hidden;
    /* The hairline, not the border weight. A whole grid of --tg-border reads as a
       wireframe; the reference keeps cell lines almost invisible and saves the firmer
       rule for the header. */
    border-right: 1px solid var(--tg-line);
    border-bottom: 1px solid var(--tg-line);
    background: var(--tg-cell-bg);
    font-size: 13px;
    color: var(--f7-text-color);
    transition: background .1s;
}
.tg-spacer { flex: 0 0 auto; }

/*
   Frozen columns are sticky, and that has to WIN over anything a cell variant sets.
   `.tg-hcell` declares position:relative — it anchors the resize handle — and at equal
   specificity, later in the file, it beat this rule: the frozen HEADER cell became
   relative, so its inline `left: 62px` shifted it 62px right instead of merely clamping
   it during scroll. The header's first column then sat one gutter-width off from the
   body's, and the next column absorbed the difference.

   `.tg-cell.tg-frozen` outranks a single class, so it holds for the header and the body
   alike. Sticky is still a containing block, so the resize handle keeps anchoring.
*/
.tg-cell.tg-frozen { position: sticky; left: 0; z-index: 2; }
.tg-frozen-first { box-shadow: 1px 0 0 var(--tg-border); }
.tg-header .tg-frozen { z-index: 5; }

.tg-al-right { justify-content: flex-end; }
.tg-al-center { justify-content: center; }

.tg-cv { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: flex; align-items: center; gap: 3px; min-width: 0; }
/* Taller rows let long text actually use the space rather than truncating in a
   row that is visibly tall enough to show more. */
.tg-row[style*="height: 56px"] .tg-cv,
.tg-row[style*="height: 88px"] .tg-cv,
.tg-row[style*="height: 128px"] .tg-cv {
    white-space: normal;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
}

/* ── the gutter ─────────────────────────────────────────────────────────── */

.tg-gutter {
    justify-content: flex-start;
    gap: 2px;
    padding: 0 4px 0 8px;
    color: var(--tg-muted);
    font-size: 11.5px;
    font-variant-numeric: tabular-nums;
}
.tg-rownum { min-width: 22px; }
.tg-rowcheck {
    width: 15px; height: 15px;
    border: 1.5px solid var(--tg-border-strong);
    border-radius: 4px;
    display: none;
    flex: 0 0 auto;
    cursor: pointer;
}
.tg-row-row:hover .tg-rowcheck { display: block; }
.tg-row-row:hover .tg-rownum { display: none; }
/* A ticked row keeps its box visible after the pointer leaves — a selection you can
   only see by hovering is not a selection you can trust. */
.tg-row-checked .tg-rowcheck { display: block; }
.tg-row-checked .tg-rownum { display: none; }
.tg-row-checked .tg-rowcheck,
.tg-selall-on,
.tg-selall-some {
    background: var(--f7-theme-color);
    border-color: var(--f7-theme-color);
    position: relative;
}
.tg-row-checked .tg-rowcheck::after,
.tg-selall-on::after {
    content: '';
    position: absolute;
    left: 3.5px; top: 0.5px;
    width: 4px; height: 8px;
    border: solid #fff;
    border-width: 0 1.5px 1.5px 0;
    transform: rotate(40deg);
}
/* Some but not all: a dash, the standard third state. */
.tg-selall-some::after {
    content: '';
    position: absolute;
    left: 2.5px; top: 5.25px;
    width: 7px; height: 0;
    border-top: 1.5px solid #fff;
}
.tg-row-checked { background: var(--tg-sel-bg); }
.tg-expand {
    display: none;
    align-items: center;
    justify-content: center;
    width: 20px; height: 20px;
    border-radius: 5px;
    color: var(--tg-muted);
    cursor: pointer;
    margin-left: auto;
}
.tg-row-row:hover .tg-expand { display: flex; }
.tg-expand:hover { background: color-mix(in srgb, var(--f7-text-color) 10%, transparent); color: var(--f7-text-color); }

/* A row somebody else owns. Rendered, not just refused — a member should never
   type into a cell that is going to say no. */
.tg-locked .tg-cell { background: color-mix(in srgb, var(--f7-text-color) 3%, var(--tg-cell-bg)); }
.tg-locked .tg-cv { opacity: .72; }

/* ── the header row ─────────────────────────────────────────────────────── */

.tg-hcell {
    font-size: 12.5px;
    font-weight: 500;
    /* Slightly recessed, like the reference: a column NAME is a label for the data,
       not a heading competing with it. 600 weight in full ink made the header row the
       loudest thing on screen. */
    color: color-mix(in srgb, var(--f7-text-color) 78%, transparent);
    background: var(--tg-head-bg);
    position: relative;
    cursor: default;
    user-select: none;
    /* The one firm rule on the grid — it separates the labels from the data. */
    border-bottom: 1px solid var(--tg-border);
}
.tg-hicon { color: var(--tg-muted); flex: 0 0 auto; }
.tg-hname { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tg-hprimary { color: var(--f7-theme-color); opacity: .8; flex: 0 0 auto; }
.tg-hwarn { color: #e8930c; display: flex; align-items: center; cursor: pointer; flex: 0 0 auto; }
.tg-hmenu {
    margin-left: auto;
    display: none;
    align-items: center;
    color: var(--tg-muted);
    cursor: pointer;
    padding: 2px;
    border-radius: 4px;
    flex: 0 0 auto;
}
.tg-hcell:hover .tg-hmenu { display: flex; }
.tg-hmenu:hover { background: color-mix(in srgb, var(--f7-text-color) 10%, transparent); color: var(--f7-text-color); }

.tg-hresize {
    position: absolute;
    top: 0; right: -3px;
    width: 7px; height: 100%;
    cursor: col-resize;
    z-index: 6;
}
.tg-hresize:hover { background: color-mix(in srgb, var(--f7-theme-color) 45%, transparent); }
body.tg-resizing { cursor: col-resize !important; user-select: none; }

.tg-hadd {
    justify-content: center;
    width: 46px;
    color: var(--tg-muted);
    cursor: pointer;
    border-bottom: none;
}
.tg-hadd:hover { color: var(--f7-theme-color); background: var(--tg-hover); }

.tg-selall {
    width: 15px; height: 15px;
    border: 1.5px solid var(--tg-border-strong);
    border-radius: 4px;
    cursor: pointer;
    /* Always there, unlike a row's box: the header has no hover row to reveal it. */
    display: block;
    flex: 0 0 auto;
    position: relative;
}

/*
   The gutter the shell's back button lives in.
   `rt-has-close` is on the root whenever the shell draws one — the side panel draws
   its own header instead, so it gets no gutter and loses no width. Which row needs the
   gutter depends on which row is FIRST: normally the tab strip, but `rt-reserve-close`
   means the strip is hidden (a single pinned table) and the view bar has moved up into
   its place.
*/
/* ── The table surface inside the room's app panel ─────────────────────────────
   The surface fills the panel from the top edge down. Two things were in the way:

   1. `room-table-list` is a custom TAG, so it is an unknown inline element wrapping
      the surface. `.room-tables { height: 100% }` resolved against that wrapper's
      indefinite height, so the surface could not fill its host.
   2. Anything the shell leaves in the flow above the surface pushed it down the
      panel — which is what left a tall empty band between the panel header and the
      view bar. Taking the surface out of the flow makes the top of the panel the top
      of the table, whatever else is in there.

   Scoped to the panel: the popup and page surfaces keep their normal flow layout. */
#app .room-app-panel-component .panel-app-view .popup-room-tables > .page-content {
    position: relative;
    overflow: hidden;
}
#app .room-app-panel-component .panel-app-view .popup-room-tables > .page-content > room-table-list {
    position: absolute;
    top: 0; right: 0; bottom: 0; left: 0;
    display: flex;
    flex-direction: column;
    min-height: 0;
}
#app .room-app-panel-component .panel-app-view .popup-room-tables .room-tables {
    flex: 1 1 auto;
    height: auto;
    min-height: 0;
}

/*
   The gutter the shell's button lives in — on the SAME side the button is.
   The side panel draws its own header, so it gets neither button nor gutter and loses no
   width. Which ROW needs the gutter depends on which row is first: normally the tab
   strip, but rt-reserve-close means the strip is hidden (a single pinned table) and the
   view bar has moved up into its place.
*/
.rt-has-close-left  > .rt-tabs { padding-left: 46px; }
.rt-has-close-right > .rt-tabs { padding-right: 46px; }
.rt-has-close-left.rt-reserve-close  > .rt-viewbar { padding-left: 46px; }
.rt-has-close-right.rt-reserve-close > .rt-viewbar { padding-right: 46px; }

/* The trash view's way out. A banner that only states a fact is a dead end when the
   fact is "your records are in here". */
.rt-trash-acts {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 9px;
    flex-wrap: wrap;
}

/* ── a synced table, said plainly ──────────────────────────────────────── */
/*
   A MIRROR MUST NEVER LOOK LIKE A LOCAL TABLE. Someone who cannot add a column needs
   to see why before they try. Three places say it: the tab wears a link glyph, the view
   bar opens with a chip naming the room it came from, and the source room's Sync button
   carries a count of the rooms holding it.
*/
.rt-tab-synced .rt-tab-link {
    color: var(--f7-theme-color);
    opacity: .75;
    margin-left: -2px;
}
.rt-vb-synced {
    color: var(--f7-theme-color);
    background: color-mix(in srgb, var(--f7-theme-color) 9%, transparent);
}
.rt-vb-synced:hover { background: color-mix(in srgb, var(--f7-theme-color) 15%, transparent); }
.rt-vb-badge {
    font-size: 10px;
    letter-spacing: .04em;
    text-transform: uppercase;
    font-weight: 700;
    padding: 1px 6px;
    border-radius: 20px;
    background: color-mix(in srgb, var(--f7-theme-color) 18%, transparent);
}

/* ── the forward / share dialogs ───────────────────────────────────────── */
.rt-share { font-size: 13.5px; }
.rt-share .rt-in { margin-top: 6px; }

/* The room button, where a native <select> used to be. It keeps .rt-in's field
   shape — this is still "the value you chose" — but behaves as a target: a name on
   the left, a chevron on the right, and the picker on tap. */
.rt-share a.rt-in.rt-share-room {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    text-decoration: none;
    cursor: pointer;
    color: var(--f7-text-color);
}
.rt-share a.rt-in.rt-share-room:hover { border-color: var(--tg-border-strong); }
.rt-share .rt-share-room-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.rt-share .rt-share-room-chev { flex: 0 0 auto; opacity: 0.45; }

/* ── the dialog's own buttons ────────────────────────────────────────────────
   Share and Forward are the primary action of a form, and F7's default renders
   them as one more line of blue text beside Done — nothing says which one commits.
   A light filled pill does, without the weight of a solid primary on what is still
   a dialog. `.dialog-button:not(.dialog-button-bold)` rules in app.css match EVERY
   button in this F7 build (that class does not exist here), so the colour has to be
   restated at a higher specificity — the same trap .compose-draft-dialog documents. */
#app .rt-share-dialog .dialog-buttons {
    padding: 4px 16px 16px;
    gap: 8px;
}
#app .rt-share-dialog .dialog-button {
    height: 38px;
    line-height: 36px;
    font-size: 14px;
    font-weight: 600;
    border-radius: 19px;
    margin: 0;
}
#app .rt-share-dialog .dialog-button:last-child,
.ios #app .rt-share-dialog .dialog-button:last-child,
.dark #app .rt-share-dialog .dialog-button:last-child {
    background: rgba(var(--f7-theme-color-rgb), 0.12);
    color: var(--f7-theme-color);
}
#app .rt-share-dialog .dialog-button:last-child:hover {
    background: rgba(var(--f7-theme-color-rgb), 0.2);
}
.rt-share-opt {
    display: flex;
    align-items: flex-start;
    gap: 9px;
    padding: 8px 10px;
    margin-top: 6px;
    border: 1px solid var(--tg-border);
    border-radius: 9px;
    cursor: pointer;
    background: var(--tg-surface);
}
.rt-share-opt:hover { border-color: var(--tg-border-strong); }
.rt-share-opt input { margin-top: 3px; flex: 0 0 auto; }
.rt-share-opt span { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.rt-share-opt b { font-size: 13px; font-weight: 600; }
.rt-share-opt em {
    font-style: normal;
    font-size: 11.5px;
    color: var(--tg-muted);
    line-height: 1.4;
}
.rt-share-list { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; }
.rt-share-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border: 1px solid var(--tg-border);
    border-radius: 9px;
    background: var(--tg-surface);
}
.rt-share-row-t {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.rt-share-badge {
    flex: 0 0 auto;
    font-size: 10px;
    letter-spacing: .04em;
    text-transform: uppercase;
    font-weight: 700;
    padding: 1px 7px;
    border-radius: 20px;
    color: var(--f7-theme-color);
    background: color-mix(in srgb, var(--f7-theme-color) 12%, transparent);
}
.rt-share-stop { flex: 0 0 auto; font-size: 12px; color: #b00020; }
.rt-share-warn {
    display: flex;
    gap: 9px;
    align-items: flex-start;
    margin-top: 10px;
    padding: 10px 12px;
    border-radius: 9px;
    font-size: 12px;
    line-height: 1.45;
    color: var(--f7-text-color);
    background: color-mix(in srgb, #e8930c 12%, transparent);
    border: 1px solid color-mix(in srgb, #e8930c 30%, transparent);
}
.rt-share-warn i { color: #e8930c; flex: 0 0 auto; }

/* ── search: an icon until it is used ──────────────────────────────────── */
/*
   The open box cost ~110px of a strip that now holds Fields, Filter, Group, Sort,
   undo, redo, Form, Share, Forward and Sync. Collapsed it is just the magnifier;
   the input is still in the DOM (so its value and its handlers survive) and simply
   has no width until the row opens.
*/
/*
   SEARCH STAYS PUT WHEN THE BAR OVERFLOWS.

   The view bar scrolls horizontally with its scrollbar hidden, and adding Forward and
   Sync pushed the search chip past the right edge — present, scrollable, and invisible,
   which read as "the search icon is missing". Sticky to the right edge keeps it where it
   belongs no matter how many controls precede it; the background stops the scrolling
   content showing through underneath.
*/
.rt-search {
    cursor: text;
    position: sticky;
    right: 0;
    z-index: 2;
    background: var(--tg-surface);
    /* A soft edge so a control scrolling under it does not appear to be cut off. */
    box-shadow: -10px 0 8px -8px var(--tg-surface);
}
.rt-search .rt-search-in {
    width: 0;
    padding: 0;
    border: 0;
    background: transparent;
    font-size: 12.5px;
    box-shadow: none;
    opacity: 0;
    transition: width .16s ease, opacity .16s ease;
}
.rt-search.rt-search-open .rt-search-in {
    width: 132px;
    padding: 0 4px;
    opacity: 1;
}
.rt-search.rt-search-open { background: var(--tg-hover); }

/* ── the record count, under the rows ──────────────────────────────────── */
.rt-foot {
    flex: 0 0 auto;
    display: flex;
    justify-content: flex-end;
    padding: 7px 14px 9px;
    border-top: 1px solid var(--tg-line);
    background: var(--tg-surface);
}
/* In the card list it rides on the Add record row instead, pushed to the far end. */
.rt-card-add .rt-count { margin-left: auto; }

/* ── the bulk bar, when rows are ticked ─────────────────────────────────── */
/*
   Sits between the view bar and the grid rather than floating over the rows: a bar
   that covers data while you are deciding what to do with it is the wrong bar.
*/
.rt-bulk {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 12px;
    background: color-mix(in srgb, var(--f7-theme-color) 8%, var(--tg-surface));
    border-bottom: 1px solid var(--tg-border);
    flex: 0 0 auto;
}
.rt-bulk-n {
    font-size: 12.5px;
    font-weight: 600;
    color: var(--f7-theme-color);
}

/* ── group headers ──────────────────────────────────────────────────────── */

.tg-row-group { z-index: 1; }
.tg-group {
    display: flex;
    align-items: center;
    gap: 8px;
    height: 100%;
    width: 100%;
    padding: 0 12px;
    background: color-mix(in srgb, var(--f7-text-color) 6%, var(--tg-cell-bg));
    border-bottom: 1px solid var(--tg-border);
    position: sticky;
    left: 0;
    cursor: pointer;
    font-size: 12.5px;
}
.tg-gtoggle { display: flex; align-items: center; color: var(--tg-muted); }
.tg-glabel { font-weight: 600; display: flex; align-items: center; }
.tg-gempty { color: var(--tg-muted); font-style: italic; font-weight: 500; }
.tg-gcount {
    font-size: 11px;
    color: var(--tg-muted);
    font-variant-numeric: tabular-nums;
    padding: 1px 7px;
    border-radius: 8px;
    background: color-mix(in srgb, var(--f7-text-color) 8%, transparent);
}

/* ── the add row ────────────────────────────────────────────────────────── */

.tg-addrow {
    display: flex;
    align-items: center;
    gap: 7px;
    height: 100%;
    padding: 0 12px;
    position: sticky;
    left: 0;
    color: var(--tg-muted);
    font-size: 12.5px;
    cursor: pointer;
    border-bottom: 1px solid var(--tg-border);
    background: var(--tg-cell-bg);
    width: 320px;
}
.tg-addrow:hover { color: var(--f7-theme-color); background: var(--tg-hover); }

/* ── selection overlay ──────────────────────────────────────────────────── */

.tg-overlay { position: absolute; inset: 0; pointer-events: none; z-index: 3; }
.tg-selbox {
    position: absolute;
    border: 2px solid var(--f7-theme-color);
    border-radius: 3px;
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--f7-theme-color) 20%, transparent);
    pointer-events: none;
}
.tg-selrange { background: color-mix(in srgb, var(--f7-theme-color) 8%, transparent); }
/* While a cell is being edited the editor draws the ring, so the selection box would
   be a second outline a hair outside it. The fill handle stays visible. */
.tg-editing .tg-selbox { display: none; }
.tg-sellocked { border-color: var(--tg-border-strong); border-style: dashed; box-shadow: none; }
.tg-fill {
    position: absolute;
    width: 8px; height: 8px;
    background: var(--f7-theme-color);
    border: 1.5px solid var(--tg-cell-bg);
    border-radius: 2px;
    cursor: crosshair;
    pointer-events: auto;
    z-index: 4;
}
/* ── live collaboration ─────────────────────────────────────────────────────
   Presence has its own layer above the selection overlay. Nothing in here is ever
   written into a row: rows are cached by rev, and a collaborator's cursor must not
   be able to evict that cache.

   Every mark is tinted from an inline `--pc`-style colour set by the grid at paint
   time (the person's own colour), so the rules below only ever describe SHAPE.
   ───────────────────────────────────────────────────────────────────────────── */

.tg-presence {
    position: absolute;
    inset: 0;
    pointer-events: none;
    /* Above the selection ring, below the editor: somebody else's cursor must never
       cover the cell you are typing in. */
    z-index: 4;
}

/* The row bar — the "who is working on which row" signal, and the one mark that
   survives their column being hidden or scrolled away. */
.tg-pbar {
    position: absolute;
    width: 3px;
    border-radius: 0 2px 2px 0;
    opacity: .9;
}

/* On a COLLAPSED group header, where the row itself is not rendered at all. Softer,
   because it means "somebody is in here" rather than "somebody is on this row". */
.tg-pbar-group { border-radius: 0 2px 2px 0; opacity: .7; }

/* The ring around the exact cell. */
.tg-pcell {
    position: absolute;
    border: 2px solid;
    border-radius: 3px;
    box-sizing: border-box;
    animation: tg-pfade .18s ease-out;
}

/* Their initial, in the corner of that cell. Deliberately initials rather than a
   photo: the Tables surface renders every person as an initial (see avatarHtml in
   table.fields.js), and a 16px avatar on a 34px row is a smudge. */
.tg-pdot {
    position: absolute;
    top: -8px;
    right: -6px;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    color: #fff;
    font-size: 9.5px;
    font-weight: 700;
    line-height: 16px;
    text-align: center;
    letter-spacing: .2px;
    box-shadow: 0 0 0 1.5px var(--tg-cell-bg);
}

/* The ghost of a cell somebody is typing in, before they commit it. Opaque, because
   it has to cover the committed value underneath — a half-typed word laid over the
   old one is unreadable. */
.tg-pghost {
    position: absolute;
    box-sizing: border-box;
    border: 2px solid;
    border-radius: 3px;
    background: var(--tg-cell-bg);
    display: flex;
    align-items: center;
    padding: 0 7px;
    overflow: hidden;
}
.tg-pghost-t {
    font-size: 13px;
    line-height: 1.3;
    color: var(--f7-text-color);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    /* It is not committed yet, and it should not read as though it were. */
    opacity: .78;
}
.tg-pcaret {
    display: inline-block;
    width: 1.5px;
    height: 13px;
    margin-left: 1px;
    vertical-align: -2px;
    animation: tg-pblink 1.1s steps(1) infinite;
}

/* The name tag over a ghost. Only drafts get a name: a ring plus an initial is
   enough for a cursor, but text appearing in a cell needs to say whose it is. */
.tg-ptag {
    position: absolute;
    top: -17px;
    left: -2px;
    height: 16px;
    padding: 0 6px;
    border-radius: 4px 4px 4px 0;
    color: #fff;
    font-size: 10.5px;
    font-weight: 600;
    line-height: 16px;
    white-space: nowrap;
    max-width: 160px;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* On the first row there is no room above, and a tag clipped by the header is worse
   than a tag in the wrong place. */
.tg-ptag-below { top: auto; bottom: -17px; border-radius: 0 4px 4px 4px; }

/* Opening a cell somebody else is already typing in. A warning, not a lock — see
   openEditor in table.grid.js. */
.tg-pediting {
    position: absolute;
    top: -18px;
    left: -2px;
    height: 17px;
    padding: 0 7px;
    border-radius: 4px 4px 4px 0;
    color: #fff;
    font-size: 10.5px;
    font-weight: 600;
    line-height: 17px;
    white-space: nowrap;
    pointer-events: none;
}

@keyframes tg-pfade { from { opacity: 0; } to { opacity: 1; } }
@keyframes tg-pblink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }

/* ── the facepile: who is connected to this table ───────────────────────────
   Connection state, not activity — everybody with the table open is here whether
   they are typing or reading. It sits in the view bar, which is the only chrome on
   screen in every view and at every scroll position.
   ───────────────────────────────────────────────────────────────────────────── */

.rt-facepile {
    display: inline-flex;
    align-items: center;
    flex: 0 0 auto;
    padding: 0 4px 0 8px;
}
.rt-facepile:empty { display: none; }

.rt-face {
    position: relative;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    /* They overlap, as a stack of faces should; the ring keeps them separable. */
    margin-left: -6px;
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    line-height: 24px;
    text-align: center;
    /* The ring is the person's colour and the gap is the surface, so the pile reads
       on any background the bar happens to have. */
    box-shadow: 0 0 0 2px var(--tg-surface);
    cursor: default;
    transition: transform .12s ease-out;
}
.rt-face:first-child { margin-left: 0; }
.rt-face:hover { transform: translateY(-1px) scale(1.06); z-index: 2; }
/* Somebody on a row can be jumped to, so they get the affordance. */
.rt-face-at { cursor: pointer; }
/* A quiet dot for somebody who is connected but has not put a cursor anywhere. */
.rt-face-idle { opacity: .62; }

.rt-face-more {
    background: color-mix(in srgb, var(--f7-text-color) 12%, transparent);
    color: var(--f7-text-color);
    font-size: 10.5px;
    cursor: pointer;
}

/* The overflow popover: names, and what each person is looking at. */
.rt-face-pop { width: 232px; }
.rt-face-pop .rt-fp-h {
    padding: 9px 12px 5px;
    font-size: 11px;
    font-weight: 600;
    color: var(--tg-muted);
    text-transform: uppercase;
    letter-spacing: .04em;
}
.rt-face-pop .rt-fp-i {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 7px 12px;
    font-size: 13px;
    cursor: pointer;
}
.rt-face-pop .rt-fp-i:hover { background: var(--tg-hover); }
.rt-face-pop .rt-fp-i:last-child { margin-bottom: 6px; }
.rt-face-pop .rt-fp-av {
    width: 22px; height: 22px; border-radius: 50%;
    color: #fff; font-size: 10px; font-weight: 700; line-height: 22px; text-align: center;
    flex: 0 0 auto;
}
.rt-face-pop .rt-fp-n { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rt-face-pop .rt-fp-w { font-size: 11px; color: var(--tg-muted); flex: 0 0 auto; }

/* ── per-card presence: kanban, calendar, gallery ───────────────────────────
   Those views show a record, never a cell, so they take the ring and the faces and
   leave the cell cursor and the ghost to the grid.
   ───────────────────────────────────────────────────────────────────────────── */

.rt-card-live {
    /* Painted with an inset shadow rather than a border: a border would move the card
       by 2px the instant somebody selected it, and a whole board twitching as people
       click around is worse than no signal at all. */
    box-shadow: inset 0 0 0 2px var(--rt-live-color, var(--f7-theme-color)),
                0 1px 3px rgba(0, 0, 0, .1) !important;
}
/* The faces hang off the top edge of a card, so the card has to be their
   containing block. None of the three was positioned before — a card is a plain
   block — and `position: relative` alone moves nothing. */
.rt-kcard, .rt-gcard, .rt-cal-card, .rt-card { position: relative; }

.rt-card-faces {
    position: absolute;
    top: -7px;
    right: 6px;
    display: flex;
    z-index: 2;
    pointer-events: none;
}
.rt-card-faces .rt-cf {
    width: 18px;
    height: 18px;
    border-radius: 50%;
    margin-left: -5px;
    color: #fff;
    font-size: 9px;
    font-weight: 700;
    line-height: 18px;
    text-align: center;
    box-shadow: 0 0 0 1.5px var(--tg-cell-bg);
}
.rt-card-faces .rt-cf:first-child { margin-left: 0; }

/* The card equivalent of tg-rowflash, for the surfaces that have no grid: kanban,
   calendar, gallery and the mobile record list. */
.rt-card-flash {
    animation: rt-card-flash-fade 2.4s ease forwards;
}
@keyframes rt-card-flash-fade {
    0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--f7-theme-color) 45%, transparent),
                       inset 0 0 0 2px var(--f7-theme-color); }
    18%  { box-shadow: 0 0 0 5px color-mix(in srgb, var(--f7-theme-color) 30%, transparent),
                       inset 0 0 0 2px var(--f7-theme-color); }
    70%  { box-shadow: 0 0 0 5px color-mix(in srgb, var(--f7-theme-color) 20%, transparent),
                       inset 0 0 0 2px color-mix(in srgb, var(--f7-theme-color) 55%, transparent); }
    100% { box-shadow: 0 0 0 0 transparent, inset 0 0 0 2px transparent; }
}
@media (prefers-reduced-motion: reduce) {
    .rt-card-flash { animation: none; box-shadow: inset 0 0 0 2px var(--f7-theme-color); }
}

/* The record panel: a face beside the field somebody else is in, and a ghost of what
   they are typing into it. */
.rt-rp-live {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-left: 6px;
    vertical-align: middle;
}
.rt-rp-live .rt-rp-face {
    width: 18px; height: 18px; border-radius: 50%;
    color: #fff; font-size: 9px; font-weight: 700; line-height: 18px; text-align: center;
}
.rt-rp-ghost {
    margin-top: 5px;
    padding: 6px 9px;
    border-radius: 6px;
    border: 1px dashed;
    font-size: 13px;
    line-height: 1.4;
    color: var(--f7-text-color);
    opacity: .85;
    white-space: pre-wrap;
    word-break: break-word;
}
.rt-rp-ghost .rt-rp-who {
    display: block;
    font-size: 10.5px;
    font-weight: 600;
    margin-bottom: 2px;
    opacity: .9;
}
/* The header chip when more than one person has the same record open. */
.rt-rp-here {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 9px 3px 4px;
    border-radius: 20px;
    background: color-mix(in srgb, var(--f7-text-color) 6%, transparent);
    font-size: 11.5px;
    color: var(--tg-muted);
}

/* ── date fields ────────────────────────────────────────────────────────────
   The native input is KEPT for its dd/mm/yyyy segments — typing a date is often
   quicker than finding it, and no text box can offer that. What is removed is the
   browser's own calendar: a different one per browser, ignoring the app's theme.
   Hiding its glyph is what makes it unreachable; F7's calendar is opened in its place,
   and it does not take focus, so the segments stay live while it is on screen.

   F7 sizes the popover itself, exactly as it does for every other date in the app, so
   there is nothing to restyle there. `tg-cal` lands on the .calendar element (F7 puts
   cssClass there, NOT on .popover) and is carried so the room's `color-<name>` can ride
   in beside it — which is the whole reason that option exists.
   ───────────────────────────────────────────────────────────────────────────── */
.tg-in-date::-webkit-calendar-picker-indicator,
.rt-date::-webkit-calendar-picker-indicator {
    display: none;
    -webkit-appearance: none;
}
/* Firefox's equivalent. Harmless where it does not exist. */
.tg-in-date::-moz-calendar-picker-indicator,
.rt-date::-moz-calendar-picker-indicator { display: none; }
/* The field is the affordance now, so it should look like one. */
.tg-in-date, .rt-date { cursor: pointer; }

/* "That row — there." Painted for a couple of seconds when the grid is opened from
   somewhere that named a row: a chat card, a notification, a link. Same shape as the
   Links app's rbl-highlight, so arriving at a row feels the same wherever you came
   from. */
.tg-rowflash {
    position: absolute;
    pointer-events: none;
    z-index: 3;
    border-radius: 4px;
    box-sizing: border-box;
    animation: tg-rowflash-fade 2.4s ease forwards;
}
@keyframes tg-rowflash-fade {
    0%   { background: color-mix(in srgb, var(--f7-theme-color) 26%, transparent);
           box-shadow: inset 0 0 0 2px var(--f7-theme-color); }
    18%  { background: color-mix(in srgb, var(--f7-theme-color) 20%, transparent);
           box-shadow: inset 0 0 0 2px var(--f7-theme-color); }
    70%  { background: color-mix(in srgb, var(--f7-theme-color) 13%, transparent);
           box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--f7-theme-color) 55%, transparent); }
    100% { background: transparent; box-shadow: inset 0 0 0 2px transparent; }
}
@media (prefers-reduced-motion: reduce) {
    /* No pulse, but the row must still be findable — so it holds a steady tint for the
       same duration rather than disappearing entirely. */
    .tg-rowflash {
        animation: none;
        background: color-mix(in srgb, var(--f7-theme-color) 16%, transparent);
        box-shadow: inset 0 0 0 2px var(--f7-theme-color);
    }
}

.tg-lockflash {
    position: absolute;
    background: color-mix(in srgb, #e8930c 22%, transparent);
    animation: tg-lockpulse .5s ease-out;
    pointer-events: none;
}
@keyframes tg-lockpulse { from { opacity: 1; } to { opacity: 0; } }

/* ── the single reused editor ───────────────────────────────────────────── */

.tg-editor {
    position: absolute;
    z-index: 10;
    background: var(--tg-cell-bg);
    border: 2px solid var(--f7-theme-color);
    border-radius: 4px;
    box-shadow: 0 6px 22px -6px rgba(0, 0, 0, .28);
    box-sizing: border-box;
    display: flex;
    align-items: stretch;
    max-width: 460px;
}
.tg-in {
    width: 100%;
    border: 0;
    outline: none;
    background: transparent;
    font-size: 13px;
    font-family: inherit;
    color: var(--f7-text-color);
    padding: 0 7px;
    box-sizing: border-box;
    resize: none;
}
.tg-editor-textarea { min-height: 76px; }
.tg-editor-textarea .tg-in { padding: 6px 7px; line-height: 1.45; }
.tg-in-num { text-align: right; }

.tg-in-opts { max-height: 260px; overflow-y: auto; padding: 5px; min-width: 190px; }
.tg-in-opt {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 8px;
    border-radius: 7px;
    cursor: pointer;
    font-size: 13px;
}
.tg-in-opt:hover { background: var(--tg-hover); }
.tg-in-opt i { margin-left: auto; color: var(--f7-theme-color); }
.tg-in-on { background: color-mix(in srgb, var(--f7-theme-color) 8%, transparent); }
/* Where the ARROW KEYS are. Distinct from :hover (the mouse) and from .tg-in-on (what
   is already chosen) — all three can be different options at once, and a picker you can
   only use with a mouse is not finished. */
.tg-in-opt.tg-in-hi {
    background: color-mix(in srgb, var(--f7-theme-color) 14%, transparent);
    box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--f7-theme-color) 45%, transparent);
}
.tg-in-clear { color: var(--tg-muted); font-size: 12px; }
.tg-in-none { padding: 10px; font-size: 12px; color: var(--tg-muted); }
.tg-in-check { padding: 0 10px; display: flex; align-items: center; font-size: 13px; cursor: pointer; }
.tg-in-rating { display: flex; align-items: center; gap: 3px; padding: 0 8px; }
.tg-in-rating .tg-star { color: color-mix(in srgb, var(--f7-text-color) 25%, transparent); cursor: pointer; }
.tg-in-rating .tg-star-on { color: #f0b429; }
.tg-in-defer { padding: 6px 10px; font-size: 12.5px; color: var(--tg-muted); display: flex; align-items: center; gap: 8px; }
.tg-in-open {
    border: 1px solid var(--f7-theme-color);
    color: var(--f7-theme-color);
    background: transparent;
    border-radius: 7px;
    padding: 3px 10px;
    font-size: 12px;
    cursor: pointer;
    width: auto;
}

/* ── cell value styling ─────────────────────────────────────────────────── */

.tg-chip {
    display: inline-flex;
    align-items: center;
    max-width: 100%;
    padding: 2px 9px;
    border-radius: 11px;
    font-size: 12px;
    font-weight: 500;
    line-height: 1.5;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    margin-right: 3px;
}
/* Chip colours are the app's own palette, so a chip in a table matches a chip
   anywhere else in the product. */
.tg-chip-blue       { background: color-mix(in srgb, #2f6fed 18%, transparent); color: color-mix(in srgb, #2f6fed 92%, var(--f7-text-color)); }
.tg-chip-teal       { background: color-mix(in srgb, #12a5a5 18%, transparent); color: color-mix(in srgb, #12a5a5 92%, var(--f7-text-color)); }
.tg-chip-green      { background: color-mix(in srgb, #21a366 18%, transparent); color: color-mix(in srgb, #21a366 92%, var(--f7-text-color)); }
.tg-chip-lime       { background: color-mix(in srgb, #85b408 18%, transparent); color: color-mix(in srgb, #85b408 92%, var(--f7-text-color)); }
.tg-chip-yellow     { background: color-mix(in srgb, #e0a30b 20%, transparent); color: color-mix(in srgb, #a37407 92%, var(--f7-text-color)); }
.tg-chip-orange     { background: color-mix(in srgb, #e8730c 18%, transparent); color: color-mix(in srgb, #e8730c 92%, var(--f7-text-color)); }
.tg-chip-red        { background: color-mix(in srgb, #dc3545 18%, transparent); color: color-mix(in srgb, #dc3545 92%, var(--f7-text-color)); }
.tg-chip-pink       { background: color-mix(in srgb, #d6338d 18%, transparent); color: color-mix(in srgb, #d6338d 92%, var(--f7-text-color)); }
.tg-chip-purple     { background: color-mix(in srgb, #8b46d1 18%, transparent); color: color-mix(in srgb, #8b46d1 92%, var(--f7-text-color)); }
.tg-chip-deeppurple { background: color-mix(in srgb, #6634c9 18%, transparent); color: color-mix(in srgb, #6634c9 92%, var(--f7-text-color)); }
.tg-chip-gray       { background: color-mix(in srgb, var(--f7-text-color) 10%, transparent); color: var(--tg-muted); }

.tg-num { font-variant-numeric: tabular-nums; }
.tg-muted { color: var(--tg-muted); }
.tg-long { display: block; overflow: hidden; text-overflow: ellipsis; }
.tg-link { color: var(--f7-theme-color); text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tg-link:hover { text-decoration: underline; }

.tg-date { white-space: nowrap; }
/* The same urgency cue a task carries, so a due date reads the same everywhere. */
.tg-date-past  { color: #dc3545; font-weight: 500; }
.tg-date-today { color: var(--f7-theme-color); font-weight: 600; }
.tg-date-soon  { color: #e8730c; }

.tg-check {
    width: 17px; height: 17px;
    border: 1.5px solid var(--tg-border-strong);
    border-radius: 4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background .12s, border-color .12s;
}
.tg-check-on { background: var(--f7-theme-color); border-color: var(--f7-theme-color); color: #fff; }

.tg-rating { display: inline-flex; gap: 1px; }
.tg-star { color: color-mix(in srgb, var(--f7-text-color) 20%, transparent); }
.tg-star-on { color: #f0b429; }

.tg-pct { display: inline-flex; align-items: center; position: relative; min-width: 54px; justify-content: flex-end; }
.tg-pct-bar {
    position: absolute;
    left: 0; top: 50%;
    transform: translateY(-50%);
    height: 4px;
    border-radius: 2px;
    background: color-mix(in srgb, var(--f7-theme-color) 28%, transparent);
    max-width: 100%;
}
.tg-pct .tg-num { position: relative; z-index: 1; }

.tg-user {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    max-width: 100%;
    padding: 1px 8px 1px 2px;
    border-radius: 11px;
    font-size: 12px;
    margin-right: 3px;
    overflow: hidden;
}
.tg-user-av {
    width: 18px; height: 18px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 10px;
    font-weight: 700;
    background: color-mix(in srgb, var(--f7-text-color) 14%, transparent);
    flex: 0 0 auto;
}
.tg-user-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* An address typed into a Qik-user column that has not been matched to an account yet —
   between the keystroke and the save. Dashed, so a cell that is waiting does not read
   the same as one that resolved. */
.tg-user-pending { border: 1px dashed var(--tg-border); background: transparent; }
.tg-user-pending .tg-user-av { background: transparent; color: var(--tg-muted); border: 1px solid var(--tg-border); }

.tg-attach {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 12px;
    color: var(--tg-muted);
    padding: 1px 7px;
    border-radius: 8px;
    background: color-mix(in srgb, var(--f7-text-color) 7%, transparent);
}
.tg-ai { color: var(--f7-text-color); }
.tg-ai-pending { color: color-mix(in srgb, var(--f7-theme-color) 60%, transparent); }

/* ── empty state ────────────────────────────────────────────────────────── */

.tg-empty {
    position: absolute;
    inset: var(--tg-header-h) 0 0 0;
    display: flex;
    align-items: center;
    justify-content: center;
    /* The GRID's empty state is a pass-through overlay: "no records match this
       filter" must not swallow clicks meant for the rows or the add-row affordance
       underneath it. */
    pointer-events: none;
}
/*
   But the APP's empty state reuses this class for "no tables in this room yet", and
   that one has a real button in it. pointer-events has to come back for the content,
   or the button is visible, hover-less and dead — which is exactly how it behaved.
   Put it on the inner box rather than the overlay, so the overlay keeps passing
   clicks through everywhere it has nothing to click.
*/
.tg-empty-in { text-align: center; color: var(--tg-muted); pointer-events: auto; }
.tg-empty-in i { opacity: .35; }
.tg-empty-t { font-size: 15px; font-weight: 600; margin-top: 10px; color: var(--f7-text-color); }
.tg-empty-s { font-size: 13px; margin-top: 4px; }

/* ── loading ────────────────────────────────────────────────────────────── */

.rt-loading {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: color-mix(in srgb, var(--f7-page-bg-color) 70%, transparent);
    z-index: 12;
}
.rt-skeleton { width: 100%; padding: var(--tg-header-h) 0 0; }
.rt-skel-row {
    height: var(--tg-row-h);
    border-bottom: 1px solid var(--tg-border);
    background: linear-gradient(90deg,
        color-mix(in srgb, var(--f7-text-color) 4%, transparent) 25%,
        color-mix(in srgb, var(--f7-text-color) 8%, transparent) 37%,
        color-mix(in srgb, var(--f7-text-color) 4%, transparent) 63%);
    background-size: 400% 100%;
    animation: rt-shimmer 1.4s ease-in-out infinite;
}
@keyframes rt-shimmer { from { background-position: 100% 0; } to { background-position: 0 0; } }

/* ── field menu + editor popovers ───────────────────────────────────────── */

.rt-fmenu { width: 250px; }
.rt-mi {
    display: flex;
    align-items: center;
    gap: 11px;
    /* Full width so the hover band reaches both edges of the popover — a highlight
       that stops short of the edge reads as a rendering fault. */
    width: 100%;
    box-sizing: border-box;
    padding: 8px 14px;
    font-size: 13.5px;
    cursor: pointer;
    color: var(--f7-text-color);
}
.rt-mi:hover { background: var(--tg-hover); }
.rt-mi i { color: var(--tg-muted); flex: 0 0 auto; }
.rt-mi-danger, .rt-mi-danger i { color: #dc3545; }
.rt-mi-off { opacity: .4; pointer-events: none; }
.rt-fmenu .rt-msep { height: 1px; background: var(--tg-border); margin: 5px 0; }
/* The table whose form is already open — offered, but marked, so picking it again is
   an obvious no-op rather than a mystery. */
.rt-mi-on {
    color: var(--f7-theme-color);
    font-weight: 600;
}
/* THE TABLE IS A HEADING NOW, not a row: the picker lists FORMS, grouped by the table
   each writes into. Not clickable, so it never competes with the form rows under it. */
.rt-mi-group {
    display: flex; align-items: center; gap: 9px;
    padding: 9px 14px 3px;
    font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
    color: var(--tg-muted);
}
.rt-mi-group i { color: var(--tg-muted); flex: 0 0 auto; }
/* Indented to the width of the group's icon plus its gap, so a form sits under the table's
   NAME rather than under its icon — which is what makes the grouping readable at a glance. */
.rt-mi-sub { padding-left: 32px; }
.rt-mi-muted { color: var(--tg-muted); }
.rt-mi-badge {
    margin-left: auto;
    font-size: 11px;
    padding: 1px 7px;
    border-radius: 8px;
    background: color-mix(in srgb, #e8930c 20%, transparent);
    color: #a76a06;
}

/* ── the field editor ───────────────────────────────────────────────────────
   BOUNDED HEIGHT IS WHAT FIXES THE POSITION, and that was not obvious.

   The editor reported "opening in the top-left corner". It was not a broken anchor: with
   padding on the container and no cap, a link or formula column rendered ~690px tall, and
   F7 positions a popover by trying above the anchor, then below, then clamping — so on any
   normal window there was nowhere to put it and it clamped to the top edge, drifting left
   as well. Capping the height and scrolling the middle means it fits beside its column
   again, which is where it always claimed to be.

   The padding moved off the container and onto the three regions, so the header and the
   footer can sit still while only the middle scrolls. */
.rt-feditor {
    width: 340px;
    max-width: calc(100vw - 24px);
    /* THE CAP COMES FROM THE ANCHOR, not from the viewport.
       A fixed height cannot satisfy both complaints this went through: short enough for
       F7 to fit beside the column, long enough not to clip the link and formula panes
       mid-sentence. openEditor measures the space above and below the header cell and
       sets --rt-fe-max-height; the fallback here is for any caller that does not. */
    max-height: var(--rt-fe-max-height, min(72vh, 620px));
    display: flex;
    flex-direction: column;
}
.rt-fe-head { flex: 0 0 auto; padding: 14px 16px 12px; }
.rt-fe-scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 0 16px;
}
.rt-feditor .rt-fe-actions { flex: 0 0 auto; padding: 12px 16px 14px; }

/* A hairline under the header and over the footer, so a mid-scroll pane does not look
   like text sliding under a shadowless edge. */
.rt-fe-head { border-bottom: 1px solid var(--tg-border); }
.rt-feditor .rt-fe-actions { border-top: 1px solid var(--tg-border); }

/* ── sections ─────────────────────────────────────────────────────────────── */
.rt-fe-sec { padding: 14px 0; border-bottom: 1px solid var(--tg-border); }
.rt-fe-sec:last-child { border-bottom: 0; }
.rt-fe-sech { font-size: 13px; font-weight: 650; }
.rt-fe-secs {
    font-size: 11.5px;
    color: var(--tg-muted);
    margin: 2px 0 10px;
    line-height: 1.45;
}

/* ── the type row, which replaced a 28-item native select ─────────────────── */
.rt-fe-typerow {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 10px;
    padding: 9px 11px;
    border: 1px solid var(--tg-border);
    border-radius: 10px;
    cursor: pointer;
}
.rt-fe-typerow:hover { background: var(--tg-hover); }
.rt-fe-typerow-off { cursor: default; opacity: 0.75; }
.rt-fe-typerow-off:hover { background: transparent; }
.rt-fe-typeicon { color: var(--tg-muted); flex: 0 0 auto; }
.rt-fe-typetext { min-width: 0; flex: 1; }
.rt-fe-typename { font-size: 13.5px; font-weight: 550; }
/* Two lines at most: the descriptions are a sentence, and letting them run made every
   row a different height, which turns a list into a wall. */
.rt-fe-typehelp {
    font-size: 11.5px;
    color: var(--tg-muted);
    line-height: 1.4;
    margin-top: 1px;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* ── the picker screen ────────────────────────────────────────────────────── */
.rt-fe-pickhead {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    font-weight: 650;
    margin-bottom: 10px;
}
.rt-fe-back {
    display: inline-flex;
    align-items: center;
    margin-left: -6px;
    padding: 2px;
    border-radius: 6px;
    color: var(--tg-muted);
    cursor: pointer;
}
.rt-fe-back:hover { background: var(--tg-hover); color: var(--f7-text-color); }
.rt-fe-grp {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--tg-muted);
    margin: 14px 0 4px;
}
.rt-fe-grp:first-child { margin-top: 6px; }
.rt-fe-trow {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 7px 8px;
    margin: 0 -8px;
    border-radius: 8px;
    cursor: pointer;
}
.rt-fe-trow:hover { background: var(--tg-hover); }
/* The current type is marked, so pressing it again is an obvious no-op rather than a
   change somebody has to undo to check. */
.rt-fe-trow-on { color: var(--f7-theme-color); }
.rt-fe-trow-on .rt-fe-typeicon,
.rt-fe-trow-on .rt-fe-typename { color: var(--f7-theme-color); }
.rt-fe-picker .rt-fe-scroll { padding-bottom: 12px; }
.rt-feditor .rt-fe-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--tg-muted);
    margin: 14px 0 6px;
}
.rt-feditor .rt-fe-label:first-child { margin-top: 0; }
/*
   THE SELECTOR LIST IS LOAD-BEARING.

   Framework7 resets every input it knows about with a TYPE + ATTRIBUTE selector:

       input[type='text'], input[type='date'], … , select, textarea {
           border: none; background: none; border-radius: 0; padding: 0;
       }

   `input[type='date']` scores (0,1,1) and a bare `.rt-in` only (0,1,0), so F7 won
   every input while LOSING on textarea (`textarea` is (0,0,1)). That is the whole
   reason the Notes box looked like a field and the Date box looked like plain text
   on the page. `input.rt-in[type]` scores (0,2,1) and takes them all back.
*/
/*
   AND THE GROW RULE IS A SEPARATE RULE — it is not a fourth selector on this list.

   It used to sit here, between `select.rt-in,` and the declarations, introduced by a
   comment. A comment inside a selector list is legal and simply vanishes, so the list
   silently became `.rt-in, input.rt-in[type], select.rt-in, textarea.rt-in.rt-in-grow`
   with only `max-height/overflow/resize` in it, and the entire box — the padding, the
   border, the radius, the background — applied to `textarea.rt-in` and to nothing
   else. Every input and every select drawn with .rt-in lost to F7's reset and rendered
   as bare browser chrome, while the one textarea beside them looked right. It is below
   this rule now, where a fourth selector cannot be mistaken for the end of the list.
*/
.rt-in,
input.rt-in[type],
select.rt-in,
textarea.rt-in {
    width: 100%;
    box-sizing: border-box;
    padding: 9px 11px;
    font-size: 13.5px;
    font-family: inherit;
    line-height: 1.45;
    color: var(--f7-text-color);
    /* White, not a tinted fill. An input has to read as somewhere you TYPE, and on
       the app's grey page (--f7-page-bg-color is #e2e2e2) a 5%-ink wash made it look
       disabled. The border carries the boundary instead.
       --tg-surface is the app's surface colour: #fff in light, #222 in dark, so this
       stays a raised surface in both rather than hard-coding white. Through the token
       rather than straight to --f7-list-bg-color, because that one is defined in
       app.css and form.qik.mt does not load app.css — reaching for it directly there
       fell through to #fff and left every input glaring white on a dark form. */
    background: var(--tg-surface);
    border: 1px solid var(--tg-border);
    border-radius: 9px;
    outline: none;
    appearance: none;
    /* A field should feel like it lifts off the page by a hair, not float. */
    box-shadow: 0 1px 1px color-mix(in srgb, var(--f7-text-color) 3%, transparent);
    transition: border-color .12s ease, box-shadow .12s ease;
}
/* A long-text box grows with what it holds (rows on first paint, autosize after), and
   scrolls only past the point where it would take over the record. 60vh, not a pixel
   figure: the cap has to mean "most of the panel" on a laptop and on a phone alike.
   `resize: vertical` stays, so anything clipped can still be pulled open by hand. */
textarea.rt-in.rt-in-grow {
    max-height: 60vh;
    overflow-y: auto;
    resize: vertical;
}
.rt-in::placeholder { color: color-mix(in srgb, var(--f7-text-color) 34%, transparent); }
.rt-in:hover:not(:focus):not([disabled]) { border-color: var(--tg-border-strong); }
.rt-in:focus,
input.rt-in[type]:focus,
select.rt-in:focus,
textarea.rt-in:focus {
    border-color: var(--f7-theme-color);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--f7-theme-color) 14%, transparent);
}
.rt-in[disabled] { opacity: .6; cursor: default; }
select.rt-in { appearance: none; background-image: none; cursor: pointer; }
/* A date or a number is a short value; stretching its box to the full column makes
   an empty field look like a missing paragraph. */
input.rt-in[type='date'] { max-width: 210px; }

input.rt-in[type='datetime-local'] { max-width: 260px; }
/* A long-text box grows with what it holds (rows on first paint, autosize after), and
   scrolls only past the point where it would take over the record. 60vh, not a pixel
   figure: the cap has to mean "most of the panel" on a laptop and on a phone alike.
   `resize: vertical` stays, so anything clipped can still be pulled open by hand. */
textarea.rt-in.rt-in-grow {
    max-height: 60vh;
    overflow-y: auto;
    resize: vertical;
}
input.rt-in[type='time'] { max-width: 150px; }
.rt-fe-help { font-size: 12px; color: var(--tg-muted); margin-top: 6px; line-height: 1.45; }

.rt-fe-opts { margin-top: 8px; display: flex; flex-direction: column; gap: 6px; }
.rt-fe-opt { display: flex; align-items: center; gap: 7px; }
.rt-fe-opt .rt-in { flex: 1 1 auto; }
.rt-fe-swatch {
    width: 22px; height: 22px;
    border-radius: 6px;
    cursor: pointer;
    flex: 0 0 auto;
    border: 1px solid var(--tg-border);
}
.rt-fe-del { color: var(--tg-muted); cursor: pointer; display: flex; align-items: center; padding: 2px; }
.rt-fe-del:hover { color: #dc3545; }
.rt-fe-add {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    color: var(--f7-theme-color);
    cursor: pointer;
    margin-top: 4px;
    width: auto;
}
.rt-fe-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 18px; }
.rt-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 32px;
    padding: 0 14px;
    border-radius: 6px;
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    /* A secondary button is a WHITE surface with a hairline, not a transparent
       outline: on a light panel a transparent button reads as a label. */
    border: 1px solid var(--tg-border);
    background: var(--tg-surface);
    color: var(--f7-text-color);
    width: auto;
    transition: background .12s, border-color .12s, box-shadow .12s;
}
.rt-btn:hover { background: var(--tg-hover); border-color: var(--tg-border-strong); }
.rt-btn:active { background: color-mix(in srgb, var(--f7-text-color) 7%, transparent); }
.rt-btn-primary {
    background: var(--f7-theme-color);
    border-color: color-mix(in srgb, #000 8%, var(--f7-theme-color));
    color: #fff;
    /* The one raised element on the panel, so the eye lands on it last. */
    box-shadow: 0 1px 2px color-mix(in srgb, var(--f7-theme-color) 30%, transparent);
}
.rt-btn-primary:hover { filter: brightness(1.05); background: var(--f7-theme-color); }
.rt-btn-primary:active { filter: brightness(.97); box-shadow: none; }
.rt-btn-quiet { background: transparent; border-color: transparent; }
.rt-btn-quiet:hover { background: var(--tg-hover); border-color: transparent; }
.rt-btn-danger { color: #dc3545; border-color: color-mix(in srgb, #dc3545 35%, transparent); }
.rt-btn[disabled] { opacity: .5; pointer-events: none; }

/* ── filter / sort / group panels ───────────────────────────────────────── */

/*
   F7 gives .popover a fixed 320px width, so a wider panel inside one is simply
   clipped — which is how the Sort panel lost the end of its sentence. These popovers
   size to their content instead, bounded by the viewport.
*/
.popover.popover-table { width: auto; max-width: min(440px, calc(100vw - 24px)); }
.popover.popover-table .popover-inner { width: auto; }

.rt-panel {
    width: min(400px, calc(100vw - 40px));
    padding: 14px;
    max-height: 70vh;
    overflow-y: auto;
    box-sizing: border-box;
}
.rt-panel-t {
    font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .06em;
    color: var(--tg-muted); margin-bottom: 10px;
}
.rt-cond { display: flex; align-items: center; gap: 6px; margin-bottom: 8px; }
.rt-cond .rt-in { padding: 7px 9px; font-size: 12.5px; }
.rt-cond-join { width: 62px; flex: 0 0 auto; font-size: 12px; color: var(--tg-muted); }
.rt-cond-field { flex: 1 1 34%; min-width: 0; }
.rt-cond-op { flex: 1 1 30%; min-width: 0; }
.rt-cond-val { flex: 1 1 36%; min-width: 0; }
.rt-cond-del { color: var(--tg-muted); cursor: pointer; display: flex; padding: 3px; flex: 0 0 auto; }
.rt-cond-del:hover { color: #dc3545; }
.rt-panel-empty {
    font-size: 12.5px; color: var(--tg-muted); padding: 6px 0 12px;
    /* It is a sentence, so it wraps. */
    white-space: normal; line-height: 1.5;
}

.rt-hidelist { max-height: 320px; overflow-y: auto; }
.rt-hide-row {
    display: flex; align-items: center; gap: 10px;
    padding: 7px 4px; font-size: 13px; cursor: pointer; border-radius: 7px;
}
.rt-hide-row:hover { background: var(--tg-hover); }
.rt-hide-row i.rt-hide-icon { color: var(--tg-muted); }
/* A hidden column reads as hidden at a glance — the eye_slash glyph alone is a small
   difference to spot in a list of ten. */
.rt-hide-row.rt-hide-off { color: var(--tg-muted); }
.rt-hide-row.rt-hide-off span { opacity: .6; }
.rt-hide-row.rt-hide-off i.rt-hide-icon { color: var(--f7-theme-color); }
.rt-hide-row.rt-hide-locked { opacity: .5; pointer-events: none; }
.rt-hide-actions { display: flex; gap: 8px; margin-top: 10px; }

/* ── the form editor's navbar ───────────────────────────────────────────── */
/*
   `.navbar-inner .left` / `.right` are flex containers, and a `.button-round` inside
   one has no intrinsic width to stop it — so it stretched into a white slab running
   half the navbar. Buttons hug their content here, as everywhere else in the app.
*/
.rtf-nav {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}
.rtf-nav .rtf-nav-btn {
    width: auto;
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 0 12px;
    max-width: 220px;
    overflow: hidden;
}
/* The table's name is the one thing here that can be long. */
.rtf-nav .rtf-nav-btn span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.rtf-nav .rtf-nav-btn i { flex: 0 0 auto; }
.rtf-nav-x {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 32px;
}

/* ── a shared table, on its own page ───────────────────────────────────── */
/*
   One table under a real navbar. The page-content is a flex column so the view bar
   pins and only the grid scrolls — a table that scrolls its own chrome away leaves
   no way to change the filter you are looking through.
*/
.page-shared-table .shared-table-content {
    padding: 0;
    display: flex;
    flex-direction: column;
    /* The navbar overlays the page in F7, so the offset it publishes is what keeps the
       first row out from under it. */
    padding-top: var(--f7-page-navbar-offset, 0px);
}
.page-shared-table .shared-table-content > .room-tables,
.page-shared-table .shared-table-content > .room-tables > .rt-body {
    flex: 1 1 auto;
    min-height: 0;
}
.shared-table-mid {
    flex: 1 1 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 30px;
}
.page-shared-table .navbar .subtitle {
    font-size: 12px;
    color: var(--tg-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ── record modal ───────────────────────────────────────────────────────── */

/*
   A record is a DOCUMENT, so it is laid out like one: a white sheet, a real title,
   and a label column that lets the eye run straight down the field names. The grey
   app page is what the sheet sits on — that contrast is the whole design, and it is
   why nothing here paints itself grey.
*/
.rt-record {
    display: flex;
    flex-direction: column;
    height: 100%;
    background: var(--tg-surface);
}
.rt-rec-head {
    padding: 22px 28px 16px;
    border-bottom: 1px solid var(--tg-line);
    flex: 0 0 auto;
    background: var(--tg-surface);
}
.rt-rec-title {
    display: block;
    font-size: 22px;
    font-weight: 650;
    letter-spacing: -.01em;
    line-height: 1.3;
    color: var(--f7-text-color);
    border: 1px solid transparent;
    outline: none;
    background: transparent;
    width: 100%;
    font-family: inherit;
    /* Negative margin, so the untouched title sits flush with the meta line below it
       and only grows a box when it is hovered or focused. An always-boxed title reads
       as a form; a title that reveals its box on approach reads as a heading you can
       fix. */
    padding: 5px 9px;
    margin: -5px -9px;
    border-radius: 8px;
    resize: none;
    overflow: hidden;
    transition: background .12s ease, border-color .12s ease;
}
textarea.rt-rec-title:hover { background: var(--tg-hover); }
textarea.rt-rec-title:focus {
    background: var(--tg-surface);
    border-color: var(--f7-theme-color);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--f7-theme-color) 14%, transparent);
}
.rt-rec-title::placeholder { color: color-mix(in srgb, var(--f7-text-color) 26%, transparent); }
.rt-rec-title[readonly] { opacity: .85; }
.rt-rec-meta {
    font-size: 12px;
    color: var(--tg-muted);
    margin-top: 9px;
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}
/* A dot between facts, rather than a wide gap that reads as two separate lines.
   `:not(:empty)` on the LEFT one because the live-presence slot is rendered always and
   filled imperatively — an empty span still counted as a preceding fact, so the line
   opened with a stray bullet: "• Created 24 Aug". */
.rt-rec-meta > span:not(:empty) + span::before {
    content: '';
    display: inline-block;
    width: 3px; height: 3px;
    border-radius: 50%;
    margin-right: 8px;
    vertical-align: middle;
    background: color-mix(in srgb, var(--f7-text-color) 26%, transparent);
}
.rt-rec-ro {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    border-radius: 20px;
    font-weight: 600;
    color: var(--f7-theme-color);
    background: color-mix(in srgb, var(--f7-theme-color) 10%, transparent);
}
.rt-rec-ro::before { display: none !important; }
/* "Submitted through a form" — a chip in the record's meta line, and a way in.
   Clickable, so it is a button in everything but name: the meta line is where the
   eye already goes for "where did this come from", and a second row of buttons for
   one link would cost more than it carries. The ::before is the same separator dot
   the other meta items get, suppressed here as it is for the read-only chip. */
.rt-rec-form {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    border-radius: 20px;
    cursor: pointer;
    color: var(--tg-muted);
    background: color-mix(in srgb, var(--f7-text-color) 6%, transparent);
    transition: color .12s, background .12s;
}
.rt-rec-form:hover {
    color: var(--f7-theme-color);
    background: color-mix(in srgb, var(--f7-theme-color) 12%, transparent);
}
.rt-rec-form::before { display: none !important; }
.rt-rec-body { flex: 1 1 auto; overflow-y: auto; padding: 12px 28px 28px; }

.rt-field {
    display: grid;
    grid-template-columns: 164px minmax(0, 1fr);
    gap: 16px;
    align-items: start;
    /* Row padding wide enough for the hover band to read as a row, and a hairline
       between rows so a long record stays scannable. */
    padding: 10px 10px 10px 8px;
    margin: 0 -10px 0 -8px;
    border-radius: 8px;
    border-bottom: 1px solid var(--tg-line);
}
.rt-field:last-of-type { border-bottom: 0; }
.rt-field:hover { background: color-mix(in srgb, var(--f7-text-color) 2%, transparent); }
.rt-field-label {
    font-size: 12.5px;
    font-weight: 500;
    color: var(--tg-muted);
    display: flex;
    align-items: center;
    gap: 7px;
    padding-top: 10px;
    min-width: 0;
}
.rt-field-label span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rt-field-label i { flex: 0 0 auto; opacity: .8; }
.rt-field-val { min-width: 0; padding-top: 1px; }
/* A read-only value is text on the sheet, not a disabled input — so it gets no box,
   only the same vertical rhythm as the input it stands in for. */
.rt-field-ro { padding: 9px 0; font-size: 13.5px; line-height: 1.45; color: var(--f7-text-color); }
.rt-field-changed { font-size: 11px; color: var(--tg-muted); margin-top: 5px; }

/* Choice / rating / people pickers: the chosen one is FULL strength with a ring in
   the room colour; the rest step back. The old `outline` drew a hard dark rectangle
   that read as a focus artefact rather than a selection. */
.rt-pick { display: flex; flex-wrap: wrap; gap: 7px; align-items: center; }
.rt-pick-opt { cursor: pointer; transition: opacity .12s ease, box-shadow .12s ease; }
.rt-pick-opt.rt-pick-off { opacity: .5; }
/* The x on a chosen person, in the form's multi-person answer. The chip itself is not a
   button there — it is the answer — so only the x takes the click. */
.rt-pick-x { cursor: pointer; opacity: .45; margin-left: 2px; }
.rt-pick-x:hover { opacity: 1; }
.rt-pick-opt.rt-pick-off:hover { opacity: .8; }
.rt-pick-opt.rt-pick-on {
    opacity: 1;
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--f7-theme-color) 55%, transparent);
}
/* In the grid a person chip sits on a cell and needs no outline of its own; here it
   is an OPTION among others, so it has to look like something you can press. */
.rt-pick .tg-user {
    padding: 2px 9px 2px 2px;
    border: 1px solid var(--tg-border);
    background: var(--tg-surface);
}
.rt-pick .tg-user.rt-pick-on { border-color: transparent; }

.rt-rec-history { border-top: 1px solid var(--tg-border); margin-top: 20px; padding-top: 14px; }
.rt-hist-row { display: flex; gap: 10px; padding: 6px 0; font-size: 12.5px; color: var(--tg-muted); }
.rt-hist-what { color: var(--f7-text-color); }

/* The one destructive action on the sheet, kept away from everything else. */
.rt-rec-danger {
    margin-top: 22px;
    padding-top: 18px;
    border-top: 1px solid var(--tg-line);
    display: flex;
    justify-content: flex-end;
}

@media (max-width: 640px) {
    .rt-rec-head { padding: 16px 18px 14px; }
    .rt-rec-body { padding: 10px 18px 24px; }
    .rt-field { grid-template-columns: 1fr; gap: 6px; padding: 12px 8px; margin: 0 -8px; }
    .rt-field-label { padding-top: 0; }
    .rt-field-val { padding-top: 0; }
}

/* ── mobile: a record list, with the grid one tap away ──────────────────── */

.rt-cards { padding: 8px; overflow-y: auto; height: 100%; box-sizing: border-box; }
.rt-card {
    background: var(--tg-cell-bg);
    border: 1px solid var(--tg-border);
    border-radius: var(--tg-radius);
    padding: 12px 14px;
    margin-bottom: 8px;
    cursor: pointer;
    transition: border-color .15s, transform .1s;
}
.rt-card:active { transform: scale(.995); }
.rt-card:hover { border-color: var(--tg-border-strong); }
.rt-card-t { font-size: 14.5px; font-weight: 600; color: var(--f7-text-color); line-height: 1.35; }
.rt-card-empty-t { color: var(--tg-muted); font-style: italic; font-weight: 500; }
.rt-card-sub { display: flex; align-items: center; gap: 8px; margin-top: 7px; flex-wrap: wrap; font-size: 12.5px; color: var(--tg-muted); }
.rt-card-locked { opacity: .72; }
.rt-card-add {
    display: flex; align-items: center; justify-content: center; gap: 7px;
    padding: 13px; border: 1px dashed var(--tg-border-strong); border-radius: var(--tg-radius);
    color: var(--tg-muted); font-size: 13.5px; cursor: pointer; margin-bottom: 8px;
}
.rt-card-add:hover { color: var(--f7-theme-color); border-color: var(--f7-theme-color); }

/* ── kanban ─────────────────────────────────────────────────────────────── */

.rt-kanban { display: flex; gap: 12px; padding: 12px; height: 100%; overflow-x: auto; box-sizing: border-box; align-items: flex-start; }
.rt-kcol {
    flex: 0 0 288px;
    max-height: 100%;
    display: flex;
    flex-direction: column;
    background: color-mix(in srgb, var(--f7-text-color) 4%, transparent);
    border-radius: 12px;
    padding: 10px;
    box-sizing: border-box;
}
.rt-kcol-head { display: flex; align-items: center; gap: 8px; padding: 0 4px 10px; flex: 0 0 auto; }
.rt-kcol-name { font-size: 13px; font-weight: 600; display: flex; align-items: center; }
.rt-kcol-count { font-size: 11px; color: var(--tg-muted); font-variant-numeric: tabular-nums; }
.rt-kcol-add { margin-left: auto; color: var(--tg-muted); cursor: pointer; display: flex; }
.rt-kcol-add:hover { color: var(--f7-theme-color); }
.rt-kcol-body { overflow-y: auto; flex: 1 1 auto; display: flex; flex-direction: column; gap: 7px; min-height: 34px; }
.rt-kcol-over { background: color-mix(in srgb, var(--f7-theme-color) 10%, transparent); outline: 2px dashed color-mix(in srgb, var(--f7-theme-color) 40%, transparent); outline-offset: -2px; }
.rt-kcard {
    background: var(--tg-cell-bg);
    border: 1px solid var(--tg-border);
    border-radius: 9px;
    padding: 10px 11px;
    cursor: grab;
    font-size: 13px;
}
.rt-kcard:hover { border-color: var(--tg-border-strong); }
.rt-kcard-drag { opacity: .45; }
.rt-kcard-t { font-weight: 600; line-height: 1.35; }
.rt-kcard-sub { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 7px; font-size: 12px; color: var(--tg-muted); }
.rt-kcard-locked { cursor: default; opacity: .72; }

/* ── calendar: the Sales lead-calendar week grid ────────────────────────── */

.rt-cal { display: flex; flex-direction: column; height: 100%; }
.rt-cal-toolbar {
    display: flex; align-items: center; gap: 8px;
    padding: 8px 12px; border-bottom: 1px solid var(--tg-border); flex: 0 0 auto;
}
.rt-cal-nav {
    width: 28px; height: 28px; border-radius: 8px;
    display: inline-flex; align-items: center; justify-content: center;
    border: 1px solid var(--tg-border); background: transparent; color: var(--f7-text-color);
    cursor: pointer; width: auto; min-width: 28px;
}
.rt-cal-nav:hover { background: var(--tg-hover); }
.rt-cal-today {
    height: 28px; padding: 0 12px; border-radius: 8px;
    border: 1px solid var(--tg-border); background: transparent;
    font-size: 12.5px; font-weight: 500; cursor: pointer; color: var(--f7-text-color); width: auto;
}
.rt-cal-today:hover { background: var(--tg-hover); }
.rt-cal-range { font-size: 13.5px; font-weight: 600; margin-left: 4px; }
.rt-cal-by { font-size: 12px; color: var(--tg-muted); }

.rt-cal-layout { display: flex; flex: 1 1 auto; min-height: 0; }
.rt-cal-zone {
    flex: 0 0 24px;
    display: flex; align-items: center; justify-content: center;
    font-size: 18px; color: var(--tg-muted);
    cursor: pointer; user-select: none;
    border-right: 1px solid var(--tg-border);
}
.rt-cal-zone:last-child { border-right: 0; border-left: 1px solid var(--tg-border); }
.rt-cal-zone:hover { background: var(--tg-hover); color: var(--f7-text-color); }
.rt-cal-zone-over { background: color-mix(in srgb, var(--f7-theme-color) 14%, transparent); }

.rt-cal-scroll { flex: 1 1 auto; overflow: auto; min-width: 0; }
.rt-cal-grid { display: grid; grid-template-columns: 56px repeat(7, minmax(110px, 1fr)); min-width: 820px; }
.rt-cal-gutter-head { position: sticky; top: 0; z-index: 3; background: var(--tg-head-bg); border-bottom: 1px solid var(--tg-border); height: 46px; }
.rt-cal-dayhead {
    position: sticky; top: 0; z-index: 3;
    background: var(--tg-head-bg);
    border-bottom: 1px solid var(--tg-border);
    border-left: 1px solid var(--tg-border);
    height: 46px;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
}
.rt-cal-wd { font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em; color: var(--tg-muted); }
.rt-cal-dn { font-size: 15px; font-weight: 600; }
.rt-cal-today-chip {
    background: var(--f7-theme-color); color: #fff;
    width: 24px; height: 24px; border-radius: 50%;
    display: inline-flex; align-items: center; justify-content: center; font-size: 12.5px;
}
.rt-cal-daycount {
    position: absolute; top: 6px; right: 8px;
    font-size: 10.5px; color: var(--tg-muted); font-variant-numeric: tabular-nums;
}
.rt-cal-dayhead { position: sticky; }
.rt-cal-hourlabel {
    height: 64px; font-size: 10.5px; color: var(--tg-muted);
    text-align: right; padding: 2px 7px 0 0;
    border-bottom: 1px solid var(--tg-border);
    box-sizing: border-box;
}
.rt-cal-cell {
    height: 64px;
    border-bottom: 1px solid var(--tg-border);
    border-left: 1px solid var(--tg-border);
    box-sizing: border-box;
    padding: 2px;
    position: relative;
    overflow: hidden;
}
/* Working hours are tinted, so the part of the day people actually book reads
   first. Straight from the Sales calendar. */
.rt-cal-working { background: color-mix(in srgb, var(--f7-text-color) 3%, transparent); }
.rt-cal-cell-over { background: color-mix(in srgb, var(--f7-theme-color) 12%, transparent); }
.rt-cal-card {
    background: var(--tg-cell-bg);
    border: 1px solid var(--tg-border);
    border-left: 3px solid var(--f7-theme-color);
    border-radius: 6px;
    padding: 4px 6px;
    font-size: 11.5px;
    margin-bottom: 2px;
    cursor: grab;
    overflow: hidden;
}
.rt-cal-card:hover { border-color: var(--tg-border-strong); }
.rt-cal-card-overdue { border-left-color: #dc3545; }
.rt-cal-card-time { font-size: 10px; color: var(--tg-muted); font-variant-numeric: tabular-nums; }
.rt-cal-card-name { font-weight: 600; line-height: 1.3; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rt-cal-add {
    position: absolute; inset: auto 2px 2px 2px;
    display: none; align-items: center; justify-content: center;
    height: 18px; border-radius: 5px;
    font-size: 10.5px; color: var(--f7-theme-color);
    background: color-mix(in srgb, var(--f7-theme-color) 10%, transparent);
    cursor: pointer;
}
.rt-cal-cell:hover .rt-cal-add { display: flex; }

/* ── gallery ────────────────────────────────────────────────────────────── */

.rt-gallery {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(228px, 1fr));
    gap: 12px;
    padding: 14px;
    overflow-y: auto;
    height: 100%;
    box-sizing: border-box;
    align-content: start;
}
.rt-gcard {
    background: var(--tg-cell-bg);
    border: 1px solid var(--tg-border);
    border-radius: 12px;
    overflow: hidden;
    cursor: pointer;
    transition: border-color .15s, transform .1s;
}
.rt-gcard:hover { border-color: var(--tg-border-strong); }
.rt-gcard:active { transform: scale(.995); }
.rt-gcard-cover {
    height: 110px;
    background: color-mix(in srgb, var(--f7-text-color) 6%, transparent);
    display: flex; align-items: center; justify-content: center;
    color: var(--tg-muted);
}
.rt-gcard-body { padding: 11px 13px 13px; }
.rt-gcard-t { font-size: 14px; font-weight: 600; line-height: 1.35; }
.rt-gcard-rows { margin-top: 8px; display: flex; flex-direction: column; gap: 5px; }
.rt-gcard-row { display: flex; gap: 8px; font-size: 12px; }
.rt-gcard-k { color: var(--tg-muted); flex: 0 0 84px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rt-gcard-v { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── forms ──────────────────────────────────────────────────────────────── */

.rt-formbuilder { display: flex; height: 100%; min-height: 0; }
.rt-fb-rail {
    flex: 0 0 244px;
    border-right: 1px solid var(--tg-border);
    padding: 14px;
    overflow-y: auto;
    box-sizing: border-box;
}
.rt-fb-rail-t { display: flex; align-items: baseline; gap: 8px; margin-bottom: 12px; }
.rt-fb-rail-t b { font-size: 13px; }
.rt-fb-rail-act { font-size: 11.5px; color: var(--f7-theme-color); cursor: pointer; }
.rt-fb-chip {
    display: flex; align-items: center; gap: 8px;
    padding: 8px 10px; margin-bottom: 6px;
    border: 1px solid var(--tg-border); border-radius: 9px;
    font-size: 13px; cursor: grab; background: var(--tg-cell-bg);
}
.rt-fb-chip:hover { border-color: var(--tg-border-strong); }
.rt-fb-chip i { color: var(--tg-muted); }
.rt-fb-chip-in { opacity: .45; }
.rt-fb-drop {
    border: 1px dashed var(--tg-border-strong);
    border-radius: 9px;
    padding: 14px 10px;
    text-align: center;
    font-size: 12px;
    color: var(--tg-muted);
    margin-top: 8px;
}
.rt-fb-canvas { flex: 1 1 auto; overflow-y: auto; padding: 22px; background: color-mix(in srgb, var(--f7-text-color) 3%, transparent); }
.rt-fb-sheet {
    max-width: 620px;
    margin: 0 auto;
    background: var(--tg-cell-bg);
    border: 1px solid var(--tg-border);
    border-radius: 14px;
    overflow: hidden;
}
.rt-fb-cover {
    height: 108px;
    background: color-mix(in srgb, var(--f7-theme-color) 12%, transparent);
    display: flex; align-items: center; justify-content: center;
    color: var(--tg-muted); font-size: 12.5px;
}
.rt-fb-sheet-in { padding: 24px 28px 28px; }
.rt-fb-title {
    font-size: 23px; font-weight: 650; width: 100%;
    border: 0; outline: none; background: transparent; color: var(--f7-text-color);
    font-family: inherit; padding: 0;
}
.rt-fb-desc {
    font-size: 14px; color: var(--tg-muted); width: 100%; margin-top: 8px;
    border: 0; outline: none; background: transparent; font-family: inherit; resize: none; padding: 0;
    line-height: 1.5;
}
.rt-fb-q { margin-top: 22px; }
.rt-fb-q-head { display: flex; align-items: center; gap: 7px; }
.rt-fb-q-label { font-size: 13.5px; font-weight: 600; }
.rt-fb-req { color: #dc3545; }
.rt-fb-q-help { font-size: 12px; color: var(--tg-muted); margin-top: 3px; }
.rt-fb-q-input {
    margin-top: 7px; padding: 10px 12px;
    border: 1px solid var(--tg-border); border-radius: 9px;
    font-size: 13.5px; color: var(--tg-muted); background: color-mix(in srgb, var(--f7-text-color) 3%, transparent);
}
/* A file question in the BUILDER preview. It was drawing the same "Your answer" box
   every other type gets, so an attachment column read as a text question — which is
   what the real form then rendered too. */
.rt-fb-q-file { display: flex; align-items: center; gap: 7px; }
/* A question filled in from the sign-in. Deliberately not an input box: nothing here
   is typed, by anybody. */
.rt-fb-auto {
    margin-top: 7px; padding: 9px 12px;
    display: flex; align-items: center; gap: 7px;
    border-radius: 9px; font-size: 13px;
    background: rgba(var(--f7-theme-color-rgb), 0.09);
    color: var(--f7-theme-color);
}
.rt-fb-q-tools { margin-left: auto; display: none; gap: 6px; }
.rt-fb-q:hover .rt-fb-q-tools { display: flex; }
.rt-fb-q-tool { color: var(--tg-muted); cursor: pointer; display: flex; padding: 2px; }
.rt-fb-q-tool:hover { color: var(--f7-text-color); }

/* ── question cards ─────────────────────────────────────────────────────────
   Every question on the builder's sheet is a card. Closed it looks like the question
   itself and opens on click; open it grows a head (field, Required, remove), typed-in-
   place label and help text, and a footer holding the "show only when" rules. */
.rt-fb-card {
    margin-top: 18px;
    padding: 12px 14px;
    border: 1px solid transparent;
    border-radius: 12px;
    cursor: pointer;
    transition: background .12s, border-color .12s;
}
.rt-fb-card:hover { background: color-mix(in srgb, var(--f7-text-color) 3%, transparent); border-color: var(--tg-border); }
.rt-fb-card .rt-fb-q-head { pointer-events: none; }
.rt-fb-card-on {
    cursor: default;
    padding: 0;
    border-color: var(--tg-border);
    box-shadow: 0 8px 28px -12px rgba(0, 0, 0, .22);
    background: var(--tg-cell-bg);
}
.rt-fb-card-on:hover { background: var(--tg-cell-bg); }
.rt-fb-card-head {
    display: flex; align-items: center; gap: 8px;
    padding: 9px 12px;
    border-bottom: 1px solid var(--tg-border);
    font-size: 13px;
}
.rt-fb-card-fname { font-weight: 600; color: var(--f7-text-color); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The column's TYPE, and a way into changing it. Reads as a control rather than a label —
   a form field is a table column, and this is the one place in the builder where that is
   actionable, so it has to look pressable or nobody finds it. */
.rt-fb-q-type {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 3px 7px; border-radius: 6px;
    color: var(--tg-muted); cursor: pointer;
    white-space: nowrap; flex-shrink: 0;
}
.rt-fb-q-type:hover { background: var(--tg-hover, rgba(0,0,0,.05)); color: var(--f7-text-color); }
.rt-fb-card-body { padding: 16px 18px 18px; }
.rt-fb-card-label {
    width: 100%; border: 0; outline: none; background: transparent; padding: 0;
    font-size: 16px; font-weight: 600; color: var(--f7-text-color); font-family: inherit;
}
.rt-fb-card-help {
    width: 100%; border: 0; outline: none; background: transparent; padding: 0; margin-top: 6px;
    font-size: 13px; color: var(--tg-muted); font-family: inherit;
}
.rt-fb-card-foot {
    padding: 12px 18px 14px;
    background: color-mix(in srgb, var(--f7-text-color) 3%, transparent);
    border-top: 1px solid var(--tg-border);
    border-radius: 0 0 12px 12px;
    font-size: 13px;
}
.rt-fb-conds { margin-top: 12px; }
.rt-fb-conds .rt-cond { background: var(--tg-cell-bg); border: 1px solid var(--tg-border); border-radius: 9px; padding: 6px 8px; }
.rt-fb-conds .rt-cond-join { width: 46px; }
.rt-fb-cond-note { font-size: 12px; color: var(--tg-muted); }
/* Closed card, with rules: said above the label the way the fill side never shows it —
   this is the builder's cue, not the respondent's. */
.rt-fb-cond-tag {
    display: inline-flex; align-items: center; gap: 5px;
    font-size: 12px; color: var(--tg-muted); margin-bottom: 6px;
}
/* A small switch, as an inline label: click anywhere on the words. */
.rt-fb-sw-l { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; user-select: none; }
.rt-sw {
    display: inline-block; position: relative; width: 30px; height: 18px; border-radius: 9px;
    background: color-mix(in srgb, var(--f7-text-color) 18%, transparent);
    transition: background .15s; flex: 0 0 auto;
}
.rt-sw::after {
    content: ''; position: absolute; top: 2px; left: 2px; width: 14px; height: 14px; border-radius: 50%;
    background: #fff; box-shadow: 0 1px 2px rgba(0,0,0,.25); transition: left .15s;
}
.rt-sw-on { background: var(--f7-theme-color); }
.rt-sw-on::after { left: 14px; }

/* ── the logo ───────────────────────────────────────────────────────────────
   A square straddling the cover's lower edge.

   THE WRAP IS AS TALL AS THE HALF THAT HANGS BELOW IT — 48 of the tile's 96 — so the
   title simply follows in normal flow. It used to be height:0, with the clearance
   added as padding on the sheet below; that works only while the two numbers agree,
   and the moment the tile was resized they did not, so the logo sat on top of the
   title. Flow cannot drift. */
.rt-fb-logo-wrap, .rt-pub-logo { position: relative; height: 48px; padding: 0 28px; z-index: 2; }
.rt-pub-logo { padding: 0 32px; }
/* SQUARE, and the SAME square the company logo is drawn in everywhere else — see
   .bp-logo-circle / .bp-logo-img in company.business.profile.popup.html: 96x96, a
   24px radius, 8px of white padding, object-fit:contain, a soft drop shadow. It is
   usually the very same image, so it should not be a different shape here.

   The old 96x68 slot letter-boxed it: a mark that is square or round in almost every
   case it is drawn from, sitting in a landscape box with dead space down both sides.
   contain still protects the odd wide wordmark — that one is simply smaller inside
   the same square. */
.rt-fb-logo {
    position: absolute; top: -48px;
    width: 96px; height: 96px; box-sizing: border-box;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
    border: 1.5px dashed var(--tg-border); border-radius: 24px;
    background: var(--tg-cell-bg); color: var(--tg-muted);
    font-size: 11px; line-height: 1.2; text-align: center; cursor: pointer; overflow: hidden;
}
.rt-fb-logo:hover { color: var(--f7-text-color); border-color: var(--f7-theme-color); }
.rt-fb-logo-set {
    border-style: solid; padding: 8px;
    background: #fff;
    box-shadow: 0 4px 16px rgba(0, 0, 0, .1);
}
.rt-fb-logo img, .rt-pub-logo img { max-width: 100%; max-height: 100%; object-fit: contain; display: block; }
/* …but NOT the public tile, which is positioned out of a 44px-tall wrap: `max-height:
   100%` resolved against that wrap and squashed a 76px square into 76x48. It carries
   its own explicit size below, so it wants no cap at all. */
.rt-pub-logo > img { max-width: none; max-height: none; }
.rt-fb-logo-set img { border-radius: 16px; }
.rt-pub-logo > img {
    position: absolute; top: -44px;
    width: 76px; height: 76px; padding: 7px; box-sizing: border-box;
    border-radius: 19px;
    /* WHITE, not the surface colour. A logo is drawn for a light ground in almost every
       case it arrives from, and a dark plate turns a black wordmark into a rectangle.
       The ring carries the boundary in dark mode instead of a border that would read as
       a frame in light. */
    background: #fff;
    box-shadow: 0 2px 10px rgba(0, 0, 0, .18),
                0 0 0 1px color-mix(in srgb, var(--f7-text-color) 9%, transparent);
}

/*
   NO COVER, STILL A LOGO — and it cannot straddle nothing.

   The tile hangs 44px above its wrap, which works because the wrap sits on the cover's
   lower edge. With no cover the wrap is the first thing in the sheet, so the tile hung
   above the sheet's first pixel and .rt-pub-sheet's own overflow:hidden clipped it away
   completely. Turning the cover off silently turned the logo off with it.

   In flow instead, inside the sheet, above the title: smaller (there is no band for it
   to punctuate), quieter (no shadow — nothing to lift off), and aligned to the same
   left edge as everything below it.
*/
.rt-pub-logo-bare { position: static; height: auto; padding: 30px 32px 0; }
.rt-pub-logo-bare > img {
    position: static;
    width: 54px; height: 54px; padding: 5px;
    border-radius: 14px;
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--f7-text-color) 10%, transparent);
}
/* The title's own 30px would stack on the 30px above the mark. */
.rt-pub-logo-bare + .rt-pub-in { padding-top: 18px; }
/* .rt-fb-sheet-in-logo / .rt-pub-in-logo used to add the clearance here as padding.
   The wrap above carries it in flow now, so the sheet keeps its ordinary top padding
   whether or not there is a logo, and neither class exists any more. */

/* NO COVER, STILL A LOGO. The tile straddles the cover's lower edge, which is why it
   is absolutely positioned out of a zero-height wrap — and why hiding it along with
   the cover was the easy reading. But the two are separate decisions: "no cover"
   answers what sits behind the title, not whether the form carries its mark, and
   turning the cover off silently took the logo with it.

   It keeps straddling either way. With no cover the edge it hangs over is the dashed
   placeholder in the builder, or the top of the sheet on the public page — one
   position, not two, or the form looks like a different design every time somebody
   changes the cover. */

/* The visitor's light/dark control, top right — the same corner and the same weight
   as the one on the sign-in screen, which is the app's only other unauthenticated
   page. Quiet until hovered: it is an escape hatch, not part of the form. */
.pubform-theme-btn {
    position: absolute; top: 14px; right: 14px; z-index: 10;
    width: 38px; height: 38px; min-width: 38px; padding: 0;
    border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    background: var(--f7-page-bg-color, #fff);
    color: var(--f7-text-color);
    box-shadow: 0 1px 6px rgba(0, 0, 0, .14);
    opacity: 0.75; transition: opacity .15s;
}
.pubform-theme-btn:hover { opacity: 1; }
@media (max-width: 480px) {
    .pubform-theme-btn { top: 10px; right: 10px; width: 34px; height: 34px; min-width: 34px; }
}

/* The public form page — its own surface, on a customer's domain or ours. */
.rt-pubform { min-height: 100%; background: color-mix(in srgb, var(--f7-text-color) 3%, transparent); padding: 28px 16px 56px; box-sizing: border-box; }
.rt-pub-sheet {
    max-width: 620px; margin: 0 auto;
    /*
       THE SURFACE COLOUR, NOT THE PAGE COLOUR. --f7-page-bg-color is #e2e2e2 light and
       #000 dark, so the sheet came out PURE BLACK in dark mode — the one value that
       reads as a hole rather than a card, and darker than the ground it was sitting on.
       --f7-list-bg-color is the app's surface (#fff / #222): the same white in light,
       and a card you can actually see in dark. Same variable the inputs above use, for
       the same reason.
    */
    background: var(--tg-surface);
    border-radius: 16px;
    box-shadow: 0 10px 40px -14px rgba(0, 0, 0, .18);
    overflow: hidden;
}
.rt-pub-cover { height: 116px; background: color-mix(in srgb, var(--f7-theme-color) 14%, transparent); }
.rt-pub-in { padding: 30px 32px 34px; }

/* ── the runner's hosts ──────────────────────────────────────────────────────
   Moved here from app.css. The three surfaces that mount <table-form-runner> are the
   app page, the narrow app panel and form.qik.mt, and only the first of those loads
   app.css — so every rule the runner needs lives in this file, which all three load. */

/*
   COMPACT MEANS THE HOST OWNS THE SURFACE.

   The form popup and the app panel both draw their own card — their own cover, their
   own ground, their own rounded corners. The runner drew a second one inside it, so a
   popup showed three backgrounds stacked: the popup's, .rt-pubform's 3% wash, and the
   sheet's. In dark mode the innermost of those was near-black over grey over grey,
   which is a stack of edges where there should be one surface.

   Compact keeps the sheet as a layout box and gives up every decoration on it. The
   page keeps all of them, because out there it IS the card.
*/
.table-form-runner.tfr-compact { background: none; padding: 0; min-height: 0; }
.table-form-runner.tfr-compact .rt-pub-sheet {
    background: none; box-shadow: none; border-radius: 0; margin: 0; max-width: none;
}
.table-form-runner.tfr-compact .rt-pub-in { padding: 16px 18px 26px; }
.table-form-runner.tfr-compact .rt-pub-title { font-size: 18px; }
.rt-pub-sched { display: flex; align-items: center; gap: 6px; font-size: 12px; opacity: 0.7; margin-top: 8px; }
.rt-pub-again { display: inline-block; margin-top: 16px; font-size: 13px; font-weight: 600; }

/* INSIDE SOMEBODY ELSE'S PAGE (form/embed.js). The embed draws the card — rounded
   corners, shadow, white ground — around the iframe, so the document inside must draw
   none of them, or the form appears as a card floating in a grey gutter inside a card.
   The class is set on <html> by form/src/boot.js, and only when the embed asked for
   it: a customer who iframes the plain link gets the plain page. */
html.qf-embedded .rt-pubform { padding: 0; background: transparent; min-height: 0; }
html.qf-embedded .rt-pub-sheet { margin: 0; max-width: none; border-radius: 0; box-shadow: none; }
html.qf-embedded .rt-pub-cover { border-radius: 0; }
.rt-pub-title { font-size: 25px; font-weight: 650; line-height: 1.25; }
.rt-pub-desc { font-size: 14.5px; color: var(--tg-muted); margin-top: 10px; line-height: 1.55; white-space: pre-wrap; }
.rt-pub-q { margin-top: 24px; }
.rt-pub-q-label { font-size: 14px; font-weight: 600; display: flex; align-items: center; gap: 5px; }
.rt-pub-q-help { font-size: 12.5px; color: var(--tg-muted); margin-top: 3px; }
/* A question the form answers for you. It reads as a STATEMENT — no border, no field
   background — because a control that looks like an input people will try to change,
   and this one cannot be changed: the value is written from the sign-in, server-side. */
.rt-pub-auto {
    display: flex; align-items: center; gap: 9px;
    margin-top: 8px; padding: 7px 11px 7px 7px;
    border-radius: 999px;
    background: rgba(var(--f7-theme-color-rgb), 0.08);
    width: fit-content; max-width: 100%;
}
.rt-pub-auto-face { width: 26px; height: 26px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.rt-pub-auto-name { font-size: 14px; font-weight: 600; }
.rt-pub-auto-note { font-size: 12px; color: var(--tg-muted); }
.rt-pub-auto.is-missing {
    background: rgba(255, 149, 0, 0.12);
    color: #8a5300; padding-left: 11px; font-size: 12.5px;
}
/* Attaching, from a form. The native file input is hidden and the label is the button:
   a bare <input type=file> renders differently in every browser and none of them match
   the rest of the form. */
.rt-pub-files { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 8px; }
.rt-pub-file-in { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.rt-pub-file-add {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 8px 13px; border-radius: 9px; cursor: pointer;
    border: 1px dashed var(--tg-border); font-size: 13px; color: var(--tg-muted);
}
.rt-pub-file-add:hover { border-color: var(--f7-theme-color); color: var(--f7-theme-color); }
.rt-pub-file {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 6px 10px; border-radius: 999px; font-size: 12.5px;
    background: color-mix(in srgb, var(--f7-text-color) 6%, transparent);
    max-width: 100%;
}
.rt-pub-file-n { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 220px; }
.rt-pub-file-x { cursor: pointer; opacity: 0.5; }
.rt-pub-file-x:hover { opacity: 1; }
.rt-pub-file.is-busy { color: var(--tg-muted); }
.rt-pub-err { color: #dc3545; font-size: 12.5px; margin-top: 5px; }
.rt-pub-actions { margin-top: 30px; display: flex; align-items: center; gap: 12px; }
.rt-pub-note { font-size: 12px; color: var(--tg-muted); }
/* ── a submitted row, read back as its form ─────────────────────────────────
   Everything below belongs to <table-form-runner> in its submission mode: the
   "who sent this" band above the questions, the values shown instead of controls
   when the reader may not change them, and the review box at the bottom.

   Deliberately quiet. The questions and the answers are the content; this is the
   frame around them, and a frame that competes with its picture is the reason a
   review screen ends up harder to read than the grid it was meant to improve. */
.rt-sub-head {
    margin-top: 22px;
    padding: 12px 14px;
    border: 1px solid var(--tg-border);
    border-radius: 10px;
    background: color-mix(in srgb, var(--f7-text-color) 3%, transparent);
    display: flex; flex-direction: column; gap: 7px;
}
.rt-sub-line { display: flex; align-items: center; gap: 8px; font-size: 13px; }
/* Edit / Cancel, pushed to the end of the first line of the band. margin-left:auto so
   it sits at the right whatever the sentence beside it says. */
.rt-sub-edit {
    margin-left: auto;
    width: auto;
    flex: none;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 4px 11px;
    border-radius: 999px;
    border: 1px solid var(--tg-border);
    background: var(--tg-surface);
    color: var(--f7-text-color);
    font-size: 12.5px;
    cursor: pointer;
    transition: background .12s, border-color .12s, color .12s;
}
.rt-sub-edit:hover { background: var(--tg-hover); border-color: var(--tg-border-strong); }
.rt-sub-edit.is-on { color: var(--f7-theme-color); border-color: color-mix(in srgb, var(--f7-theme-color) 45%, transparent); }
.rt-sub-line i { color: var(--tg-muted); flex: none; }
.rt-sub-muted { color: var(--tg-muted); }
.rt-sub-ask {
    margin-top: 3px; padding-top: 10px;
    border-top: 1px solid var(--tg-border);
    display: flex; flex-direction: column; gap: 6px;
}
.rt-sub-ask-t { display: flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 600; }
.rt-sub-ask-t i { color: var(--f7-theme-color); flex: none; }
/* pre-line, like the thank-you above: remarks are somebody's own words and their
   line breaks are part of them. */
.rt-sub-ask-r { font-size: 13px; color: var(--tg-muted); white-space: pre-line; padding-left: 22px; }
.rt-sub-ask-done { font-size: 12.5px; color: var(--tg-muted); padding-left: 22px; }

/* WHAT THE ROW ALREADY SAYS — above the questions of a continuation form.

   Read-only by construction: this is not a set of inputs rendered disabled, it is the
   values, because the form does not ask them and must not appear to. Same key/value
   grid as the case drawer's "What was submitted", for the same reason — somebody
   moving between the two is reading one thing in two places. */
.rt-known {
    margin-top: 22px;
    padding: 12px 14px;
    border: 1px solid var(--tg-border);
    border-radius: 10px;
    background: color-mix(in srgb, var(--f7-text-color) 3%, transparent);
}
.rt-known-head {
    display: flex; align-items: center; gap: 7px;
    font-size: 11px; font-weight: 700; letter-spacing: .4px; text-transform: uppercase;
    color: var(--tg-muted);
    margin-bottom: 10px;
}
.rt-known-grid {
    display: grid;
    grid-template-columns: minmax(90px, 34%) 1fr;
    gap: 6px 14px;
    font-size: 13px;
}
.rt-known-k { color: var(--tg-muted); }
/* pre-line and break-word: a complaint's detail is a paragraph somebody typed, and its
   line breaks are part of what it says. */
.rt-known-v { white-space: pre-line; word-break: break-word; }
.rt-known-note {
    display: flex; align-items: flex-start; gap: 6px;
    margin-top: 11px; padding-top: 9px;
    border-top: 1px solid var(--tg-border);
    font-size: 12px; color: var(--tg-muted);
}
.rt-known-note i { margin-top: 2px; flex: none; }
/* One column on a narrow sheet: 34% of 300px is not a label, it is a word per line. */
@media (max-width: 520px) {
    .rt-known-grid { grid-template-columns: 1fr; gap: 2px 0; }
    .rt-known-k { margin-top: 6px; font-size: 11.5px; }
}

/* WHAT WAS ASKED AND WHAT CAME BACK — the reviews on this submission, under the
   band that says who sent it.

   This is the ONLY place a review's remarks are readable, so it is a list rather than
   a count: the ask, the answer, and who said each. Indented under the band it belongs
   to, quiet enough that a row with four reviews does not out-shout the answers. */
.rt-revs {
    margin-top: 4px;
    padding-top: 10px;
    border-top: 1px solid var(--tg-border);
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.rt-rev-row { display: flex; flex-direction: column; gap: 4px; }
.rt-rev-row-top { display: flex; align-items: center; gap: 8px; font-size: 12.5px; flex-wrap: wrap; }
.rt-rev-who { font-weight: 600; }
.rt-rev-mode {
    padding: 1px 8px;
    border-radius: 999px;
    font-size: 11.5px;
    color: var(--tg-muted);
    background: color-mix(in srgb, var(--f7-text-color) 7%, transparent);
}
.rt-rev-when { color: var(--tg-muted); }
.rt-rev-drop { margin-left: auto; color: var(--tg-muted); font-size: 12px; }
.rt-rev-drop:hover { color: #dc3545; }
/* pre-line: remarks are somebody's own words and their line breaks are part of them. */
.rt-rev-said { font-size: 13px; white-space: pre-line; padding-left: 2px; color: var(--f7-text-color); }
.rt-rev-said-back { color: var(--tg-muted); }
.rt-rev-back { display: flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--tg-muted); }
.rt-rev-looks_good i { color: #21a366; }
.rt-rev-needs_changes i { color: #e8930c; }
.rt-rev-edited i { color: var(--f7-theme-color); }
.rt-rev-row.is-cancelled { opacity: .55; }

/* The answer where a control would be. Same left edge and same line height as the
   inputs, so a read-only form and an editable one do not jump when you switch. */
.rt-ro-val {
    margin-top: 7px; font-size: 14px; line-height: 1.45;
    white-space: pre-line; word-break: break-word;
}
.rt-ro-empty { color: var(--tg-muted); font-style: italic; }

.rt-sub-actions { flex-wrap: wrap; row-gap: 14px; }
.rt-rev-box {
    margin-top: 26px; padding: 14px;
    border: 1px solid var(--tg-border); border-radius: 10px;
    background: var(--tg-surface);
}
.rt-rev-q { font-size: 13px; font-weight: 600; margin-bottom: 10px; }
.rt-rev-pills { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
.rt-rev-pill {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 7px 13px; border-radius: 999px; cursor: pointer;
    border: 1px solid var(--tg-border); background: var(--tg-surface);
    color: var(--f7-text-color); font-size: 13px; width: auto;
    transition: background .12s, border-color .12s;
}
.rt-rev-pill:hover { background: var(--tg-hover); }
.rt-rev-pill.is-on {
    border-color: var(--f7-theme-color);
    background: color-mix(in srgb, var(--f7-theme-color) 10%, transparent);
    color: var(--f7-theme-color);
}
.rt-rev-note { width: 100%; }
/* .rt-pub-actions carries a 30px top margin for the bottom of a whole form; inside
   the review box it is one gap between a box and its button. */
.rt-rev-box .rt-pub-actions { margin-top: 14px; }
.rt-rev-hint { display: flex; align-items: center; gap: 7px; font-size: 12px; color: var(--tg-muted); margin-top: 9px; }
/* A choice that is already made. The chip keeps its own colour — that is what makes
   the option recognisable — and gains a ring, so "selected" reads the same on every
   palette rather than depending on one of them being darker. */
.rt-choice { cursor: pointer; padding: 5px 12px; }
.rt-choice-on { box-shadow: 0 0 0 2px var(--f7-theme-color); }

/* The minted review link, ON ONE LINE: the link, what it allows, and Copy.

   NO WRAP, deliberately. With flex-wrap the Copy button dropped to a second line the
   moment the select beside it grew — which read as two unrelated controls and left a
   button floating under a text field. The link field is the only thing here that can
   give: `flex: 1 1 0` with `min-width: 0` lets it shrink to whatever is left, and a
   link is the one item you do not need to read in full — you copy it. */
.rt-ask {
    width: 100%;
    display: flex;
    align-items: center;
    flex-wrap: nowrap;
    gap: 8px;
    margin-top: 12px;
}
.rt-ask-note { flex: 1 1 0; min-width: 0; }
.rt-ask-note[readonly] { cursor: text; color: var(--tg-muted); }
.rt-ask .rt-btn { flex: none; }

/* What the link allows — a select, not a pair of buttons. Two options, one of which is
   always the answer, and a select says that in the width of the answer itself rather
   than the width of both.

   `select.rt-in` zeroes the background-image to kill the native arrow, so the caret is
   drawn back here at matching specificity (element+class, later in the file) — a select
   with no affordance at all reads as a disabled field. Neutral grey rather than a theme
   colour so it holds up in both schemes. */
select.rt-ask-sel {
    flex: none;
    width: auto;
    padding-right: 28px;
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%23888' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round' d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 10px center;
    background-size: 10px 7px;
}

.rt-btn-ghost { background: transparent; border-color: var(--tg-border); }
.rt-btn-ghost:hover { background: var(--tg-hover); }
.rt-btn-ghost span { display: inline-block; margin-left: 6px; }

.rt-pub-done { text-align: center; padding: 44px 24px; }
.rt-pub-done i { color: #21a366; }
.rt-pub-done-t { font-size: 19px; font-weight: 600; margin-top: 14px; }
/* pre-line: a thank-you the owner wrote across two lines arrives with newlines in it, and
   collapsing them silently rewrites their copy. max-width keeps a long one readable. */
.rt-pub-done-s { font-size: 14px; color: var(--tg-muted); margin-top: 7px;
    white-space: pre-line; max-width: 460px; margin-left: auto; margin-right: auto; }
/* The honeypot. Never rendered visibly, so a person cannot fill it in; hidden with
   position rather than display:none because some bots skip display:none inputs. */
.rt-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* ── import wizard ──────────────────────────────────────────────────────── */

.rt-import { padding: 20px 24px 26px; }
.rt-imp-steps { display: flex; gap: 8px; margin-bottom: 20px; }
.rt-imp-step {
    display: flex; align-items: center; gap: 7px;
    font-size: 12.5px; color: var(--tg-muted);
}
.rt-imp-step-n {
    width: 20px; height: 20px; border-radius: 50%;
    background: color-mix(in srgb, var(--f7-text-color) 10%, transparent);
    display: inline-flex; align-items: center; justify-content: center; font-size: 11px;
}
.rt-imp-step-on { color: var(--f7-text-color); font-weight: 600; }
.rt-imp-step-on .rt-imp-step-n { background: var(--f7-theme-color); color: #fff; }
.rt-imp-sources { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; }
/* Import: a saved Airtable connection, and the remember-me line under a typed token. */
.rt-imp-conn { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border: 1px solid var(--tg-border); border-radius: 10px; }
.rt-imp-remember { display: flex; align-items: flex-start; gap: 8px; margin-top: 8px; font-size: 12.5px; color: var(--tg-muted, #6b7280); cursor: pointer; }
.rt-imp-remember input { margin-top: 2px; }
.rt-imp-source {
    border: 1px solid var(--tg-border); border-radius: 11px;
    padding: 16px 12px; text-align: center; cursor: pointer;
    font-size: 13px; transition: border-color .15s;
}
.rt-imp-source:hover { border-color: var(--f7-theme-color); }
.rt-imp-source i { color: var(--tg-muted); }
.rt-imp-source-t { margin-top: 8px; font-weight: 500; }
.rt-imp-source-s { font-size: 11.5px; color: var(--tg-muted); margin-top: 3px; }
.rt-imp-drop {
    border: 1.5px dashed var(--tg-border-strong); border-radius: 12px;
    padding: 34px 20px; text-align: center; color: var(--tg-muted); font-size: 13.5px;
    margin-top: 14px;
}
.rt-imp-drop-on { border-color: var(--f7-theme-color); background: color-mix(in srgb, var(--f7-theme-color) 6%, transparent); }
.rt-imp-map { max-height: 42vh; overflow-y: auto; margin-top: 12px; }
/*
   Four columns when the import is CREATING fields (source, arrow, new name, type) and
   three when it is mapping onto an existing table (source, arrow, which field) — because
   an existing field's type is not ours to change, so offering a type control there was
   an input that did nothing.
*/
.rt-imp-maprow { display: grid; grid-template-columns: 1fr 26px 1fr 130px; gap: 8px; align-items: center; padding: 6px 0; }
.rt-imp-maprow:has(.rt-imp-target) { grid-template-columns: 1fr 26px minmax(0, 1.2fr); }
.rt-imp-target { padding: 6px 9px; font-size: 12.5px; }
/* A column going nowhere is stated, not just faint: unmapped means its values are left
   behind, and that has to be visible before the Import button is pressed. */
.rt-imp-skipped { opacity: .5; }
.rt-imp-skipped .rt-imp-src { text-decoration: line-through; }

/* Whether row one is a label row or data. */
.rt-imp-header {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 10px 0 4px;
    font-size: 12.5px;
    cursor: pointer;
}
.rt-imp-guess { color: var(--tg-muted); }
.rt-imp-src { font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rt-imp-samples { font-size: 11px; color: var(--tg-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rt-imp-arrow { color: var(--tg-muted); text-align: center; }
.rt-imp-warn {
    background: color-mix(in srgb, #e8930c 12%, transparent);
    border: 1px solid color-mix(in srgb, #e8930c 30%, transparent);
    border-radius: 10px; padding: 12px 14px; font-size: 13px; margin-top: 14px;
    display: flex; gap: 10px; align-items: flex-start;
}
.rt-imp-warn i { color: #e8930c; flex: 0 0 auto; }
.rt-imp-progress { margin-top: 16px; }
.rt-imp-bar { height: 6px; border-radius: 3px; background: color-mix(in srgb, var(--f7-text-color) 10%, transparent); overflow: hidden; }
.rt-imp-bar-in { height: 100%; background: var(--f7-theme-color); transition: width .25s; }
.rt-imp-nums { font-size: 12px; color: var(--tg-muted); margin-top: 7px; font-variant-numeric: tabular-nums; }

/* ── automation trigger picker ──────────────────────────────────────────── */

.rt-trigs { padding: 6px; width: 380px; max-height: 62vh; overflow-y: auto; }
.rt-trig-search {
    display: flex; align-items: center; gap: 8px;
    padding: 8px 10px; margin-bottom: 6px;
    border-bottom: 1px solid var(--tg-border);
}
.rt-trig-search input { border: 0; outline: none; background: transparent; font-size: 14px; flex: 1 1 auto; color: var(--f7-text-color); font-family: inherit; }
.rt-trig {
    display: flex; align-items: flex-start; gap: 12px;
    padding: 11px 12px; border-radius: 10px; cursor: pointer;
}
.rt-trig:hover { background: var(--tg-hover); }
.rt-trig-on { background: color-mix(in srgb, var(--f7-theme-color) 9%, transparent); }
.rt-trig-tick { width: 16px; flex: 0 0 auto; color: var(--f7-theme-color); padding-top: 2px; }
.rt-trig-icon {
    width: 30px; height: 30px; border-radius: 8px; flex: 0 0 auto;
    display: flex; align-items: center; justify-content: center;
    background: color-mix(in srgb, var(--f7-text-color) 8%, transparent);
}
.rt-trig-t { font-size: 14px; font-weight: 600; }
.rt-trig-s { font-size: 12.5px; color: var(--tg-muted); margin-top: 2px; line-height: 1.4; }

/* ── the quarantine review list ─────────────────────────────────────────── */

.rt-quar { width: 420px; padding: 14px; max-height: 60vh; overflow-y: auto; }
.rt-quar-row { display: grid; grid-template-columns: 1fr auto; gap: 10px; align-items: center; padding: 7px 0; border-bottom: 1px solid var(--tg-border); }
.rt-quar-raw { font-size: 13px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; overflow: hidden; text-overflow: ellipsis; }
.rt-quar-why { font-size: 11.5px; color: var(--tg-muted); }

/* ── mobile ─────────────────────────────────────────────────────────────── */

@media (max-width: 910px) {
    .rt-viewbar { padding: 6px; }
    .rt-vb { height: 32px; }
    .rt-fb-rail { display: none; }
    .rt-cal-grid { grid-template-columns: 46px repeat(7, minmax(96px, 1fr)); min-width: 720px; }
}

/* ── spinner ────────────────────────────────────────────────────────────── */
/*
   Our own, deliberately.

   F7's .preloader element has its inner markup injected by F7 itself. Inside a
   reactive template that F7 is also diffing, those two owners collide and the
   component's creation rejects with an appendChild error. One div and a keyframe
   has no such ambiguity.
*/
.rt-spin {
    width: 26px;
    height: 26px;
    border-radius: 50%;
    border: 2.5px solid color-mix(in srgb, var(--f7-theme-color) 22%, transparent);
    border-top-color: var(--f7-theme-color);
    animation: rt-spin 0.7s linear infinite;
}
@keyframes rt-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
    .rt-spin { animation-duration: 2s; }
}

/* ── command-out cards ──────────────────────────────────────────────────── */
/*
   The agent's answers about tables. Deliberately compact: a card is read inside a
   run, not worked in, so it shows enough to trust the answer and hands off to the
   real app for anything more.
*/
.co-table-list { display: flex; flex-direction: column; gap: 1px; margin: 4px 0; }
.co-table-row {
    display: flex; align-items: center; gap: 8px;
    padding: 7px 8px; border-radius: 8px; cursor: pointer; font-size: 13px;
}
.co-table-row:hover { background: color-mix(in srgb, var(--f7-text-color) 6%, transparent); }
.co-table-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
/*
   The richer row used by the tables_list CARD: name on top, then what the table
   actually holds — the counts, and its description when it has one. The plain
   .co-table-row above is still what the tool-result card draws, where the answer
   is a bare list and the extra line would be noise.
*/
.co-table-row-rich { align-items: flex-start; padding: 9px 8px; }
.co-table-row-rich > .f7-icons:first-child { margin-top: 1px; }
.co-table-rowbody { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.co-table-row-rich .co-table-name { flex: 0 0 auto; }
.co-table-sub {
    /* center, not baseline: the owner chip is a pill with an avatar in it, and a
       baseline row hangs it off the text it sits beside. */
    display: flex; flex-wrap: wrap; align-items: center; gap: 3px 8px;
    font-size: 11px; line-height: 1.4;
}
.co-table-desc {
    color: color-mix(in srgb, var(--f7-text-color) 45%, transparent);
    min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.co-table-go {
    flex: 0 0 auto; font-size: 14px; margin-top: 1px;
    color: color-mix(in srgb, var(--f7-text-color) 28%, transparent);
}
.co-table-count {
    font-size: 11px; color: color-mix(in srgb, var(--f7-text-color) 55%, transparent);
    font-variant-numeric: tabular-nums; white-space: nowrap;
}

.co-table-title {
    display: flex; align-items: center; gap: 7px;
    font-size: 13px; font-weight: 600; margin: 2px 0 8px;
}
.co-table-title .co-table-count { margin-left: auto; font-weight: 400; }

.co-table-schema { margin: 4px 0; }
.co-field-row {
    display: flex; align-items: center; gap: 8px;
    padding: 5px 6px; font-size: 12.5px;
    border-bottom: 1px solid color-mix(in srgb, var(--f7-text-color) 8%, transparent);
}
.co-field-row:last-child { border-bottom: 0; }
.co-field-name { font-weight: 500; }
.co-field-type { color: color-mix(in srgb, var(--f7-text-color) 50%, transparent); font-size: 11.5px; }
.co-field-flag {
    font-size: 10px; padding: 1px 6px; border-radius: 7px;
    background: color-mix(in srgb, var(--f7-theme-color) 16%, transparent);
    color: var(--f7-theme-color);
}
.co-field-choices {
    margin-left: auto; font-size: 11px;
    color: color-mix(in srgb, var(--f7-text-color) 45%, transparent);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 45%;
}
.co-table-share {
    display: flex; align-items: center; gap: 6px; margin-top: 9px;
    padding: 7px 9px; border-radius: 8px; font-size: 11.5px;
    background: color-mix(in srgb, var(--f7-theme-color) 8%, transparent);
    color: var(--f7-theme-color); word-break: break-all;
}

.co-table-records { margin: 4px 0; }
.co-table-rows { display: flex; flex-direction: column; gap: 4px; }
.co-table-rec {
    display: flex; flex-wrap: wrap; gap: 4px 14px;
    padding: 7px 9px; border-radius: 8px; cursor: pointer; font-size: 12.5px;
    background: color-mix(in srgb, var(--f7-text-color) 4%, transparent);
}
.co-table-rec:hover { background: color-mix(in srgb, var(--f7-text-color) 8%, transparent); }
.co-rec-cell { display: inline-flex; gap: 5px; min-width: 0; }
.co-rec-cell b {
    font-weight: 500; opacity: .55;
    max-width: 110px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.co-table-more { font-size: 11.5px; opacity: .55; padding: 3px 9px; }

/* The cells a record write actually SET, read back from props.

   Laid out as label-over-value rows rather than the inline chips co-table-rec uses: a
   written value is often a paragraph (a research note, a rationale), and the inline form
   truncates all of them to nothing. Each value is allowed three lines and then clamps. */
.co-rec-fields { display: flex; flex-direction: column; gap: 2px; }
.co-rec-field {
    display: flex; flex-direction: column; gap: 1px;
    padding: 7px 9px; border-radius: 8px;
    background: color-mix(in srgb, var(--f7-text-color) 4%, transparent);
}
.co-rec-field-name {
    font-size: 11px; font-weight: 500; opacity: .55;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.co-rec-field-value {
    font-size: 12.5px; line-height: 1.45; word-break: break-word;
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}

/* ═══════════════════════════════════════════════════════════════════════════
   P6 — relations, derived columns, comments and locked columns
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── linked-record chips in the grid ────────────────────────────────────── */
/*
   A link chip and a lookup chip look deliberately different. A link chip is a
   way to REACH another record, so it reads as something you can act on; a lookup
   chip is a reading OF another record and must not invite a click it cannot
   honour. Same shape, different weight.
*/
/* In-cell link editor: the linked chips with an x each, and the + that opens the picker. */
.tg-in-links { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; padding: 4px 6px; min-height: 30px; }
.tg-in-linkchip {
    display: inline-flex; align-items: center; gap: 4px; max-width: 100%;
    padding: 2px 6px 2px 8px; border-radius: 5px; font-size: 12px; line-height: 1.5; white-space: nowrap;
    background: color-mix(in srgb, var(--f7-theme-color) 12%, transparent); color: var(--f7-theme-color);
}
.tg-in-linkx { cursor: pointer; opacity: .6; border-radius: 3px; padding: 1px; }
.tg-in-linkx:hover { opacity: 1; background: color-mix(in srgb, var(--f7-theme-color) 18%, transparent); }
.tg-in-linkadd {
    display: inline-flex; align-items: center; gap: 4px; height: 24px; padding: 0 8px; border-radius: 6px;
    border: 1px dashed var(--tg-border); background: transparent; color: var(--tg-muted); font-size: 12px; cursor: pointer;
}
.tg-in-linkadd:hover { color: var(--f7-theme-color); border-color: var(--f7-theme-color); }
.tg-link-chip {
    display: inline-flex;
    align-items: center;
    max-width: 100%;
    margin-right: 4px;
    padding: 2px 8px;
    border-radius: 5px;
    font-size: 12px;
    line-height: 1.5;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    background: color-mix(in srgb, var(--f7-theme-color) 12%, transparent);
    color: color-mix(in srgb, var(--f7-theme-color) 88%, var(--f7-text-color));
    border: 1px solid color-mix(in srgb, var(--f7-theme-color) 24%, transparent);
}
.tg-look-chip {
    display: inline-flex;
    align-items: center;
    max-width: 100%;
    margin-right: 4px;
    padding: 2px 7px;
    border-radius: 5px;
    font-size: 12px;
    line-height: 1.5;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    background: color-mix(in srgb, var(--f7-text-color) 7%, transparent);
    color: var(--tg-muted);
}
/* A formula that could not be worked out for one row. Named, not blank. */
.tg-formula-err {
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: .02em;
    padding: 1px 6px;
    border-radius: 4px;
    background: color-mix(in srgb, #dc3545 14%, transparent);
    color: color-mix(in srgb, #dc3545 88%, var(--f7-text-color));
    cursor: help;
}

/* ── the comment badge on a grid row ───────────────────────────────────── */
/*
   It lives in the gutter beside the expand affordance, and appears only when the
   row actually has comments — a badge showing 0 on every row is noise.
*/
.tg-cmt-badge {
    position: absolute;
    right: 3px;
    bottom: 2px;
    display: inline-flex;
    align-items: center;
    gap: 2px;
    padding: 0 4px;
    height: 14px;
    border-radius: 7px;
    font-size: 9.5px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    background: color-mix(in srgb, var(--f7-theme-color) 16%, transparent);
    color: color-mix(in srgb, var(--f7-theme-color) 90%, var(--f7-text-color));
    pointer-events: none;
}
.tg-gutter { position: relative; }

/* ── the link control in the expanded record ───────────────────────────── */
.rt-links {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
}
.rt-link-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    max-width: 100%;
    padding: 3px 5px 3px 9px;
    border-radius: 6px;
    font-size: 13px;
    background: color-mix(in srgb, var(--f7-theme-color) 11%, transparent);
    border: 1px solid color-mix(in srgb, var(--f7-theme-color) 22%, transparent);
    color: color-mix(in srgb, var(--f7-theme-color) 90%, var(--f7-text-color));
}
.rt-link-chip-t {
    cursor: pointer;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.rt-link-chip-t:hover { text-decoration: underline; }
.rt-link-x {
    cursor: pointer;
    opacity: .5;
    border-radius: 3px;
    padding: 1px;
}
.rt-link-x:hover { opacity: 1; background: color-mix(in srgb, var(--f7-text-color) 10%, transparent); }
.rt-link-add {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 4px 10px;
    border: 1px dashed color-mix(in srgb, var(--f7-text-color) 22%, transparent);
    border-radius: 6px;
    background: transparent;
    color: var(--tg-muted);
    font-size: 12.5px;
    font-family: inherit;
    cursor: pointer;
    width: auto;
}
.rt-link-add:hover {
    border-color: var(--f7-theme-color);
    color: var(--f7-theme-color);
    border-style: solid;
}
.rt-spin-sm { width: 15px; height: 15px; border-width: 2px; }

/* ── the record picker ─────────────────────────────────────────────────── */
.rt-picker { width: 268px; }
.rt-picker-head {
    padding: 10px 12px 6px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--tg-muted);
}
.rt-picker-search {
    display: flex;
    align-items: center;
    gap: 7px;
    margin: 0 10px 6px;
    padding: 5px 9px;
    border-radius: 7px;
    background: color-mix(in srgb, var(--f7-text-color) 6%, transparent);
}
.rt-picker-search input {
    flex: 1;
    min-width: 0;
    border: 0;
    outline: 0;
    background: transparent;
    color: var(--f7-text-color);
    font-size: 13px;
    font-family: inherit;
}
.rt-picker-list { max-height: 260px; overflow-y: auto; padding: 0 6px 8px; }
.rt-picker-row {
    padding: 7px 10px;
    border-radius: 6px;
    font-size: 13.5px;
    cursor: pointer;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.rt-picker-row:hover { background: color-mix(in srgb, var(--f7-theme-color) 12%, transparent); }
.rt-picker-empty { padding: 12px; font-size: 12.5px; color: var(--tg-muted); text-align: center; }

/* ── a locked column, said in words ────────────────────────────────────── */
.rt-field-lock {
    display: flex;
    align-items: center;
    gap: 5px;
    margin-top: 5px;
    font-size: 11.5px;
    color: var(--tg-muted);
}

/*
   Record comments are DEFERRED.

   The thread UI that lived here has been removed: conversation about a record will
   arrive as room chat in a later phase rather than as a second, parallel comment
   system nobody would find. The server side (table_comment, the endpoints, the
   socket relay) is untouched and still records what it is given — this is a UI
   decision, not a data one. See also `.tg-cmt-badge`, kept for the same reason.
*/

/* ── the field editor's new panes ─────────────────────────────────────── */
.rt-fe-check {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 8px;
    font-size: 13px;
    cursor: pointer;
}
.rt-fe-tokens {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
    margin-top: 2px;
}
.rt-fe-fn {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 3px 8px;
    border-radius: 5px;
    font-size: 11.5px;
    cursor: pointer;
    background: color-mix(in srgb, var(--f7-text-color) 7%, transparent);
    color: var(--f7-text-color);
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.rt-fe-fn:hover { background: color-mix(in srgb, var(--f7-theme-color) 16%, transparent); color: var(--f7-theme-color); }
.rt-fe-fn-code {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 11px;
    letter-spacing: -.01em;
}
textarea.rt-fe-formula {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 12.5px;
    line-height: 1.6;
}
.rt-fe-ferr, .rt-fe-fok {
    display: flex;
    align-items: flex-start;
    gap: 6px;
    margin-top: 7px;
    font-size: 11.5px;
    line-height: 1.5;
}
.rt-fe-ferr { color: color-mix(in srgb, #dc3545 85%, var(--f7-text-color)); }
.rt-fe-fok  { color: color-mix(in srgb, #21a366 82%, var(--f7-text-color)); }

/* ═══════════════════════════════════════════════════════════════════════════
   The "New table" dialog
   ═══════════════════════════════════════════════════════════════════════════
   A create dialog is not a page. It should be the size of its content, read in
   one glance, and put the primary action where the eye finishes — so: narrow,
   height-hugging, one column of fields, and a footer that stays put.

   The popup root asks for height:auto (see the shell), which needs the page
   content to stop insisting on 100% or F7 leaves a void under the buttons.
   ═══════════════════════════════════════════════════════════════════════════ */

/*
   A DIALOG THAT SIZES TO ITS CONTENT.

   F7 centres a desktop popup with negative margins:

       left: 50%; top: 50%;
       margin-left: calc(-1 * var(--f7-popup-tablet-width) / 2);
       margin-top:  calc(-1 * var(--f7-popup-tablet-height) / 2);

   `calc(-1 * auto / 2)` is not a valid length, so the declaration is DROPPED and
   margin-top falls back to 0 — the popup's top edge lands at 50% of the viewport and
   the rest of it hangs off the bottom of the screen. That is what "stuck halfway down"
   was: not a broken animation, arithmetic on `auto`.

   Centred with `inset: 0; margin: auto` instead, which needs no height to do its job.
   `transform` is deliberately left alone — F7 animates the popup in and out on that
   property, so taking it for centring would kill the animation (the same trap the
   agent-live popup documents in CLAUDE.md).

   Scoped to F7's own tablet media query so the mobile full-screen popup is untouched.
*/
@media (min-width: 630px) and (min-height: 630px) {
    .popup.popup-table-new:not(.popup-tablet-fullscreen) {
        /*
           `bottom` is deliberately `auto`. A fixed-position box with BOTH top and
           bottom pinned and `height: auto` does not hug its content — it stretches to
           fill, and the max-height then becomes the height. That is where the 265px of
           dead space under the footer came from.

           So: pin the top only, and do the centring in `transform`, whose percentages
           resolve against the element's OWN height — which is exactly the number CSS
           refuses to give the margin.
        */
        left: 50%;
        top: 50%;
        right: auto;
        bottom: auto;
        width: min(94vw, 520px);
        height: auto;
        max-height: 86vh;
        margin: 0 0 0 calc(-1 * min(94vw, 520px) / 2);
        /* Off the bottom of the screen, then centred — F7's own slide, re-expressed
           with the -50% that centring needs baked in. Both states are overridden or
           the popup would jump the moment F7's rule takes over. */
        transform: translate3d(0, calc(100vh - 50%), 0);
    }
    .popup.popup-table-new:not(.popup-tablet-fullscreen).modal-in {
        transform: translate3d(0, -50%, 0);
    }
    .popup.popup-table-new:not(.popup-tablet-fullscreen).modal-out {
        transform: translate3d(0, calc(100vh - 50%), 0);
    }
}

.rt-new {
    display: flex;
    flex-direction: column;
    height: auto;
    max-height: calc(86vh - var(--f7-navbar-height, 56px));
    overflow: hidden;                 /* the BODY scrolls, not the whole sheet */
}
/*
   The same editor, embedded in an agent create card.
   No popup shell, so no viewport-relative height: the card is already inside a
   scrolling run of cards and a second scroller nested in it would trap the wheel.
*/
.rt-new-inline { display: block; }
.rt-new-inline .rt-new {
    max-height: none;
    overflow: visible;
}
.rt-new-inline .rt-new-body {
    overflow: visible;
    padding: 4px 0 0;
}
/*
   Inline, the footer is not a pinned bar — it is the last row of a card that is
   already a surface. So it drops the page-coloured band (which paints the card's
   own dark page colour back over it and reads as a black stripe) and keeps only
   the hairline that separates it from the columns. The horizontal padding goes
   with the band: the note and the buttons line up with the body above them
   instead of being inset inside a strip of their own.
*/
.rt-new-inline .rt-new-foot {
    padding: 12px 0 2px;
    background: transparent;
    border-top-color: var(--tg-line);
}
.rt-new-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 16px 18px 14px;
}

/* ── labels and hints ──────────────────────────────────────────────────── */
.rt-new-label {
    display: block;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .07em;
    text-transform: uppercase;
    color: var(--tg-muted);
}
.rt-new-name { margin-top: 6px; font-size: 14px; padding: 10px 12px; }

/*
   BUSY, THE ONLY WAY THAT WORKS HERE.

   `?disabled=${x}` — lit-html's boolean-attribute syntax — is SILENTLY IGNORED by this
   Framework7 build: the attribute is never written, so every "disabled while saving"
   button in this module was clickable and a double-click submitted twice. And the plain
   form is worse, not better: `disabled=${false}` stringifies to `disabled="false"`,
   which HTML treats as disabled. (Same family as the interpolated `checked` trap.)

   An interpolated CLASS does work, so busy-ness rides in one. `pointer-events: none` is
   what actually prevents the second click; the opacity is so it looks the way it behaves.
   Handlers guard themselves as well — a keyboard can still reach a focused control.
*/
.rt-btn.rt-btn-busy,
.rt-in.rt-in-busy {
    opacity: .55;
    pointer-events: none;
}

/* ── describe it and the agent designs it ───────────────────────────────── */
/*
   Sits between the name and the columns, because that is the order of the thought:
   what it is called, what it is for, what is in it. A tinted band rather than a plain
   row so it reads as a different KIND of input — one that fills the form in below it
   rather than being part of it.
*/
.rt-new-ai {
    margin-top: 16px;
    padding: 10px 12px;
    border-radius: 10px;
    background: color-mix(in srgb, var(--f7-theme-color) 6%, transparent);
    border: 1px solid color-mix(in srgb, var(--f7-theme-color) 16%, transparent);
}
.rt-new-ai .rt-new-label { color: color-mix(in srgb, var(--f7-theme-color) 88%, var(--f7-text-color)); }
.rt-new-ai-row {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 7px;
}
.rt-new-ai-in { flex: 1 1 auto; min-width: 0; padding: 8px 10px; font-size: 13px; }
.rt-new-ai-go {
    flex: 0 0 auto;
    gap: 5px;
    background: var(--f7-theme-color);
    color: #fff;
    border-color: transparent;
}
.rt-new-ai-go:disabled { opacity: .55; }
.rt-new-ai-note {
    margin-top: 7px;
    font-size: 11.5px;
    line-height: 1.45;
    color: var(--tg-muted);
}

.rt-new-sec {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 18px 0 0;
}
/* The first one is the table's own name, at the very top of the body — it needs no
   leading gap, and the label inside it already carries its own. */
.rt-new-body > .rt-new-sec:first-child { margin-top: 0; }
/* "TABLE NAME" is two words and was wrapping to two lines once the pill sat beside it. */
.rt-new-sec > .rt-new-label { white-space: nowrap; }

/* Locked on: the design already has this form, so it is stated rather than offered. */
.rt-new-addform.is-locked { cursor: default; }
.rt-new-addform.is-locked:hover { background: color-mix(in srgb, var(--f7-theme-color) 12%, transparent); }

/* "Add form", beside the table's name rather than after the columns: whether people will
   fill this in is a fact about the thing being made, not a step that comes later. */
.rt-new-addform {
    /* Hugs its text. A bare <button> inherits a full-width box from the framework, which
       is how this ended up as a pill the width of the dialog. */
    margin-left: auto;
    width: auto; flex: 0 0 auto;
    display: inline-flex; align-items: center; gap: 5px;
    white-space: nowrap;
    padding: 4px 10px; border-radius: 999px;
    font-size: 11.5px; font-weight: 600;
    cursor: pointer;
    color: var(--f7-theme-color);
    background: transparent;
    border: 1px solid color-mix(in srgb, var(--f7-theme-color) 35%, transparent);
}
.rt-new-addform:hover { background: color-mix(in srgb, var(--f7-theme-color) 8%, transparent); }
.rt-new-addform.is-on {
    background: color-mix(in srgb, var(--f7-theme-color) 12%, transparent);
    border-color: color-mix(in srgb, var(--f7-theme-color) 55%, transparent);
}
.rt-new-form { margin-top: 12px; }
.rt-new-form .rt-new-hint { margin-bottom: 0; }
.rt-new-count {
    font-size: 10.5px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    padding: 1px 6px;
    border-radius: 8px;
    color: var(--tg-muted);
    background: color-mix(in srgb, var(--f7-text-color) 8%, transparent);
}
.rt-new-hint {
    margin: 5px 0 10px;
    font-size: 11.5px;
    line-height: 1.5;
    color: var(--tg-muted);
}

/* ── the column rows ───────────────────────────────────────────────────── */
/*
   A grid, not flex percentages: the name and the type have to line up down the
   list, and the trailing slot has to hold either a remove button or the "title"
   marker without the row shifting.
*/
.rt-new-cols { display: grid; gap: 6px; }
.rt-new-col {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 148px 24px;
    align-items: center;
    gap: 6px;
}
/* The form marker rides INSIDE the name box, at its right edge — a row of columns is
   already three controls wide and a fourth track cost width the names needed. */
.rt-new-cnamewrap { position: relative; min-width: 0; }
.rt-new-cnamewrap .rt-new-cname { padding-right: 30px !important; }

/*
   Shown on every eligible column, not only the chosen ones: "not asked" is a state
   somebody needs to see in order to change it. ONE size — the difference is colour, the
   way an enabled control differs from a disabled one. Two sizes read as two different
   controls rather than two states of the same one.
 */
.rt-new-onform {
    position: absolute; right: 5px; top: 50%; transform: translateY(-50%);
    display: flex; align-items: center; justify-content: center;
    width: 22px; height: 22px; border-radius: 6px;
    cursor: pointer;
    color: var(--tg-muted);
    opacity: .45;
    transition: opacity .12s ease, color .12s ease;
}
.rt-new-onform i { font-size: 15px; }
.rt-new-onform:hover { opacity: .8; background: color-mix(in srgb, var(--f7-text-color) 7%, transparent); }
.rt-new-onform.is-on {
    opacity: 1;
    color: color-mix(in srgb, var(--f7-theme-color) 88%, var(--f7-text-color));
}
.rt-new-onform.is-on:hover { opacity: 1; }
/* ASKED — BUT BY A LATER FORM.

   A third state, because grey meant "nobody will ever fill this in" and that was wrong
   about every column the second form of a workflow collects. Half-strength theme colour:
   related to the solid mark, plainly not the same thing, and the tooltip names the form.
   It stays clickable — adding it to THIS form as well is a legitimate choice, and the
   design repair will decide which form keeps it. */
.rt-new-onform.is-other {
    opacity: .75;
    color: color-mix(in srgb, var(--f7-theme-color) 45%, var(--tg-muted));
}
/* Not a question and never can be — no press, no hover, and it says why on hover. */
.rt-new-onform.is-never { opacity: .22; cursor: help; }
.rt-new-onform.is-never:hover { opacity: .35; background: none; }

/* THE OTHER FORMS THIS TABLE GETS — listed, not edited. Quiet: the dialog is about the
   table and the form that starts the case, and these are here so its promise matches
   what the build makes. */
.rt-new-forms2 {
    margin-top: 14px;
    padding-top: 12px;
    border-top: 1px solid var(--tg-border);
}
.rt-new-forms2-h {
    font-size: 11px; font-weight: 700; letter-spacing: .4px; text-transform: uppercase;
    color: var(--tg-muted);
    margin-bottom: 8px;
}
.rt-new-forms2-row { display: flex; align-items: flex-start; gap: 9px; padding: 5px 0; }
.rt-new-forms2-row i { color: var(--tg-muted); margin-top: 2px; flex: none; }
.rt-new-forms2-body { min-width: 0; }
.rt-new-forms2-t { font-size: 13.5px; font-weight: 600; }
.rt-new-forms2-q { font-size: 12px; color: var(--tg-muted); margin-top: 2px; }

/*
   The question card, opened under its own row.

   It is the FORM BUILDER'S card — the same .rt-fb-card markup and the same styles — so
   there is one design for "this is a question and here is how it reads", wherever you
   happen to be standing. Only the placement is defined here.
 */
.rt-new-colblock { display: grid; gap: 6px; }
.rt-fldcard { padding: 2px 0 6px; }
.rt-fldcard .rt-fb-card { margin: 0; }
.rt-fldcard .rt-fb-q-tool.is-off { opacity: .25; pointer-events: none; }
.rt-fldpop-note {
    font-size: 12px; line-height: 1.45;
    color: var(--tg-muted);
}
.rt-fldpop-add { margin-top: 10px; }
.rt-new-col .rt-in { padding: 8px 10px; font-size: 13px; border-radius: 7px; }
.rt-new-cname { min-width: 0; }

/* A select with appearance:none has no arrow, so it reads as a text box you can
   type into. The caret says it is a choice. */
.rt-new-ctype { position: relative; min-width: 0; }
.rt-new-ctype select { width: 100%; padding-right: 26px !important; }
.rt-new-caret {
    position: absolute;
    right: 8px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 13px;
    color: var(--tg-muted);
    pointer-events: none;
}

.rt-new-title {
    font-size: 9.5px;
    font-weight: 700;
    letter-spacing: .05em;
    text-transform: uppercase;
    text-align: center;
    color: color-mix(in srgb, var(--f7-theme-color) 85%, var(--f7-text-color));
    cursor: help;
}
.rt-new-x {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: 6px;
    color: var(--tg-muted);
    cursor: pointer;
}
.rt-new-x:hover {
    color: #dc3545;
    background: color-mix(in srgb, #dc3545 12%, transparent);
}

/* A column something else depends on — a workflow's approval step writes into it, a
   branch tests it, a form asks for it. It keeps its type control (nothing references
   a column's TYPE) and loses its name box and its delete, because the design points
   at it by name. The lock sits where the × would, so the row does not shift. */
.rt-new-lock {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: 6px;
    color: var(--tg-muted);
    opacity: .7;
    cursor: help;
}
.rt-new-col.is-locked .rt-new-cname {
    cursor: help;
    opacity: .75;
    background: color-mix(in srgb, var(--f7-text-color) 5%, transparent);
}

.rt-new-add {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    margin-top: 10px;
    padding: 5px 9px 5px 6px;
    border: 0;
    border-radius: 7px;
    background: transparent;
    color: var(--f7-theme-color);
    font-family: inherit;
    font-size: 12.5px;
    font-weight: 600;
    cursor: pointer;
    width: auto;
}
.rt-new-add:hover { background: color-mix(in srgb, var(--f7-theme-color) 10%, transparent); }

/* ── the footer ────────────────────────────────────────────────────────── */
/*
   Pinned rather than floating at the end of the content: the primary action
   should be findable without scrolling, whatever the column count.
*/
.rt-new-foot {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 11px 18px;
    border-top: 1px solid var(--tg-line);
    /*
       A wash of the text colour, not --f7-page-bg-color. The page colour is #e2e2e2
       in light and #000 in dark: on the light sheet it read as a recessed bar, which
       is what was wanted, but inside the dark sheet (a grey surface) it painted a
       PURE BLACK strip across the bottom of the card. A percentage of the ink sits
       just off the surface in both themes, so the bar stays a bar and never becomes
       a hole.
    */
    background: color-mix(in srgb, var(--f7-text-color) 4%, transparent);
}
.rt-new-note { font-size: 11px; color: var(--tg-muted); }
.rt-new-acts { display: flex; align-items: center; gap: 8px; }

@media (max-width: 560px) {
    .rt-new-col { grid-template-columns: minmax(0, 1fr) 120px 24px; }
    .rt-new-foot { flex-direction: column; align-items: stretch; }
    .rt-new-note { order: 2; text-align: center; }
    .rt-new-acts { justify-content: flex-end; }
}

/* ══════════════════════════════════════════════════════════════════════════
   Room user, approval, and the row's chat
   ══════════════════════════════════════════════════════════════════════════ */

/* ── the row's chat button ─────────────────────────────────────────────── */
/*
   It lives in the 46px lane the header's `+` occupies, so it costs no width: that
   lane is dead space on every data row and the table already ends there.

   Two states, and they are the whole design:
     • HOVER — it appears while the pointer is on the row, so a table being read is
       not a wall of bubble icons.
     • SELECTED — it stays put while the row holds the cell selection, because that
       is the row you are working on and a control that vanishes when you look away
       from it is a control you cannot click.
   A row that already has messages keeps it visible always: a conversation you
   cannot see is one nobody joins.
*/
.tg-rowchat {
    width: 46px;
    flex: 0 0 46px;
    justify-content: center;
    align-items: center;
    color: var(--tg-muted);
    cursor: pointer;
    gap: 3px;
    /* visibility, not display: the lane must keep its width or the row's total
       width changes on hover and the horizontal scrollbar twitches. */
    visibility: hidden;
}
.tg-row-row:hover .tg-rowchat,
.tg-row-sel .tg-rowchat,
.tg-rowchat-has { visibility: visible; }
.tg-rowchat:hover { color: var(--f7-theme-color); background: var(--tg-hover); }
.tg-rowchat-has { color: color-mix(in srgb, var(--f7-theme-color) 85%, var(--f7-text-color)); }
.tg-rowchat-n {
    font-size: 10px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

/* ── the form mark, at the end of the first cell ────────────────────────────
   A row that arrived through a form, and the way back to it.

   A PLAIN FLEX ITEM, and that is load-bearing. The first try positioned it
   absolutely inside the cell, which meant giving the cell `position: relative` —
   and the first data cell is `.tg-cell.tg-frozen` (position: sticky) with an inline
   `left: 57px` that offsets it past the gutter. Same specificity, later in the file,
   so relative won: the column stopped freezing AND the inline left shifted every
   first cell 57px to the right, over the next column. The cell is already
   `display: flex; align-items: center`, so the mark needs no positioning at all.

   Nor a mask. Fading the last 34px of `.tg-cv` to make room clipped the content
   instead of the overflow — a right-aligned date column lost its year ("12 Sep 202").
   As a flex item the mark takes its own width and `.tg-cv` (min-width: 0, ellipsis)
   simply shrinks, which is what ellipsis is for.

   Quiet by default and full strength on hover, like the row's chat button: on a
   request table EVERY row came from a form, and a mark at full contrast on every
   line reads as a column of noise next to the one thing the first cell must stay
   legible for — the row's own title. */
.tg-formmark {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    border-radius: 5px;
    color: var(--tg-muted);
    opacity: .4;
    cursor: pointer;
    transition: opacity .12s, color .12s, background .12s;
}
.tg-row-row:hover .tg-formmark { opacity: .8; }
.tg-formmark:hover {
    opacity: 1;
    color: var(--f7-theme-color);
    background: var(--tg-hover);
}

/* ── the room-user picker, inside a cell ───────────────────────────────── */
.tg-in-people { max-height: 260px; }
.tg-in-person {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}
.tg-in-person .tg-user { min-width: 0; }

/* ── the task column ───────────────────────────────────────────────────── */
/*
   A task cell is the SAME line the room's task list draws, reduced to what fits a
   grid row: a tick, the words, and the state that changes what you should do about
   it. The rest of a to-do — description, notes, history — is one tap away in the
   task itself, which is where it belongs.

   The empty cell shows the ASK and is quiet until hovered, exactly as the approval
   column is, and for the same reason: a hundred empty rows must not read as a
   hundred buttons.
*/
.tg-task-ask {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 11.5px;
    color: var(--tg-muted);
    opacity: 0;
    cursor: pointer;
    transition: opacity .12s ease;
}
.tg-row-row:hover .tg-task-ask,
.tg-row-sel .tg-task-ask { opacity: 1; }
.tg-task-ask:hover { color: var(--f7-theme-color); }

.tg-task {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    min-width: 0;
    max-width: 100%;
    font-size: 12px;
    cursor: pointer;
}
.tg-task i { flex: none; }
/* The words take the room that is left and give way with an ellipsis. A task title is
   a sentence; a cell is one line. */
.tg-task-t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
/* Done is struck through and dimmed — the two things every task list in the world
   does, so nobody has to learn this one. */
.tg-task-yes { color: var(--tg-muted); }
.tg-task-yes .tg-task-t { text-decoration: line-through; }
.tg-task-yes i { color: #21a366; }
.tg-task-wait i { color: var(--tg-muted); }
/* Overdue is the one state worth a colour: it is the only one that says DO SOMETHING. */
.tg-task-no i { color: #dc3545; }
.tg-task-no .tg-task-t { color: #dc3545; }
/* "+3" — how much more is in this cell, without pretending the cell is a list. */
.tg-task-more {
    flex: none;
    margin-left: 6px;
    padding: 1px 6px;
    border-radius: 999px;
    font-size: 10.5px;
    font-weight: 600;
    color: var(--tg-muted);
    background: color-mix(in srgb, var(--f7-text-color) 8%, transparent);
}

/* ── the approval column ───────────────────────────────────────────────── */
/*
   An empty approval cell shows the ASK rather than nothing: the column exists to be
   started, and "no request yet" is the state most rows are in most of the time. It
   is quiet until hovered so a hundred empty rows do not read as a hundred buttons.
*/
.tg-ap-ask {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 11.5px;
    color: var(--tg-muted);
    opacity: 0;
    cursor: pointer;
    transition: opacity .12s ease;
}
.tg-row-row:hover .tg-ap-ask,
.tg-row-sel .tg-ap-ask { opacity: 1; }
.tg-ap-ask:hover { color: var(--f7-theme-color); }

.tg-ap {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    max-width: 100%;
    padding: 2px 8px 2px 6px;
    border-radius: 11px;
    font-size: 11.5px;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
}
/* Named for the STATE, not the colour, so a palette change is one edit here and the
   cell renderer never learns a hex value. */
.tg-ap-wait {
    background: color-mix(in srgb, #e0a30b 16%, transparent);
    color: color-mix(in srgb, #8a6300 82%, var(--f7-text-color));
}
.tg-ap-yes {
    background: color-mix(in srgb, #21a366 16%, transparent);
    color: color-mix(in srgb, #14724a 82%, var(--f7-text-color));
}
.tg-ap-no {
    background: color-mix(in srgb, #dc3545 15%, transparent);
    color: color-mix(in srgb, #a71d2a 84%, var(--f7-text-color));
}
/* ── the workflow case column ───────────────────────────────────────────────
   A TAG, and never editable. Deliberately the same geometry as .tg-ap above: two state
   columns side by side (Manager approval, Workflow case) should read as one system, not
   two. The empty state borrows .tg-ap-ask's manners for the same reason — quiet until the
   row is hovered, so a hundred rows with no case do not read as a hundred buttons. */
/* An approval a workflow case owns: the state, and no invitation. The case popup is where
   it is answered, so the cell reads as information rather than a control. */
.tg-ap-locked { cursor: pointer; opacity: 0.9; }
.tg-ap-locked::after {
    content: '';
    width: 4px; height: 4px; border-radius: 50%;
    background: currentColor; opacity: 0.5; margin-left: 2px;
}

.tg-wfc {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    max-width: 100%;
    padding: 2px 8px 2px 6px;
    border-radius: 11px;
    font-size: 11.5px;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
}
/* Named for the case's STATUS. Only needs_attention earns a warning tone: an open case is
   normal and a finished one is history. */
.tg-wfc-blue {
    background: color-mix(in srgb, var(--f7-theme-color) 15%, transparent);
    color: color-mix(in srgb, var(--f7-theme-color) 86%, var(--f7-text-color));
}
.tg-wfc-orange {
    background: color-mix(in srgb, #e0a30b 16%, transparent);
    color: color-mix(in srgb, #8a6300 82%, var(--f7-text-color));
}
.tg-wfc-green {
    background: color-mix(in srgb, #21a366 16%, transparent);
    color: color-mix(in srgb, #14724a 82%, var(--f7-text-color));
}
.tg-wfc-gray {
    background: color-mix(in srgb, var(--f7-text-color) 9%, transparent);
    color: var(--tg-muted);
}
.tg-wfc-new {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 11.5px;
    color: var(--tg-muted);
    opacity: 0;
    cursor: pointer;
    transition: opacity .12s ease;
}
.tg-row-row:hover .tg-wfc-new,
.tg-row-sel .tg-wfc-new { opacity: 1; }
.tg-wfc-new:hover { color: var(--f7-theme-color); }

.tg-ap-change {
    background: color-mix(in srgb, #8b46d1 15%, transparent);
    color: color-mix(in srgb, #6a2ea8 84%, var(--f7-text-color));
}

/* The Approve / Reject / Needs-changes menu, put up by the host over the cell. */
.rt-apmenu { min-width: 232px; padding: 5px; }
.rt-apmenu-head {
    padding: 7px 10px 8px;
    font-size: 12px;
    line-height: 1.45;
    color: var(--tg-muted);
    border-bottom: 1px solid var(--tg-border);
    margin-bottom: 4px;
}
.rt-apmenu-head b { color: var(--f7-text-color); font-weight: 600; }
.rt-apmenu-note {
    width: 100%;
    margin: 2px 0 6px;
    padding: 7px 9px;
    font-size: 13px;
    font-family: inherit;
    border: 1px solid var(--tg-border-strong);
    border-radius: 7px;
    background: var(--f7-page-bg-color);
    color: var(--f7-text-color);
    resize: vertical;
}

/* ── the room-user / approval panes in the expanded record ─────────────── */
.rt-ap-panel {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
}
.rt-ap-meta {
    flex: 1 1 100%;
    font-size: 12px;
    color: var(--tg-muted);
}
.rt-ap-btn {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 5px 11px;
    border-radius: 7px;
    border: 1px solid var(--tg-border-strong);
    background: transparent;
    color: var(--f7-text-color);
    font-size: 13px;
    font-weight: 600;
    /* Hug the label. A fixed or full-width action button in a panel of several
       reads as a form submit, which none of these are. */
    width: auto;
    cursor: pointer;
}
.rt-ap-btn:hover { background: var(--tg-hover); }
.rt-ap-btn-yes { border-color: color-mix(in srgb, #21a366 55%, transparent); color: #14724a; }
.rt-ap-btn-no  { border-color: color-mix(in srgb, #dc3545 55%, transparent); color: #a71d2a; }

/* A read-only grid never offers the ask: a shared link, a guest and the Trash view
   can all see an approval's state, and none of them can start one. */
.tg-readonly .tg-ap-ask { display: none; }
.tg-readonly .tg-ap { cursor: default; }

/* THE SYNCED TAG IS A LABEL, NOT A BUTTON.
   It stands where Form / Share / Forward / Sync stand in the room that owns the table,
   so it inherits .rt-vb's shape — but it does nothing on click, and saying so with the
   cursor stops people hunting for what it opens. The room name is the variable-length
   part of a bar that also holds Fields, Filter, Group, Sort and search, so it truncates
   rather than pushing the search icon off the end; the full name is on hover. */
/* cursor is set with the popover rules further down — this tag is a control now. */
.rt-vb-synced .rt-vb-src {
    max-width: 190px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
@media (max-width: 600px) { .rt-vb-synced .rt-vb-src { max-width: 110px; } }

/* ── the forms popover ─────────────────────────────────────────────────────
   Wider than the plain menu because a form row carries three things — the name, whether
   it is live, and what it holds — and 250px forced the meta line to wrap under a two-word
   title. */
.rt-fmenu-forms { width: 320px; }

/* Says what the list IS. Without it the first row read as the table's own name repeated,
   which is exactly what a form defaults to being called. */
.rt-fmenu-head {
    padding: 10px 14px 6px;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--tg-muted);
}
.rt-fmenu-sep { height: 1px; background: var(--tg-border); margin: 5px 0; }

.rt-fi {
    display: flex;
    align-items: center;
    gap: 11px;
    width: 100%;
    box-sizing: border-box;
    padding: 8px 14px;
    cursor: pointer;
    color: var(--f7-text-color);
}
.rt-fi:hover { background: var(--tg-hover); }
.rt-fi-icon { color: var(--tg-muted); flex: 0 0 auto; }
.rt-fi-body { min-width: 0; flex: 1; }
.rt-fi-t {
    display: flex;
    align-items: center;
    gap: 7px;
    min-width: 0;
    font-size: 13.5px;
    font-weight: 500;
}
/* The name gives way, never the state pill — "Project intak…" still tells you which form
   this is, while a clipped pill tells you nothing. */
.rt-fi-t > span:first-child {
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.rt-fi-m { font-size: 11.5px; color: var(--tg-muted); margin-top: 1px; }

/* Draft is the resting state and reads as quiet; LIVE is the one worth noticing, because
   it means the form is answering strangers on a public URL right now. */
.rt-fi-pill {
    flex: 0 0 auto;
    font-size: 9.5px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    padding: 1px 6px;
    border-radius: 20px;
    color: var(--tg-muted);
    background: rgba(128, 128, 128, 0.14);
}
.rt-fi-live {
    color: #1a7f37;
    background: rgba(26, 127, 55, 0.13);
}

/* Dim until the row is hovered. Two buttons on every row, and a list of four forms would
   otherwise put eight icons on screen — the loudest thing in a popover whose job is to
   let you read the forms. */
.rt-fi-act {
    flex: 0 0 auto;
    padding: 5px;
    border-radius: 6px;
    color: var(--tg-muted);
    opacity: 0;
}
.rt-fi:hover .rt-fi-act { opacity: 1; }
.rt-fi-act:hover { color: var(--f7-theme-color); background: var(--tg-hover); }
/* The destructive one is the only one that turns red, and only under the pointer. */
.rt-fi-del:hover { color: #dc3545; background: rgba(220, 53, 69, 0.12); }
/* Touch has no hover, so the buttons are simply always there. */
@media (hover: none) { .rt-fi-act { opacity: 0.6; } }

/* ── the form's Settings tab ────────────────────────────────────────────────
   Eleven controls used to run down one column with nothing between them, so finding the
   one you came for meant reading all of them. The controls are unchanged; these are the
   groups they now sit in. */
/* A tick and its label on a form-settings card, with the help text underneath it
   belonging to the tick rather than to the card. Its own rule because the settings
   panel had no checkbox until the continuation options needed two. */
.rt-fs-check {
    display: flex;
    align-items: center;
    gap: 9px;
    margin-top: 14px;
    font-size: 13.5px;
    cursor: pointer;
}
.rt-fs-check input { flex: none; }

.rt-fs-card {
    background: var(--f7-card-bg-color, #fff);
    border: 1px solid var(--tg-border);
    border-radius: 12px;
    padding: 14px 16px 16px;
    margin-bottom: 14px;
}
.rt-fs-card:last-child { margin-bottom: 30px; }
.rt-fs-h { font-size: 14px; font-weight: 650; }
/* What the group DECIDES, so a heading is not the only clue. Sits tight under the
   heading and above the first label, which keeps its own spacing. */
.rt-fs-sub {
    font-size: 12px;
    color: var(--tg-muted);
    margin: 2px 0 12px;
    line-height: 1.45;
}
/* Two ends of one answer. Collapses on a narrow popup rather than squeezing two
   datetime inputs — those have a wide intrinsic minimum and clip their own text. */
.rt-fs-pair {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
}
@media (max-width: 560px) { .rt-fs-pair { grid-template-columns: 1fr; } }

/* ── the column type, at the top of the header menu ────────────────────────
   A header cell's glyph is the only clue to a column's type, which is no clue at all
   between Lookup, Rollup and Formula. The menu answers it where somebody already is, and
   pressing it opens the field editor — the same action as "Edit field" below it. */
.rt-fmenu-typed { width: 300px; }
.rt-fmenu-type {
    display: flex;
    align-items: center;
    gap: 11px;
    width: 100%;
    box-sizing: border-box;
    padding: 10px 14px;
    cursor: pointer;
    color: var(--f7-text-color);
}
.rt-fmenu-type:hover { background: var(--tg-hover); }
/* The chevron is the quiet half — it says "there is more behind this" without competing
   with the type name for attention. */
.rt-fmenu-type > .f7-icons:last-child { color: var(--tg-muted); flex: 0 0 auto; }

/* ── the synced tag, and the popover behind it ──────────────────────────────
   The tag started inert — a label saying where the table came from. But "where does this
   live?" is almost always followed by "take me there", because the owning room is the only
   place a synced table's columns, forms or sharing can be changed. So it is a control now,
   and says so with the cursor. */
.rt-vb-synced { cursor: pointer; }
.rt-syncpop { width: 250px; padding: 13px 14px 14px; }
.rt-syncpop-h {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13.5px;
    font-weight: 600;
}
.rt-syncpop-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
.rt-syncpop-s {
    font-size: 12px;
    color: var(--tg-muted);
    line-height: 1.5;
    margin: 6px 0 0;
}
/* Full width, because it is the only action in the popover and a hugging button in a
   250px panel reads as an afterthought. */
.rt-syncpop-go {
    width: 100%;
    justify-content: center;
    margin-top: 12px;
}

/* ── attaching a file to a cell ─────────────────────────────────────────────
   The attachment column used to be readable and unfillable: the cell said "open the
   record", and the record said "attach them from the Files app, then link them here" —
   a route that does not exist. Both now open the room's own upload popover. */
.rt-attach-btn { margin-top: 4px; }
/* The defer box is the cell's editor for a type that cannot be typed. It holds a count
   and its two actions, so the buttons sit at the end rather than filling it. */
.tg-in-attach {
    margin-left: auto;
    border: 1px solid var(--f7-theme-color);
    background: var(--f7-theme-color);
    color: #fff;
    border-radius: 7px;
    padding: 3px 10px;
    font-size: 12px;
    cursor: pointer;
    width: auto;
    flex: 0 0 auto;
}
/* Attach is filled because it is what the cell now exists for; Open stays outlined as the
   way to manage several files at once. When both are present only Attach pushes right. */
.tg-in-defer .tg-in-attach + .tg-in-open { margin-left: 0; }
