/* Rankings page - Pages table + Keyword table column discipline (CYT rank-filter follow-up).
   Layout/spacing/widths only. No colours declared here - only var() tokens
   already used elsewhere in this design system (var(--portal-*)).

   ---------------------------------------------------------------------------
   READ THIS BEFORE TOUCHING THESE TWO TABLES AGAIN.

   public/css/cxs-skin.css (loads AFTER every other portal stylesheet, cannot
   be edited from here) forces every .sb-data-table - both tables below carry
   that class - to `table-layout: auto !important`. That is DELIBERATE on the
   skin's part (see its own "section 46" comment: fixed layout + its
   `width: 1%` first-column idiom caused the billing page to crush a column to
   9px). Do not fight it with `table-layout: fixed`.

   The skin ALSO carries `.kt-table th:first-child:has(~ th) { width: 1%; }`.
   Under AUTO layout that is the intentional "shrink this column to its
   content" idiom and is harmless. Under FIXED layout the browser takes that
   1% literally and the first column collapses to ~14px - this happened live
   on a sibling page in this same repo before the fix below was proven.

   THE WORKING IDIOM (auto layout, proven live: a 431px prose column grew to
   1231px, metric columns settled at 116px/91px, row height 431px -> 57px,
   zero horizontal overflow at 1920/1440/390):
     - table stays `table-layout: auto`
     - the PROSE column (the one holding a URL or keyword) gets
       `width: auto; min-width: 0` - no ceiling, it absorbs whatever is left
     - the truncating block inside that prose cell gets
       `display: block; max-width: none; overflow: hidden;
        text-overflow: ellipsis; white-space: nowrap`
       - CRITICAL: `max-width: none`, never `max-width: 100%`. A percentage
         max-width on a block whose own column width is what auto-layout is
         trying to solve for creates a cyclic dependency - the browser reads
         the cell's contributed content width as ~0 and the prose column
         starves to nothing. That is the exact bug this rule set avoids.
     - every METRIC column (a count, a date-stamped rank, a dollar figure)
       gets `width: 1%; white-space: nowrap` - the "shrink to its own
       content, don't grow" auto-layout idiom - and stays right-aligned so
       several of them read as one clustered block instead of floating
       mid-row.
   Reuse this exact recipe for any other data table on this page; do not
   reach for `table-layout: fixed` here even if it looks like the obvious
   tool - it collides with the skin's own first-column idiom every time.

   A SECOND LESSON, measured while first building the Keyword table below:
   the "prose absorbs the remainder" idiom above only works with ONE
   unconstrained prose column per table. Giving TWO columns (Keyword AND
   Page) `width: auto; min-width: 0` at the same time did not make either
   shrink to fit - Chromium split the leftover space EVENLY between them
   (both landed at an identical, viewport-unresponsive 740px, on a table
   that was supposed to fit a 1126-1438px card) rather than shrinking either
   to its content. If a table genuinely needs more than one prose-holding
   piece of information in a row, do not give each its own unconstrained
   table column - merge them into ONE column instead (see section 2 below
   for the working version of exactly this case), or give every prose
   column but one a real ceiling.
   --------------------------------------------------------------------------- */

/* ==========================================================================
   1. Pages appearing in results (.cxp-rankings-pages-table)
   Columns: Page (prose) | Keywords | Estimated visits | Average position
   ========================================================================== */
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-pages-table {
  table-layout: auto !important;
  width: 100% !important;
  max-width: 100% !important;
}

body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-pages-table th:first-child,
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-pages-table td:first-child {
  width: auto !important;
  min-width: 0 !important;
}

body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-pages-table td:first-child {
  display: block !important;
  max-width: none !important;
  overflow: hidden !important;
  text-overflow: ellipsis !important;
  white-space: nowrap !important;
}

body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-pages-table th:nth-child(n+2),
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-pages-table td:nth-child(n+2) {
  width: 1% !important;
  white-space: nowrap !important;
  /* 2026-08-20: text-align:right removed here. Adir: numbers align LEFT.
     The columns still cluster on the right - that is the width:1% above,
     not the text alignment. See public/css/portal-table-standards.css. */
}

/* At mobile the metric columns shrink to ~60px and the "Keywords" /
   "Estimated visits" / "Average position" header labels are two words each
   - they need to wrap at the space, not mid-word (a base site rule sets
   overflow-wrap: anywhere broadly and was fragmenting them letter by
   letter). Headers only - the data cells above stay nowrap. */
