/* ============================================================================
 * From/To Cleaning — re-skin scoped to body.page-fromto-cleaning
 *   Page: FromToCleaning.aspx (DNN-routed) hosting
 *         DesktopModules/Milbros/FromToCleaning.ascx
 *   Figma: oM4wJDLxiHlaKMCy6GvTXs / 90:7406 (empty) + 119:9501 (selected)
 *
 * Strategy: keep markup. Wrap #upFromToCleaning as the card. The legacy 6-col
 * header table + 4-cell controls row are flattened/regridded via CSS. The
 * #lbViewRecommendations LinkButton is lifted to the card header via absolute
 * positioning. Orange/Green strip divs are repurposed as dot + helper line.
 * ============================================================================ */

body.page-fromto-cleaning {
    /* --- Tokens — re-derived from Figma file oM4wJDLxiHlaKMCy6GvTXs ------- *
     * Sources:
     *   - get_variable_defs (90:7406): base/primary=var(--brand-primary),
     *     orange/300=#fdba74, green/300=#86efac, muted=var(--surface-muted)
     *   - color-pick fromtocleaning-empty.png:
     *     Recommend(empty) bg @ (1768..1838, 130..148) = #86c9c3 (mode 953/2200)
     *     = base/primary at 50% opacity over white
     *     Recommend(selected) bg @ same = var(--brand-primary) (mode 953/2200)
     *     Search button bg @ (938..998, 275) = var(--brand-primary) solid
     *     Orange dot @ (346..358, 315) = #fcba74 solid (Tailwind #fdba74)
     *     Green dot @ (1150..1162, 355) = #86efac solid (Tailwind #86efac)
     *     Panel bg (From/To insets) @ multiple = var(--surface-muted) (base/muted)
     *     Swap button bg @ (1095, 260) = var(--surface-muted) (matches panel bg)
     *     Swap icon strokes = var(--accent-foreground) (slate-900)
     */
    /* Bound to global semantic tokens (QEN-8704 P1 tokenization).
       Local --ftc-* aliases remain for intent-naming; their VALUES now
       inherit from .q88-refresh scope so portal/theme changes propagate. */
    --ftc-card-bg:        var(--surface-raised);
    --ftc-card-border:    var(--border-default);            /* base/border = slate-200 */
    --ftc-card-radius:    8px;                              /* border-radius/rounded-md */
    --ftc-card-shadow:    0 1px 2px rgba(15,23,42,.04), 0 4px 12px rgba(15,23,42,.06);
    --ftc-card-pad:       24px;

    --ftc-title-color:    var(--text-primary);              /* base/foreground = slate-900 */
    --ftc-heading-color:  var(--text-primary);
    --ftc-body-color:     var(--text-primary);
    --ftc-muted-color:    var(--text-muted);                /* base/muted-foreground = slate-500 */

    --ftc-panel-bg:       var(--surface-muted);             /* base/muted = slate-100 */
    --ftc-panel-pad:      16px;                             /* Figma frame-19 inner padding */
    --ftc-panel-radius:   8px;

    /* Page-pinned to teal — Figma base/primary = #0d9488 across both portals.
     * On Q88 portal var(--brand-primary) resolves to slate-blue #4F72A4 (portal
     * cascade), which is wrong for this design; hardcoding here keeps teal on
     * both portals. Confirmed via get_variable_defs on file
     * oM4wJDLxiHlaKMCy6GvTXs / 119:9501. */
    --ftc-accent:         var(--primary);                          /* base/primary = teal-600 (page-pinned) */
    --ftc-accent-hover:   var(--button-primary-hover);                          /* teal-700 */
    --ftc-accent-text:    #ffffff;                          /* white on teal */
    --ftc-accent-ghost-bg:    #86c9c3;                      /* page-specific: primary @ 50% opacity over white */
    --ftc-accent-ghost-text:  #ffffff;

    --ftc-input-bg:       var(--surface-raised);
    --ftc-input-border:   var(--border-default);            /* base/input */
    --ftc-input-text:     var(--text-primary);
    --ftc-input-placeholder: var(--text-tertiary);          /* slate-400 */

    --ftc-orange-dot:     #fdba74;        /* tailwind orange/300 (was orange-500 #F97316) */
    --ftc-orange-row:     #FED7AA;        /* orange-200 — selected From row tint */
    --ftc-green-dot:      #86efac;        /* tailwind green/300 (was green-500 #22C55E) */
    --ftc-green-row:      #BBF7D0;        /* green-200 — selected To row tint */

    --ftc-swap-icon:      var(--accent-foreground);        /* slate-800 — Figma swap-button icon stroke */
}

/* =====================================================================
 * 1) Page chrome — neutralise the legacy 975px outer wrappers
 *    and rebuild the card around #upFromToCleaning.
 * ===================================================================== */
/* Card chrome (bg/border/radius/shadow/padding/margin/box-sizing/font/color)
 * comes from the shared `.q88-card` rule in components.css. The class is
 * attached to the UpdatePanel via codebehind (FromToCleaning.ascx.cs Page_Load).
 * Page-specific overrides:
 *   - radius 12 (rounded-xl) vs shared 8 — Figma file oM4wJDLxiHlaKMCy6GvTXs
 *     119:9506 Card uses rounded-xl=12 per var defs.
 *   - padding 24 all-around vs shared 24/28 — Figma JSX has only `py-24`
 *     on the card; horizontal pad lives on each Section (Header/Content/Footer)
 *     via `px-24`, so net inset from card edge is 24, not 28.
 */
body.page-fromto-cleaning #upFromToCleaning {
    /* Anchor for the absolutely-positioned Recommend pill below. */
    position: relative;
    padding: 24px !important;
    border-radius: 12px !important;
}

/* The legacy `<table.ContentPane>` chain release is now shared in
 * components.css (scoped to `body.sidebar-nav … :has(.q88-card)`).
 * No page-specific copy needed here — #upFromToCleaning carries
 * .q88-card via codebehind, so the shared rule applies. */

