/* ==================================================================
   refine.css - premium refinement layer
   ------------------------------------------------------------------
   Loaded after style.css, so everything here wins on equal specificity
   and the sheet can be removed in one line to go back to the previous
   look. Nothing here introduces new markup: it only re-dresses classes
   that already exist.

   The four things it fixes:
     1. type that never scaled - one fixed size per heading, at every
        viewport, in every language
     2. surfaces with no material - a border and a flat tint, so cards
        sat on the page instead of above it
     3. white text on fills too light to carry it, now that the accent
        is the brand teal rather than a mid indigo
     4. no visible focus ring anywhere on a site that takes logins
   ================================================================== */


/* ------------------------------------------------------------------
   1. Type scale

   clamp() so headings track the viewport instead of stepping at two
   breakpoints. The middle term is the fluid one: a vw slope on top of
   a rem floor, so the curve cannot collapse on a narrow phone.

   Tracking is deliberately scoped to LTR. Arabic is a connected
   script - negative letter-spacing pulls the joins apart and makes
   Kufi look broken - so the Arabic face keeps its natural fit and
   gains line-height instead, which is what actually makes Kufi read
   well at display sizes.
------------------------------------------------------------------ */
.hero-text h1 {
    font-size: clamp(2.05rem, 1.25rem + 3.1vw, 3.55rem);
    font-weight: 800;              /* the variable faces stop at 800; 900
                                      was being clamped to this anyway, and
                                      asking for it risks synthetic bolding */
    line-height: 1.12;
    margin-bottom: 26px;
    text-wrap: balance;
}
.section-header h2 {
    font-size: clamp(1.7rem, 1.1rem + 2.6vw, 2.9rem);
    font-weight: 800;
    line-height: 1.18;
    margin-bottom: 18px;
    text-wrap: balance;
}
html[dir="ltr"] .hero-text h1 { letter-spacing: -0.028em; }
html[dir="ltr"] .section-header h2 { letter-spacing: -0.022em; }
html[dir="rtl"] .hero-text h1 { line-height: 1.32; }
html[dir="rtl"] .section-header h2 { line-height: 1.38; }

.hero-text p {
    font-size: clamp(1rem, 0.95rem + 0.35vw, 1.18rem);
    max-width: 54ch;
    text-wrap: pretty;
}
.section-header { max-width: 760px; margin-bottom: 68px; }
.section-header p { font-size: 1.06rem; text-wrap: pretty; }

/* The gradient runs bright-to-bright. The old one started on the mid
   indigo, which on this background read as a dark smudge exactly where
   the sentence puts its emphasis. */
.text-gradient {
    background: linear-gradient(105deg, var(--primary), var(--secondary) 92%);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
}


/* ------------------------------------------------------------------
   2. Section tag

   Was a solid gradient pill with white text. At 0.85rem/600 over a
   light teal that no longer clears AA, and a filled pill competes with
   the heading it is meant to introduce. An outlined chip with a live
   dot sits behind the heading instead of in front of it.
------------------------------------------------------------------ */
.section-tag {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    padding: 7px 17px 7px 13px;
    background: rgba(var(--primary-rgb), 0.08);
    border: 1px solid rgba(var(--primary-rgb), 0.28);
    color: var(--primary);
    border-radius: 50px;
    font-size: 0.82rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    margin-bottom: 20px;
    backdrop-filter: blur(6px);
}
html[dir="rtl"] .section-tag { padding: 7px 13px 7px 17px; }
.section-tag::before {
    content: '';
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--primary);
    box-shadow: 0 0 0 3px rgba(var(--primary-rgb), 0.22);
    flex-shrink: 0;
}

.hero-badge {
    background: rgba(var(--primary-rgb), 0.07);
    border: 1px solid rgba(var(--primary-rgb), 0.26);
    color: var(--primary);
    font-size: 0.86rem;
    letter-spacing: 0.02em;
}


