/*
 * AI Traffic Tracker: app specific styles.
 *
 * The theme (style.css) is a compiled Tailwind build. Its utility set is limited to
 * the classes the template's own 78 pages happened to use, so utilities we need for
 * new markup are simply absent from it. Verified missing and therefore defined here:
 * animate-pulse (and its keyframes), fractional widths, break-word wrapping and every
 * geometry class the skeleton and empty state need.
 *
 * Rule 4 of the build (no inline <style> or <script> in any partial) means this file
 * is the only place app CSS may live.
 *
 * This file is loaded by BOTH sides: partials/head.php for the merchant pages and
 * admin/partials/head.php for the admin. That is why the palette lives here rather than
 * in admin.css, which only the admin loads.
 */

/* ---------------------------------------------------------------------------
 * THE BRAND PALETTE.
 *
 * These values are generated from, and verified against, tools/theme.php. That file is the
 * source of truth; this block is its output. Editing a value here without editing it there
 * makes `php tools/theme.php` pass while the two disagree, so change SCALES and re-derive.
 *
 * Why the bundle needs them. style.css is compiled Tailwind with no theme layer: it emits
 * colours as literals, `rgb(72 127 255 / var(--tw-bg-opacity))`. tools/theme.php lifts the
 * ten primary literals into the ten variables below, which recolours every state at once
 * because hover, focus, active, dark mode, badges, rings, borders and gradient stops all
 * compile down to the same literal. Deleting this block does not restore the old blue; it
 * leaves every primary surface with no colour at all, which is why theme.php --check fails
 * when a variable the bundle references is missing.
 *
 * Derived from the supplied logo. Each scale pins its anchor step to a logo colour exactly,
 * holds the hue, runs a monotonic lightness scale, eases saturation at the pale end and
 * drifts the hue slightly deeper at the dark end so shades stay coral rather than turning
 * fire-engine red.
 *
 * `neutral` and `gray` are deliberately NOT retuned. They share their literals in this
 * bundle, so tinting them navy would tint every border, divider and disabled control, which
 * reads as muddy rather than as branded. The navy is spent deliberately, as --att-ink.
 * `info` and `blue` are left alone too: they are semantic, and recolouring them would leave
 * the product with no cool colour and make an info state indistinguishable from a primary one.
 * ------------------------------------------------------------------------ */