/* The outer 975px tables in the .ascx markup — let them expand. */
body.page-fromto-cleaning #upFromToCleaning > table {
    width: 100% !important;
    border-collapse: collapse;
    table-layout: auto;
}

/* The outermost <td> wrapping the whole .ascx — also 975px-ish; release. */
body.page-fromto-cleaning #upFromToCleaning > table > tbody > tr > td {
    padding: 0;
    width: auto;
}

/* =====================================================================
 * 2) Card header — title (left) + Recommend button (right)
 *    Title row #trTitle becomes the visible header strip. Recommend is
 *    absolute-positioned out of the controls row.
 * ===================================================================== */
body.page-fromto-cleaning #trTitle {
    display: table-row;
}
body.page-fromto-cleaning #ctl00_cphMiddle_ctl00_tdSearchTitle,
body.page-fromto-cleaning td.PageTitle {
    padding: 0 !important;
    background: transparent !important;
    border: 0 !important;
    color: var(--ftc-title-color) !important;
    font: 600 18px/24px var(--font-sans, "Inter", system-ui, sans-serif) !important;
    font-weight: 600 !important;
    letter-spacing: -0.01em;
    text-transform: none;
    height: 36px !important;
    line-height: 36px !important;
}

/* Recommend pill — absolute-positioned to card header right. The wrapping
 * inner table (numberBlock "3" + empty + Recommend cell) is flattened and
 * the numberBlock badge hidden.
 * Specificity bumped (body + a + id = 0,2,1) to beat components.css
 * `body.sidebar-nav a.LinkButton` (0,2,1, but ours loads later). */
body.page-fromto-cleaning a#lbViewRecommendations,
body.page-fromto-cleaning a.LinkButton#lbViewRecommendations {
    position: absolute;
    top: 24px;
    right: 24px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box !important;
    height: 32px !important;
    min-height: 32px !important;
    padding: 0 16px !important;
    background: var(--ftc-accent-ghost-bg) !important;
    color: var(--ftc-accent-ghost-text) !important;
    font: 500 13px/30px var(--font-sans, "Inter", system-ui, sans-serif) !important;
    font-weight: 500 !important;
    text-decoration: none !important;
    /* No border per Figma color-pick — empty pill is uniformly #86c9c3 with no
     * darker outline. Prior pass added border-teal-600 which created a
     * "boxed-in" appearance that doesn't match Figma. */
    border: 0 !important;
    border-radius: 8px !important; /* rounded-md per Figma button instance */
    cursor: pointer;
    transition: background-color .15s ease, color .15s ease;
    z-index: 2;
    white-space: nowrap;
}
body.page-fromto-cleaning a#lbViewRecommendations:hover,
body.page-fromto-cleaning a#lbViewRecommendations.is-active {
    background: var(--ftc-accent) !important;
    color: var(--ftc-accent-text) !important;
}
/* Auto-active when BOTH commodity strips have selected content. */
body.page-fromto-cleaning:has(#callBackDivFrom:not(:empty)):has(#callBackDivTo:not(:empty)) a#lbViewRecommendations {
    background: var(--ftc-accent) !important;
    color: var(--ftc-accent-text) !important;
}
/* Kill the legacy <ins> spacer that ASP.NET LinkButton injects. */
body.page-fromto-cleaning a#lbViewRecommendations > ins,
body.page-fromto-cleaning a.LinkButton#lbViewRecommendations > ins {
    display: none !important;
}

/* The cell wrapping the recommend+number3 inner table — collapse to 0 so the
 * controls row reclaims the space. */
body.page-fromto-cleaning #upFromToCleaning td:has(#lbViewRecommendations) {
    width: 0 !important;
    padding: 0 !important;
    border: 0 !important;
    overflow: visible;
}

/* =====================================================================
 * 3) Body — the inner content table holds spacer row, the
 *    FromToTab labels row, the controls row, and the callback row.
 *    Hide spacer + FromToTab; layout the controls + callback as a
 *    2-column grid: [From column] [swap-icon-col] [To column].
 * ===================================================================== */

/* The body table that holds spacer/labels/controls/callback rows.
 * Figma layout: 2 inset panels (Frame 19 / Frame 20) each 729 wide on a 1534
 * body-slot, with a 76px gap holding the 36×36 swap button. The panels stretch
 * vertically to contain BOTH controls row and helper row (no visual gap between
 * them) — so row-gap = 0 and panels paint via #tdFrom + #callBackDivFrom both
 * getting the slate-100 bg with stitched border-radii.
 */
body.page-fromto-cleaning #upFromToCleaning > table:first-of-type > tbody > tr:nth-child(2) > td > table {
    display: grid !important;
    width: 100% !important;
    grid-template-columns: 1fr 76px 1fr;
    grid-template-rows: auto auto;
    column-gap: 0;
    row-gap: 0;
    margin-top: 24px; /* Figma: card header bottom (y=60) → body slot top (y=84) = 24 */
}
body.page-fromto-cleaning #upFromToCleaning > table:first-of-type > tbody > tr:nth-child(2) > td > table > tbody {
    display: contents;
}

/* Hide spacer row (single empty td) and the FromToTab label row (6 empty/labels). */
body.page-fromto-cleaning #upFromToCleaning > table:first-of-type > tbody > tr:nth-child(2) > td > table > tbody > tr:nth-child(1),
body.page-fromto-cleaning #upFromToCleaning > table:first-of-type > tbody > tr:nth-child(2) > td > table > tbody > tr:nth-child(2) {
    display: none !important;
}

/* Controls row -- becomes a 3-col grid: From | Swap | To.
 * The legacy 4th cell (Recommend wrapper) is collapsed in §2.
 * Cells flatten via display:contents. */