/* 2026-08-20: this was written for mobile but was never scoped to it, so
   "Est. visits" and "Avg. position" also wrapped onto two lines inside a
   1438px desktop table with room to spare - the defect Adir reported. It is
   now held below 1200px; above that the global one-line header rule wins. */
@media (max-width: 1199px) {
  body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-pages-table th:nth-child(n+2) {
    white-space: normal !important;
    overflow-wrap: normal !important;
    word-break: normal !important;
    line-height: 1.25 !important;
  }
}

/* CORRECTION (round-3 regression, 2026-08-30): the rule above only ever
   reached the <th> itself. The visible header LABEL lives inside a
   `.cxp-table-sort` button (injected by bindPortalTableSorting(),
   topbar-user.blade.php), and cxp.css sets `overflow-wrap: anywhere` on
   every `.cxp-table-sort` inside `.cxp-rankings-page` at <=760px ("Keep
   every rankings sort label inside its own header cell") - that rule is
   scoped to the button, not inherited from the th, so it kept winning
   there regardless of the th-level fix above. Measured live at 390px:
   button computed overflow-wrap was "anywhere" while the th's own was
   correctly "normal" - "Est. visits" fragmented to "Est. visit" / "s" and
   "Avg. position" to "Avg. posit" / "ion", one wrapped letter per line.
   Same doubled-body-class + !important out-specify idiom used everywhere
   else in this file, aimed at the button this time instead of the cell. */
@media (max-width: 1199px) {
  body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-pages-table th:nth-child(n+2) .cxp-table-sort {
    overflow-wrap: normal !important;
    word-break: normal !important;
    white-space: normal !important;
  }
}

/* ==========================================================================
   2. Keyword results (.cxp-rankings-keyword-table)
   Columns: Search term + page (ONE prose column) | history x N (metric) | Current (metric)

   CORRECTNESS BUG FOUND AND FIXED (second review). This table used to carry
   Keyword and Page as TWO separate <td>s, each given `display: block` so it
   could ellipsis-truncate. `display: block` is not `display: table-cell` -
   the browser wraps each block <td> in its own anonymous table-cell box to
   preserve the row, and those two anonymous cells silently ate one column
   slot between them. Every column after them (Aug 12, Aug 5, Jul 20,
   Current) shifted one slot left while the header row did not move - a
   customer read rank numbers under a "Page" heading, one date's readings
   under the PREVIOUS date's heading, and an empty "Current" column. Row
   height measurement cannot catch a bug like this; only an explicit
   td[i].x === th[i].x assertion can, which is why one is included in the
   verification for this page every time these two tables change.

   THE FIX: do not fake two columns as one with CSS - merge Keyword and Page
   into a SINGLE real <td> in the blade template, using the same "identity"
   idiom already used on the Content page and the dashboard:
     <div class="sb-dense-identity">
       <strong class="cxp-rankings-truncate">keyword</strong>
       <a class="cxp-rankings-truncate cxp-rankings-truncate--sub">page url</a>
     </div>
   This is also the cleaner resolution of the "two unconstrained prose
   columns split evenly instead of shrinking" problem documented in the file
   header above - there is only one prose column now, so that failure mode
   cannot happen here.

   WHY THIS TRUNCATES CORRECTLY WHERE a bare white-space: nowrap directly
   on a <td> did not: .cxp-rankings-truncate lives INSIDE a flex container
   (.sb-dense-identity, from cxp.css), not directly on the <td>. A flex
   item's "automatic minimum size" is reduced when its own overflow is
   non-visible (CSS Sizing L3) - so an overflow:hidden flex child contributes
   almost nothing to the table's auto-layout min-content calculation for
   that column. A <td> with white-space:nowrap set directly on it has no
   such allowance under the legacy table auto-layout algorithm - its minimum
   content width IS its full unwrapped text, full stop. Put the truncating
   block inside a flex (or grid) child, never directly on a table cell.
   ========================================================================== */
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table {
  table-layout: auto !important;
  width: 100% !important;
  max-width: 100% !important;
}

/* The merged "Search term and page" column - the table's only prose column
   now, absorbs whatever the metric columns below do not need. Same proven
   idiom as the Pages table's single prose column. */
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table th.cxp-col-kw,
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table td.cxp-col-kw {
  width: 480px !important;
  min-width: 0 !important;
}
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table td.cxp-col-kw {
  display: block !important;
}