:root {
    /* Coral. Anchored at 600 on #F04E5C. Consumed by the bundle. */
    --att-primary-50: 252 235 236;
    --att-primary-100: 249 207 211;
    --att-primary-200: 245 170 177;
    --att-primary-300: 243 132 142;
    --att-primary-400: 241 103 115;
    --att-primary-500: 240 89 102;
    --att-primary-600: 240 78 92;
    /*
     * NOT the value the ramp derivation produced. That was #E91B32, which measures
     * 4.4991:1 against white and fails WCAG AA for normal text by nine ten-thousandths.
     * This step carries white text on every primary button and the active nav item, so a
     * boundary value is not good enough. #DC1930 is 4.9706:1 and still visibly lighter
     * than 800.
     */
    --att-primary-700: 220 25 48;
    --att-primary-800: 193 21 44;
    --att-primary-900: 146 17 38;

    /*
     * Danger, deepened and moved off the brand's hue. See tools/theme.php for the reasoning:
     * the template's #DC2626 and the brand's #DC1930 share a red channel and measure 1.03:1
     * apart, which is no separation at all once the brand is warm. Moving danger rather than
     * cooling the brand also fixes an AA failure the template shipped with: text-danger-600
     * on bg-danger-100 was 3.95:1 and is now 5.78:1.
     */
    --att-danger-50: 249 237 235;
    --att-danger-100: 242 210 207;
    --att-danger-200: 234 171 164;
    --att-danger-300: 227 124 112;
    --att-danger-400: 223 72 55;
    --att-danger-500: 197 42 25;
    --att-danger-600: 155 29 15;
    --att-danger-700: 130 25 13;
    --att-danger-800: 106 20 11;
    --att-danger-900: 83 16 9;

    /*
     * ROLE VARIABLES.
     *
     * Buttons, the active nav item, a focused input and a selected tab are the surfaces that
     * carry the brand as a field of colour rather than as an accent. tools/theme.php points
     * those specific rules at these two names, so the weight of every action surface in the
     * product is decided here, on two lines, rather than by editing the bundle again.
     */
    --att-action: var(--att-primary-700);
    --att-action-hover: var(--att-primary-800);

    /* Orange. Anchored at 600 on #F5822B. The far end of the logo gradient. */
    --att-orange-50: 253 242 234;
    --att-orange-100: 250 225 206;
    --att-orange-200: 248 202 168;
    --att-orange-300: 246 179 129;
    --att-orange-400: 245 162 99;
    --att-orange-500: 245 153 84;
    --att-orange-600: 245 130 43;
    --att-orange-700: 242 112 18;
    --att-orange-800: 201 88 13;
    --att-orange-900: 152 64 11;

    /* Pink magenta. Anchored at 600 on #E8397A. */
    --att-accent-50: 252 235 241;
    --att-accent-100: 247 209 223;
    --att-accent-200: 242 173 199;
    --att-accent-300: 238 137 174;
    --att-accent-400: 236 109 156;
    --att-accent-500: 234 94 146;
    --att-accent-600: 232 57 122;
    --att-accent-700: 228 32 108;
    --att-accent-800: 189 25 92;
    --att-accent-900: 143 20 73;

    /* Deep navy. Anchored at 900 on #233457. */
    --att-ink-50: 240 242 247;
    --att-ink-100: 219 225 237;
    --att-ink-200: 191 202 224;
    --att-ink-300: 163 179 212;
    --att-ink-400: 141 161 203;
    --att-ink-500: 131 153 198;
    --att-ink-600: 114 140 192;
    --att-ink-700: 81 113 179;
    --att-ink-800: 64 92 150;
    --att-ink-900: 35 52 87;

    /* The logo gradient, coral to orange, as one value so every surface using it agrees. */
    --att-brand-gradient: linear-gradient(135deg, rgb(var(--att-primary-600)) 0%, rgb(var(--att-orange-600)) 100%);

    /*
     * The two the template left dangling.
     *
     * style.css has always referenced var(--primary-50) and var(--primary-600) in the
     * `.dropdown > button.show` rule and has never defined them anywhere, so an open dropdown
     * button has had no background colour and no text colour for as long as this template has
     * existed. Defined here, in the brand palette, which is what they were evidently meant to
     * be. Unlike the --att-* variables these are full colour values, because that is how the
     * bundle uses them.
     */
    --primary-50: rgb(var(--att-primary-50));
    --primary-600: rgb(var(--att-primary-600));
}

/* ---------------------------------------------------------------------------
 * Brand surfaces built from the palette.
 *
 * Authored rather than composed from bundle utilities in one case only: the gradient. The
 * bundle ships bg-gradient-to-r and from-primary-*, but its from/to pairs can only reach one
 * scale at a time, and the logo gradient crosses two hues. Everything else uses the bundle's
 * own primary utilities, which now resolve to the brand colour.
 * ------------------------------------------------------------------------ */
.att-brand-gradient {
    background-image: var(--att-brand-gradient);
}

.att-ink {
    color: rgb(var(--att-ink-900));
}

/* ---------------------------------------------------------------------------
 * Dark text steps the bundle does not ship.
 *
 * The bundle emits exactly one text utility per colour family, `text-<name>-600`, and no
 * darker step for any of them. That was survivable with a blue brand and is not with a coral
 * one, because of a hard arithmetic limit rather than a matter of taste:
 *
 *   #F04E5C has a relative luminance of 0.2475. WCAG contrast is (L1+0.05)/(L2+0.05), so
 *   reaching 4.5:1 against it needs a background luminance of 1.289. The maximum possible
 *   is 1.0, for pure white. **There is no background on which text-primary-600 passes AA.**
 *
 * So the brand colour at 600 is a surface colour, not a text colour, and anywhere it was
 * being used as small text needs a darker step that does not exist. These are that step.
 * They are defined here because this file exists for exactly this: utilities the template's
 * own pages happened not to use, and which are therefore absent from its build.
 *
 * text-primary-900 on bg-primary-100 measures 6.39:1.
 * ------------------------------------------------------------------------ */
.text-primary-800 {
    color: rgb(var(--att-primary-800));
}