body.page-fromto-cleaning #upFromToCleaning > table:first-of-type > tbody > tr:nth-child(2) > td > table > tbody > tr:nth-child(3) {
    display: contents !important;
}
body.page-fromto-cleaning #upFromToCleaning > table:first-of-type > tbody > tr:nth-child(2) > td > table > tbody > tr:nth-child(3) > td:nth-child(1) {
    grid-column: 1;
    grid-row: 1;
    background: transparent !important;
    width: auto !important;
    height: auto !important;
    padding: 0 !important;
}
body.page-fromto-cleaning #upFromToCleaning > table:first-of-type > tbody > tr:nth-child(2) > td > table > tbody > tr:nth-child(3) > td:nth-child(2) {
    grid-column: 2;
    grid-row: 1;
    display: flex !important;
    align-items: center;
    justify-content: center;
    padding: 0 !important;
}
body.page-fromto-cleaning #upFromToCleaning > table:first-of-type > tbody > tr:nth-child(2) > td > table > tbody > tr:nth-child(3) > td:nth-child(3) {
    grid-column: 3;
    grid-row: 1;
    background: transparent !important;
    width: auto !important;
    padding: 0 !important;
}

/* Callback strips row -- map orange/green divs to the same 2 columns. */
body.page-fromto-cleaning #upFromToCleaning > table:first-of-type > tbody > tr:nth-child(2) > td > table > tbody > tr:nth-child(4) {
    display: contents !important;
}
body.page-fromto-cleaning #upFromToCleaning > table:first-of-type > tbody > tr:nth-child(2) > td > table > tbody > tr:nth-child(4) > td:nth-child(1) {
    grid-column: 1;
    grid-row: 2;
    padding: 0 !important;
}
body.page-fromto-cleaning #upFromToCleaning > table:first-of-type > tbody > tr:nth-child(2) > td > table > tbody > tr:nth-child(4) > td:nth-child(2) {
    grid-column: 2;
    grid-row: 2;
    padding: 0 !important;
}
body.page-fromto-cleaning #upFromToCleaning > table:first-of-type > tbody > tr:nth-child(2) > td > table > tbody > tr:nth-child(4) > td:nth-child(3) {
    grid-column: 3;
    grid-row: 2;
    padding: 0 !important;
}
/* Hide the trailing empty 4th td in callback row, if present. */
body.page-fromto-cleaning #upFromToCleaning > table:first-of-type > tbody > tr:nth-child(2) > td > table > tbody > tr:nth-child(4) > td:nth-child(4) {
    display: none !important;
}

/* =====================================================================
 * 4) From + To column inner layout
 *    Each is a div (#tdFrom / #tdTo) inside a td.GradientBG. The div
 *    contains an inline table with numberBlock badge + radios + input
 *    + Search button. Convert to a 2-row stack: title -> controls.
 * ===================================================================== */
body.page-fromto-cleaning td.GradientBG {
    width: auto !important;
    height: auto !important;
    background: transparent !important;
}

/* From / To inset panel (top half — controls). Slate-100 bg + top-rounded
 * corners. Helper row below (#callBackDivFrom/To) stitches bottom corners
 * via row-gap:0 + matching bg. */
/* NOTE: qualify with `div#` — the JS-injected commodity-details markup emits a
 * `<td id="tdFrom">` / `<td id="tdTo">` (duplicate id from the web method),
 * which would otherwise inherit the panel chrome + ::before title and produce a
 * second "From commodity" line. `div#tdFrom` matches only the panel wrapper. */
body.page-fromto-cleaning div#tdFrom,
body.page-fromto-cleaning div#tdTo {
    display: block !important;
    width: 100% !important;
    height: auto !important;
    position: relative !important;
    margin: 0 !important;
    padding: 16px 16px 0 16px !important;
    background: var(--ftc-panel-bg) !important;
    border-radius: var(--ftc-panel-radius) var(--ftc-panel-radius) 0 0 !important;
    box-sizing: border-box;
}

/* Section title — synthesise via ::before since the FromToTab row was hidden. */
body.page-fromto-cleaning div#tdFrom::before {
    content: "From commodity";
    display: block;
    font: 600 16px/24px var(--font-sans, "Inter", system-ui, sans-serif);
    color: var(--ftc-heading-color);
    margin-bottom: 16px;
}
body.page-fromto-cleaning div#tdTo::before {
    content: "To commodity";
    display: block;
    font: 600 16px/24px var(--font-sans, "Inter", system-ui, sans-serif);
    color: var(--ftc-heading-color);
    margin-bottom: 16px;
}

/* Inner search div + its table — flatten to inline-flex row. */
body.page-fromto-cleaning #divFromSearch,
body.page-fromto-cleaning #divToSearch {
    display: block;
    width: 100%;
}
body.page-fromto-cleaning #divFromSearch > table,
body.page-fromto-cleaning #divToSearch > table {
    display: block;
    width: 100%;
    border-collapse: collapse;
}
body.page-fromto-cleaning #divFromSearch > table > tbody,
body.page-fromto-cleaning #divToSearch > table > tbody {
    display: block;
    width: 100%;
}
body.page-fromto-cleaning #divFromSearch > table > tbody > tr,
body.page-fromto-cleaning #divToSearch > table > tbody > tr {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    width: 100%;
}
body.page-fromto-cleaning #divFromSearch > table > tbody > tr > td,
body.page-fromto-cleaning #divToSearch > table > tbody > tr > td {
    padding: 0 !important;
    border: 0 !important;
    vertical-align: top;
}

/* Hide the numberBlock badges (1, 2, 3). */
body.page-fromto-cleaning .numberBlock,
body.page-fromto-cleaning td.numberBlock {
    display: none !important;
}

/* The cell that contains radios + input + Search becomes a flex column:
 *   row 1: All / Favorites radios
 *   row 2: input + Search button
 * The inner ASP RadioButtonList renders <span> wrapping <input> + <label>. */
body.page-fromto-cleaning #divFromSearch > table > tbody > tr > td:last-child,
body.page-fromto-cleaning #divToSearch > table > tbody > tr > td:last-child {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 8px;
    width: 100%;
    flex: 1 1 auto;
}

/* Force the natural inline flow of the radios+input+button into structured wrap. */
body.page-fromto-cleaning #divFromSearch > table > tbody > tr > td:last-child::before,
body.page-fromto-cleaning #divToSearch > table > tbody > tr > td:last-child::before {
    /* spacer for grid separation only — no content */
    content: "";
    display: none;
}