/* ROUND-7 REWRITE: this used to be `display:table-cell` gated by a VIEWPORT
   media query (`min-width: 641px`), with a second, separate VIEWPORT query
   (`max-width: 600px`, section 3 below) giving the block-mode `<td>` a
   narrower literal width. Two independent viewport breakpoints 41px apart,
   governing a CARD whose width does not track the viewport 1:1 (Filament's
   sidebar - see the container-query comment block in the blade template),
   produced a real gap: at viewport 601-750px (card ~550-650px, confirmed
   live on carkeysfortlauderdale.com) NEITHER query matched, so `<td>` fell
   through to the literal `display:block; width:480px` above - 480px inside
   a <650px card - and the 664px table's excess became an inner sideways
   scroller on `.cxp-rankings-table-scroll` that clipped the entire Current
   column at rest. Confirmed bug via a 380-2000px sweep (25px steps): 605,
   630, 655, 680, 705px all measured scrollWidth 664 > clientWidth 551-651.

   FIX: one container-driven rule, no viewport axis at all, no second
   breakpoint to drift out of sync with the first. `table-cell` stays on
   whenever container-query support exists (every real browser this table
   is served to), and its own ceiling shrinks CONTINUOUSLY with the card via
   `cqi` (container-query-inline-size) units instead of jumping at a second,
   independently-numbered breakpoint:
     max-width: min(480px, calc(100cqi - 148px))
   480px is the same ceiling the old >=641px rule used, so wide cards are
   pixel-identical to before (1920/1440 unaffected - confirmed below). The
   148px reserve is Current's own real rendered footprint measured live via
   headless sweep (605/630/655px card widths: kwWidth + 144px = table
   scrollWidth exactly, every time - td.cxp-col-now's 6.5rem/104px
   basis-text width plus that cell's own padding/border share), rounded up
   4px for safety margin (a first attempt at 140px still measured a
   consistent 4px inner-scroller overflow at 605-655px - table borders and
   subpixel layout eat a few px the arithmetic alone does not predict, which
   is exactly why this constant is DERIVED FROM A LIVE MEASUREMENT, not
   computed from the CSS values in isolation). Below a ~630px card the
   formula caps this column narrower than 480 so Current always keeps its
   floor; at the extreme small end (a 266px card, i.e. a 320px phone) the
   formula alone lands at ~118px, close enough to the old hand-tuned 130px
   small-phone override that the separate blade rule for it was removed as
   redundant - one mechanism now covers the entire 0-1920+px range with no
   second number to keep in sync (re-verified zero overflow at 380px after
   the removal - see the full-sweep results in the round-7 report).
   No `@supports not(...)` fallback is needed for the mode switch itself
   (table-cell is now unconditional, see the base declaration removed
   above) - only the SIZE needs a fallback for browsers without container
   queries, provided below. */
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table td.cxp-col-kw {
  display: table-cell !important;
}
@supports (container-type: inline-size) {
  body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table td.cxp-col-kw {
    max-width: min(480px, calc(100cqi - 148px)) !important;
  }
}
@supports not (container-type: inline-size) {
  /* Old viewport-driven fallback, verbatim, for the rare browser with no
     container-query support at all - accepts the same 601-640px gap this
     table always had pre-round-7, since nothing here can query the card's
     real width without the feature this branch is compensating for. */
  @media (min-width: 641px) {
    body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table td.cxp-col-kw {
      max-width: 480px !important;
    }
  }
  @media (max-width: 600px) {
    body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table td.cxp-col-kw {
      display: block !important;
      width: 200px !important;
    }
    /* Pre-round-6 fallback: without container-query support the blade
       template's `@container rkcard` fold/disclosure rules never apply
       either (they are fenced by the same `@supports (container-type:
       inline-size)`), so a non-supporting browser needs this viewport
       version of history-hide/disclosure-reveal too - otherwise it would
       show all 3 history columns un-folded on a phone, which the blade
       file's own comment already documents as "far worse than the
       behaviour it replaces". */
    body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table th.cxp-col-hist,
    body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table td.cxp-col-hist {
      display: none !important;
    }
    body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-mobile-detail-row {
      display: table-row !important;
    }
  }
}