.text-primary-900 {
    color: rgb(var(--att-primary-900));
}

.text-danger-800 {
    color: rgb(var(--att-danger-800));
}

/*
 * The hover variant, for the same reason. A nav link whose rest state is dark neutral and
 * whose hover state is coral-600 spends the hover at 3.53:1, which fails AA for small text.
 * The state a control is in while you are pointing at it is not a state where legibility is
 * optional.
 */
.hover\:text-primary-800:hover {
    color: rgb(var(--att-primary-800));
}

/* ---------------------------------------------------------------------------
 * Brand mark in the sidebar.
 * The theme collapses the sidebar logo by swapping <img class="light-logo"> for
 * <img class="logo-icon">. We do not ship the template's logo images (they carry the
 * template's branding), so the logo is an Iconify mark plus a wordmark and we mirror
 * the same collapse behaviour against our own class names.
 * ------------------------------------------------------------------------ */
.sidebar-logo .brand-mark {
    flex-shrink: 0;
    font-size: 1.75rem;
    line-height: 1;
}

.sidebar-logo .brand-word {
    display: inline-block;
    white-space: nowrap;
    font-size: 1.0625rem;
    font-weight: 600;
    line-height: 1.2;
}

.sidebar.active .sidebar-logo .brand-word {
    display: none;
}

.sidebar.active:hover .sidebar-logo .brand-word {
    display: inline-block;
}

/* ---------------------------------------------------------------------------
 * Pulse. The theme has no animate-pulse rule and no pulse keyframes, so the
 * skeletons in the reference app never actually animated. Defined here under the
 * same class name so the familiar idiom keeps working.
 * ------------------------------------------------------------------------ */
@keyframes att-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.45; }
}

.animate-pulse {
    animation: att-pulse 1.6s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}

@media (prefers-reduced-motion: reduce) {
    .animate-pulse {
        animation: none;
        opacity: 0.6;
    }
}

/* ---------------------------------------------------------------------------
 * Skeletons, used by partials/loading-state.php and the sidebar plan box.
 * Sized like the content they stand in for.
 * ------------------------------------------------------------------------ */
.skeleton-bar {
    display: block;
    height: 1rem;
    border-radius: 0.375rem;
    background-color: rgb(226 228 233);
}

.dark .skeleton-bar {
    background-color: rgb(75 85 99);
}

.skeleton-bar + .skeleton-bar {
    margin-top: 0.75rem;
}

.skeleton-bar-xs { height: 0.625rem; }
.skeleton-bar-sm { height: 0.75rem; }
.skeleton-bar-lg { height: 1.5rem; }
.skeleton-bar-xl { height: 2.5rem; }

.skeleton-w-25  { width: 25%; }
.skeleton-w-40  { width: 40%; }
.skeleton-w-60  { width: 60%; }
.skeleton-w-75  { width: 75%; }
.skeleton-w-100 { width: 100%; }

.skeleton-block {
    display: block;
    width: 100%;
    height: 16rem;
    border-radius: 0.5rem;
    background-color: rgb(226 228 233);
}

.dark .skeleton-block {
    background-color: rgb(75 85 99);
}

.skeleton-row {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding-top: 0.875rem;
    padding-bottom: 0.875rem;
    border-bottom: 1px solid rgb(235 236 239);
}

.dark .skeleton-row {
    border-bottom-color: rgb(55 65 81);
}

.skeleton-row > .skeleton-bar {
    margin-top: 0;
    flex: 1 1 0%;
}

/* ---------------------------------------------------------------------------
 * Empty state. Built new: the theme ships no empty-state component anywhere, and a
 * blank chart on a fresh install reads as a broken app rather than as no data yet.
 * ------------------------------------------------------------------------ */
.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 2.5rem 1.5rem;
}

.empty-state-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 72px;
    height: 72px;
    border-radius: 9999px;
    font-size: 2rem;
    margin-bottom: 1.25rem;
}

.empty-state-body {
    max-width: 34rem;
    margin-left: auto;
    margin-right: auto;
}

/* ---------------------------------------------------------------------------
 * Chart mounts. ApexCharts sets the element height itself from the height it is
 * given, so the mount only needs a floor to stop the card collapsing before the
 * chart renders. The height itself travels on data-att-chart-height.
 * ------------------------------------------------------------------------ */