/* ------------------------------------------------------------------
   3. Surfaces

   The material is three things stacked, not one:
     - a tight contact shadow that seats the panel
     - a wide ambient shadow that lifts it
     - a 1px inset highlight along the top edge

   That last line does most of the work. On a dark canvas a border alone
   reads as a drawn rectangle; a lit top edge reads as a real surface
   catching light from above, which is why every OS window and every
   well-made dark interface has one.
------------------------------------------------------------------ */
:root { --surface-grid: #0e1a25; }
[data-theme="light"] { --surface-grid: #ffffff; }

.about-card,
.service-card,
.tool-card,
.portfolio-card,
.hero-card,
.security-card,
.contact-card,
.stat-card {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm), inset 0 1px 0 var(--specular);
    transition: transform .45s var(--ease-out),
                box-shadow .45s var(--ease-out),
                border-color .45s var(--ease-out),
                background .45s var(--ease-out);
}

/* Blur is charged per painted pixel, and the tools grid alone puts 24 of
   these on one screen. Over a flat background it buys nothing you cannot
   get from an opaque surface, so the repeated grid cards get a real fill
   and the expensive treatment is kept for the handful of panels that
   actually sit over the page glow. */
.about-card,
.service-card,
.tool-card,
.portfolio-card {
    background: var(--surface-grid);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}
.hero-card,
.security-card,
.contact-card,
.stat-card {
    background: var(--bg-card);
    backdrop-filter: blur(16px) saturate(1.25);
    -webkit-backdrop-filter: blur(16px) saturate(1.25);
}

.about-card:hover,
.service-card:hover,
.tool-card:hover,
.portfolio-card:hover,
.security-card:hover {
    transform: translateY(-6px);
    border-color: rgba(var(--primary-rgb), 0.42);
    box-shadow: var(--shadow-lg),
                inset 0 1px 0 var(--specular),
                0 0 0 1px rgba(var(--primary-rgb), 0.10),
                var(--glow);
}

/* Keyboard users get the same affordance as the mouse. Without this the
   card lifts on hover and does precisely nothing on Tab. */
.service-card:focus-visible,
.tool-card:focus-visible,
.portfolio-card:focus-visible {
    transform: translateY(-6px);
    border-color: rgba(var(--primary-rgb), 0.5);
}

/* The sweep across the top of a service card, retimed. It used to share
   the blanket 0.3s "all" transition, which made it snap. */
.service-card::before {
    height: 3px;
    background: linear-gradient(90deg, var(--primary), var(--secondary));
    transition: transform .55s var(--ease-out);
}


/* ------------------------------------------------------------------
   4. Icon tiles

   A solid teal tile with a white glyph does not clear 3:1, which is the
   floor for a graphic that carries meaning. Inverting it - brand glyph
   on a tinted glass tile - fixes the contrast and, as a side effect,
   stops six identical saturated blocks from shouting over the copy they
   sit above.
------------------------------------------------------------------ */
.service-icon,
.tool-card-icon {
    background: linear-gradient(160deg,
                rgba(var(--primary-rgb), 0.18),
                rgba(var(--primary-rgb), 0.06));
    border: 1px solid rgba(var(--primary-rgb), 0.24);
    color: var(--primary);
    border-radius: 15px;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .09);
    transition: transform .45s var(--ease-out), box-shadow .45s var(--ease-out);
}
.service-card:hover .service-icon,
.tool-card:hover .tool-card-icon {
    transform: translateY(-2px) scale(1.04);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .14),
                0 10px 26px rgba(var(--primary-rgb), .22);
}
.tool-card-service .tool-card-icon {
    background: linear-gradient(160deg,
                rgba(var(--secondary-rgb), 0.18),
                rgba(var(--secondary-rgb), 0.06));
    border-color: rgba(var(--secondary-rgb), 0.26);
    color: var(--secondary);
}
.about-card .icon { color: var(--primary); }


/* ------------------------------------------------------------------
   5. Buttons

   The primary fill is solid, not a gradient. Two reasons: white on the
   bright end of a teal gradient fails AA, and a flat brand colour with
   a lit top edge and a coloured shadow already reads as more considered
   than a gradient does.
------------------------------------------------------------------ */
.btn {
    border-radius: 50px;
    font-weight: 600;
    letter-spacing: 0.01em;
    transition: transform .3s var(--ease-out),
                box-shadow .3s var(--ease-out),
                background .3s var(--ease-out),
                border-color .3s var(--ease-out),
                color .3s var(--ease-out);
}
.btn-primary {
    background: var(--primary-solid);
    color: #fff;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .16),
                0 10px 26px rgba(var(--brand-rgb), .30);
}
.btn-primary:hover {
    background: color-mix(in srgb, var(--primary-solid) 88%, #fff);
    transform: translateY(-2px);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .22),
                0 16px 38px rgba(var(--brand-rgb), .42);
}
.btn-primary:active { transform: translateY(0); }

.btn-outline {
    border: 1px solid var(--border-strong);
    background: rgba(var(--primary-rgb), 0.03);
    color: var(--text);
    backdrop-filter: blur(8px);
}
.btn-outline:hover {
    border-color: rgba(var(--primary-rgb), .55);
    background: rgba(var(--primary-rgb), .10);
    color: var(--primary);
    transform: translateY(-2px);
}
.btn-lg { padding: 15px 34px; font-size: 1.05rem; }


/* The remaining white-on-gradient fills, on the service and tool pages.
   Each began on var(--primary): white over the bright end of a teal
   gradient is under 2:1, where it was passable over the old mid indigo.
   Solid brand, same as the buttons. */
