/* Shared PHP-rendered component hooks. */
[x-cloak],[hidden]{display:none!important}
.sr-only{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}
.page-end-spacer{height:2.4rem}
.download-error[hidden]{display:none!important}
[data-primary-navigation][data-open]{display:flex}
.download-spinner{display:inline-block;width:1em;height:1em;border:.14em solid currentColor;border-right-color:transparent;border-radius:50%;animation:snaptik-spin .7s linear infinite}
.ad-frame{width:100%;padding-top:10px;min-height:250px}
/* Width and reserved height are set once, further down with .ad-wrap. This
   keeps only what is specific to the home placement. */
.home-ad-wrap{padding-top:10px}
#beauty-primary-menu{display:none}
#beauty-primary-menu[data-open]{display:block}
@media(min-width:768px){#beauty-primary-menu{display:block}}
@keyframes snaptik-spin{to{transform:rotate(1turn)}}
@media(max-width:600px){.theme-monster .content>table{display:block!important;width:100%!important;max-width:100%!important;overflow-x:auto!important;-webkit-overflow-scrolling:touch}}


/* ─────────────────────────────────────────────────────────────────────────
   Landing page components.

   Every value below is derived from the existing theme tokens (--panel,
   --line, --ink, --mut, --a1, --a2) and from patterns already in the theme:
   the .9rem radius of .tool-card, the 900 weight of .sect-h, and the
   a1 -> a2 gradient used by .hm-brand and the .sect-h border-image. Nothing
   here introduces a new colour, so dark mode inherits automatically through
   the same custom properties.

   Signature: the gradient rail running down the workflow timeline. It is the
   same gradient the brand mark already uses, turned on its side, so the
   sequence reads as part of the identity rather than a bolted-on widget.
   ───────────────────────────────────────────────────────────────────────── */

/* Workflow timeline ------------------------------------------------------ */
.flow{list-style:none!important;margin:1.25rem 0 0;padding:0!important;position:relative;display:grid;gap:.9rem;counter-reset:flow}
.flow>li{position:relative;counter-increment:flow;display:grid;grid-template-columns:2.5rem 1fr;gap:.9rem;align-items:start;background:var(--panel,#fff);border:1px solid var(--line,#e5ddcf);border-radius:.9rem;padding:.95rem 1rem}
/* The rail is drawn per-step so it survives reflow and never overshoots the
   last card, which a single absolutely positioned line would do. */
.flow>li:not(:last-child):before{content:"";position:absolute;inset-inline-start:2.15rem;top:3.15rem;bottom:-.9rem;width:2px;background:linear-gradient(180deg,var(--a1,#ff6a3d),var(--a2,#b476ff));opacity:.35}
.flow-num{grid-row:1/span 2;display:flex;align-items:center;justify-content:center;width:2.5rem;height:2.5rem;border-radius:999px;font-weight:900;font-size:.95rem;color:#fff;background:linear-gradient(135deg,var(--a1,#ff6a3d),var(--a2,#b476ff));box-shadow:0 2px 8px rgb(0 0 0/.12)}
.flow-num:after{content:counter(flow)}
/* min-width:0 stops a long unbroken word (a URL, a hashtag) from forcing the
   grid column wider than its track and pushing the card out of the page. */
.flow-body{min-width:0}
.flow-t{margin:0;font-size:1rem;font-weight:800;line-height:1.35;color:var(--ink,#181a22)}
.flow-d{margin:.3rem 0 0;font-size:.94rem;line-height:1.6;color:var(--mut,#5c6172)}

/* Feature cards ---------------------------------------------------------- */
.fcards{list-style:none!important;margin:1.25rem 0 0;padding:0!important;display:grid;gap:.8rem;grid-template-columns:1fr}
.fcard{background:var(--panel,#fff);border:1px solid var(--line,#e5ddcf);border-radius:.9rem;padding:1rem 1.05rem;position:relative;overflow:hidden}
/* A short gradient edge instead of a full border keeps the card quiet while
   still tying it to the accent. */
.fcard:before{content:"";position:absolute;inset-block:0;inset-inline-start:0;width:3px;background:linear-gradient(180deg,var(--a1,#ff6a3d),var(--a2,#b476ff))}
.fcard-t{display:block;font-weight:800;font-size:1rem;color:var(--ink,#181a22);margin-bottom:.28rem}
.fcard-d{margin:0;font-size:.94rem;line-height:1.6;color:var(--mut,#5c6172)}

/* Call-to-action banner -------------------------------------------------- */
.cta-band{margin:2.2rem 0 0;border-radius:1.1rem;padding:1.6rem 1.35rem;text-align:center;color:#fff;background:linear-gradient(120deg,var(--a1,#ff6a3d),var(--a2,#b476ff));box-shadow:0 8px 24px rgb(0 0 0/.14)}
.cta-t{margin:0;font-size:1.2rem;font-weight:900;line-height:1.3}
.cta-d{margin:.5rem auto 1.1rem;max-width:34rem;font-size:.96rem;line-height:1.6;opacity:.94}
.cta-btn{display:inline-flex;align-items:center;justify-content:center;height:3rem;padding-inline:1.6rem;border-radius:999px;font-weight:800;text-decoration:none;background:#fff;color:#181a22;transition:transform .18s ease,box-shadow .18s ease}
.cta-btn:hover{transform:translateY(-1px);box-shadow:0 6px 16px rgb(0 0 0/.18)}
.cta-btn:focus-visible{outline:3px solid #fff;outline-offset:3px}

/* Knowledge cards -------------------------------------------------------- */
.kcards{list-style:none!important;margin:1.25rem 0 0;padding:0!important;display:grid;gap:.8rem;grid-template-columns:1fr}
.kcard{background:var(--panel,#fff);border:1px solid var(--line,#e5ddcf);border-inline-start:3px solid var(--a2,#b476ff);border-radius:.9rem;padding:.95rem 1.05rem;font-size:.94rem;line-height:1.6;color:var(--mut,#5c6172)}
.kcard strong{color:var(--ink,#181a22);font-weight:800}

@media(min-width:640px){
  .fcards,.kcards{grid-template-columns:1fr 1fr}
  .flow>li{grid-template-columns:2.75rem 1fr}
}
@media(prefers-reduced-motion:reduce){
  .cta-btn{transition:none}
  .cta-btn:hover{transform:none}
}

/* Icon sizing safety net -------------------------------------------------
   Inline SVGs carry a viewBox but no intrinsic size. When the sizing class is
   missing the element collapses and the button renders empty, which is exactly
   what happened to the header menu icon after conversion. These rules make a
   missing utility class a cosmetic detail rather than an invisible control. */
.hbtn svg{width:1.25rem;height:1.25rem;flex:none}
.tool-ic svg{width:1.25rem;height:1.25rem;flex:none}

/* Ownership notice -------------------------------------------------------
   Set apart rather than shrunk. The left rule uses the accent gradient so the
   block reads as part of the page's voice, not as boilerplate bolted on. */
.notice{margin:1.35rem 0 0;padding:1rem 1.15rem;border:1px solid var(--line,#e5ddcf);border-inline-start:4px solid transparent;border-image:linear-gradient(180deg,var(--a1,#ff6a3d),var(--a2,#b476ff)) 1;border-radius:.6rem;background:var(--panel,#fff)}
.notice p{margin:0;font-size:.92rem;line-height:1.65;color:var(--mut,#5c6172)}

/* Why-cards -------------------------------------------------------------
   Same card family as .fcard but with the accent on the top edge instead of
   the left, so two card sections on one page read as related without looking
   like the same block repeated. */
.wcards{list-style:none!important;margin:1.25rem 0 0;padding:0!important;display:grid;gap:.8rem;grid-template-columns:1fr}
.wcard{position:relative;overflow:hidden;background:var(--panel,#fff);border:1px solid var(--line,#e5ddcf);border-radius:.9rem;padding:1.15rem 1.05rem 1rem}
.wcard:before{content:"";position:absolute;inset:0 0 auto 0;height:3px;background:linear-gradient(90deg,var(--a1,#ff6a3d),var(--a2,#b476ff))}
.wcard-t{display:block;font-weight:800;font-size:1rem;color:var(--ink,#181a22);margin-bottom:.28rem}
.wcard-d{margin:0;font-size:.94rem;line-height:1.6;color:var(--mut,#5c6172)}

/* AdSense Auto Ads containment ------------------------------------------
   Auto ads inject <ins> elements at positions Google chooses, which is what
   breaks layouts: dropped into a grid the ad becomes a grid item and steals a
   column; left unconstrained it can be wider than the viewport and create
   horizontal scroll. These rules do not block ads, they make an injected ad
   behave like a well-formed block wherever it lands. Which areas serve ads at
   all is controlled in the AdSense dashboard, not here. */
.ad-wrap ins.adsbygoogle iframe,.home-ad-wrap ins.adsbygoogle iframe,
.ad-modal__panel ins.adsbygoogle iframe{max-width:100%!important}
/* Injected into any of our grids: span the full row instead of taking a cell. */
.flow>ins.adsbygoogle,.fcards>ins.adsbygoogle,.kcards>ins.adsbygoogle,
.wcards>ins.adsbygoogle,.tools-grid>ins.adsbygoogle{grid-column:1/-1;margin-block:.5rem}
/* Structural blocks that must never be split by an ad: the gradient banner,
   the ownership notice, and the inside of a single timeline step. */
.cta-band ins.adsbygoogle,.notice ins.adsbygoogle,
.flow-body ins.adsbygoogle,.fcard ins.adsbygoogle,
.wcard ins.adsbygoogle,.kcard ins.adsbygoogle{display:none!important}
/* An ad landing between paragraphs should not inherit prose spacing. */
.content>ins.adsbygoogle{margin-block:1.2rem}

@media(min-width:640px){.wcards{grid-template-columns:1fr 1fr}}

/* Card icons -------------------------------------------------------------
   Inline <svg> is not on the AST tag whitelist, and widening that whitelist
   to please a decoration would be the wrong trade. These icons are CSS masks
   instead: the shape comes from a data URI, the colour from the brand
   gradient, so they inherit the theme and need no markup beyond one class. */
.ic{display:inline-block;width:1.35rem;height:1.35rem;flex:none;vertical-align:-.2rem;margin-inline-end:.5rem;
    background:linear-gradient(135deg,var(--a1,#ff6a3d),var(--a2,#b476ff));
    -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
    -webkit-mask-position:center;mask-position:center;
    -webkit-mask-size:contain;mask-size:contain}
.cta-btn .ic{background:#181a22;margin-inline-end:.45rem}

.ic-shield{-webkit-mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M12%202%204%205v6c0%205%203.4%209.4%208%2011%204.6-1.6%208-6%208-11V5zm-1.2%2013.2L7%2011.4l1.4-1.4%202.4%202.4%205-5L17.2%209z%22%2F%3E%3C%2Fsvg%3E");mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M12%202%204%205v6c0%205%203.4%209.4%208%2011%204.6-1.6%208-6%208-11V5zm-1.2%2013.2L7%2011.4l1.4-1.4%202.4%202.4%205-5L17.2%209z%22%2F%3E%3C%2Fsvg%3E")}
.ic-hd{-webkit-mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M12%202l2.4%205.5L20%208.2l-4%204%201%205.8-5-2.8-5%202.8%201-5.8-4-4%205.6-.7z%22%2F%3E%3C%2Fsvg%3E");mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M12%202l2.4%205.5L20%208.2l-4%204%201%205.8-5-2.8-5%202.8%201-5.8-4-4%205.6-.7z%22%2F%3E%3C%2Fsvg%3E")}
.ic-browser{-webkit-mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M3%204h18a1%201%200%20011%201v14a1%201%200%2001-1%201H3a1%201%200%2001-1-1V5a1%201%200%20011-1zm1%204v10h16V8zM5%205.5h1.5V7H5zm2.5%200H9V7H7.5z%22%2F%3E%3C%2Fsvg%3E");mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M3%204h18a1%201%200%20011%201v14a1%201%200%2001-1%201H3a1%201%200%2001-1-1V5a1%201%200%20011-1zm1%204v10h16V8zM5%205.5h1.5V7H5zm2.5%200H9V7H7.5z%22%2F%3E%3C%2Fsvg%3E")}
.ic-free{-webkit-mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M20%207h-2.2a3%203%200%2000-4.3-3.9L12%204.2l-1.5-1.1A3%203%200%20006.2%207H4a1%201%200%2000-1%201v3h18V8a1%201%200%2000-1-1zM4%2012v8a1%201%200%20001%201h6v-9zm9%209h6a1%201%200%20001-1v-8h-7z%22%2F%3E%3C%2Fsvg%3E");mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M20%207h-2.2a3%203%200%2000-4.3-3.9L12%204.2l-1.5-1.1A3%203%200%20006.2%207H4a1%201%200%2000-1%201v3h18V8a1%201%200%2000-1-1zM4%2012v8a1%201%200%20001%201h6v-9zm9%209h6a1%201%200%20001-1v-8h-7z%22%2F%3E%3C%2Fsvg%3E")}
.ic-file{-webkit-mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M6%202h8l6%206v14H6a2%202%200%2001-2-2V4a2%202%200%20012-2zm7%202v5h5zm-3%208v6l5-3z%22%2F%3E%3C%2Fsvg%3E");mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M6%202h8l6%206v14H6a2%202%200%2001-2-2V4a2%202%200%20012-2zm7%202v5h5zm-3%208v6l5-3z%22%2F%3E%3C%2Fsvg%3E")}
.ic-lock{-webkit-mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M12%202a5%205%200%20015%205v3h1a1%201%200%20011%201v10a1%201%200%2001-1%201H6a1%201%200%2001-1-1V11a1%201%200%20011-1h1V7a5%205%200%20015-5zm3%208V7a3%203%200%2010-6%200v3z%22%2F%3E%3C%2Fsvg%3E");mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M12%202a5%205%200%20015%205v3h1a1%201%200%20011%201v10a1%201%200%2001-1%201H6a1%201%200%2001-1-1V11a1%201%200%20011-1h1V7a5%205%200%20015-5zm3%208V7a3%203%200%2010-6%200v3z%22%2F%3E%3C%2Fsvg%3E")}
.ic-page{-webkit-mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M6%202h8l6%206v12a2%202%200%2001-2%202H6a2%202%200%2001-2-2V4a2%202%200%20012-2zm7%202v5h5zM8%2012h8v2H8zm0%204h8v2H8z%22%2F%3E%3C%2Fsvg%3E");mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M6%202h8l6%206v12a2%202%200%2001-2%202H6a2%202%200%2001-2-2V4a2%202%200%20012-2zm7%202v5h5zM8%2012h8v2H8zm0%204h8v2H8z%22%2F%3E%3C%2Fsvg%3E")}
.ic-nouser{-webkit-mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M12%2012a5%205%200%20100-10%205%205%200%20000%2010zm-8%209a8%208%200%200113.5-5.8l-1.5%201.4A6%206%200%20006%2021zm12.5-4.9l1.4-1.4%202.1%202.1%202.1-2.1%201.4%201.4-2.1%202.1%202.1%202.1-1.4%201.4-2.1-2.1-2.1%202.1-1.4-1.4%202.1-2.1z%22%2F%3E%3C%2Fsvg%3E");mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M12%2012a5%205%200%20100-10%205%205%200%20000%2010zm-8%209a8%208%200%200113.5-5.8l-1.5%201.4A6%206%200%20006%2021zm12.5-4.9l1.4-1.4%202.1%202.1%202.1-2.1%201.4%201.4-2.1%202.1%202.1%202.1-1.4%201.4-2.1-2.1-2.1%202.1-1.4-1.4%202.1-2.1z%22%2F%3E%3C%2Fsvg%3E")}
.ic-badge{-webkit-mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M12%201l2.9%202.1%203.5-.3%201.1%203.4%202.9%202-1.4%203.3%201.4%203.3-2.9%202-1.1%203.4-3.5-.3L12%2023l-2.9-2.1-3.5.3-1.1-3.4-2.9-2L3%2012.5%201.6%209.2l2.9-2%201.1-3.4%203.5.3zm-1%2014.4%206-6L15.6%208%2011%2012.6%208.4%2010%207%2011.4z%22%2F%3E%3C%2Fsvg%3E");mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M12%201l2.9%202.1%203.5-.3%201.1%203.4%202.9%202-1.4%203.3%201.4%203.3-2.9%202-1.1%203.4-3.5-.3L12%2023l-2.9-2.1-3.5.3-1.1-3.4-2.9-2L3%2012.5%201.6%209.2l2.9-2%201.1-3.4%203.5.3zm-1%2014.4%206-6L15.6%208%2011%2012.6%208.4%2010%207%2011.4z%22%2F%3E%3C%2Fsvg%3E")}
.ic-devices{-webkit-mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M3%204h12a1%201%200%20011%201v3h-2V6H4v9h6v2H3a1%201%200%2001-1-1V5a1%201%200%20011-1zm14%206h4a1%201%200%20011%201v9a1%201%200%2001-1%201h-4a1%201%200%2001-1-1v-9a1%201%200%20011-1zm.5%202v6h3v-6zM6%2018h4v2H6z%22%2F%3E%3C%2Fsvg%3E");mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M3%204h12a1%201%200%20011%201v3h-2V6H4v9h6v2H3a1%201%200%2001-1-1V5a1%201%200%20011-1zm14%206h4a1%201%200%20011%201v9a1%201%200%2001-1%201h-4a1%201%200%2001-1-1v-9a1%201%200%20011-1zm.5%202v6h3v-6zM6%2018h4v2H6z%22%2F%3E%3C%2Fsvg%3E")}
.ic-layers{-webkit-mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M12%202l10%205-10%205L2%207zm0%2011.2L4.6%2010%202%2011.3l10%205%2010-5L19.4%2010z%22%2F%3E%3C%2Fsvg%3E");mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M12%202l10%205-10%205L2%207zm0%2011.2L4.6%2010%202%2011.3l10%205%2010-5L19.4%2010z%22%2F%3E%3C%2Fsvg%3E")}
.ic-info{-webkit-mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M12%202a10%2010%200%20100%2020%2010%2010%200%20000-20zm1%2015h-2v-6h2zm0-8h-2V7h2z%22%2F%3E%3C%2Fsvg%3E");mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M12%202a10%2010%200%20100%2020%2010%2010%200%20000-20zm1%2015h-2v-6h2zm0-8h-2V7h2z%22%2F%3E%3C%2Fsvg%3E")}
.ic-clock{-webkit-mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M12%202a10%2010%200%20100%2020%2010%2010%200%20000-20zm1%2010.6V6h-2v7.4l4.8%202.9%201-1.7z%22%2F%3E%3C%2Fsvg%3E");mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M12%202a10%2010%200%20100%2020%2010%2010%200%20000-20zm1%2010.6V6h-2v7.4l4.8%202.9%201-1.7z%22%2F%3E%3C%2Fsvg%3E")}
.ic-phone{-webkit-mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M8%202h8a2%202%200%20012%202v16a2%202%200%2001-2%202H8a2%202%200%2001-2-2V4a2%202%200%20012-2zm8%2015V5H8v12zm-4%203.4a1%201%200%20100-2%201%201%200%20000%202z%22%2F%3E%3C%2Fsvg%3E");mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M8%202h8a2%202%200%20012%202v16a2%202%200%2001-2%202H8a2%202%200%2001-2-2V4a2%202%200%20012-2zm8%2015V5H8v12zm-4%203.4a1%201%200%20100-2%201%201%200%20000%202z%22%2F%3E%3C%2Fsvg%3E")}
.ic-signal{-webkit-mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M2%2020h3v-6H2zm5.5%200h3V9h-3zm5.5%200h3V4h-3zm5.5%200h3v-9h-3z%22%2F%3E%3C%2Fsvg%3E");mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M2%2020h3v-6H2zm5.5%200h3V9h-3zm5.5%200h3V4h-3zm5.5%200h3v-9h-3z%22%2F%3E%3C%2Fsvg%3E")}
.ic-image{-webkit-mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M21%203H3a2%202%200%2000-2%202v14a2%202%200%20002%202h18a2%202%200%20002-2V5a2%202%200%2000-2-2zm0%2016H3V5h18zM8.5%2011a2.5%202.5%200%20100-5%202.5%202.5%200%20000%205zM6%2017l3.8-4.8%202.4%202.9%203.4-4.4L20%2017z%22%2F%3E%3C%2Fsvg%3E");mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M21%203H3a2%202%200%2000-2%202v14a2%202%200%20002%202h18a2%202%200%20002-2V5a2%202%200%2000-2-2zm0%2016H3V5h18zM8.5%2011a2.5%202.5%200%20100-5%202.5%202.5%200%20000%205zM6%2017l3.8-4.8%202.4%202.9%203.4-4.4L20%2017z%22%2F%3E%3C%2Fsvg%3E")}
.ic-bookmark{-webkit-mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M6%202h12a1%201%200%20011%201v19l-7-4.4L5%2022V3a1%201%200%20011-1z%22%2F%3E%3C%2Fsvg%3E");mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M6%202h12a1%201%200%20011%201v19l-7-4.4L5%2022V3a1%201%200%20011-1z%22%2F%3E%3C%2Fsvg%3E")}
.ic-download{-webkit-mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M5%2020h14v-2H5zM19%209h-4V3H9v6H5l7%207z%22%2F%3E%3C%2Fsvg%3E");mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M5%2020h14v-2H5zM19%209h-4V3H9v6H5l7%207z%22%2F%3E%3C%2Fsvg%3E")}

/* CTA section (rendered by the template after the FAQ, outside .content) ---
   Placed outside .content deliberately. Inside it, `.content a` from
   theme.css and main.css (specificity 0,1,1) beat `.cta-btn` (0,1,0) and
   repainted the button sky-blue with an underline. Moving the banner out of
   the prose container removes the conflict at its source; the !important
   below is a belt-and-braces guard, not the fix. */
.cta-section{padding:0 0 3rem}
.cta-band .cta-btn{color:#181a22!important;text-decoration:none!important;font-weight:800}
.cta-band .cta-btn:hover{text-decoration:none!important}

/* Supported-downloads cards ---------------------------------------------
   The tools already have their own linked grid further down the page ("More
   free TikTok tools"), so repeating the links here added a second identical
   link set for no reader benefit and diluted the internal linking. These are
   descriptive cards: they say what each format gives you, and the linked grid
   remains the single place those pages are linked from. */
.scards{list-style:none!important;margin:1.25rem 0 0;padding:0!important;display:grid;gap:.8rem;grid-template-columns:1fr}
.scard{display:flex;gap:.75rem;align-items:flex-start;background:var(--panel,#fff);border:1px solid var(--line,#e5ddcf);border-radius:.9rem;padding:1rem 1.05rem}
.scard .ic{margin-top:.1rem}
.scard-b{min-width:0}
.scard-t{display:block;font-weight:800;font-size:1rem;color:var(--ink,#181a22);margin-bottom:.22rem}
.scard-d{margin:0;font-size:.94rem;line-height:1.6;color:var(--mut,#5c6172)}
@media(min-width:640px){.scards{grid-template-columns:1fr 1fr}}

/* The button sits on a hardcoded white pill, so its ink is hardcoded too.
   Deriving it from --ink made the label invisible in dark mode, where that
   token flips to #f5f3ef: light text on a white button. */

/* Smooth return to the download form -------------------------------------
   The header is position:sticky, so an untreated jump to #main-form parks the
   form underneath it. scroll-margin-top reserves the header's height plus a
   little breathing room. Motion is opt-out: anyone who has asked their system
   to reduce motion gets the instant jump instead. */
#main-form{scroll-margin-top:5rem}
@media(prefers-reduced-motion:reduce){html{scroll-behavior:auto}}

/* Right-to-left ----------------------------------------------------------
   The theme carries no RTL rules of its own, so these components use logical
   properties (inset-inline-start, border-inline-start, margin-inline-end)
   rather than left/right. Arabic then mirrors correctly with no separate
   stylesheet, and the same rules keep working for the other 21 languages.
   The one thing that must not mirror is the gradient rail's direction: it
   reads top-to-bottom in every language because the sequence does. */
[dir="rtl"] .notice{border-image-slice:1}
[dir="rtl"] .flow-num{transform:none}

/* The slot must be allowed to take the full width it was given. Padding on the
   panel plus padding on the viewport was costing four rem on a phone, which is
   why the unit sat in the middle with white margins either side. */

/* Ad slots ---------------------------------------------------------------
   AdSense writes an inline width onto the <ins> once a unit is filled, and an
   inline style beats a stylesheet rule, which is why the unit kept sitting
   narrower than the space reserved for it. !important is the only way to win
   that argument; the iframe inside still renders at whatever size the creative
   actually is. */
/* The creative brings its own background, usually white, and dropped straight
   onto the page it reads as a broken element rather than an ad. Clipping it to
   the same corner radius the rest of the interface uses makes it look placed
   instead of pasted, and `contain` stops a misbehaving unit from disturbing the
   layout around it.

   The reserved height is smaller on a phone: a responsive unit is rarely taller
   than about 250px there, and holding 280px pushed the whole page down for
   space that was never used. */
/* Nothing here rounds, crops or masks the creative. Rounded corners were tried
   and removed: they only look tidy by clipping the ad, and altering how a unit
   is presented is the sort of interference AdSense treats as a policy breach
   rather than a style choice. The wrapper reserves space and nothing more.

   The reserved height is smaller on a phone: a responsive unit is rarely taller
   than about 250px there, and holding 280px pushed the whole page down for
   space that was never used. */
.ad-wrap,.home-ad-wrap{
  position:relative;display:block;width:100%;max-width:100%;
  min-height:250px;
  contain:layout}
@media(min-width:640px){.ad-wrap,.home-ad-wrap{min-height:280px}}
/* One rule for every AdSense unit, including the ones Auto Ads injects into
   places we never chose. Two separate rules for the same selector lived here
   before and quietly disagreed about margins; this says it once. */
/* Scoped to the slots we place ourselves. Auto Ads units are positioned and
   sized by Google, and the anchor in particular is position:fixed with a width
   it works out from the viewport. Forcing width on that fought its own layout
   and left the gap down the right of the page, so those units are now left
   entirely alone apart from a guard that stops any of them being wider than the
   screen. We size what we own; Google sizes what it injects. */
.ad-wrap > ins.adsbygoogle,
.home-ad-wrap > ins.adsbygoogle,
.ad-modal__panel ins.adsbygoogle{
  display:block!important;width:100%!important;max-width:100%!important;
  margin-inline:0!important}

/* Anchor and vignette formats are exempt even from the guard: both are fixed to
   the viewport and clipping them is the kind of interference AdSense treats as
   a policy problem. */
ins.adsbygoogle:not(.adsbygoogle-noablate):not([data-anchor-status]):not([data-vignette-loaded]){
  max-width:100vw}

/* The label is the placeholder, not a permanent caption. It holds the space a
   lazily loaded unit will occupy so nothing shifts when the ad arrives, then
   gets out of the way. Absolute positioning means it never competes with the
   creative for layout. */
.ad-label{
  position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  pointer-events:none;
  font-size:.7rem;line-height:1.4;letter-spacing:.14em;text-transform:uppercase;
  color:var(--mut,#6b7280);opacity:.55}

/* AdSense sets data-ad-status itself, which is the only trustworthy signal
   that a creative actually arrived. :has covers current browsers; the
   data-ad-rendered attribute set from script covers the rest. */
.ad-wrap:has(ins[data-ad-status="filled"]) .ad-label,
.home-ad-wrap:has(ins[data-ad-status="filled"]) .ad-label,
.ad-wrap:has(iframe) .ad-label,
.home-ad-wrap:has(iframe) .ad-label,
[data-ad-rendered="true"] .ad-label{display:none}

.ad-wrap:has(iframe),.home-ad-wrap:has(iframe),
[data-ad-rendered="true"]{min-height:0}

/* An unfilled slot should not keep holding a screenful of nothing. */
.ad-wrap:has(ins[data-ad-status="unfilled"]),
.home-ad-wrap:has(ins[data-ad-status="unfilled"]),
[data-ad-rendered="empty"]{min-height:0}
[data-ad-rendered="empty"] .ad-label{display:none}

/* Horizontal overflow guard ----------------------------------------------
   A single element wider than the screen makes the whole page scroll sideways,
   which is why the header looked shifted and clipped at the left edge. Ad
   iframes are the usual cause because their width is decided by a third party.
   `clip` rather than `hidden` so position:sticky in the header keeps working.
*/
html{scroll-behavior:smooth}
html,body{max-width:100%;overflow-x:clip}

/* Failure note under the slideshow button. Deliberately plain: it is a
   diagnostic, not a design element, and it should read as information rather
   than alarm. */
.slideshow-error{
  margin:.5rem 0 0;font-size:.8rem;line-height:1.5;
  color:var(--bad,#f43f5e);opacity:.9;word-break:break-word}

/* Full bleed ad slots on a phone ------------------------------------------
   The slot sits inside .wrap, which carries 1rem of side padding. That padding
   is right for text and wrong for an ad: it leaves a visible gutter down one
   side while the unit, sized against the viewport rather than its container,
   spills past the other. Cancelling the padding with a matching negative
   margin gives the ad the whole screen width and keeps both edges flush.

   The arithmetic is tied to .wrap's padding-inline. If that value ever changes,
   this one has to change with it or the gutter returns. */
@media(max-width:639px){
  .ad-wrap,.home-ad-wrap{
    margin-inline:-1rem;
    width:calc(100% + 2rem);
    max-width:calc(100% + 2rem)}
}

/* Ad interstitial -------------------------------------------------------
   Deliberately plain. The creative brings its own frame, so anything we add
   around it is chrome competing with the ad for attention, and the previous
   version's white panel left a blank slab whenever a unit came back unfilled.

   The close control sits inside the panel rather than fixed to the viewport,
   where it used to land on top of the site header. A full-width button below
   the ad carries the same action in the place people reach for it. */
.ad-modal{position:fixed;inset:0;z-index:2000;overflow:auto;background:rgba(0,0,0,.72)}
.ad-modal__viewport{min-height:100%;display:flex;align-items:center;justify-content:center;padding:1rem}
/* The white card gd uses. Without it the creative's own background sits
   straight on the dark overlay and reads as part of the page rather than as a
   panel that can be dismissed. No overflow clipping here: the card frames the
   ad, it must never crop it. */
.ad-modal__panel{
  position:relative;width:100%;max-width:min(28rem,100%);
  background:#fff;padding:.75rem;
  /* The card sits above the dark overlay and below nothing else, so the
     creative inside it is never covered by the page behind. */
  z-index:1}
@media(max-width:639px){.ad-modal__panel{padding:.5rem}}
/* The slot needs a real box, and for a reason that is not about layout shift.
   An <ins> with no height collapses, the card shrinks to a strip, and the close
   button ends up floating across the middle of the creative. Giving the slot
   the size the unit expects means the ad fills the card instead of escaping it.

   min-height, never a fixed height: a taller creative grows the card rather
   than being cut off. position:relative so anything absolutely placed inside
   attaches to the slot rather than to the whole panel. */
.ad-modal__slot{position:relative;display:block;width:100%;min-height:250px}
@media(min-width:640px){.ad-modal__slot{min-height:280px}}
.ad-modal__close{
  position:absolute;top:-.25rem;right:-.25rem;transform:translate(0,-100%);
  display:inline-flex;align-items:center;justify-content:center;
  width:2.25rem;height:2.25rem;border-radius:999px;border:0;cursor:pointer;
  background:rgba(0,0,0,.55);color:#fff}
.ad-modal__close svg{width:1.25rem;height:1.25rem}
/* Sits inside the white card, so it needs its own contrast rather than the
   theme's panel colour, and a top border to separate it from the creative. */
.ad-modal__dismiss{
  display:block;width:100%;margin-top:.75rem;padding:.9rem 1rem;
  border:0;border-top:1px solid rgba(0,0,0,.1);cursor:pointer;
  font:inherit;font-size:1rem;font-weight:600;
  background:#f2f3f5;color:#111}
.ad-modal__dismiss:active{background:#e6e8ea}
@media(max-width:639px){
  .ad-modal__viewport{padding:.5rem}
  .ad-modal__close{top:0;right:0;transform:translate(0,-115%)}
}

/* Submit status ----------------------------------------------------------
   Timed in CSS rather than JavaScript: a form POST leaves the document waiting
   to be replaced and browsers suspend timers in that state, so anything driven
   by setTimeout stops after the first step.

   The schedule matches how long a resolve really takes, two to four seconds,
   so a typical wait shows three or four messages rather than one. */
.dl-progress{
  position:relative;overflow:hidden;
  height:3px;margin:.85rem 0 0;border-radius:999px;
  background:rgba(255,255,255,.12)}
.dl-progress>span{
  display:block;height:100%;width:0;border-radius:inherit;
  background:linear-gradient(90deg,var(--a1,#ff6a3d),var(--a2,#b476ff));
  animation:snaptik-progress 12s cubic-bezier(.25,.8,.35,1) forwards}
/* Stops short of the end on purpose: the bar cannot know when the server will
   answer, and a full bar that then sits still reads as a hang. */
@keyframes snaptik-progress{
  0%{width:6%}
  10%{width:34%}
  30%{width:58%}
  60%{width:76%}
  100%{width:93%}
}

.dl-status{
  position:relative;height:1.4rem;margin:.55rem 0 0;
  font-size:.9rem;line-height:1.4;text-align:center;color:var(--mut,#9aa1ad)}
/* One animation per message, all four handovers inside the first six seconds.
   The final message has no successor, so it fades in and holds. */
.dl-status>span{
  position:absolute;inset-inline:0;top:0;opacity:0;
  animation-name:snaptik-status-step;animation-timing-function:linear;
  animation-fill-mode:forwards}
.dl-status>span:nth-child(1){animation-delay:0s;animation-duration:1s}
.dl-status>span:nth-child(2){animation-delay:.8s;animation-duration:1.2s}
.dl-status>span:nth-child(3){animation-delay:1.8s;animation-duration:1.6s}
.dl-status>span:nth-child(4){animation-delay:3.2s;animation-duration:2.5s}
.dl-status>span:nth-child(5){animation-delay:5.5s;animation-duration:.4s;
  animation-name:snaptik-status-last}
@keyframes snaptik-status-step{
  0%{opacity:0}
  18%{opacity:1}
  82%{opacity:1}
  100%{opacity:0}
}
@keyframes snaptik-status-last{from{opacity:0}to{opacity:1}}

/* Reduced motion keeps the information and drops the movement. */
@media(prefers-reduced-motion:reduce){
  .dl-progress>span{animation:none;width:45%}
  .dl-status>span{animation:none;opacity:0}
  .dl-status>span:nth-child(1){opacity:1}
}

/* Slideshow archive button. Sits with the image grid it belongs to, full width
   on a phone where the grid is already edge to edge. */
.zip-btn{grid-column:1/-1;width:100%;margin:.75rem 0 .25rem;cursor:pointer}
.zip-btn[disabled]{opacity:.65;cursor:progress}

/* Retry ------------------------------------------------------------------
   The retry button is a plain submit inside the form that already holds the
   URL and a valid token, so pressing it repeats the request exactly. No script
   is involved, which is why it also works with JavaScript disabled. */
.err-retry{margin:.75rem 0 0}
.err-retry button{width:100%;cursor:pointer}