.chart-mount {
    min-height: 16rem;
}

/* ---------------------------------------------------------------------------
 * Legend swatches for the confirmed / suspected series.
 * ------------------------------------------------------------------------ */
.legend-swatch {
    display: inline-block;
    width: 0.75rem;
    height: 0.75rem;
    border-radius: 0.125rem;
    flex-shrink: 0;
}

/* ---------------------------------------------------------------------------
 * Table helpers. Long landing page paths have to wrap somewhere.
 * ------------------------------------------------------------------------ */
.cell-wrap {
    overflow-wrap: anywhere;
    word-break: break-word;
}

.table-scroll {
    width: 100%;
    overflow-x: auto;
}

/* The pricing table's own scroll container.

   `overflow-x: auto` makes `overflow-y` compute to `auto` as well, which turns the box
   into a clipping context. Anything positioned above the table is then cut off at its
   top edge, which is what happened to the "Most popular" and "Your plan" badges: they
   were `absolute -top-3`, half outside the header cell by design, and half of each one
   was being clipped away.

   Those badges are in normal flow now, so nothing needs to escape and nothing is
   clipped. This rule keeps the scroll behaviour for genuinely narrow screens and
   removes the vertical clipping that came with it. */
.att-pricing-scroll {
    width: 100%;
    overflow-x: auto;
    overflow-y: visible;
}

/* ---------------------------------------------------------------------------
 * Modal shell. Flowbite handles the show/hide; this only carries the geometry the
 * theme's compiled utility set is missing.
 * ------------------------------------------------------------------------ */
.modal-shell {
    width: 100%;
    max-width: 32rem;
    margin: 1rem auto;
}

.modal-backdrop-tint {
    background-color: rgba(15, 23, 42, 0.5);
}

/* ---------------------------------------------------------------------------
 * Utilities the theme's Tailwind build omitted.
 *
 * The template compiled only against its own 78 pages, so a utility it never
 * used does not exist in style.css even though it looks like ordinary Tailwind.
 * Anything added here is a genuine Tailwind utility, not an invented name.
 * ------------------------------------------------------------------------ */

.max-w-full {
    max-width: 100%;
}

/* Found 2026-08-11, when tools/css_coverage.php stopped scanning only index.php among the
   root-level merchant screens. All five had been resolving to nothing since those screens were
   built: the account avatar circle had no size, and two definition lists and the reconciliation
   split fell back to full-width rows at every breakpoint. Added here rather than swapped for
   nearby defined utilities, because that would have quietly redesigned three working screens to
   suit the template's build. Values and breakpoints are Tailwind's own, transcribed from the
   compiled bundle: sm is 576px, lg is 992px. */

.w-20 {
    width: 5rem;
}

.h-20 {
    height: 5rem;
}

@media (min-width: 576px) {
    .sm\:col-span-8 {
        grid-column: span 8 / span 8;
    }
}

@media (min-width: 992px) {
    .lg\:col-span-5 {
        grid-column: span 5 / span 5;
    }

    .lg\:col-span-7 {
        grid-column: span 7 / span 7;
    }
}

/* The bundle carries `items-end` and `text-end` but no responsive variant of
   either, at any breakpoint. Used by the headless pricing page's top bar, so the
   current-plan column sits against the right edge beside the other two on a wide
   screen and stacks left-aligned below one, which is what the reference's own bar
   does. 768px is the template's own `md` breakpoint, taken from style.css. */
@media (min-width: 768px) {
    .md\:items-end {
        align-items: flex-end;
    }

    .md\:text-end {
        text-align: end;
    }
}

/* ---------------------------------------------------------------------------
 * Template hooks the theme names but never styles.
 *
 * wowdash's own pages carry .badge, .alert and .toggle-password with no rule
 * behind them: the utility classes alongside do the work and these are just
 * hooks. We match that convention, but every class we ship must resolve to a
 * real rule, so each gets the one declaration it genuinely needs.
 * ------------------------------------------------------------------------ */

.badge {
    display: inline-block;
    white-space: nowrap;
}

.alert {
    display: block;
}

.alert-danger {
    /* Colour comes from the danger utilities alongside. This only guarantees the
       block reads as a distinct region for assistive technology styling. */
    border-style: solid;
}