/* Radio button list — the <span> wrapper. */
body.page-fromto-cleaning #ctl00_cphMiddle_ctl00_rblSearchTypeFrom,
body.page-fromto-cleaning #ctl00_cphMiddle_ctl00_rblSearchTypeTo {
    display: inline-flex;
    align-items: center;
    gap: 16px;
    font: 400 14px/16px var(--font-sans, "Inter", system-ui, sans-serif);
    color: var(--ftc-body-color);
}
body.page-fromto-cleaning #ctl00_cphMiddle_ctl00_rblSearchTypeFrom input[type="radio"],
body.page-fromto-cleaning #ctl00_cphMiddle_ctl00_rblSearchTypeTo  input[type="radio"] {
    width: 16px;
    height: 16px;
    margin: 0 6px 0 0;
    accent-color: var(--ftc-accent);
    vertical-align: middle;
}
body.page-fromto-cleaning #ctl00_cphMiddle_ctl00_rblSearchTypeFrom label,
body.page-fromto-cleaning #ctl00_cphMiddle_ctl00_rblSearchTypeTo  label {
    margin: 0 12px 0 0;
    cursor: pointer;
    vertical-align: middle;
}

/* Search input + Search button — wrap them into a row via inline-flex on the
 * containing span. They're siblings inside the same td:last-child cell. */
body.page-fromto-cleaning #txtSearchFrom,
body.page-fromto-cleaning #txtSearchTo {
    flex: 1 1 auto;
    height: 32px;
    padding: 6px 12px;
    border: 1px solid var(--ftc-input-border);
    border-radius: 8px; /* rounded-md per Figma input instance */
    background: var(--ftc-input-bg);
    color: var(--ftc-input-text);
    font: 400 14px/20px var(--font-sans, "Inter", system-ui, sans-serif);
    box-sizing: border-box;
    box-shadow: none;
    outline: none;
    width: auto !important;
    min-width: 0;
}
body.page-fromto-cleaning #txtSearchFrom:focus,
body.page-fromto-cleaning #txtSearchTo:focus {
    border-color: var(--ftc-accent);
    box-shadow: 0 0 0 3px rgba(var(--primary-tint), .18); /* teal-600 @ 18% (was Phase-3 #00B8D4) */
}
body.page-fromto-cleaning #txtSearchFrom.DisabledText,
body.page-fromto-cleaning #txtSearchTo.DisabledText,
body.page-fromto-cleaning #txtSearchFrom::placeholder,
body.page-fromto-cleaning #txtSearchTo::placeholder {
    color: var(--ftc-input-placeholder);
    font-style: normal;
}

/* Search LinkButton — teal pill 64×32. Base `.LinkButton` rule paints
 * `--brand-primary` slate-blue and adds 5px asymmetric padding — we override
 * with !important on every property because the base class lives across
 * tokens.css/Q88Whiteout.css and wins on cascade order. */
body.page-fromto-cleaning a.LinkButton#ctl00_cphMiddle_ctl00_lbSearchFrom,
body.page-fromto-cleaning a.LinkButton#ctl00_cphMiddle_ctl00_lbSearchTo,
body.page-fromto-cleaning a[id$="_lbSearchFrom"].LinkButton,
body.page-fromto-cleaning a[id$="_lbSearchTo"].LinkButton {
    display: inline-flex !important;
    align-items: center;
    justify-content: center;
    box-sizing: border-box !important;
    height: 32px !important;
    min-height: 32px !important;
    width: 64px !important;          /* Figma: Button width=64 — fixed, not min */
    min-width: 64px !important;
    padding: 0 !important;           /* text centered via flex; padding makes it overshoot 64 */
    background: var(--ftc-accent) !important;
    background-color: var(--ftc-accent) !important;
    background-image: none !important;
    color: var(--surface-raised) !important;
    border: 1px solid var(--ftc-accent) !important;
    border-radius: 8px !important; /* rounded-md per Figma button instance */
    font: 500 13px/30px var(--font-sans, "Inter", system-ui, sans-serif) !important;
    text-decoration: none !important;
    cursor: pointer;
    transition: background-color .15s ease;
    white-space: nowrap;
}
body.page-fromto-cleaning a[id$="_lbSearchFrom"].LinkButton:hover,
body.page-fromto-cleaning a[id$="_lbSearchTo"].LinkButton:hover {
    background: var(--ftc-accent-hover) !important;
    background-color: var(--ftc-accent-hover) !important;
}
body.page-fromto-cleaning a[id$="_lbSearchFrom"].LinkButton > ins,
body.page-fromto-cleaning a[id$="_lbSearchTo"].LinkButton > ins {
    display: none !important;
}

/* The legacy <br> between Search and the AutoCompleteExtender style tag
 * inserts a line break. Kill it. */
body.page-fromto-cleaning #divFromSearch br,
body.page-fromto-cleaning #divToSearch br {
    display: none;
}

/* The flow of children inside td:last-child is: rbl <span>, &nbsp;&nbsp;,
 * input, anchor (Search). Group radios separately from input+Search.
 * Wrap input + button into a flex row via container query on the input span:
 * we can't add a wrapper without markup edits, so use grid + auto-flow. */
body.page-fromto-cleaning #divFromSearch > table > tbody > tr > td:last-child,
body.page-fromto-cleaning #divToSearch > table > tbody > tr > td:last-child {
    display: grid;
    grid-template-areas: "radios" "search";
    grid-template-rows: auto auto;
    row-gap: 8px;
}
body.page-fromto-cleaning [id$="rblSearchTypeFrom"],
body.page-fromto-cleaning [id$="rblSearchTypeTo"] {
    grid-area: radios;
    justify-self: start;
}
body.page-fromto-cleaning #txtSearchFrom,
body.page-fromto-cleaning #txtSearchTo,
body.page-fromto-cleaning a[id$="_lbSearchFrom"].LinkButton,
body.page-fromto-cleaning a[id$="_lbSearchTo"].LinkButton {
    grid-area: search;
}