/* The flex container itself (cxp.css .sb-dense-identity) also needs
   min-width:0 + overflow:hidden - min-width:0/overflow:hidden on its
   CHILDREN only governs how they shrink relative to each other once the
   container's own size is already resolved. When an ANCESTOR (this <td>,
   doing table auto-layout intrinsic sizing) measures the container itself,
   a flex container's own min-content contribution is (for a row-direction
   flex, the default here) the SUM of its children's min-content sizes
   unless the container is also told it may shrink. Without this, the
   merged column measured 1006px in a ~550px budget - confirmed by measuring
   before and after adding this rule. */
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table .sb-dense-identity {
  display: block !important;
  min-width: 0 !important;
  max-width: none !important;
}
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table .cxp-rankings-truncate--sub {
  margin-top: .15rem;
}

/* The keyword line and the page-url line inside it each truncate to one
   line independently, each carrying its own title (set in the blade
   template) for the full value on hover. max-width is none, never a
   percentage - see the file header for why a percentage here is the exact
   cyclic-dependency trap that starves the column. */
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table .cxp-rankings-truncate {
  display: block !important;
  min-width: 0 !important;
  max-width: none !important;
  overflow: hidden !important;
  text-overflow: ellipsis !important;
  white-space: nowrap !important;
}
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table .cxp-rankings-truncate--sub {
  color: var(--portal-muted, #667085);
  font-size: 12px;
}

/* History (w1/w2/w3) + Current are all metric columns - shrink to content,
   never grow, cluster right. Count varies 0-3 per row (only snapshot dates
   that actually exist render a column), so this targets the class, not a
   fixed nth-child. */
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table th.cxp-col-hist,
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table td.cxp-col-hist,
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table th.cxp-col-now,
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table td.cxp-col-now {
  width: 1% !important;
  white-space: nowrap !important;
}

/* Inside a metric cell the rank/date stack (.cxp-rankings-position-stack)
   carries its own inline max-width:100% from the blade template (written
   for a fixed-layout world). Left alone that is the exact cyclic-dependency
   trap described above, scoped to this narrower column instead of the whole
   table. Give the stack a real (non-percentage) ceiling instead. */
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table .cxp-rankings-position-stack {
  width: auto !important;
  min-width: 0 !important;
  max-width: none !important;
}

/* The row height (measured 100-105px on garagefl.com originally - it was
   NOT wrapping, it was genuine 3-line stacked content: rank number /
   "From N impressions" / an orange "Limited data" qualifier that only shows
   on low-sample keywords; those ~85px rows are correct and stay). This only
   tightens the cell's own vertical padding, the one legitimate remaining
   lever once every text line is confirmed single-line. */
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table th.cxp-col-hist,
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table td.cxp-col-hist,
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table th.cxp-col-now,
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table td.cxp-col-now,
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table th.cxp-col-kw,
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table td.cxp-col-kw {
  padding-top: .3rem !important;
  padding-bottom: .3rem !important;
}

/* CORRECTION (QA defect #2): the basis line ("From 42 impressions", "Est.
   monthly search volume at lookup: 12,000") used to be capped at 6.5rem with
   an ellipsis - the same "144px column, no room" trap the mobile disclosure
   override further below in this file was already written to avoid. On
   desktop this silently hid most of the sentence, reachable only via a
   title-attribute hover (not discoverable, useless on touch). This table
   already tolerates a multi-line metric cell (rank number / basis line /
   qualifier can be 3 lines - see the row-height note above), so let this
   line wrap instead of hiding text.

   SECOND LESSON, found fixing this one: the first attempt used
   `max-width: 9rem` + `white-space: normal` and it broke the date/"Current"
   HEADER row instead - "Aug 22, 2026" collapsed to a ~24px column and
   wrapped one letter per line. With wrapping enabled, this element's
   min-content shrinks to its narrowest unbreakable fragment (near a single
   character with overflow-wrap:anywhere), and an auto-layout table sizes
   the whole COLUMN by the smallest min-content it can get away with once
   wrapping makes that possible - the header cell (nowrap, no say in the
   matter) got dragged down with it. A real, non-percentage `width` (not
   `max-width`) makes this element's min-content AND max-content both a
   fixed 9rem regardless of its text, so it can no longer participate in
   that shrink - the column settles at max(header intrinsic width, this
   fixed 9rem), never below either. */
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table .cxp-rankings-position-stack small {
  display: block !important;
  width: 9rem !important;
  overflow: visible !important;
  text-overflow: clip !important;
  white-space: normal !important;
  overflow-wrap: anywhere !important;
}

/* ROUND-7: Current's own basis-text floor, made UNCONDITIONAL (moved out of
   the old `@media (max-width: 600px)` block below, where it was one more
   viewport-keyed rule this table no longer needs). It was already narrower
   than the 9rem base above specifically for `.cxp-col-now` - 6.5rem instead
   of 9rem - and the 9rem figure's own job (stopping the HISTORY columns'
   date headers from collapsing into vertical letter-wrap, see the comment
   above) has nothing to do with Current, which has no such header risk
   ("Current" is one word, sized by its own `.cxp-table-sort` rule, not by
   this element). Applying 6.5rem to Current at every width - not just
   <=600px viewport - is also what makes the td.cxp-col-kw
   `calc(100cqi - 148px)` formula above's reserve a genuine CONSTANT at
   every card width; keeping it viewport-conditional would have
   reintroduced a second, independently-numbered axis for the same
   footprint this rule now guarantees everywhere. */
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table td.cxp-col-now .cxp-rankings-position-stack small {
  width: 6.5rem !important;
}

/* ==========================================================================
   3. Mobile (<=600px, matching this table's existing collapse breakpoint):
   History columns fold into the disclosure row that already exists in the
   template ("Show history"); the merged Search-term-and-page column and
   Current stay visible. Page no longer has its own column to hide - it now
   lives inside the merged prose cell and is visible at every width, so the
   disclosure row's own duplicate "Page" entry was removed from the blade
   template (it would otherwise show the same URL twice).

   The merged column needs its own narrower ceiling here too (200px, down
   from 480px at desktop) - the same reason section 1/2 give every prose
   column a real ceiling: table auto-layout's CAPmin for a nowrap cell is
   the FULL unwrapped text width regardless of nested overflow:hidden, and
   at a ~336px mobile card only ONE metric column (Current) is left to
   share it with. */
/* ROUND-7: the keyword-table-specific rules that used to live in this
   `@media (max-width: 600px)` block (history-column hide, disclosure-row
   reveal, the 200px cxp-col-kw cap, Current's 6.5rem basis floor) are gone
   from here. All four are now driven by the container-query system:
   history-hide/disclosure-reveal by `@container rkcard (max-width: 847px)`
   in rankings-keywords.blade.php (round-6), the cxp-col-kw ceiling by the
   `calc(100cqi - 148px)` formula above (round-7), and Current's basis floor
   by the unconditional rule above (round-7). A `@supports not(...)` copy of
   the pre-round-7 viewport rules is kept a few hundred lines down (with the
   >=641px fallback next to the container-query rule it replaces) for the
   rare browser with no container-query support - real browsers this table
   is served to never take that branch. This `@media (max-width: 600px)`
   block now holds ONLY the Pages-table (.cxp-rankings-pages-table) rules
   below, which do not use a container query and are unrelated to this
   round's migration - untouched. */
@media (max-width: 600px) {
  /* CORRECTION (round-3 regression, 2026-08-30): this 150px cap combined
     with the base rule's `overflow:hidden !important; text-overflow:
     ellipsis !important; white-space: nowrap !important;` (section 1 above)
     truncated every row to the same ~20 leading characters - every URL on
     this client's site starts with its own domain
     (carkeysfortlauderdale.com/...), so all 14 rows rendered as the
     visually identical "carkeysfortlauderdal..." with no way to tell rows
     apart. The domain is redundant on every row of a single-client portal
     page (see rankings-pages.blade.php - the cell's text is now split into
     a `.cxp-rankings-page-host` span and a `.cxp-rankings-page-path` span,
     concatenated with no separator so desktop's already-correct rendering
     is byte-for-byte unchanged). At mobile only: hide the host, let the
     distinguishing path wrap onto its own lines instead of ellipsis-hiding
     it - the full original string, domain included, stays on `title` for
     reference (unchanged, already on the <td>). */
  body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-pages-table .cxp-rankings-page-host {
    display: none !important;
  }
  body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-pages-table th:first-child,
  body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-pages-table td:first-child {
    max-width: none !important;
    width: auto !important;
  }
  body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-pages-table td:first-child {
    display: block !important;
    overflow: visible !important;
    text-overflow: clip !important;
    white-space: normal !important;
    overflow-wrap: anywhere !important;
    word-break: break-word !important;
    line-height: 1.3 !important;
  }
}

/* Neither table gets its own horizontal scroller - the auto-layout recipe
   above keeps every column within the table's own 100% width at every
   viewport, so the wrapper never needs to clip or scroll. Restated as
   `auto` (never `scroll`) as a safety net only. */
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-table-scroll {
  overflow-x: auto !important;
  overflow-y: hidden !important;
}

/* ==========================================================================
   4. Keywords we track for you (.cxp-tracked-kw-table, CYT-722)
   Same auto-layout recipe as sections 1-2: Keyword is the one unconstrained
   prose column (auto/min-width:0 + ellipsis truncation), every other column
   is a metric column that shrinks to its own content (width:1%, nowrap),
   clustered right. No inner horizontal scroller - reuses the existing
   .cxp-rankings-table-scroll wrapper (overflow-x auto, never scroll). */

.cxp-tracked-kw-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  padding: 0 var(--kt-card-padding-x, 1.5rem) 1rem;
}