.toggle-password {
    /* A JS hook in the template. It is an icon acting as a button, so it must not
       be selectable when clicked repeatedly. */
    user-select: none;
    -webkit-user-select: none;
}

.sidebar-plan-box {
    /* Plan box in the merchant sidebar. Keeps its contents from escaping when a
       long plan name arrives from Wix. */
    overflow-wrap: anywhere;
    background: #ffe0c47d;
}

.att-pricing-shell {
    /* Content column for the headless pricing page, which has no sidebar to take
       the width off it. The template's `.container` is the only cap our bundle
       ships and it opens to 1600px at 1650px wide, which is wider than any
       readable text column. Authored rather than reached for as a utility because
       no `max-w-*` in the compiled bundle lands between 672px and 100%.

       WIDENED 2026-08-18, from 64rem to 80rem. 64rem is a dashboard content column,
       and this page is not a dashboard screen: it is the app's external pricing page,
       opened by Wix in its own window, and since the same date it carries a single
       comparison table with four plan columns and a feature column. At 64rem those
       five columns are about 200px each, which wraps every cell and every plan name.
       80rem is what the reference caps its own pricing page at, in its
       `max-w-custom-pricing`, for a table with one column fewer. */
    width: 100%;
    max-width: 80rem;
    margin-inline: auto;
}

.att-pricing-table {
    /* The comparison table's geometry, in ONE rule.

       There were two `.att-pricing-table` blocks in this file until 2026-08-18, one
       setting `min-width: 0` and a later one setting `min-width: 860px`. The later won
       by cascade order, so the first was doing nothing, and the first was written to
       override `.table`'s `min-width: max-content` from the compiled bundle, which the
       pricing table never had: its markup is
       `class="w-full border-collapse text-sm att-pricing-table"` and carries no
       `.table` at all. A dead override cancelling nothing, underneath a live floor.

       `table-layout: fixed` is what makes the `colgroup` percentages hold. Without it
       the longest cell decides each column and one long feature note pushes the buy
       buttons past the right edge, which is the cropping that was reported.

       The 860px floor is the reference's own number and is kept: below that width the
       four columns genuinely cannot be read and `.table-scroll` scrolls instead. Above
       it, and every desktop is above it, the table is exactly its container's width and
       nothing scrolls or crops. */
    table-layout: fixed;
    width: 100%;
    min-width: 860px;
}

.att-pricing-table th[scope="row"] {
    width: 34%;
}

@media (max-width: 900px) {
    /* Narrow screens give the feature column less, because the plan columns carry a
       price and a button and stop being usable first. */
    .att-pricing-table th[scope="row"] {
        width: 40%;
    }
}

/* Vertical alignment for the comparison table.

   `align-top`, `align-middle` and `align-bottom` are Tailwind utilities that the
   compiled bundle does not ship: the template's own 78 pages never used them, so they
   were never emitted. They were on this table doing nothing until 2026-08-18. Authored
   here as two named classes rather than as the missing utilities, because what the
   table needs is a header treatment and a cell treatment rather than three generic
   alignment helpers nothing else would use. */
.att-pricing-table .att-pricing-head {
    vertical-align: top;
}

.att-pricing-table .att-pricing-cell {
    vertical-align: middle;
}

/* ---------------------------------------------------------------------------
 * A BUTTON IN THE SIDEBAR RAIL, 2026-08-18.
 *
 * The rail gained one control that is not a destination: "Rate this app", which opens the
 * review modal rather than navigating. Every other entry is an `<a>`, and every rule that
 * makes the rail look like the rail is written against `.sidebar-menu li a` in the compiled
 * bundle, which a `<button>` does not match.
 *
 * The alternative was `<a href="#">`, and it is worse in two specific ways rather than
 * merely being untidy: it puts a stray `#` in the address bar of anyone who activates it,
 * and it announces itself to a screen reader as a link to somewhere, which is the one thing
 * this control is not. A button that opens a dialog is a button.
 *
 * So the button is given the anchor's treatment here. Every declaration below is copied from
 * the corresponding `.sidebar-menu li a` rule in style.css rather than invented, including
 * the two collapsed-rail rules: without those the label stays visible when the rail is
 * collapsed to icons, which is a visible defect and the reason this block is not shorter.
 *
 * `all: unset` is deliberately not used. It would also drop the cursor, the focus ring and
 * the inherited font, and this control needs all three.
 * ------------------------------------------------------------------------ */