/* Use sub-flex on the search row: input grows, button is fixed. */
body.page-fromto-cleaning #divFromSearch > table > tbody > tr > td:last-child > #txtSearchFrom {
    justify-self: stretch;
    width: 100% !important;
}
body.page-fromto-cleaning #divToSearch > table > tbody > tr > td:last-child > #txtSearchTo {
    justify-self: stretch;
    width: 100% !important;
}

/* Because grid-area "search" stacks input + button, we need them on one row.
 * Set the cell's search row to a sub-grid: 1fr | auto. */
body.page-fromto-cleaning #divFromSearch > table > tbody > tr > td:last-child,
body.page-fromto-cleaning #divToSearch > table > tbody > tr > td:last-child {
    grid-template-areas: "radios radios" "input button";
    grid-template-columns: 1fr auto;
}
body.page-fromto-cleaning [id$="rblSearchTypeFrom"],
body.page-fromto-cleaning [id$="rblSearchTypeTo"] {
    grid-area: radios;
}
body.page-fromto-cleaning #txtSearchFrom,
body.page-fromto-cleaning #txtSearchTo {
    grid-area: input;
    width: 100% !important;
}
body.page-fromto-cleaning a[id$="_lbSearchFrom"].LinkButton,
body.page-fromto-cleaning a[id$="_lbSearchTo"].LinkButton {
    grid-area: button;
    margin-left: 12px;
    align-self: stretch;
}

/* =====================================================================
 * 5) Swap-icon button (#ibSwapFromTo)
 *
 *    Prior approach used `filter: brightness(0) + mask-image: <Lucide SVG>`
 *    on the <input type="image" src="Refresh.gif"> to recolor + reshape the
 *    raster. That produced visibly broken arrowheads — the SVG paths use
 *    `fill="none" stroke=...` (Lucide stroke-only convention), so the mask
 *    has alpha only along the 2px-wide stroke outlines. Combined with
 *    mask-size scaling and filter ordering, the chevron arrowheads collapse
 *    to faint dot fragments while the long horizontal shafts render solid.
 *    Verified via 8× zoom crop of baseline-q88-full.png vs Figma card PNG.
 *
 *    Replacement: hide the <input> (opacity:0, fills the TD as click target)
 *    and paint the icon on the wrapping TD via ::after with a plain
 *    `background-image: <Lucide SVG>` — no mask, no filter, browser draws
 *    the SVG directly so strokes + linecaps render correctly.
 *
 *    Figma glyph confirmed via get_design_context on Card 119:9506:
 *    Component name `Icon / 24px / ArrowRightLeft` (node
 *    I119:9506;25831:122485;119:9512;119:7949;46:193), rendered at 16×16
 *    inside a 36×36 secondary button. Stroke = base/foreground (#0f172a
 *    slate-900) per file's get_variable_defs.
 * ===================================================================== */
body.page-fromto-cleaning #ctl00_cphMiddle_ctl00_ibSwapFromTo {
    /* Invisible click-target spanning the TD; the icon is painted on the TD's
     * ::after below. opacity:0 keeps it clickable (visibility:hidden would not). */
    position: absolute;
    inset: 0;
    width: 100% !important;
    height: 100% !important;
    margin: 0;
    padding: 0 !important;
    border: 0 !important;
    background: transparent !important;
    opacity: 0;
    cursor: pointer;
    z-index: 1;
}

/* The TD that hosts the swap button sits in grid column 2 (76px wide). Span
 * both rows so the 36×36 button vertically centers across the whole panel
 * (controls row + helper row), not just the controls row.
 *
 * This TD paints BOTH the chrome (panel-bg + rounded corners) AND the icon
 * itself (via ::after). The <input> inside is opacity:0 click-target only. */
body.page-fromto-cleaning #upFromToCleaning > table:first-of-type > tbody > tr:nth-child(2) > td > table > tbody > tr:nth-child(3) > td:nth-child(2) {
    grid-row: 1 / span 2 !important;
    align-self: center;
    justify-self: center;
    position: relative !important;
    padding: 0 !important;
    width: 36px !important;
    height: 36px !important;
    background: var(--ftc-panel-bg) !important;
    border-radius: 8px !important;
    box-sizing: border-box !important;
    overflow: hidden;
    cursor: pointer;
    transition: background-color .15s ease;
}
/* Icon glyph painted directly via background-image (no mask, no filter).
 * Lucide ArrowRightLeft (https://lucide.dev/icons/arrow-right-left), 16×16
 * to match Figma's icon container, stroke #0f172a slate-900. */
body.page-fromto-cleaning #upFromToCleaning > table:first-of-type > tbody > tr:nth-child(2) > td > table > tbody > tr:nth-child(3) > td:nth-child(2)::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%230f172a' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='m16 3 4 4-4 4'/><path d='M20 7H4'/><path d='m8 21-4-4 4-4'/><path d='M4 17h16'/></svg>");
    background-position: center;
    background-repeat: no-repeat;
    background-size: 16px 16px;
}
body.page-fromto-cleaning #upFromToCleaning > table:first-of-type > tbody > tr:nth-child(2) > td > table > tbody > tr:nth-child(3) > td:nth-child(2):hover {
    background: #E2E8F0 !important;
}
/* The empty TD that WAS at grid col 2 row 2 — now redundant since swap spans
 * row 2. Hide so it doesn't reserve a grid track. */
body.page-fromto-cleaning #upFromToCleaning > table:first-of-type > tbody > tr:nth-child(2) > td > table > tbody > tr:nth-child(4) > td:nth-child(2) {
    display: none !important;
}

