/*
 * Additions on top of the reference theme, for content it does not ship with:
 * a monogram when no logo is uploaded, wordmarks when there are no client
 * logos, and inline validation for the contact form.
 */

.site-monogram {
    align-items: center;
    background-color: var(--primary-color, #00de51);
    border-radius: 999px;
    color: #000;
    display: inline-flex;
    font-family: var(--secondary-font), sans-serif;
    font-size: 16px;
    height: 40px;
    justify-content: center;
    letter-spacing: -0.02em;
    line-height: 1;
    width: 40px;
}

.user-logo .site-monogram,
.f-logo .site-monogram {
    height: 32px;
    width: 32px;
    font-size: 13px;
}

/* Wordmarks stand in for logo images in the brand strip. */
.brand-wordmark {
    color: var(--black-56, rgba(0, 0, 0, 0.56));
    display: inline-block;
    font-family: var(--third-font), sans-serif;
    font-size: 15px;
    letter-spacing: -0.01em;
    line-height: 26px;
    white-space: nowrap;
}

.dark-mode .brand-wordmark {
    color: rgba(255, 255, 255, 0.56);
}

.foot-wordmark {
    font-size: clamp(40px, 9vw, 104px);
    line-height: 1;
    margin: 0;
}

/* Contact form: inline errors and the honeypot. */
.contact-form {
    --contact-error: #ff5a4e;
}

.contact-field-error {
    color: var(--contact-error);
    margin: 6px 0 0;
    animation: contact-rise 0.35s cubic-bezier(0.22, 1, 0.36, 1) both;
}

.contact-form .contact-form-input[aria-invalid='true'] {
    border-color: var(--contact-error);
    box-shadow: 0 0 0 3px rgba(255, 90, 78, 0.14);
}

.contact-hp {
    height: 0;
    left: -9999px;
    opacity: 0;
    position: absolute;
    width: 0;
}

/*
 * Submit: the theme's pill, taller, with its icon slot carrying the request's
 * state — send glyph, then a spinner, then a check that draws itself. The
 * labels share one grid cell so the pill never changes width mid-request.
 */
.contact-actions {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 14px 20px;
    margin-top: 8px;
}

.contact-submit {
    background: none;
    border: 0;
    color: var(--button-text-color, #000);
    cursor: pointer;
    font: inherit;
    padding: 0;
    border-radius: 999px;
}

.contact-submit .ic-wrap,
.contact-submit .text {
    color: var(--button-text-color, #000);
    height: 52px;
}

.contact-submit .text {
    padding-inline: 26px;
}

.contact-submit:focus-visible {
    outline: 2px solid var(--primary-color, #00de51);
    outline-offset: 4px;
}

.contact-submit:disabled {
    cursor: progress;
}

/* Scoped under the button to outrank the theme's `.tf-btn-action .text { display: flex }`. */
.contact-submit .contact-submit__labels {
    align-items: center;
    display: grid;
    overflow: hidden;
}

.contact-submit__labels > span {
    grid-area: 1 / 1;
    text-align: center;
    transition:
        opacity 0.3s ease,
        transform 0.45s cubic-bezier(0.22, 1, 0.36, 1);
    white-space: nowrap;
}

.contact-submit__labels > span[aria-hidden='true'] {
    opacity: 0;
    transform: translateY(70%);
}

.contact-submit .ic-wrap:first-child {
    position: relative;
}

.contact-submit__icon,
.contact-submit__spinner,
.contact-submit__check {
    inset: 0;
    margin: auto;
    position: absolute;
    transition:
        opacity 0.25s ease,
        transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}

.contact-submit__icon {
    height: 1em;
    line-height: 1;
    width: 1em;
}

.contact-submit__spinner {
    animation: contact-spin 0.8s linear infinite;
    border: 2px solid currentColor;
    border-inline-end-color: transparent;
    border-radius: 50%;
    height: 18px;
    opacity: 0;
    transform: scale(0.6);
    width: 18px;
}

.contact-submit__check {
    opacity: 0;
    stroke-dasharray: 24;
    stroke-dashoffset: 24;
}

.contact-form[data-state='sending'] .contact-submit__icon,
.contact-form[data-state='sent'] .contact-submit__icon {
    opacity: 0;
    transform: scale(0.6);
}

.contact-form[data-state='sending'] .contact-submit__spinner {
    opacity: 1;
    transform: none;
}

.contact-form[data-state='sent'] .contact-submit__check {
    opacity: 1;
    stroke-dashoffset: 0;
    transition:
        opacity 0.2s ease,
        stroke-dashoffset 0.5s 0.1s cubic-bezier(0.22, 1, 0.36, 1);
}

/* The theme's hover swaps the two icon slots; hold still while busy or done. */
.contact-form[data-state='sending'] .contact-submit .ic-wrap:nth-child(1),
.contact-form[data-state='sent'] .contact-submit .ic-wrap:nth-child(1) {
    margin-inline: 0;
    transform: none;
}

.contact-form[data-state='sending'] .contact-submit .ic-wrap:nth-child(3),
.contact-form[data-state='sent'] .contact-submit .ic-wrap:nth-child(3) {
    margin-inline: -48px 0;
    opacity: 0;
    transform: scale(0);
    visibility: hidden;
}

.contact-form[data-state='sending'] .contact-submit::after {
    animation: contact-sheen 1.1s cubic-bezier(0.22, 1, 0.36, 1) infinite;
}

/* Outcome line beside the button; an icon as well as a colour carries the tone. */
.contact-status {
    align-items: center;
    display: flex;
    gap: 10px;
    margin: 0;
    animation: contact-rise 0.4s cubic-bezier(0.22, 1, 0.36, 1) both;
}

.contact-status:empty {
    display: none;
}

.contact-status::before {
    align-items: center;
    border-radius: 50%;
    display: inline-flex;
    flex: 0 0 22px;
    font-size: 12px;
    font-weight: 700;
    height: 22px;
    justify-content: center;
}

.contact-status.is-success {
    color: var(--primary-color, #00de51);
}

.contact-status.is-success::before {
    background: rgba(0, 222, 81, 0.14);
    content: '✓';
}

.contact-status.is-error {
    color: var(--contact-error);
}

.contact-status.is-error::before {
    background: rgba(255, 90, 78, 0.14);
    content: '!';
}

@keyframes contact-spin {
    to { transform: rotate(1turn); }
}

@keyframes contact-sheen {
    from { transform: translateX(0) skewX(-18deg); }
    to   { transform: translateX(320%) skewX(-18deg); }
}

@keyframes contact-rise {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .contact-field-error,
    .contact-status,
    .contact-form[data-state='sending'] .contact-submit::after {
        animation: none;
    }

    .contact-submit__labels > span,
    .contact-submit__icon,
    .contact-submit__spinner,
    .contact-submit__check {
        transition: none;
    }
}

/*
 * Work highlights: a project with no cover image would otherwise collapse the
 * card, so a titled panel keeps the reference card's proportions.
 */
/* Generated stand-in cover, for the projects with no publishable screenshot. */
.project-cover {
    align-items: center;
    aspect-ratio: 700 / 427;
    background:
        radial-gradient(90% 120% at var(--cover-x, 50%) var(--cover-y, 20%), rgba(0, 222, 81, 0.22) 0%, rgba(0, 222, 81, 0) 62%),
        linear-gradient(160deg, #191919 0%, #0e0e0e 100%);
    border-radius: inherit;
    display: flex;
    flex-direction: column;
    gap: 14px;
    justify-content: center;
    overflow: hidden;
    position: relative;
    width: 100%;
}

.project-cover__grid {
    background-image:
        linear-gradient(rgba(255, 255, 255, 0.045) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255, 255, 255, 0.045) 1px, transparent 1px);
    background-size: 44px 44px;
    inset: -30%;
    position: absolute;
    transform: rotate(var(--cover-tilt, 0deg));
}

.project-cover__mark {
    color: rgba(255, 255, 255, 0.9);
    font-family: var(--secondary-font), serif;
    font-size: clamp(56px, 12vw, 132px);
    letter-spacing: -0.04em;
    line-height: 1;
    position: relative;
    text-shadow: 0 18px 50px rgba(0, 0, 0, 0.55);
}

.project-cover__type {
    border: 1px solid rgba(255, 255, 255, 0.14);
    border-radius: 999px;
    color: rgba(255, 255, 255, 0.56);
    padding: 6px 14px;
    position: relative;
}

/* The blurred copy behind the floating panel is a tall portrait crop. */
.w-image .project-cover {
    aspect-ratio: 468 / 856;
    height: 100%;
}

/* Project detail: pieces the reference page does not include. */
.project-stack {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    list-style: none;
    margin: 0 0 32px;
}

.project-stack__item {
    border: 1px solid var(--black-15, rgba(0, 0, 0, 0.15));
    border-radius: 999px;
    color: var(--black-72, rgba(0, 0, 0, 0.72));
    padding: 6px 14px;
}

.dark-mode .project-stack__item {
    border-color: rgba(255, 255, 255, 0.15);
    color: rgba(255, 255, 255, 0.72);
}

.project-gallery {
    display: grid;
    gap: 24px;
    margin-bottom: 40px;
}

@media (min-width: 768px) {
    .project-gallery {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

.project-gallery__item {
    margin: 0;
}

.project-gallery__item img {
    border-radius: 12px;
    width: 100%;
}

.project-gallery__item figcaption {
    margin-top: 8px;
}

.project-action {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
}

/* Link on from the shortened work list to the full projects page. */
.work-more {
    display: flex;
    justify-content: center;
    margin-top: 48px;
}

/* ---------------------------------------------------------------- Preloader */

body.preloading {
    overflow: hidden;
}

/*
 * The theme's own `.preloader` rule drives its exit from a `--preloader-clip`
 * inset and styles any `span` at 80px/800 uppercase — neither suits a full name
 * and a counter. The overlay is reset here and the exit is done with the gutter
 * bars instead.
 */
.preloader {
    --pl-surface: #0b0b0b;
    --pl-eyebrow: rgba(255, 255, 255, 0.4);
    --pl-name: #ffffff;
    --pl-rail: rgba(255, 255, 255, 0.1);
    --pl-count: rgba(255, 255, 255, 0.92);
    --pl-glow: rgba(0, 222, 81, 0.13);

    align-items: center;
    background-color: var(--pl-surface);
    clip-path: none;
    display: flex;
    justify-content: center;

    /*
     * The theme sizes this at 100vw × 100vh. On mobile, 100vh is the viewport
     * with the browser chrome retracted, so the bottom of the overlay — and the
     * rail pinned there — sits under Samsung Internet's bottom toolbar or
     * Chrome's bottom URL bar. 100dvh tracks the area actually visible.
     */
    width: 100%;
    height: 100vh;
    height: 100dvh;
}

/*
 * The overlay covers the page before any of the theme's own scripts run, so a
 * fixed dark ground flashes black in front of a light page. It follows the
 * resolved mode instead; the inline script in the layout settles that before
 * this element paints.
 */
body:not(.dark-mode) .preloader {
    --pl-surface: #f5f5f5;
    --pl-eyebrow: rgba(0, 0, 0, 0.45);
    --pl-name: #0f0f0f;
    --pl-rail: rgba(0, 0, 0, 0.1);
    --pl-count: rgba(0, 0, 0, 0.88);
    --pl-glow: rgba(0, 222, 81, 0.16);
}

/* A faint accent glow behind the name. */
.preloader::before {
    background: radial-gradient(60% 55% at 50% 50%, var(--pl-glow) 0%, rgba(0, 0, 0, 0) 70%);
    content: '';
    inset: 0;
    position: absolute;
    z-index: 1;
}

.preloader-inner {
    padding: 0 24px;
    position: relative;
    text-align: center;
    z-index: 3;
    transition:
        opacity 0.45s ease,
        transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}

.preloader-eyebrow {
    color: var(--pl-eyebrow);
    font-family: var(--third-font), sans-serif;
    font-size: 11px;
    letter-spacing: 0.24em;
    margin: 0 0 14px;
    opacity: 0;
    text-transform: uppercase;
    animation: preloader-fade 0.8s 0.15s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}

.preloader-name {
    margin: 0;
    overflow: hidden;
}

/* The name rises into place from behind its own mask. */
.preloader-name__line {
    color: var(--pl-name);
    display: block;
    font-family: var(--secondary-font), sans-serif;
    font-size: clamp(26px, 5.2vw, 58px);
    font-style: normal;
    font-weight: 400;
    letter-spacing: -0.03em;
    line-height: 1.1;
    text-transform: none;
    transform: translateY(110%);
    animation: preloader-rise 1s 0.2s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}

.preloader-meta {
    align-items: center;
    bottom: 0;
    display: flex;
    gap: clamp(16px, 4vw, 48px);
    left: 0;
    padding: clamp(20px, 4vw, 44px);
    padding-bottom: calc(clamp(20px, 4vw, 44px) + env(safe-area-inset-bottom, 0px));
    position: absolute;
    right: 0;
    z-index: 3;
}

.preloader-rail {
    background: var(--pl-rail);
    flex: 1 1 auto;
    height: 1px;
    overflow: hidden;
}

.preloader-rail__fill {
    background: var(--primary-color, #00de51);
    height: 100%;
    transform: scaleX(0);
    transform-origin: 0 50%;
    transition: transform 0.25s linear;
    width: 100%;
}

body[dir='rtl'] .preloader-rail__fill {
    transform-origin: 100% 50%;
}

.preloader-count {
    color: var(--pl-count);
    direction: ltr;
    flex: 0 0 auto;
    font-family: var(--primary-font), serif;
    font-size: clamp(34px, 7vw, 76px);
    font-variant-numeric: tabular-nums;
    font-weight: 400;
    line-height: 0.9;
    margin: 0;
    text-transform: none;
}

.preloader-count i {
    font-style: normal;
}

.preloader-count::after {
    color: var(--primary-color, #00de51);
    content: '%';
    font-size: 0.4em;
    vertical-align: super;
}

/* Eight bars form the curtain; they sweep up in sequence to reveal the page. */
.preloader .preloader-gutters {
    z-index: 2;
}

.preloader .preloader-gutters .bar {
    background-color: var(--pl-surface);
    transform: translateY(0);
    transition: transform 0.8s cubic-bezier(0.76, 0, 0.24, 1);
    width: 12.5%;
}

.preloader.is-leaving {
    background-color: transparent;
    pointer-events: none;
}

.preloader.is-leaving::before {
    opacity: 0;
    transition: opacity 0.3s ease;
}

.preloader.is-leaving .preloader-inner {
    opacity: 0;
    transform: translateY(-16px);
}

.preloader.is-leaving .preloader-meta {
    opacity: 0;
    transition: opacity 0.3s ease;
}

.preloader.is-leaving .preloader-gutters .bar {
    transform: translateY(-101%);
}

.preloader.is-leaving .preloader-gutters .bar:nth-child(1) { transition-delay: 0.00s; }
.preloader.is-leaving .preloader-gutters .bar:nth-child(2) { transition-delay: 0.05s; }
.preloader.is-leaving .preloader-gutters .bar:nth-child(3) { transition-delay: 0.10s; }
.preloader.is-leaving .preloader-gutters .bar:nth-child(4) { transition-delay: 0.15s; }
.preloader.is-leaving .preloader-gutters .bar:nth-child(5) { transition-delay: 0.20s; }
.preloader.is-leaving .preloader-gutters .bar:nth-child(6) { transition-delay: 0.25s; }
.preloader.is-leaving .preloader-gutters .bar:nth-child(7) { transition-delay: 0.30s; }
.preloader.is-leaving .preloader-gutters .bar:nth-child(8) { transition-delay: 0.35s; }

@keyframes preloader-fade {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: none; }
}

@keyframes preloader-rise {
    from { transform: translateY(110%); }
    to   { transform: none; }
}

/* ------------------------------------------------------------ Mobile chrome */

/*
 * No section is meant to scroll sideways. A single element a few pixels too
 * wide gives the page a horizontal scroll, and in RTL the browser anchors that
 * scroll to the right edge, so every section appears shifted and clipped.
 */
html,
body {
    overflow-x: clip;
}

/*
 * Below the desktop breakpoint the gear (which opens the full menu) takes the
 * top corner the theme's hamburger used to hold; the hamburger is gone.
 */
@media (max-width: 991.98px) {
    .tf-left-bar {
        top: 24px;
        transform: none;
    }

    /*
     * The profile card is the home page's hero. The theme stacks it above
     * every page on narrow screens, so inner pages open on a full-height photo
     * before their own content; keep it on home only.
     */
    body:not(#page-home) .sidebar-user {
        display: none;
    }
}

/* ------------------------------------------------------------------ Skills */

.section-skills .skill-grid {
    display: grid;
    gap: 32px 40px;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}

.skill-group__name {
    font-family: var(--secondary-font), sans-serif;
    letter-spacing: -0.02em;
    margin: 0 0 4px;
}

.skill-group__desc {
    margin: 0 0 14px;
}

.skill-list {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    list-style: none;
    margin: 0;
}

.skill-chip {
    border: 1px solid var(--black-15, rgba(0, 0, 0, 0.15));
    border-radius: 999px;
    color: var(--black-72, rgba(0, 0, 0, 0.72));
    padding: 6px 14px;
    transition:
        border-color 0.3s ease,
        color 0.3s ease,
        transform 0.3s cubic-bezier(0.22, 1, 0.36, 1);
}

.skill-chip:hover {
    border-color: var(--primary-color, #00de51);
    color: var(--primary-color, #00de51);
    transform: translateY(-2px);
}

.dark-mode .skill-chip {
    border-color: rgba(255, 255, 255, 0.15);
    color: rgba(255, 255, 255, 0.72);
}

/* ------------------------------------------------- Intro author refinement */

.section-intro .intro-author {
    align-items: center;
    display: flex;
    gap: 14px;
}

.section-intro .intro-author .author-image {
    border-radius: 999px;
    flex: 0 0 auto;
    height: 52px;
    overflow: hidden;
    position: relative;
    width: 52px;
}

.section-intro .intro-author .author-image::after {
    border: 1px solid var(--primary-color, #00de51);
    border-radius: inherit;
    content: '';
    inset: 0;
    opacity: 0.45;
    position: absolute;
}

.section-intro .intro-author .author-image img {
    height: 100%;
    object-fit: cover;
    object-position: center top;
    width: 100%;
}

.section-intro .intro-author .info_name {
    font-family: var(--secondary-font), sans-serif;
    letter-spacing: -0.02em;
    margin-bottom: 2px;
}

/* --------------------------------------------------------- Motion polish */

/*
 * Work cards carry no hover state at all, by request — and `.wg-work` must
 * never be given a transform in particular. The detail panel is
 * `position: fixed` and this element is one of its ancestors; a transform on an
 * ancestor becomes the containing block for a fixed descendant, so a hover lift
 * here dragged the panel out of the sidebar column and dropped it on top of the
 * card artwork for exactly as long as the pointer was over the card.
 */

/* Buttons pick up a subtle sheen on hover. */
.tf-btn-action,
.tf-btn {
    position: relative;
    overflow: hidden;
}

.tf-btn-action::after,
.tf-btn::after {
    background: linear-gradient(120deg, transparent 0%, rgba(255, 255, 255, 0.22) 50%, transparent 100%);
    content: '';
    inset: 0 auto 0 -60%;
    pointer-events: none;
    position: absolute;
    width: 60%;
    transform: skewX(-18deg);
    transition: transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
}

.tf-btn-action:hover::after,
.tf-btn:hover::after {
    transform: translateX(320%) skewX(-18deg);
}

/* A thin progress rail so long pages read as one continuous scroll. */
.scroll-progress {
    background: var(--primary-color, #00de51);
    height: 2px;
    inset: 0 auto auto 0;
    position: fixed;
    transform-origin: 0 50%;
    transform: scaleX(0);
    width: 100%;
    z-index: 999;
}

body[dir='rtl'] .scroll-progress {
    transform-origin: 100% 50%;
}

@media (prefers-reduced-motion: reduce) {
    .skill-chip,
    .tf-btn-action::after,
    .tf-btn::after,
    .preloader {
        transition: none;
    }

    .preloader-eyebrow,
    .preloader-name__line {
        animation: none;
        opacity: 1;
        transform: none;
    }

    .preloader .preloader-gutters .bar,
    .preloader-inner,
    .preloader-rail__fill {
        transition: none;
    }

    .scroll-progress {
        display: none;
    }
}

/* ------------------------------------------------------- Project showcase */

/*
 * `.gsap-anime-2` has no rule of its own in the theme, but its script
 * positions the cards from the parent's centre, so the container needs a size.
 */
.project-showcase {
    height: clamp(320px, 42vw, 520px);
    margin: 48px 0 8px;
    position: relative;
    width: 100%;
}

.project-showcase .flip-image {
    border-radius: 16px;
    box-shadow:
        0 18px 40px rgba(0, 0, 0, 0.45),
        0 0 0 1px rgba(255, 255, 255, 0.08) inset;
    display: block;
    overflow: hidden;
    text-decoration: none;
    transition:
        box-shadow 0.4s ease,
        filter 0.4s ease;
}

.project-showcase .flip-image::after {
    background: linear-gradient(to top, rgba(0, 0, 0, 0.78) 0%, rgba(0, 0, 0, 0) 62%);
    border-radius: inherit;
    content: '';
    inset: 0;
    opacity: 0;
    position: absolute;
    transition: opacity 0.35s ease;
}

.project-showcase .flip-image:hover {
    box-shadow:
        0 24px 54px rgba(0, 0, 0, 0.6),
        0 0 0 1px var(--primary-color, #00de51) inset;
    z-index: 20 !important;
}

.project-showcase .flip-image:hover::after {
    opacity: 1;
}

.flip-image__label {
    bottom: 10px;
    color: #fff;
    font-family: var(--third-font), sans-serif;
    font-size: 11px;
    inset-inline: 10px;
    line-height: 1.3;
    opacity: 0;
    overflow: hidden;
    position: absolute;
    text-overflow: ellipsis;
    transition:
        opacity 0.35s ease,
        transform 0.35s ease;
    transform: translateY(6px);
    white-space: nowrap;
    z-index: 2;
}

.project-showcase .flip-image:hover .flip-image__label {
    opacity: 1;
    transform: none;
}

/* ---------------------------------------------------------- Timeline rhythm */

/*
 * The theme spaces entries 88px apart, which suits its four-item demo. With a
 * full career history that reads as large empty gaps, so the rhythm is tighter
 * and scales down further on small screens.
 */
.section-education-experience .timeline-item:not(:last-child) {
    padding-bottom: clamp(32px, 4vw, 52px);
}

/*
 * The theme pins the rail at a fixed `left: 126px` but gives the date column
 * only `min-width: 120px`. Its own demo dates ("2021 - 2023") never exceed that,
 * so the dot lands on the rail by coincidence. Real employment ranges are longer
 * and each row is a different length, so every dot drifts a different distance
 * to the right of the rail. Pinning the column restores the alignment and lets
 * the date wrap to a second line instead, which is what the reference shows.
 */
@media (min-width: 576px) {
    .section-education-experience .timeline-item .timeline-date {
        flex: 0 0 120px;
        width: 120px;
    }
}

.section-education-experience .timeline-item .timeline-content {
    padding-bottom: 2px;
}

.section-education-experience .timeline-item .timeline-dot {
    transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}

.section-education-experience .timeline-item.active .timeline-dot {
    transform: scale(1.18);
}

@media (max-width: 575px) {
    .project-showcase {
        height: 300px;
    }
}

@media (prefers-reduced-motion: reduce) {
    .project-showcase .flip-image,
    .flip-image__label,
    .section-education-experience .timeline-item .timeline-dot {
        transition: none;
    }
}

/* ---------------------------------------------------------------------------
   Sidebar card: the text sits over the photo, so it needs its own scrim and a
   hard ceiling on how far it can climb.
   --------------------------------------------------------------------------- */
.sidebar-user .user-info {
    /* Isolate so the scrim below can sit behind the text but above the photo. */
    isolation: isolate;
}

.sidebar-user .user-info::before {
    background: linear-gradient(
        to top,
        rgba(0, 0, 0, 0.92) 0%,
        rgba(0, 0, 0, 0.78) 45%,
        rgba(0, 0, 0, 0.35) 78%,
        rgba(0, 0, 0, 0) 100%
    );
    border-radius: 0 0 18px 18px;
    bottom: -40px;
    content: "";
    left: -40px;
    pointer-events: none;
    position: absolute;
    right: -40px;
    top: -56px;
    z-index: -1;
}

/*
 * On phones the text block is inset only 20px from the card, so the 40px
 * bleed above ran past the viewport and gave the page a horizontal scroll —
 * which in RTL shifts every section sideways. Bleed exactly to the card edge.
 */
@media (max-width: 991px) {
    .sidebar-user .user-info::before {
        left: -20px;
        right: -20px;
    }
}

.sidebar-user .user-info .introduce {
    -webkit-box-orient: vertical;
    display: -webkit-box;
    -webkit-line-clamp: 4;
    overflow: hidden;
}

/* ---------------------------------------------------------------------------
   Contact: the direct lines beside the form.
   --------------------------------------------------------------------------- */
.contact-channels {
    display: grid;
    gap: 20px 32px;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    margin-bottom: 40px;
}

.contact-channel {
    display: grid;
    gap: 6px;
}

.contact-channel__label {
    text-transform: uppercase;
    letter-spacing: 0.08em;
}

.contact-channel__row {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
}

.contact-channel__value {
    color: var(--black-72, rgba(0, 0, 0, 0.72));
    transition: color 0.3s ease;
    word-break: break-word;
}

.contact-channel__value:hover {
    color: var(--primary);
}

.contact-channel__wa {
    align-items: center;
    border: 1px solid var(--black-15, rgba(0, 0, 0, 0.15));
    border-radius: 999px;
    color: var(--black-72, rgba(0, 0, 0, 0.72));
    display: inline-flex;
    gap: 6px;
    padding: 4px 12px 4px 10px;
    transition: all 0.3s ease;
}

.contact-channel__wa .icon {
    height: 18px;
    width: 18px;
}

.contact-channel__wa:hover {
    background-color: var(--primary);
    border-color: var(--primary);
    color: var(--black);
}

/* ---------------------------------------------------------------------------
   Work card: the detail panel.

   Two departures from the reference, both because real content behaves
   differently from the demo's.

   1. The reference fills the panel with a blurred, darkened copy of the project
      image and lays the text over it. That works for soft photography; with
      screenshots and interface mockups the copy stays legible enough to read as
      a second image sitting on top of the card. The panel is a plain surface
      here, so the artwork appears once.

   2. The reference panel is dark in both themes, because the darkened image
      backdrop made it dark either way. Without that backdrop a fixed dark panel
      becomes a black slab on the light page, so the panel follows the theme and
      its text tokens are flipped with it — the same trick the theme itself uses
      for dark mode.
   --------------------------------------------------------------------------- */
.wg-work .work-content .w-image {
    display: none;
}

@media (min-width: 992px) {
    .wg-work .wrap .work-content {
        background:
            radial-gradient(120% 90% at 0% 0%, rgba(0, 222, 81, 0.09) 0%, rgba(0, 222, 81, 0) 55%),
            var(--white);
        border: 1px solid var(--black-6, rgba(0, 0, 0, 0.06));
        box-shadow: var(--shadow);
    }

    .dark-mode .wg-work .wrap .work-content {
        background:
            radial-gradient(120% 90% at 0% 0%, rgba(0, 222, 81, 0.1) 0%, rgba(0, 222, 81, 0) 55%),
            #121212;
        backdrop-filter: none;
        border-color: rgba(255, 255, 255, 0.08);
    }
}

/*
 * The panel's copy is written in `--white-*` tokens because the reference
 * always had a dark backdrop. On a light surface those tokens have to invert —
 * all but `--white-92`, which is the action button's fill rather than text.
 */
body:not(.dark-mode) .wg-work .wrap {
    --white-8: rgba(0, 0, 0, 0.08);
    --white-40: rgba(0, 0, 0, 0.40);
    --white-56: rgba(0, 0, 0, 0.56);
    --white-72: rgba(0, 0, 0, 0.72);
}

body:not(.dark-mode) .wg-work .wrap .tf-btn-action.style-white .ic-wrap,
body:not(.dark-mode) .wg-work .wrap .tf-btn-action.style-white .text {
    background-color: #0f0f0f;
}

body:not(.dark-mode) .wg-work .wrap .tf-btn-action.style-white .icon,
body:not(.dark-mode) .wg-work .wrap .tf-btn-action.style-white .text {
    color: rgba(255, 255, 255, 0.92);
}

/*
 * Stacked layout: the cover belongs under the one-line description, not above
 * the title. The two cannot be reordered with `order` because the title sits
 * two levels deeper than the image, so the card carries a second copy of the
 * cover inside the content and each breakpoint shows exactly one of them.
 */
.wg-work .w-inline-image {
    display: none;
}

@media (max-width: 991px) {
    /* theme.css carries `.wg-work a.work-image { display: block }`, so this
       has to outrank it rather than simply follow it. */
    body .wg-work > a.work-image {
        display: none;
    }

    .wg-work .w-inline-image {
        border-radius: 14px;
        display: block;
        margin-bottom: 28px;
        overflow: hidden;
    }

    .wg-work .w-inline-image img {
        aspect-ratio: 700 / 427;
        display: block;
        height: auto;
        object-fit: cover;
        width: 100%;
    }
}