.mobile-nav-app,
.service-hero-icon,
.process-num,
.tool-gate-icon {
    background: var(--primary-solid);
    color: #fff;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .16),
                0 10px 26px rgba(var(--brand-rgb), .26);
}


/* ------------------------------------------------------------------
   6. Header

   saturate() alongside blur is the difference between frosted glass and
   grey fog: blur on its own washes the colour out of whatever scrolls
   under the bar.
------------------------------------------------------------------ */
.header.scrolled {
    background: color-mix(in srgb, var(--bg) 72%, transparent);
    backdrop-filter: blur(22px) saturate(1.7);
    -webkit-backdrop-filter: blur(22px) saturate(1.7);
    border-bottom: 0;
    box-shadow: 0 1px 0 rgba(var(--primary-rgb), .10),
                0 10px 34px rgba(0, 0, 0, .22);
}
.nav a { transition: color .3s var(--ease); }
.nav a::after {
    height: 2px;
    border-radius: 2px;
    background: var(--primary);
    transition: width .35s var(--ease-out);
}
.icon-btn {
    background: rgba(var(--primary-rgb), .05);
    border-color: var(--border);
    transition: background .3s var(--ease), color .3s var(--ease),
                border-color .3s var(--ease), transform .3s var(--ease-out);
}
.icon-btn:hover {
    background: rgba(var(--primary-rgb), .14);
    border-color: rgba(var(--primary-rgb), .42);
    color: var(--primary);
    transform: translateY(-1px);
}


/* ------------------------------------------------------------------
   7. Stats

   Tabular figures so the count-up animation does not shuffle the row
   sideways on every tick as digit widths change.
------------------------------------------------------------------ */
.stat-num {
    font-size: clamp(1.8rem, 1.4rem + 1.5vw, 2.6rem);
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
}
html[dir="ltr"] .stat-num { letter-spacing: -0.02em; }


/* ------------------------------------------------------------------
   7b. Anchor landing

   Every nav link on the homepage is a same-page anchor, and the header
   is fixed - so until now each one parked the section heading directly
   underneath the bar and the reader arrived looking at the wrong line.
   scroll-margin is the whole fix, and it costs nothing.
------------------------------------------------------------------ */
section[id], [id]:target { scroll-margin-top: 96px; }


/* ------------------------------------------------------------------
   7c. Form fields

   The inputs were flat panels on a flat panel: --bg-secondary sits
   close enough to --bg-card that on the contact card the field and the
   surface behind it read as one shape. Recessing them - darker than
   their container, with an inset top shadow - makes the box look like
   somewhere text goes.
------------------------------------------------------------------ */
.form-group label { font-size: .9rem; font-weight: 600; letter-spacing: .01em; color: var(--text-muted); }
.form-group input,
.form-group select,
.form-group textarea,
.security-input input {
    background: rgba(0, 0, 0, .22);
    border: 1px solid var(--border);
    box-shadow: inset 0 1px 3px rgba(0, 0, 0, .28);
    transition: border-color .3s var(--ease), box-shadow .3s var(--ease),
                background .3s var(--ease);
}
.form-group input::placeholder,
.form-group textarea::placeholder,
.security-input input::placeholder { color: var(--text-muted); opacity: .75; }

.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus,
.security-input input:focus {
    border-color: rgba(var(--primary-rgb), .6);
    background: rgba(0, 0, 0, .28);
    box-shadow: inset 0 1px 3px rgba(0, 0, 0, .28),
                0 0 0 3px rgba(var(--primary-rgb), .18);
}

[data-theme="light"] .form-group input,
[data-theme="light"] .form-group select,
[data-theme="light"] .form-group textarea,
[data-theme="light"] .security-input input {
    background: rgba(7, 33, 28, .035);
    box-shadow: inset 0 1px 2px rgba(7, 33, 28, .07);
}
[data-theme="light"] .form-group input:focus,
[data-theme="light"] .form-group select:focus,
[data-theme="light"] .form-group textarea:focus,
[data-theme="light"] .security-input input:focus {
    background: #fff;
    box-shadow: inset 0 1px 2px rgba(7, 33, 28, .05),
                0 0 0 3px rgba(var(--primary-rgb), .16);
}


/* ------------------------------------------------------------------
   7d. Bidirectional text

   In Arabic the contact card printed "+974 3370 6934" as
   "6934 3370 974+": the number is a run of neutral characters inside a
   right-to-left paragraph, so the bidi algorithm lays its groups out
   right-to-left and the plus sign lands at the wrong end. A reader
   cannot dial that.

   plaintext resolves each element's direction from its own first
   strong character instead of inheriting the page's. The e-mail and
   the phone numbers come out left-to-right; an Arabic address in the
   same block still comes out right-to-left.
------------------------------------------------------------------ */
.contact-card p { unicode-bidi: plaintext; }
html[dir="rtl"] .contact-card p { text-align: right; }