/* =====================================================================
 * 6) Selected-commodity name (under title) + always-on helper note
 *
 *    Figma 766:12203 (selected state) shows the panel stacked as:
 *      title → selected commodity name → search row → dot + helper note.
 *    The chosen commodity name sits directly UNDER the title, and the
 *    colored dot + helper note stays visible at the panel bottom in BOTH
 *    empty and selected states.
 *
 *    UX R&D feedback (annotations carried in the Figma node itself):
 *      #1 "Selected commodity should be shown here right under the from
 *          commodity"  → name block below the title (markup-moved
 *          #callBackDivFrom/#callBackDivTo now live inside #tdFrom/#tdTo).
 *      #2 "The notes of color is missing in dev environment" → the colored
 *          dot used to vanish on selection because the helper shared the
 *          same div as the commodity name. The note is now a separate
 *          always-on element (.ftc-note) in the callback row.
 * ===================================================================== */

/* Selected commodity name — JS injects the commodity-details markup into
 * #callBackDivFrom/#callBackDivTo, which now sit between the title (::before)
 * and the search row inside #tdFrom/#tdTo. Figma node 119:7939:
 * Inter semibold 16/24, base/accent-foreground (#1e293b slate-800).
 * Collapses when empty so the empty state reads title → search → note. */
body.page-fromto-cleaning #callBackDivFrom,
body.page-fromto-cleaning #callBackDivTo {
    display: block !important;
    width: 100% !important;
    height: auto !important;
    min-height: 0 !important;
    margin: 0 0 16px 0 !important;          /* 16px gap to the search row (Figma) */
    padding: 0 !important;
    background: transparent !important;     /* sits on the slate panel already */
    border-radius: 0 !important;
    box-sizing: border-box;
    color: var(--accent-foreground) !important;  /* #1e293b slate-800 */
    font: 600 16px/24px var(--font-sans, "Inter", system-ui, sans-serif) !important;
}
/* Empty state: no name block, no gap — panel reads title → search → note. */
body.page-fromto-cleaning #callBackDivFrom:empty,
body.page-fromto-cleaning #callBackDivTo:empty {
    display: none !important;
}
/* The injected commodity-details table carries .ComLabel (#ADADAD @ .5 opacity)
 * plus a forced black TD color from the ASCX inline <style>. Override so the
 * name renders solid slate-800 semibold per Figma, not faded gray. */
body.page-fromto-cleaning #callBackDivFrom table,
body.page-fromto-cleaning #callBackDivTo table,
body.page-fromto-cleaning #callBackDivFrom td,
body.page-fromto-cleaning #callBackDivTo td,
body.page-fromto-cleaning #callBackDivFrom strong,
body.page-fromto-cleaning #callBackDivTo strong {
    opacity: 1 !important;
    color: var(--accent-foreground) !important;
    background: transparent !important;
    border: 0 !important;
    padding: 0 !important;
    font: 600 16px/24px var(--font-sans, "Inter", system-ui, sans-serif) !important;
}

/* Always-on helper note (dot + text) — static markup in the callback row
 * (grid row 2), slate bg + bottom-rounded corners stitching with #tdFrom/#tdTo
 * above. The colored dot (the "note color" the UX feedback flagged) is now
 * always painted, in both empty and selected states. */
body.page-fromto-cleaning .ftc-note {
    display: flex !important;
    align-items: center;
    gap: 12px;                              /* Figma Frame 40: dot → text inset 12 */
    width: 100%;
    box-sizing: border-box;
    margin: 0 !important;
    padding: 16px !important;
    background: var(--ftc-panel-bg) !important;
    border-radius: 0 0 var(--ftc-panel-radius) var(--ftc-panel-radius) !important;
    font: 400 14px/16px var(--font-sans, "Inter", system-ui, sans-serif);  /* text/sm */
    color: var(--ftc-muted-color);
}
/* 16px colored dot with shadow-xs (Figma Ellipse: size-16 rounded-full + shadow/xs). */
body.page-fromto-cleaning .ftc-note::before {
    content: "";
    flex: 0 0 16px;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    box-shadow: 0 1px 2px 0 rgba(0,0,0,.05);     /* shadow/xs */
}
body.page-fromto-cleaning .ftc-note-from::before { background: var(--ftc-orange-dot); }
body.page-fromto-cleaning .ftc-note-to::before   { background: var(--ftc-green-dot); }

/* =====================================================================
 * 7) Data grid (ucSearch / grdResults) — minimalize chrome to Figma.
 *    The grid is a DevExtreme ASPxGridView inside the second outer table.
 *    Hide the inner filter row (already 0-height) and the bottom pager
 *    (Figma omits a pager). Restyle header + row borders + selector dot.
 * ===================================================================== */
body.page-fromto-cleaning #upFromToCleaning > table:nth-of-type(2) {
    margin-top: 24px;
}
body.page-fromto-cleaning #upFromToCleaning > table:nth-of-type(2) > tbody > tr > td {
    padding: 0 !important;
    background: transparent !important;
}

/* Hide ucSearch internal search controls (already collapsed but enforce). */
body.page-fromto-cleaning [id$="_ucSearch_tdOptions"],
body.page-fromto-cleaning [id$="_ucSearch_divSearchPadding"] [id$="_rblSearchType"] {
    display: none !important;
}

/* Pager — Figma has no pager; hide it. */
body.page-fromto-cleaning .dxgvPagerBottomPanel,
body.page-fromto-cleaning [id$="_grdResults_tcPagerBarB"] {
    display: none !important;
}

/* Grid wrapper. */
body.page-fromto-cleaning table[id$="_grdResults"].dxgvControl {
    width: 100% !important;
    background: var(--ftc-card-bg);
    border: 0;
}

/* DX MainTable carries its own legacy chrome — `.dxgvTable.dxgvRBB` paints
 * a 1px #9f9f9f border on all four sides (visible as gray vertical hairlines
 * on the left + right edges of the grid area, full-table-height tall). The
 * outer `.dxgvControl` border was already cleared but the inner `.dxgvTable`
 * was missed because its id ends in `_DXMainTable`, not `_grdResults`. */
body.page-fromto-cleaning table[id$="_DXMainTable"].dxgvTable,
body.page-fromto-cleaning table[id$="_DXMainTable"] {
    border: 0 !important;
}