.cxp-tracked-kw-add {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex: 1 1 260px;
  min-width: 0;
}

.cxp-tracked-kw-add input[type="text"] {
  flex: 1 1 auto;
  min-width: 0;
}

.cxp-tracked-kw-cap {
  white-space: nowrap;
}

.cxp-tracked-kw-readonly-note {
  padding: 0 var(--kt-card-padding-x, 1.5rem) 1rem;
}

.cxp-tracked-kw-suggestions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  padding: 0 var(--kt-card-padding-x, 1.5rem) 1rem;
}

.cxp-tracked-kw-chip {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.15rem;
  max-width: 260px;
  padding: 0.4rem 0.65rem;
  border: 1px dashed var(--border);
  border-radius: var(--radius, 0.5rem);
  background: transparent;
  cursor: pointer;
  text-align: left;
}

.cxp-tracked-kw-chip-term {
  font-size: var(--text-sm);
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}

.cxp-tracked-kw-chip-reason {
  font-size: var(--text-xs);
  color: var(--muted-foreground);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}

/* min-width:0 on both the table and its scroll wrapper matters here in a way
   the sibling table above never hit: this table's real min-content width
   (measured live: 357.875px at a 390px viewport, card usable width 338px)
   is WIDER than its card. A flex/grid ancestor gives a child an implicit
   min-width:auto (not 0) by default, so without this the wrapper does not
   clip or scroll at all - it just grows past the card's own right edge and
   gets silently clipped by the card's overflow-x:hidden (measured: table
   right edge landed at 384.875px while the card itself ends at 364px - the
   "Remove" button was being cut off by the CARD, not by any inner scroller,
   which is why the wrapper's own scrollWidth===clientWidth check missed it
   entirely). width:100% on an auto-layout table is a floor, never a
   ceiling - it will grow past 100% for content that refuses to shrink, so
   the column-width budget below still has to add up on its own. */
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-tracked-kw-table,
body.fi-panel-portal.fi-panel-portal .cxp-rankings-tracked-card .cxp-rankings-table-scroll {
  min-width: 0;
}