.sidebar-menu li > button {
  display: flex;
  width: 100%;
  align-items: center;
  border-radius: 0.5rem;
  padding: 0.625rem 0.75rem;
  font-size: 0.875rem;
  font-weight: 500;
  color: rgb(75 85 99);
  text-align: left;
  background: none;
  border: 0;
  cursor: pointer;
  transition-property: color, background-color, border-color;
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
  transition-duration: 150ms;
}

@media (min-width: 1650px) {
  .sidebar-menu li > button {
    font-size: 1rem;
  }
}

.sidebar-menu li > button:hover {
  color: rgb(var(--att-primary-600));
}

.sidebar-menu li > button .menu-icon {
  margin-inline-end: 0.5rem;
  font-size: 1.125rem;
}

.dark .sidebar-menu li > button {
  color: rgb(209 213 219);
}

.dark .sidebar-menu li > button .menu-icon {
  color: rgb(209 213 219);
}

@media (min-width: 1650px) {
  .sidebar-menu li > button .menu-icon {
    margin-inline-end: 0.75rem;
    font-size: 1.375rem;
  }
}

/* Collapsed rail: label out, icon centred. Mirrors the two `li a` rules in style.css. */
.sidebar.active .sidebar-menu li > button span {
  display: none;
}

.sidebar.active .sidebar-menu li > button .menu-icon {
  margin-inline-end: 0;
}

/* Collapsed rail, hovered: the label comes back, same as the links beside it. */
.sidebar.active:hover .sidebar-menu li > button span {
  display: inline-block;
}

.sidebar.active:hover .sidebar-menu li > button .menu-icon {
  margin-inline-end: 0.25rem;
}

/* ---------------------------------------------------------------------------
 * An uploaded app icon in the rail, beside the Iconify glyphs.
 *
 * `.menu-icon` in the bundle is a font-size and a margin, which does nothing to an <img>.
 * The size is given here so an operator's logo occupies the same box as a glyph however
 * large the source file is, and `object-fit` keeps a wide wordmark from being stretched
 * into the square.
 * ------------------------------------------------------------------------ */

.sidebar-menu li a img.menu-icon {
  width: 1.25rem;
  height: 1.25rem;
  object-fit: contain;
  flex-shrink: 0;
}

@media (min-width: 1650px) {
  .sidebar-menu li a img.menu-icon {
    width: 1.5rem;
    height: 1.5rem;
  }
}

/* ---------------------------------------------------------------------------
 * THREE UTILITIES THE COMPILED BUNDLE DOES NOT SHIP, 2026-08-18.
 *
 * style.css is a compiled Tailwind build whose utility set is exactly what the
 * template's own 78 pages happened to use. These three were never among them, so
 * every use of them in this application has been resolving to no rule at all:
 *
 *   object-contain  on the uploaded app icons and testimonial photographs. Without
 *                   it an image is stretched to fill its box, which is visible and
 *                   wrong on a wide wordmark in a square well.
 *   w-fit           on the "View in the App Market" buttons, which without it
 *                   stretch to the full card width.
 *   min-w-0         on the flex children that hold a name and a blurb. Without it a
 *                   long unbroken string refuses to shrink and pushes its sibling
 *                   out of the row, because a flex item's default min-width is auto.
 *
 * Found by a coverage sweep of the merchant partials after the pricing table turned
 * out to be broken by the same cause (`lg:hidden`). Authored here rather than left,
 * because each one is doing visible work in markup that already ships.
 * ------------------------------------------------------------------------ */

.object-contain {
    object-fit: contain;
}

.w-fit {
    width: -moz-fit-content;
    width: fit-content;
}

.min-w-0 {
    min-width: 0;
}