/* Header strip — Card-embedded Table render. Per Figma Table component
 * (119:7864) tokens + Card-context color-pick on fromtocleaning-card-hires.png:
 *   - header bg = var(--surface-muted) (Card overrides Table's #f5f5f5 default)
 *   - header text = var(--text-primary) slate-900 semibold (Card's base/foreground)
 *   - cell horizontal padding = 16 (Table token cellHorizontalPadding)
 *   - row border = var(--border-default) (Card overrides Table's #e5e5e5)
 *   - font = sm 14/20 (Table token text/sm), not 12/16
 *   - header height ≈ 36 visual (solid bg y=269..305 in card hi-res PNG)
 *
 * DX header row builds an inner table per cell with caption + filter chevron;
 * the inner table is what controls visual height, so target both the td and
 * the inner-caption padding. */
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] {
    width: 100% !important;
    border-collapse: separate;
    border-spacing: 0;
}
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] tr.dxgvHeader,
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] [id$="_DXHeadersRow0"] {
    height: 36px !important;
}
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] td.dxgvHeader {
    background: var(--surface-muted) !important;             /* Card-context muted (was #F8FAFC) */
    color: var(--text-primary) !important;                  /* slate-900 (was muted var(--text-muted)) */
    border: 0 !important;
    border-bottom: 1px solid var(--border-default) !important;/* Card-context border (was #E2E8F0 token same actually, but explicit) */
    box-sizing: border-box !important;
    padding: 0 16px !important;                 /* Table token cellHorizontalPadding (was 12) */
    font: 600 14px/20px var(--font-sans, "Inter", system-ui, sans-serif) !important;
    text-transform: none !important;
    height: 36px !important;
    line-height: 20px !important;
    vertical-align: middle !important;
}
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] td.dxgvHeader > table.tblColHeader,
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] td.dxgvHeader > div {
    height: 20px !important;
    line-height: 20px !important;
}
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] td.dxgvHeader > table.tblColHeader > tbody,
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] td.dxgvHeader > table.tblColHeader > tbody > tr {
    height: 20px !important;
}
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] td.dxgvHeader [id$="_tdCaption"] {
    padding: 0 !important;
    height: 20px !important;
    line-height: 20px !important;
    font-size: 14px !important;                 /* was 12 — Table token text/sm */
    font-weight: 600 !important;                /* semibold — Table token */
    color: var(--text-primary) !important;                  /* slate-900 — Card-context fg */
    vertical-align: middle !important;
}
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] td.dxgvHeader [id$="_tdCaption"] > div {
    height: 20px !important;
    line-height: 20px !important;
}
/* Sortable column headers render the title as `<a href="#hdrClk">Commodity</a>`
 * inside the caption div. The `<a>` inherits MainWhiteout's `a:link/visited`
 * which sets `color: var(--brand-primary)` (default blue/teal-ish) and
 * `font-weight: bold` (variable) — but the Inter font weight 400 specifically
 * gets applied here, NOT the semibold 600 of the cell. Override both. */
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] td.dxgvHeader a,
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] td.dxgvHeader a:link,
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] td.dxgvHeader a:visited,
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] td.dxgvHeader a:hover,
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] td.dxgvHeader a:active {
    color: var(--text-primary) !important;       /* slate-900 — matches non-sortable cells */
    font-weight: 600 !important;     /* semibold — Table token */
    font-size: 14px !important;      /* sm — Table token */
    text-decoration: none !important;
    cursor: pointer;
}
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] td.dxgvHeader [id$="_tdFilterMenu"] {
    display: none !important;
}
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] td.dxgvHeader img.Img-SortedAsc,
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] td.dxgvHeader img.Img-SortedDesc {
    margin-left: 4px;
    opacity: .6;
}

/* Data rows — Card-context divider var(--border-default); row height ≈ 36 per Table component
 * color-pick at y=72..107 (35-36 px between dividers); font sm 14/20 regular.
 *
 * vertical-align: middle so multi-line cell content (commodity name + spec
 * stacked, MARPOL "Not Classified" wrapping) centres the row's other cells
 * (selector dot, single-line numerics) against the visual midline. Default
 * `vertical-align: top` made the dot and other short cells anchor to the row
 * top while the long text consumed the bottom half. */
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] tr.dxgvDataRow td {
    background: var(--ftc-card-bg) !important;
    color: var(--text-primary) !important;                  /* slate-900 (was --ftc-body-color same) */
    border: 0 !important;
    border-bottom: 1px solid var(--border-default) !important;/* was #F1F5F9 — too light vs Figma */
    padding: 8px 16px !important;               /* was 6 12 — cellHorizontalPadding 16, vertical 8 fits row h=36 */
    font: 400 14px/20px var(--font-sans, "Inter", system-ui, sans-serif) !important;
    height: 36px !important;                    /* was 32 — Table per-row measured 35-36 in Figma */
    vertical-align: middle !important;
}

/* Selected row highlights — overrides the legacy GridRowFocusFrom/To bg.
 *
 * Row-shrink trap: FromToCleaning.ascx L209 overwrites the TR className to
 * just "GridRowFocusFrom" / "GridRowFocusTo" on selection, DROPPING the
 * `dxgvDataRow` class. The base data-row rule above (`tr.dxgvDataRow td`)
 * therefore no longer matches on a selected row, and the ASCX inline style
 * `.GridRowFocusTo td, .GridRowFocusFrom td { padding: 1px 6px 3px 7px }`
 * (L52) takes over — selected rows visibly shrink to ~16px tall vs the
 * 36px of unselected rows. Restore the data-row geometry here so the row
 * keeps its size regardless of which classes JS leaves on the TR. */
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] tr.GridRowFocusFrom td,
body.page-fromto-cleaning .GridRowFocusFrom td {
    background: var(--ftc-orange-row) !important;
    padding: 8px 16px !important;
    height: 36px !important;
    vertical-align: middle !important;
    font: 400 14px/20px var(--font-sans, "Inter", system-ui, sans-serif) !important;
    color: var(--text-primary) !important;
    border: 0 !important;
    border-bottom: 1px solid var(--border-default) !important;
}
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] tr.GridRowFocusTo td,
body.page-fromto-cleaning .GridRowFocusTo td {
    background: var(--ftc-green-row) !important;
    padding: 8px 16px !important;
    height: 36px !important;
    vertical-align: middle !important;
    font: 400 14px/20px var(--font-sans, "Inter", system-ui, sans-serif) !important;
    color: var(--text-primary) !important;
    border: 0 !important;
    border-bottom: 1px solid var(--border-default) !important;
}
/* Selector cell on selected rows — restore the 48px width + center align that
 * the base `tr.dxgvDataRow > td:first-child` rule loses when dxgvDataRow is
 * dropped by JS on selection. */
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] tr.GridRowFocusFrom > td:first-child,
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] tr.GridRowFocusTo > td:first-child {
    text-align: center !important;
    width: 48px !important;
    padding: 6px 0 !important;
}