/* .kt-card-table (public/css/metronic/components.css) is `display:grid`
   with NO grid-template-columns - every direct child (header, toolbar,
   table wrapper) becomes an item on one implicit `auto` track, and an
   `auto` track sizes itself from the widest child's INTRINSIC content
   width, uncapped by that child's own width/max-width/min-width (all of
   which are just candidate SUGGESTIONS to the track-sizing algorithm, not
   enforced ceilings - confirmed live across three different attempts:
   width:100%, then width:0 + min-width:100% on the wrapper, then even
   table-layout:fixed with definite pixel column widths, and the measured
   overflow (357.875px against a 338px card) never moved by a single
   pixel). The card's own overflow:hidden then silently clips whatever
   rendered past its right edge, which is what was actually cutting off
   the "Remove" button - not any inner scroller, and not this table's own
   column widths at all.
   The real fix is one level up: stop this card from being a CSS grid
   container in the first place. Block layout's shrink-to-fit + overflow
   handling does not have this failure mode - a wide descendant simply
   overflows the box and gets clipped/scrolled by that box's own overflow
   property, it cannot drag the ancestor's box wider. `.kt-card-table`'s
   `display:grid` has no template columns/rows defined anywhere for this
   card shape, so nothing here relies on grid placement - overriding to
   block is a safe, targeted escape for this specific card. */