/* ---------------------------------------------------------------------------
 * PRICING PAGE, PORTED FROM THE REFERENCE, 2026-08-18.
 *
 * These rules are `google-indexing`'s own, from `assets/css/custom-style.css` and the
 * inline <style> block at the foot of its `pricing.php`, carried across so this page
 * matches rather than resembles. Two earlier attempts rebuilt the page from its shape
 * and neither matched; this one starts from its markup and its rules and changes only
 * the data.
 *
 * Adapted in exactly two ways, both forced:
 *
 *   - Its colours come from `--clr-primary`, `--txt-primary` and `--bg-primary-rgb`,
 *     which are its variable names. Ours are the `--att-*` set that `tools/theme.php`
 *     derives, so the values are swapped and the rules are not.
 *   - Its `.pricing-page .lg\:hidden` workaround is generalised below, because the
 *     missing utility is missing for every page here, not only this one.
 * ------------------------------------------------------------------------ */

/* The reference caps its pricing page at 80rem with `max-w-custom-pricing`. Ours is
   `att-pricing-shell`, already at 80rem, so nothing to port. */

/* The reference's `.pricing-compare-table { min-width: 860px }` is folded into the
   single `.att-pricing-table` rule above rather than repeated here. */

/* The Monthly / Yearly toggle. Verbatim. */
.toggle-btn.active {
    background: white;
    box-shadow: 0 1px 6px rgba(0, 0, 0, .15);
}

:is(.dark .toggle-btn.active) {
    background: rgb(64 64 64);
}

/* The FAQ accordion's open state. The reference's three rules, with our variables. */
.cstm-accordion-item [aria-expanded="true"],
.cstm-accordion-item [aria-expanded="true"] span {
    color: rgb(var(--att-primary-800));
}

.cstm-accordion-item [aria-expanded="true"] {
    background-color: rgb(var(--att-primary-600) / 0.1);
}

.cstm-accordion-item [aria-expanded="true"]:hover {
    background-color: rgb(var(--att-primary-600) / 0.1);
}

/* The trusted-by strip's cards, from the inline block at the foot of its pricing.php. */
.logo-card {
    width: 100%;
    height: 120px;
    padding: 16px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #fff;
}

:is(.dark .logo-card) {
    background: rgb(38 38 38);
}

.logo-img {
    width: 100%;
    height: auto;
}

/* ---------------------------------------------------------------------------
 * `lg:hidden`, which the compiled bundle does not ship.
 *
 * The reference hit this too and patched it inside its own page scope:
 * `.pricing-page .lg\:hidden { display: none; }` at min-width 992px, with the comment
 * "Without this the mobile plan cards render underneath the desktop table". That is
 * the identical bug this project found on its own pricing page.
 *
 * Generalised rather than scoped, because the utility is absent for every page here.
 * The breakpoint is 1024px, which is what `lg:` means in the compiled bundle, rather
 * than the reference's 992px, which is its own Bootstrap-derived scale.
 * ------------------------------------------------------------------------ */
@media (min-width: 1024px) {
    .lg\:hidden {
        display: none;
    }
}

/* ---------------------------------------------------------------------------
 * UTILITIES THE PRICING PAGE'S PORTED MARKUP NEEDS, 2026-08-18.
 *
 * The reference's pricing markup uses these; the compiled bundle does not ship them,
 * because the template's own 78 pages never used them. Authored here so the ported
 * markup renders as it does there rather than resembling it.
 *
 * `monthly`, `yearly`, `plan-variant` and `discount-sticker` are deliberately NOT here.
 * They are hooks: assets/js/pricing-toggle.js selects on them and toggles `hidden`,
 * which the bundle does ship. A hook with no rule is correct; a hook with an invented
 * rule is a second place the design lives.
 * ------------------------------------------------------------------------ */

.text-4xl {
    font-size: 2.25rem;
    line-height: 2.5rem;
}

.align-top {
    vertical-align: top;
}

.mr-2 {
    margin-right: 0.5rem;
}

.whitespace-nowrap {
    white-space: nowrap;
}

/* The "Most popular" and "Save N%" pills, which sit half outside their own cell. */
.-top-3 {
    top: -0.75rem;
}

.bottom-10 {
    bottom: 2.5rem;
}

.-right-20 {
    right: -5rem;
}

.dark\:text-neutral-300:is(.dark *) {
    --tw-text-opacity: 1;
    color: rgb(212 212 212 / var(--tw-text-opacity));
}

.dark\:hover\:bg-neutral-700:hover:is(.dark *) {
    --tw-bg-opacity: 1;
    background-color: rgb(64 64 64 / var(--tw-bg-opacity));
}