/* Hover — neutralise every hovered-row repaint so each row keeps its resting
 * background (Figma shows no hover state). Three competing rules paint the
 * hovered row on this page:
 *   - components.css `.q88-refresh .dxgvDataRow:hover td`  → --grid-row-focus
 *     (pale yellow #ffffc0)               specificity (0,3,1) !important
 *   - CustomGrid.css `.dxgvDataRow:hover td` → pale blue   specificity (0,2,1)
 *   - cleaning-recommendations.css `#upFromToCleaning tr.dxgvDataRow:hover > td`
 *     → --surface-card (slate-50 #f8fafc)  specificity (1,2,2) !important
 *       (an unscoped rule in that sibling page's sheet that LEAKS here because
 *        both pages share the #upFromToCleaning UpdatePanel id; it carries an
 *        ID, so this override must include #upFromToCleaning to outweigh it.)
 * Including #upFromToCleaning lifts these overrides to (1,4,4) so they beat the
 * ID-bearing leak. This is why normal rows (class dxgvDataRow) used to flash a
 * pale box while selected rows (class GridRowFocusFrom, no dxgvDataRow) didn't.
 * (UX R&D #3 "hovering the table gives the text a white/pale background".) */
body.page-fromto-cleaning #upFromToCleaning table[id$="_grdResults_DXMainTable"] tr.dxgvDataRow:hover td,
body.page-fromto-cleaning #upFromToCleaning table[id$="_grdResults_DXMainTable"] tr.dxgvDataRowAlt:hover td {
    background: var(--ftc-card-bg) !important;
}
body.page-fromto-cleaning #upFromToCleaning table[id$="_grdResults_DXMainTable"] tr.GridRowFocusFrom:hover td,
body.page-fromto-cleaning #upFromToCleaning .GridRowFocusFrom:hover td {
    background: var(--ftc-orange-row) !important;
}
body.page-fromto-cleaning #upFromToCleaning table[id$="_grdResults_DXMainTable"] tr.GridRowFocusTo:hover td,
body.page-fromto-cleaning #upFromToCleaning .GridRowFocusTo:hover td {
    background: var(--ftc-green-row) !important;
}

/* Selector dot column (left column) — strip native border, set Figma 48px col width.
 * NOTE on `>` combinator: `td:first-child` (descendant) accidentally matches the
 * inner-table first-td that contains the Img-StarOff favorite icon (in the 2nd
 * outer-row TD), which made the star icon paint as a second radio-like circle.
 * Use direct-child combinator `tr > td:first-child > img` to target ONLY the
 * outer row's selector cell.
 *
 * vertical-align: middle so the dot centres against the multi-line commodity
 * name in the adjacent cell (rows are 60-77px tall in this layout). */
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] tr.dxgvDataRow > td:first-child,
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] td.dxgvHeader:first-child {
    text-align: center !important;
    vertical-align: middle !important;
    width: 48px !important;  /* Figma table selector col size-[48px] (90:7464) */
    padding: 6px 0 !important;
}

/* The radio-image in the leftmost cell — the legacy markup uses an <img> with
 * inline background URL. Restyle to a Lucide-style hollow/filled circle.
 * Figma Radio (I119:9506;...;65:321): size-[16px] rounded-full bg-white
 * border 1px var(--base/border,var(--border-default)) shadow-xs. */
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] tr.dxgvDataRow > td:first-child > img {
    width: 16px !important;
    height: 16px !important;
    border-radius: 50%;
    background-image: none !important;
    background-color: var(--surface-raised) !important;
    border: 1px solid var(--border-default);
    box-sizing: border-box;
    box-shadow: 0 1px 2px 0 rgba(0,0,0,.05); /* shadow/xs from Figma var defs */
}
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] tr.GridRowFocusFrom > td:first-child > img {
    background-color: var(--ftc-orange-dot) !important;
    border-color: var(--ftc-orange-dot) !important;
}
body.page-fromto-cleaning table[id$="_grdResults_DXMainTable"] tr.GridRowFocusTo > td:first-child > img {
    background-color: var(--ftc-green-dot) !important;
    border-color: var(--ftc-green-dot) !important;
}

/* Empty data row caption (when grid has no data). */
body.page-fromto-cleaning [id$="_grdResults_DXEmptyRow"] td {
    background: var(--ftc-card-bg) !important;
    color: var(--ftc-muted-color) !important;
    text-align: center;
    padding: 24px !important;
    font: 400 13px/20px var(--font-sans, "Inter", system-ui, sans-serif) !important;
}

/* =====================================================================
 * 8) Misc legacy chrome we don't want
 * ===================================================================== */
body.page-fromto-cleaning #spanLoadingFrom,
body.page-fromto-cleaning #spanLoadingTo {
    display: inline-block;
}
body.page-fromto-cleaning #spanLoadingFrom img,
body.page-fromto-cleaning #spanLoadingTo img {
    vertical-align: middle;
}

/* =====================================================================
 * 9) Favorite star icon — moved to components.css global Lucide swap
 *    (24×24, matches Figma Milbros file ISZ58jrGcp1LwMEJeeG00Q node
 *    2064:10023). Per-page rule removed to enforce a single canonical
 *    star size across all favorite renders.
 * ===================================================================== */