/* ------------------------------------------------------------------
   8. Focus

   One ring, every interactive element, both themes. :focus-visible so
   it appears for the keyboard and stays out of the way of the mouse.
------------------------------------------------------------------ */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 3px;
    border-radius: 6px;
}
.btn:focus-visible { outline-offset: 4px; }


/* ------------------------------------------------------------------
   9. Page furniture
------------------------------------------------------------------ */
::selection { background: rgba(var(--primary-rgb), .28); color: var(--text); }

* { scrollbar-color: rgba(var(--primary-rgb), .35) transparent; scrollbar-width: thin; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
    background: rgba(var(--primary-rgb), .28);
    border-radius: 10px;
    border: 2px solid transparent;
    background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover {
    background: rgba(var(--primary-rgb), .5);
    background-clip: content-box;
}

.security-shield {
    background: var(--primary-solid);
    box-shadow: 0 10px 30px rgba(var(--brand-rgb), .42),
                inset 0 1px 0 rgba(255, 255, 255, .18);
}
.security-shield .shield-ring { border-color: rgba(var(--primary-rgb), .40); }

.footer { background: color-mix(in srgb, var(--bg) 82%, #000 18%); }
.footer-links a:hover { color: var(--primary); }

.preloader-progress { background: linear-gradient(90deg, var(--primary), var(--secondary)); }

/* Section rhythm. 100px at every width meant a phone spent a third of
   its screen on padding while a wide monitor felt cramped. */
.section { padding: clamp(64px, 4vw + 42px, 116px) 0; }


/* ------------------------------------------------------------------
   10. Light theme corrections

   Glass that works on ink does not work on paper: the specular edge has
   to become a white seam and the shadows have to lose their opacity, or
   the whole page turns grey.
------------------------------------------------------------------ */
[data-theme="light"] .about-card,
[data-theme="light"] .service-card,
[data-theme="light"] .tool-card,
[data-theme="light"] .portfolio-card,
[data-theme="light"] .hero-card,
[data-theme="light"] .security-card {
    box-shadow: var(--shadow-sm), inset 0 1px 0 rgba(255, 255, 255, .9);
}
[data-theme="light"] .hero-card,
[data-theme="light"] .security-card { background: rgba(255, 255, 255, .82); }
[data-theme="light"] .header.scrolled {
    background: rgba(255, 255, 255, .78);
    box-shadow: 0 1px 0 rgba(7, 33, 28, .07), 0 10px 30px rgba(7, 33, 28, .07);
}
[data-theme="light"] .service-icon,
[data-theme="light"] .tool-card-icon { box-shadow: none; }

/* Badges set their label in the same hue as their own tint. On ink that
   lands around 8:1; on paper the tint turns pale and the pair collapses
   to 4.4:1 - under the 4.5 floor, and these run at 11.5px where the
   floor actually applies. Darker ink on a paler wash fixes both. */
[data-theme="light"] .tool-badge,
[data-theme="light"] .portfolio-tags span {
    color: #05614c;
    background: rgba(var(--primary-rgb), .10);
}
[data-theme="light"] .tool-card-service .tool-badge {
    color: #045c43;
    background: rgba(4, 120, 87, .11);
}

/* Leaflet draws its own close button at #757575 - 3.4:1, and it is the
   only way to dismiss the popup. A literal colour, not var(--text):
   Leaflet's popup stays white in both themes, so the token would turn
   the glyph white-on-white as soon as the page went dark. */
.leaflet-popup-close-button { color: #3a4a46 !important; }
.leaflet-popup-close-button:hover { color: #07211c !important; }
[data-theme="light"] .footer { background: #ffffff; }
[data-theme="light"] .btn-outline { background: rgba(var(--primary-rgb), .04); }


/* ==================================================================
   12. Client portal and admin

   Same four fixes as the public site, applied to the surfaces behind
   the login: material, type scale, contrast on tinted labels, and the
   white-on-bright-teal fills that the new accent broke.

   This is where people do actual work, so the bias here is legibility
   over decoration: denser panels, quieter chrome, numbers that line up.
   ================================================================== */

/* ---- panels ------------------------------------------------------
   Same three-part material as the public cards. The repeated grids
   (stat tiles, KPI tiles, quick actions) lose the backdrop blur for
   the reason the tools grid did: a dashboard puts a dozen of them on
   screen at once and they sit over a flat page, so the blur is paid
   for and never seen.
------------------------------------------------------------------ */
.dash-stat,
.admin-stat,
.stats-kpi,
.dash-action,
.stats-periods {
    background: var(--surface-grid);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border: 1px solid var(--border);
    box-shadow: var(--shadow-sm), inset 0 1px 0 var(--specular);
}

.dash-table-wrap,
.admin-table-wrap,
.dash-form,
.stats-card,
.auth-card {
    background: var(--bg-card);
    border: 1px solid var(--border);
    box-shadow: var(--shadow), inset 0 1px 0 var(--specular);
    backdrop-filter: blur(16px) saturate(1.25);
    -webkit-backdrop-filter: blur(16px) saturate(1.25);
}

/* Both stat grids fixed their column count in advance and neither count
   divides the number of tiles: the portal puts 4 tiles in 3 columns and
   the admin page 8 in 5, so each ends on a short, lopsided row. Sizing
   by the tile instead of by the column fills every row at any width and
   removes the need for the two breakpoints that followed. */
.dash-stats { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
.admin-stats { grid-template-columns: repeat(auto-fit, minmax(178px, 1fr)); }

.dash-action {
    transition: transform .4s var(--ease-out),
                box-shadow .4s var(--ease-out),
                border-color .4s var(--ease-out);
}
.dash-action:hover,
.dash-action:focus-visible {
    transform: translateY(-3px);
    border-color: rgba(var(--primary-rgb), .42);
    box-shadow: var(--shadow), inset 0 1px 0 var(--specular), var(--glow);
}

/* The primary quick action set a white glyph on a primary-to-secondary
   gradient. That cleared 3:1 against the old mid indigo and does not
   against the brand teal, so it takes the solid fill the buttons use. */
.dash-action-primary .dash-action-icon {
    background: var(--primary-solid);
    color: #fff;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .18);
}
.dash-action-success .dash-action-icon {
    background: rgba(var(--success-rgb), .15);
    color: var(--success);
}

/* ---- headings ---------------------------------------------------- */
.dash-head h1,
.admin-head h1 {
    font-size: clamp(1.35rem, 1.1rem + 1vw, 1.85rem);
    line-height: 1.25;
}
html[dir="ltr"] .dash-head h1,
html[dir="ltr"] .admin-head h1 { letter-spacing: -0.018em; }
.dash-section-title,
.dash-account h2 { font-size: clamp(1.15rem, 1rem + .6vw, 1.45rem); }

/* ---- numbers -----------------------------------------------------
   Every figure on these pages is meant to be compared with the one
   above it. Proportional digits make a column of numbers ragged, and
   they make a live counter jitter as the width of each digit changes.
------------------------------------------------------------------ */
.dash-stat span,
.admin-stat span,
.stats-kpi strong,
.stats-count,
.admin-table td,
.dash-table td {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
}
.dash-stat span,
.admin-stat span { letter-spacing: -0.015em; }

/* ---- tables ------------------------------------------------------
   The header row is sticky, so its background has to be fully opaque:
   over a translucent one the scrolling rows show through the labels.
   It also needs to out-rank the panel it sits in rather than inherit
   a see-through tint.
------------------------------------------------------------------ */
.admin-table th,
.dash-table th {
    background: var(--surface-grid);
    color: var(--text-muted);
    border-bottom: 1px solid var(--border-strong);
    box-shadow: inset 0 -1px 0 var(--border-strong);
}

/* A row you can follow across nine columns. Without it the eye loses
   the line somewhere around the date and has to start again. */
.admin-table tbody tr,
.dash-table tbody tr { transition: background .18s var(--ease); }
.admin-table tbody tr:hover,
.dash-table tbody tr:hover { background: rgba(var(--primary-rgb), .055); }

/* The id column was held at opacity .6, which drags --text-muted well
   under AA. Muted colour does the same job and stays legible. */
.admin-table td:first-child,
.dash-table td:first-child {
    opacity: 1;
    color: var(--text-muted);
    font-size: .86em;
}

.admin-table .row-unread { background: rgba(var(--primary-rgb), .09); }
.admin-table .row-overdue { background: rgba(var(--danger-rgb), .09); }

/* ---- status badges -----------------------------------------------
   A dot carries the state at a glance; the word confirms it. Useful in
   a table where the same six words repeat down a column.
------------------------------------------------------------------ */
.status-badge {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 5px 13px 5px 10px;
    font-size: .78rem;
    letter-spacing: .01em;
    border: 1px solid currentColor;
    background: transparent;
}
html[dir="rtl"] .status-badge { padding: 5px 10px 5px 13px; }
.status-badge::before {
    content: '';
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: currentColor;
    flex-shrink: 0;
}
.status-pending   { color: var(--warning); background: rgba(var(--warning-rgb), .10); }
.status-progress  { color: var(--secondary); background: rgba(var(--secondary-rgb), .10); }
.status-completed { color: var(--success); background: rgba(var(--success-rgb), .10); }
.status-rejected,
.status-express   { color: var(--danger); background: rgba(var(--danger-rgb), .10); }
.status-badge { border-color: color-mix(in srgb, currentColor 34%, transparent); }

/* ---- alerts -------------------------------------------------------
   A rule down the leading edge, so the kind of message is readable
   before the text is.
------------------------------------------------------------------ */
.alert {
    border-inline-start: 3px solid currentColor;
    border-radius: 12px;
    box-shadow: var(--shadow-sm);
}
.alert-error {
    background: rgba(var(--danger-rgb), .10);
    color: var(--danger);
    border-color: rgba(var(--danger-rgb), .30);
    border-inline-start-color: var(--danger);
}
.alert-success {
    background: rgba(var(--success-rgb), .10);
    color: var(--success);
    border-color: rgba(var(--success-rgb), .30);
    border-inline-start-color: var(--success);
}

/* ---- stats page ---------------------------------------------------
   The selected period and the team switch both set white on
   var(--primary). That is the bright teal now, so both take the solid
   fill instead.
------------------------------------------------------------------ */
/* The selected period. Deliberately over-specified: its failure mode is
   a label the same colour as the fill behind it, which reads as an empty
   button rather than as a styling slip, so it should not be losing ties
   to anything. */
.stats-page .stats-periods a[aria-current="page"],
.stats-periods a[aria-current="page"] {
    background: var(--primary-solid);
    color: #fff;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .16);
}
.stats-team-toggle.is-on .stats-switch {
    background: var(--primary-solid);
    border-color: var(--primary-solid);
}
.stats-kpi strong { letter-spacing: -0.02em; }
.stats-meter { background: rgba(var(--primary-rgb), .16); }

/* ---- fitting the tables on the page -------------------------------
   These tables were given `width: max-content` and a blanket
   `white-space: nowrap`, so nine columns of Arabic prose, dates, file
   pickers and select menus laid themselves out on one endless line and
   the panel scrolled sideways. The reasoning was sound - the browser's
   fallback was to crush the prose column until every word sat on its
   own line - but sideways scrolling in a table you read row by row
   costs more than it saves: the client name is at one end and the
   status at the other, and you can never see both.

   Three changes make it fit instead:

   1. The shell gets the width it deserves. Both panels were inheriting
      .container's 1200px cap, which exists to keep marketing prose at a
      readable measure. A data table is not prose - it should use the
      monitor.
   2. The table fills its box rather than its content.
   3. nowrap is lifted from the cells and kept only where a break would
      be wrong: the row number, a status pill, the overdue flag.

   The prose column no longer collapses, because with the shell this
   wide there is room to share out.
------------------------------------------------------------------ */
.admin-container,
.dash-container {
    width: 96%;
    max-width: 1560px;
}

.admin-table,
.dash-table {
    width: 100%;
    min-width: 0;
}

.admin-table td, .dash-table td,
.admin-table td *, .dash-table td * { white-space: normal; }

.admin-table td:first-child, .dash-table td:first-child,
.admin-table .status-badge, .dash-table .status-badge,
.admin-table .overdue-flag, .dash-table .overdue-flag,
.admin-table th, .dash-table th { white-space: nowrap; }

/* Narrow columns stay narrow: the row number, and the date, which reads
   just as well broken after the day as it does on one line. */
.admin-table td:first-child, .dash-table td:first-child { width: 1%; }

/* The controls stack instead of sitting side by side. A select and a
   button in a row set that column's minimum to the sum of the two;
   stacked, it is the wider of them. */
.admin-table .admin-form,
.dash-table .admin-form {
    flex-direction: column;
    align-items: stretch;
    gap: 7px;
}
.admin-table .admin-form select,
.admin-table .admin-form .btn,
.dash-table .admin-form select,
.dash-table .admin-form .btn { width: 100%; }

.admin-table .deliver, .dash-table .deliver { min-width: 0; }
.admin-table .deliver input[type="file"],
.dash-table .deliver input[type="file"] { max-width: 100%; }

/* Long file names and e-mail addresses have no spaces to break at, so
   left alone they set a floor under their column's width.

   `anywhere` is confined to those, because it also lets the browser
   break a word mid-way when measuring - which is what we want for
   client.longname@example.com and not at all what we want for Arabic
   prose. Ordinary cells get `break-word`, which only steps in when a
   word genuinely cannot fit. */
.admin-table td, .dash-table td { overflow-wrap: break-word; }
.admin-email,
.delivered a, .deliver-list a,
.admin-table td a[href^="mailto"], .dash-table td a[href^="mailto"] { overflow-wrap: anywhere; }

/* The description column keeps a comfortable measure without being
   allowed to dominate. */
.dash-details { min-width: 190px; max-width: 360px; line-height: 1.65; }

/* On a laptop the widest table lands about 30px over. Tightening the
   cells rather than letting it scroll: at this width the padding is the
   least useful thing on screen. */
@media (max-width: 1200px) {
    .admin-table th, .admin-table td,
    .dash-table th, .dash-table td { padding: 11px 10px; }
    .admin-table, .dash-table { font-size: .88rem; }
    .admin-table-wrap, .dash-table-wrap { padding: 20px 16px; }
    .dash-details { min-width: 150px; }
}

/* The scroll stays as a safety net for the band between the stacked
   layout below and the width where the table genuinely fits; it is
   contained inside the panel, so the page itself never moves sideways. */
.admin-table-wrap, .dash-table-wrap { overflow-x: auto; }


/* ---- stacked rows on small screens --------------------------------
   Nine columns of selects, file pickers and Arabic prose cannot be a
   table on a phone at any font size. Below this width each row becomes
   its own card and each cell carries the heading it came from, printed
   from the data-label that page-chrome.js copies off the <th>.

   The head is hidden from sight but left in the accessibility tree -
   removing it would leave a screen reader with a grid whose columns
   have no names.
------------------------------------------------------------------ */
@media (max-width: 1000px) {
    .admin-table-wrap, .dash-table-wrap {
        overflow-x: visible;
        padding: 16px 14px;
    }

    .admin-table, .dash-table,
    .admin-table tbody, .dash-table tbody,
    .admin-table tr, .dash-table tr,
    .admin-table td, .dash-table td {
        display: block;
        width: auto;
    }

    .admin-table thead, .dash-table thead {
        position: absolute;
        width: 1px;
        height: 1px;
        margin: -1px;
        padding: 0;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
        border: 0;
    }

    .admin-table tr, .dash-table tr {
        background: var(--surface-grid);
        border: 1px solid var(--border);
        border-radius: var(--radius-sm);
        box-shadow: var(--shadow-sm), inset 0 1px 0 var(--specular);
        padding: 4px 15px 8px;
        margin-bottom: 14px;
    }
    .admin-table tr:last-child, .dash-table tr:last-child { margin-bottom: 0; }

    /* The label sits beside the value, not above it: a phone has the
       height to spare far less often than it has 90px of width. */
    .admin-table td, .dash-table td {
        display: grid;
        grid-template-columns: minmax(78px, 32%) 1fr;
        gap: 12px;
        align-items: start;
        padding: 10px 0;
        border-bottom: 1px solid var(--border);
        white-space: normal;
        max-width: none;
        min-width: 0;
        opacity: 1;
    }
    .admin-table td:last-child, .dash-table td:last-child { border-bottom: 0; }

    .admin-table td[data-label]::before,
    .dash-table td[data-label]::before {
        content: attr(data-label);
        color: var(--text-muted);
        font-size: .74rem;
        font-weight: 600;
        letter-spacing: .03em;
        line-height: 1.6;
        padding-top: 2px;
    }

    /* The actions column has no heading, so it has no label and takes
       the whole width rather than leaving a hole where one would be. */
    .admin-table td:not([data-label]),
    .dash-table td:not([data-label]) { display: block; }

    /* The row number reads as the card's own heading. */
    .admin-table td:first-child, .dash-table td:first-child {
        font-weight: 700;
        color: var(--text);
        font-size: 1rem;
    }

    .dash-details { max-width: none; min-width: 0; }
    .admin-table .deliver input[type="file"],
    .dash-table .deliver input[type="file"] { max-width: 100%; }
}

/* Without the script the cards would lose their column names, so the
   table stays a table and keeps its own scroll instead. page-chrome.js
   sets this flag once it has labelled everything. */
@media (max-width: 1000px) {
    html:not(.has-table-labels) .admin-table-wrap,
    html:not(.has-table-labels) .dash-table-wrap { overflow-x: auto; }
    html:not(.has-table-labels) .admin-table,
    html:not(.has-table-labels) .dash-table { display: table; width: max-content; min-width: 100%; }
    html:not(.has-table-labels) .admin-table thead,
    html:not(.has-table-labels) .dash-table thead { position: static; width: auto; height: auto; margin: 0; clip-path: none; }
    html:not(.has-table-labels) .admin-table tbody,
    html:not(.has-table-labels) .dash-table tbody { display: table-row-group; }
    html:not(.has-table-labels) .admin-table tr,
    html:not(.has-table-labels) .dash-table tr { display: table-row; background: none; border: 0; box-shadow: none; padding: 0; margin: 0; }
    html:not(.has-table-labels) .admin-table td,
    html:not(.has-table-labels) .dash-table td { display: table-cell; }
    html:not(.has-table-labels) .admin-table td::before,
    html:not(.has-table-labels) .dash-table td::before { content: none; }
}


/* ---- bare links ---------------------------------------------------
   The portal and the admin pages put plain <a> elements in table cells,
   in empty-state sentences and around mailto addresses. Nothing ever
   gave them a colour, so they were falling back to the browser default
   #0000EE - about 1.8:1 on this canvas, effectively unreadable. It only
   shows behind a login, which is why it survived this long.

   Scoped to the places prose links actually appear, not to the whole
   panel. "Any unclassed anchor inside .admin-container" was the first
   attempt and it was wrong: the period selector on the statistics page
   is built from plain <a> elements, so the rule painted them brand
   teal - including the selected one, which sets the same colour as its
   own background and disappeared entirely. A control is not a link in
   a sentence, and a selector that cannot tell them apart will keep
   finding new ones to break.
------------------------------------------------------------------ */
.admin-table td a:not([class]),
.dash-table td a:not([class]),
.dash-empty a:not([class]),
.admin-email a:not([class]),
.stats-card a:not([class]),
.plan-admin a:not([class]),
.plan-box a:not([class]),
.dash-manager a:not([class]),
/* mailto: and tel: are always an address to read, never a control, so
   these two are safe to catch anywhere in either panel. */
.dash-container a[href^="mailto:"],
.admin-container a[href^="mailto:"],
.dash-container a[href^="tel:"],
.admin-container a[href^="tel:"] {
    color: var(--primary);
    text-decoration-color: color-mix(in srgb, currentColor 40%, transparent);
    text-underline-offset: 3px;
}
.admin-table td a:not([class]):hover,
.dash-table td a:not([class]):hover,
.dash-empty a:not([class]):hover,
.admin-email a:not([class]):hover,
.stats-card a:not([class]):hover,
.plan-admin a:not([class]):hover,
.plan-box a:not([class]):hover,
.dash-manager a:not([class]):hover,
.dash-container a[href^="mailto:"]:hover,
.admin-container a[href^="mailto:"]:hover,
.dash-container a[href^="tel:"]:hover,
.admin-container a[href^="tel:"]:hover { text-decoration-color: currentColor; }

/* "Delete permanently" set white on --danger, which is 3.4:1 - below the
   floor, on the one button in the product where misreading the label has
   consequences. The solid fill clears 6:1. */
.btn-danger {
    background: var(--danger-solid);
    border-color: var(--danger-solid);
    color: #fff;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .16),
                0 10px 26px rgba(var(--danger-rgb), .26);
}
.btn-danger:hover {
    background: color-mix(in srgb, var(--danger-solid) 88%, #000);
    border-color: color-mix(in srgb, var(--danger-solid) 88%, #000);
    filter: none;
    color: #fff;
}

/* ---- forms and misc ---------------------------------------------- */
.admin-form select,
.dash-form input,
.dash-form select,
.dash-form textarea { font-family: inherit; }
.admin-email { color: var(--text-muted); }
.doc-link { color: var(--primary); }
.dash-empty { color: var(--text-muted); font-size: .94rem; }
.dash-manager { box-shadow: var(--shadow-sm), inset 0 1px 0 var(--specular); }

/* ---- light theme --------------------------------------------------
   The portal panels need the same paper treatment as the public cards:
   an opaque white surface and a white seam instead of a lit edge.
------------------------------------------------------------------ */
[data-theme="light"] .dash-table-wrap,
[data-theme="light"] .admin-table-wrap,
[data-theme="light"] .dash-form,
[data-theme="light"] .stats-card,
[data-theme="light"] .auth-card {
    background: rgba(255, 255, 255, .86);
    box-shadow: var(--shadow), inset 0 1px 0 rgba(255, 255, 255, .9);
}
[data-theme="light"] .dash-stat,
[data-theme="light"] .admin-stat,
[data-theme="light"] .stats-kpi,
[data-theme="light"] .dash-action,
[data-theme="light"] .stats-periods {
    box-shadow: var(--shadow-sm), inset 0 1px 0 rgba(255, 255, 255, .9);
}
[data-theme="light"] .admin-table tbody tr:hover,
[data-theme="light"] .dash-table tbody tr:hover { background: rgba(var(--primary-rgb), .06); }


/* ------------------------------------------------------------------
   11. Reduced motion

   Covers the decorative loops the rest of the sheet leaves running -
   the orbit rings, the shield ripple, the logo pulse. Someone who has
   asked the OS to stop animation should not be handed a spinning radar.
------------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .001ms !important;
        scroll-behavior: auto !important;
    }
    .hero-scene .hx-spin-slow,
    .hero-scene .hx-spin-rev,
    .hero-scene .hx-spin-fast,
    .security-shield .shield-ring,
    .whatsapp-float::before { animation: none !important; }
    .about-card:hover, .service-card:hover,
    .tool-card:hover, .portfolio-card:hover { transform: none; }
}