@media (min-width: 1024px) {
    .lg\:grid-cols-5 {
        grid-template-columns: repeat(5, minmax(0, 1fr));
    }
}

/* Locked paid controls, drawn present and disabled. `opacity-60` is not in the bundle. */
.opacity-60 {
    opacity: 0.6;
}

/* ---------------------------------------------------------------------------
 * PLAN COLUMN ALIGNMENT, 2026-08-18, second attempt.
 *
 * Every plan column's button on one baseline.
 *
 * ## What the first attempt got wrong
 *
 * It reserved a min-height on each of three blocks: the identity, the price and the
 * control. That fails for a reason worth writing down, because it looks like it should
 * work: reserving a maximum per block aligns the blocks, not the buttons. A column's
 * button sits at the SUM of the blocks above it, and each column has a different
 * combination, so three maxima summed give three different totals and the shortest
 * column pays for every reserve it does not use. That is both symptoms at once: Basic's
 * button sat higher than the other two, and every column carried a gap.
 *
 * ## What this does instead
 *
 * Aligns by measurement. The tallest column sets the row height, because table cells in
 * a row already stretch to the tallest, and every column's control is then pushed to
 * that shared bottom edge with `margin-top: auto`. Nothing is reserved, so no column can
 * carry space it did not earn: the only empty space anywhere is the gap the alignment
 * itself creates in the columns that are shorter than the tallest one.
 *
 * `height: 1px` on the header cell is the enabling trick, and it is not a real height: a
 * table cell always grows to its content, so this is ignored as a size and exists only
 * to give `height: 100%` on the wrapper something to resolve against. Without it the
 * percentage has no basis and the wrapper collapses to its content, which is the
 * `<th>` height behaviour that the reserved-height approach was avoiding rather than
 * solving.
 * ------------------------------------------------------------------------ */

.att-pricing-table thead th {
    height: 1px;
}

.att-plan-head-inner {
    height: 100%;
    display: flex;
    flex-direction: column;
}

/*
 * The body of a column: the price and the control. `flex: 1` makes it absorb whatever
 * height the tallest column forced onto the row, and the control inside it is pushed to
 * the bottom of that.
 *
 * `:not(.hidden)` matters. `.hidden` is `display: none` from the compiled bundle at
 * specificity 0,1,0, and `.att-plan-head-inner .plan-variant` is 0,2,0, so a plain
 * descendant rule would win and un-hide the variant the toggle had just hidden. The
 * negation keeps the specificity and applies only while the variant is visible.
 */
.att-plan-head-inner .att-plan-body,
.att-plan-head-inner .plan-variant:not(.hidden) {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
}

.att-plan-cta {
    margin-top: auto;
    padding-top: 1.5rem;
}

/*
 * The badge row, and the ONLY thing on this page that reserves space.
 *
 * Two of the three columns carry a badge and one does not, so without a reserve the plan
 * name sits a line higher in that column and every figure below it follows. One line is
 * reserved, not a block maximum: the badge is a single pill of known height, so this
 * reserve is exactly what it costs and is never larger than its content.
 */
.att-plan-badge {
    min-height: 1.75rem;
    margin-bottom: 0.5rem;
}

/*
 * One box for every plan control.
 *
 * The flex alignment above pins each column's control to a shared bottom edge, and
 * measurement confirmed it: the buttons' bottoms landed on 625px in all three columns.
 * Their TOPS still differed, by two pixels and then by twenty, for two reasons that had
 * nothing to do with the layout:
 *
 *   - the disabled control is `btn-outline-primary` and carries a 1px border the filled
 *     buy button does not, so it was 42px against 40px;
 *   - "Switch in the Wix app market" wrapped to two lines in a 24 percent column, so it
 *     was 62px.
 *
 * Aligning bottoms is not enough when the boxes are different heights. This gives every
 * control in a plan column the same box: same height, same border, centred content. The
 * labels were shortened to one line each in the same pass, so the min-height is never
 * the thing doing the work and a long label cannot quietly reintroduce the problem.
 */
.att-plan-button {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    min-height: 2.75rem;
    border-width: 1px;
    border-style: solid;
    text-align: center;
}