body.fi-panel-portal.fi-panel-portal .cxp-rankings-tracked-card {
  display: block !important;
}

body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-tracked-kw-table {
  table-layout: auto;
  width: 100%;
}

body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-tracked-kw-table th:first-child,
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-tracked-kw-table td:first-child {
  width: auto;
  min-width: 0;
}

body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-tracked-kw-table th:first-child .cxp-rankings-truncate,
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-tracked-kw-table td:first-child .cxp-rankings-truncate {
  display: block;
  max-width: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-tracked-kw-table th:not(:first-child),
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-tracked-kw-table td:not(:first-child) {
  width: 1%;
  min-width: 44px;
  white-space: nowrap;
  text-align: right;
}

/* The site-wide sortable-header script (topbar-user.blade.php) wraps every
   <th> label in a button, and rankings.blade.php's own <style> block forces
   that button to `width:100%; white-space:normal` for the OTHER tables on
   this page. Under this table's auto/min-width:0 first column, that pairing
   is the exact cyclic-dependency trap documented at the top of this file:
   the TH's min-content shrinks to a single wrapped character and the button
   inflates in height instead (measured live: a "Keyword" header collapsed to
   ~280px tall on a 390px viewport). Higher specificity (one extra class)
   than that page-level rule beats it regardless of file/source order. */
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-tracked-kw-table .cxp-table-sort {
  width: auto !important;
  white-space: nowrap !important;
}

@media (max-width: 600px) {
  /* Root cause of the mobile overflow, found after three failed attempts at
     width/min-width tricks on the wrapper: .kt-card-table (public/css/
     metronic/components.css) is `display:grid` with NO grid-template-columns
     - an implicit `auto` track. An `auto` grid track sizes itself from its
     widest child's INTRINSIC min-content, and under table-layout:auto (the
     skin's sb-data-table !important rule) this table's min-content is
     content-driven and NOT capped by any width/max-width on its cells
     (those are advisory hints to the auto-layout algorithm, not enforced
     ceilings - confirmed live: a th had computed width 127.969px against
     its own max-width:108px !important). The grid track then grew to fit
     it (measured 357.875px against a 338px card), and the card's own
     overflow:hidden silently clipped whatever rendered past its right edge
     - which is what was cutting off "Remove", not any inner scroller.
     table-layout:fixed makes column widths a DEFINITE, non-content-driven
     sum instead, which is a real, enforceable minimum the grid track can
     size against correctly. Higher specificity (four classes) than the
     skin's `.sb-data-table { table-layout:auto !important }` (two classes)
     wins despite both being !important - specificity still breaks ties
     between two !important declarations. Scoped to mobile only; desktop
     keeps table-layout:auto to match every sibling table on this page. */
  body.fi-panel-portal.fi-panel-portal .cxp-rankings-tracked-card .cxp-tracked-kw-table {
    table-layout: fixed !important;
  }

  .cxp-tracked-kw-toolbar {
    flex-direction: column;
    align-items: stretch;
  }
  .cxp-tracked-kw-add {
    flex-basis: auto;
  }

  /* Same reason as section 3 above: at a ~336px mobile card, 4 metric
     columns plus the prose column cannot all keep their desktop widths.
     "Best" is the lowest-value figure here (it never says "not ranking",
     just a historical high) so it drops first, matching the same table's
     own "demoted, still on desktop" pattern used elsewhere on this page.
     Keyword gets an explicit ceiling (measured: without one, table
     auto-layout sizes it to the single longest UNWRAPPED keyword across
     every visible row, not the current row - 458px on a 390px viewport in
     the first version of this table). Position is allowed to wrap - the
     required "Checking soon" copy (CYT-722 acceptance criterion, never
     shortened) is wider than a bare "#12" and does not fit one nowrap
     line at this width. */
  body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-tracked-kw-table th.cxp-col-tkw-best,
  body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-tracked-kw-table td.cxp-col-tkw-best {
    display: none !important;
  }
  body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-tracked-kw-table th:first-child,
  body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-tracked-kw-table td:first-child {
    width: 108px !important;
    max-width: 108px !important;
  }
  /* By and Actions were left to shrink-to-content (badge / button text)
     instead of an explicit width, and their real content - "Cyticx"/"You"
     badge, "Remove" button - measured wider than expected once padding and
     the kt-badge/kt-btn chrome were included. Give both an explicit ceiling
     too so all four visible columns have a real, additive budget instead of
     three fixed numbers plus two "however wide the browser wants" ones. */
  body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-tracked-kw-table th.cxp-col-tkw-by,
  body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-tracked-kw-table td.cxp-col-tkw-by {
    width: 54px !important;
    max-width: 54px !important;
    padding-left: 4px !important;
    padding-right: 4px !important;
  }
  body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-tracked-kw-table th.cxp-col-actions,
  body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-tracked-kw-table td.cxp-col-actions {
    width: 74px !important;
    max-width: 74px !important;
    padding-left: 4px !important;
    padding-right: 4px !important;
  }
  body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-tracked-kw-table .cxp-col-actions .kt-btn {
    padding-left: 6px !important;
    padding-right: 6px !important;
    font-size: 12px !important;
  }
  body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-tracked-kw-table th:not(:first-child):not(.cxp-col-tkw-best):not(.cxp-col-tkw-by):not(.cxp-col-actions),
  body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-tracked-kw-table td:not(:first-child):not(.cxp-col-tkw-best):not(.cxp-col-tkw-by):not(.cxp-col-actions) {
    width: 66px !important;
    white-space: normal !important;
  }
  body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-tracked-kw-table .cxp-table-sort {
    white-space: normal !important;
  }
}


/* ============================================================================
   CYT-855 - Keyword positions page: three defects Adir named from a
   screenshot of anccartransport.com's /portal/rankings/keywords.
   ============================================================================ */

/* (1) The two explainer lines above the Position filters ran the full card
   width (up to ~1400px at 1920px) with no measure limit - a genuinely hard
   line length to read. .cxp-meta is a shared, portal-wide utility class
   (used for one-line captions elsewhere), so the cap is scoped to this one
   header, not the class itself. */
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-filters-card .kt-card-header .cxp-meta {
  display: block;
  max-width: 68ch;
}

/* (2) "Keyword results": the merged Search-term column absorbed the table's
   entire leftover width (measured live at 1203px in a 1438px card, versus
   its own documented 480px design cap two rules above) instead of the
   actual keyword/URL text it holds, leaving a dead gap of ~700-800px before
   "Current" - "the term column is starved while ~60% of the row is blank
   space" (Adir). The 480px cap above IS declared, but table-layout:auto
   does not enforce a cell max-width during its own excess-width
   distribution once the table is forced to width:100% - proven live via
   CDP: adding !important width/max-width to every cell in the row,
   including th, changed nothing, while table-layout:fixed with definite
   per-column widths gave exact, deterministic results every time.

   Scoped to container widths >=848px - the exact point this file's own
   fold logic (above, `@container rkcard (max-width: 847px)`) already
   established as "every client's last history column is gone, real table
   mode ends here". Below that the existing narrow-card/mobile rendering is
   untouched - it was never reported broken and carries its own history of
   hand-tuned fixes; this fix only reaches the desktop width where the dead
   gap was actually screenshotted. */
@supports (container-type: inline-size) {
  @container rkcard (min-width: 848px) {
    body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table {
      table-layout: fixed !important;
      width: auto !important;
      max-width: 100% !important;
    }
    body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table th.cxp-col-now,
    body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table td.cxp-col-now,
    body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table th.cxp-col-hist,
    body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table td.cxp-col-hist {
      width: 184px !important;
    }
    body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table th.cxp-col-kw,
    body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table td.cxp-col-kw {
      width: 480px !important;
      max-width: 480px !important;
    }
  }
}

/* (3) The "Compare" column header is an `.sr-only` span - meant to be
   screen-reader-only, keeping the checkbox column visually unlabeled - but
   `.sr-only` has no CSS rule anywhere in this app, so it always rendered as
   plain visible text. That went unnoticed while the column was accidentally
   ~90px wide (the same table-layout bug fixed above); now that
   .cxp-col-select is a real, deterministic 36px under table-layout:fixed,
   "Compare" has nowhere to go and wraps one letter per line. Give it the
   standard visually-hidden treatment here instead of widening the column
   back out to fit a label nobody was meant to see. */
body.fi-panel-portal.fi-panel-portal .cxp-rankings-page .cxp-rankings-keyword-table th.cxp-col-select .sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
