/* ============================================================
   THEME - single source of truth for every colour on the site.
   ------------------------------------------------------------
   This is a complete daisyUI theme definition, the same shape daisy's own
   themes.css uses ([data-theme="name"] { --color-*: ... }). Previously this
   block only overrode 2 of daisy's ~20 tokens (primary, accent) and silently
   inherited the rest from daisy's stock "night" theme on the CDN - so nobody
   editing this file could see, or fully control, the theme actually in use.
   Every token below is now defined locally: --color-primary/--color-accent
   are OUR brand values (unchanged from before); everything else is daisy's
   stock "night" theme, copied in as plain hex so this file is authoritative
   and does not silently drift if the CDN's theme ever changes upstream.
   Change a value here and every Tailwind/daisy utility that uses it
   (text-accent, border-primary, bg-neutral, .btn-primary, ...) follows.
   ============================================================ */
[data-theme="night"] {
  color-scheme: dark;

  /* --- brand: RatchTech's own colours --- */
  --color-primary: #70f1f1; /* cyan - primary buttons, active nav, borders */
  --color-accent: #fa85ea; /* pink - "TECH" in the wordmark, headings, the duck mark */

  /* --- surfaces & text (daisy's stock "night" values) --- */
  --color-base-100: #0f172a;
  --color-base-200: #0c1425;
  --color-base-300: #0a1120;
  --color-base-content: #c9cbd0;
  --color-neutral: #1e293b;
  --color-neutral-content: #cdd0d4;

  /* --- text-on-colour pairs (contrast-checked against OUR brand values:
     primary 14.5:1, accent 9.1:1 - both comfortably pass, so daisy's stock
     pairing still works and needed no change) --- */
  --color-primary-content: #010d15;
  --color-accent-content: #14040c;

  /* --- secondary (daisy stock - not currently used by any component on the
     site, kept for completeness/future use) --- */
  --color-secondary: #818cf8;
  --color-secondary-content: #060715;

  /* --- status colours (daisy stock; not currently used, kept for
     completeness so the theme is genuinely complete in one place) --- */
  --color-info: #0ca5e9;
  --color-info-content: #000000;
  --color-success: #2fd4bf;
  --color-success-content: #01100d;
  --color-warning: #f4bf51;
  --color-warning-content: #140d02;
  --color-error: #fb7085;
  --color-error-content: #150406;

  /* --- shape (daisy stock) --- */
  --radius-selector: 1rem;
  --radius-field: 0.5rem;
  --radius-box: 1rem;
  --size-selector: 0.25rem;
  --size-field: 0.25rem;
  --border: 1px;
  --depth: 0;
  --noise: 0;
}

/* Non-daisy surfaces used directly by this site's markup (footer, service
   cards, About panels, the contact form) - kept in the same theme block so
   every colour choice lives in one place, not just daisy's own tokens. */
:root {
  --rt-panel-surface: #14161a; /* dark tile: footer, cards, About panels */
  /* Lightbox sizing is fluid - see #image-modal img. The gap scales with the
     viewport so it is not a chunky border on a phone. */
  --rt-modal-gap: clamp(0.5rem, 1.6vw, 0.875rem);
  --rt-header-surface: #05070cd8; /* header/#main_content field - same colour,
     84% alpha so page content can show a little through underneath it */
}

/* ------------------------------------------------------------
   Third-party BRAND colours - fixed by the other company's guidelines, not
   part of our theme, and must never be retuned alongside it. Named here so
   every colour used on the site is still visible from a single file.
   ------------------------------------------------------------ */
:root {
  --rt-beckhoff-red: #e2001a;
  --rt-siemens-teal: #009999;
  --rt-rockwell-red: #cd163f; /* Rockwell Automation (Allen-Bradley's owner) */
}

/* ------------------------------------------------------------
   NOT reachable from here - flagged so a future theme change doesn't quietly
   miss them. Browsers never apply a page's CSS to a separately-loaded image
   file (favicon, <img src="...svg">), so these bake their colours in at
   generation time and must be regenerated by hand if the theme changes:
     assets/favicon/favicon.svg              #14161a  #fa85ea
     assets/brand/quack_wordmark.svg       #f2f2f0  (off-white, not themed)
     assets/social/rt_linkedin_*.svg     #14161a  #fa85ea
     assets/wallpaper/rt_4k*.svg         #14161a  #fa85ea  #f2f2f0
   ------------------------------------------------------------ */

:root {
  --hero-nav-h: var(--nav-height, 4.5rem); /* measured in scripts.js */
  --hero-max-w: 102rem; /* max width of the hero row */
  --hero-side-pad: clamp(6rem, 4vw, 4rem); /* hero padding, left + right */
  --hero-gap: clamp(0.5rem, 6vw, 11rem); /* gap between graphic and copy */
  --hero-copy-w: 34rem; /* width of the copy column */
  --hero-svg-scale: 0.9; /* stacked layout: graphic size as a share of the height left */
  --hero-top-margin: 5svh; /* side-by-side layout: clear space above the graphic and the duck */
}

#home_hero {
  position: relative;
  /* isolation/overflow-clip previously existed to contain the rotated duck
     watermark, which now lives on #duck-watermark and is fixed to the viewport.
     Nothing in the hero needs clipping any more. */
  margin-top: 0vh;
  align-items: center;
  justify-items: center;
}

#home_hero_content {
  width: 100%;
  max-width: var(--hero-max-w);
  gap: var(--hero-gap);
}

/* Duck watermark, fixed to the viewport so it stays visible the whole way down
   the page instead of scrolling away with the hero.

   It hangs off <body> rather than #home_hero: the hero clips its own overflow,
   which is what confined the old version to the first screen.

   position: fixed takes it out of flow and pins it to the viewport, and a
   NEGATIVE z-index drops it behind page content. That only works because body
   creates no stacking context of its own - so the duck sits beneath the
   surfaces painted by .header_block, #main_content and the footer.

   Those surfaces are translucent (--rt-header-surface is 84% alpha), which is
   deliberate: the duck reads faintly through them, so the whole page shares one
   continuous backdrop rather than the watermark stopping at the hero's edge. */
/* daisyUI paints background-color on :root, and a background on the ROOT
   element is propagated to the canvas - painted beneath everything. Moving the
   page colour onto <body> instead keeps the watermark layer paintable. */
:root {
  background-color: transparent;
}

body {
  /* The page colour, already composited. This used to be produced by painting
     a translucent --rt-header-surface over base-100 in three places. */
  background-color: #070911;
}

/* Duck watermark.
   ---------------------------------------------------------------
   A real element (#duck-watermark, first child of <body>) rather than a
   pseudo-element, so it can be selected and inspected in DevTools.

   The 30deg tilt is baked into duck_watermark_tilted.svg, so this rule needs no
   rotation at all.

   position: fixed keeps it on screen the whole way down; scripts.js writes
   --duck-drift from the scroll position so it visibly travels while you
   scroll. */
#duck-watermark {
  position: fixed;
  inset: 0;
  z-index: 0;
  transform: translateY(var(--duck-drift, 0px));
  /* promote to its own layer up front, so the SVG is rasterised once at load
     instead of on the first scroll frame - that first-move hitch reads as a jump */
  will-change: transform;
  pointer-events: none;
  background-image: url("../img/duck_watermark_tilted.svg");
  background-repeat: no-repeat;
  background-position: 12% 50%;
  /* 15% smaller than the original min(58vw, 950px) */
  background-size: min(49.3vw, 807.5px) auto;
  opacity: 0.07;
}

/* From 64rem the home hero is copy on the left, #pulsing_duck on the right,
   with "Take back control" bottom-aligned in the left column. The duck is
   placed in the space above that copy, over the copy column, with its top
   edge level with the top of #pulsing_duck - so the two start on the same
   line, --hero-top-margin (about 5% of the screen) or more below the header.
   The numbers come from the artwork: the duck itself occupies roughly the
   middle 75% of the image's height (12.6% clear above it) and is centred
   51% of the way across; the image is 0.976 as tall as it is wide.
     --hero-row-top  where the hero row, and so #pulsing_duck, starts: the row
                     is centred in the space under the nav
     --duck-room     height available for the duck: from that line down to
                     just above the copy block (19rem of copy and CTA,
                     measured up from the bottom of the row)
     --duck-w        image width: the 15%-reduced size, or less when there is
                     not room for that above the copy */
@media (width >= 64rem) {
  #duck-watermark {
    --hero-row-top: calc(
      var(--hero-nav-h) + (100svh - var(--hero-nav-h) - var(--hero-svg-size)) /
        2
    );
    --duck-room: calc(var(--hero-svg-size) - 19rem - 1rem);
    --duck-w: max(
      12rem,
      min(49.3vw, 807.5px, calc(var(--duck-room) / 0.75 / 0.976))
    );
    background-size: var(--duck-w) auto;
    background-position: calc(
        var(--page-buffer-vw) + var(--hero-copy-w) / 2 - 0.51 * var(--duck-w)
      )
      calc(var(--hero-row-top) - 0.126 * 0.976 * var(--duck-w));
  }
}

@media (width < 48rem) {
  #duck-watermark {
    /* was 92vw; reduced by the same 15% */
    background-size: 78vw auto;
    background-position: 50% 50%;
    opacity: 0.05;
  }
}

/* Reduced motion: no scroll-linked drift. scripts.js checks the same query. */
@media (prefers-reduced-motion: reduce) {
  #duck-watermark {
    transform: none;
  }
}

/* The watermark is a sibling of the page content in the same stacking context,
   so the content is lifted above it explicitly. */
.header_block,
#main_content,
footer {
  position: relative;
  z-index: 1;
}

#home_hero_copy {
  width: 100%;
  max-width: var(--hero-copy-w);
}

/* ------------------------------------------------------------
   PAGE WIDTH - every section lines up with the hero.
   The hero row is copy + gap + graphic, centred, so its left/right buffer is
   whatever is left over: (available width - row width) / 2. The graphic is
   sized from the viewport, so that buffer changes with the window; these
   variables rebuild the same sum so .page-width can pad any other block by
   exactly that much and its content edges land under the hero's.
     --hero-svg-size  the graphic's size (also used by #pulsing_duck itself)
     --hero-row-w     copy + gap + the graphic's lg:ms-5 margin + graphic
     --page-buffer    the buffer, as a share of the containing block (so a
                      scrollbar is accounted for); floor of 1rem is the hero's
                      own padding
     --page-buffer-vw same sum against the viewport, for sizing things that
                      cannot use a percentage (the carousel arrows)
   Below 64rem the hero stacks, so its width is the wider of the copy column
   and the graphic (the graphic only wins on tall tablets).
   ------------------------------------------------------------ */
:root {
  --hero-svg-size: max(
    15rem,
    min(calc((100svh - 26rem - var(--hero-gap)) * var(--hero-svg-scale)), 78vw)
  );
  --hero-row-w: max(var(--hero-copy-w), var(--hero-svg-size));
  --page-buffer: max(1rem, calc((100% - var(--hero-row-w)) / 2));
  --page-buffer-vw: max(1rem, calc((100vw - var(--hero-row-w)) / 2));
}

@media (width >= 64rem) {
  :root {
    /* as tall as the space under the nav allows with --hero-top-margin clear
       above and below it, unless the width runs out first */
    --hero-svg-size: min(
      calc(100svh - var(--hero-nav-h) - 2 * var(--hero-top-margin)),
      calc(
        min(100vw, var(--hero-max-w)) - var(--hero-side-pad) -
          var(--hero-gap) - var(--hero-copy-w)
      )
    );
    --hero-row-w: calc(
      var(--hero-copy-w) + var(--hero-gap) + 1.25rem + var(--hero-svg-size)
    );
  }
}

.page-width {
  max-width: none;
  margin-inline: 0;
  padding-inline: var(--page-buffer);
}

/* Typed sub-heading under the hero h1 (typewriter-effect; see
   setupHeroTyping in scripts.js). nowrap + a fixed one-line height mean the
   line never grows or wraps as the phrases change length, so nothing below
   it - or #pulsing_duck - moves. */
#hero_typed {
  height: 1lh;
  white-space: nowrap;
}

/* The library injects and blinks its own cursor element; this only colours
   it. */
#hero_typed .Typewriter__cursor {
  color: var(--color-primary);
}

#pulsing_duck {
  flex: 0 0 auto;
  width: auto;
  max-width: 100%;
  height: var(--hero-svg-size);
}

/* "How D.U.C.K Works" switches: daisyUI radio tabs (tabs-box), restyled to
   read as a two-position slider - one bordered track in primary, split down
   the middle, with the chosen half filled in primary like the site's
   buttons. --tab-bg is daisy's own hook for the active tab's fill.
   The two halves sit flush against the track and against each other: no inner
   padding, and no rounding on the halves themselves, so the edges where they
   meet are straight. Only the track's own outer corners are rounded, and it
   clips the halves to that shape.
   .duck-switch is the shared look (used as-is by the Single / Multi Node
   switch); #duck_mode_switch is the section's main control, so it also gets
   larger type. */
.duck-switch {
  padding: 0;
  overflow: hidden;
  border: 1px solid var(--color-primary);
  background-color: var(--rt-panel-surface);
}

.duck-switch > .tab {
  flex: 1 1 0;
  border-radius: 0;
  font-weight: 600;
  transition:
    background-color 0.2s,
    color 0.2s;
}

/* the divider between the two halves */
.duck-switch > .tab + .tab {
  border-left: 1px solid var(--color-primary);
}

/* the track clips anything outside it, so the keyboard focus ring is drawn
   just inside the half instead */
.duck-switch > .tab:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: -4px;
}

.duck-switch > .tab:checked {
  --tab-bg: var(--color-primary);
  color: var(--color-primary-content);
}

@media (width >= 64rem) {
  #duck_mode_switch {
    --tab-height: 4.25rem;
  }

  #duck_mode_switch > .tab {
    font-size: 1.5rem;
  }

  .duck-switch > .tab {
    font-size: 1.125rem;
  }
}

/* "Your controller" brand hint in the Co-operative copy: a daisyUI tooltip
   listing the supported controller brands. The dotted primary underline is the
   cue that the phrase can be hovered (or focused / tapped). */
.controller-hint {
  color: #ffffff;
  border-bottom: 2px dotted var(--color-primary);
  cursor: help;
}

.controller-hint:hover,
.controller-hint:focus-visible {
  color: var(--color-primary);
  outline: none;
}

.controller-brands {
  --tt-bg: var(--rt-panel-surface);
}

.controller-brands > .tooltip-content {
  padding: 0.6rem 0.9rem;
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.5;
  /* a crisp 1px ring standing in for a border; no soft shadow */
  box-shadow: 0 0 0 1px var(--color-primary);
}

/* "What D.U.C.K Offers": each card opens a fuller explanation in the shared
   #feature_expanded area (see scripts.js). The card's button is stretched over the
   whole card with ::after, so the card itself is the click target while the
   markup stays a real <button>. */
/* The two rows of cards. Markup-wise each is a daisyUI carousel, which is what
   makes it a sideways, snap-scrolling strip on small screens; from 48rem up
   that is switched off and the row is an ordinary grid. */
#feature_stack {
  display: flex;
  flex-direction: column;
  gap: 2rem;
}

.feature-row {
  gap: 1rem;
}

.feature-row > .feature-card {
  /* less than the full width, so the next card peeks in as the cue to swipe */
  width: 82%;
}

@media (width < 48rem) {
  .feature-row {
    /* room for the cards' glow and focus ring, which the scroll container
       would otherwise clip; pulled back out with a matching negative margin */
    padding: 0.75rem 0.5rem;
    margin: -0.75rem -0.5rem;
    scroll-padding-inline: 0.5rem;
    /* daisy hides a carousel's scrollbar. Put a thin one back: it shows there
       is more to the side, and gives mouse users something to drag */
    scrollbar-width: thin;
    scrollbar-color: color-mix(in oklab, var(--color-primary) 55%, transparent)
      transparent;
  }

  .feature-row::-webkit-scrollbar {
    display: block;
    height: 6px;
  }

  .feature-row::-webkit-scrollbar-thumb {
    border-radius: 9999px;
    background-color: color-mix(in oklab, var(--color-primary) 55%, transparent);
  }
}

@media (width >= 48rem) {
  .feature-row {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 2rem;
    overflow: visible;
  }

  .feature-row > .feature-card {
    width: auto;
  }
}

@media (width >= 80rem) {
  .feature-row {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

@media (prefers-reduced-motion: reduce) {
  .feature-row {
    scroll-behavior: auto;
  }
}

.feature-card {
  transition:
    border-color 0.2s,
    background-color 0.2s;
}

/* the selected card turns pink: border and a faint tint of the surface */
.feature-card.feature-active {
  border-color: var(--color-accent);
  background-color: color-mix(
    in oklab,
    var(--color-accent) 9%,
    var(--rt-panel-surface)
  );
}

.feature-card:has(.feature-trigger:focus-visible) {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* The feature expanded block takes focus when a card opens it (scripts.js).
   It is a region, not a control, so it gets no focus ring of its own. */
#feature_expanded:focus {
  outline: none;
}

/* Keeping the page still while feature cards are opened and switched:
   - the browser must not "helpfully" re-anchor the scroll position when the
     expanded block is inserted above whatever you were looking at; scripts.js
     decides where the page goes
   - the block is header / body / footer, and the eight headers and eight
     bodies are each stacked in one grid cell, so the block is as tall as the
     longest of them whichever one is showing. The hidden ones are
     inert (set by scripts.js) and only made invisible here, so they still
     hold the height. */
#feature_stack {
  overflow-anchor: none;
  /* scripts.js measures the expanded block's offset from this element */
  position: relative;
}

#feature_expanded_header > [data-feature-header],
#feature_expanded_body > [data-feature] {
  grid-area: 1 / 1;
}

#feature_expanded_header > [data-feature-header][inert],
#feature_expanded_body > [data-feature][inert] {
  visibility: hidden;
}

/* Footer buttons in the feature expanded block (previous, hide, next).
   Deliberately quiet:
   a faint outline and dimmed text at rest, turning pink (outline and text
   only, no fill) on hover and keyboard focus. daisy's .btn paints from these
   custom properties. */
.feature-nav-btn {
  --btn-bg: transparent;
  --btn-fg: color-mix(in oklab, var(--color-base-content) 60%, transparent);
  --btn-border: color-mix(in oklab, var(--color-base-content) 22%, transparent);
  --btn-shadow: none;
  background-color: transparent;
  border-color: color-mix(in oklab, var(--color-base-content) 22%, transparent);
  color: color-mix(in oklab, var(--color-base-content) 60%, transparent);
  font-weight: 500;
  /* One fixed size for both buttons, whatever the feature name inside. 7rem
     fits the short "Previous" / "Next" labels used on phones. */
  width: 7rem;
  flex: none;
}

/* From 40rem the buttons carry the feature's name: wide enough for the
   longest one, but never more than half the row so two always fit. A name
   that still does not fit is cut with an ellipsis rather than growing the
   button. Each sits in its own column of the footer grid, so 100% here is
   that column. */
@media (width >= 40rem) {
  .feature-nav-btn {
    width: min(15rem, 100%);
  }
}

/* "Hide" is a single short word, so it does not take the wide size */
#feature_expanded_close {
  width: 7rem;
}

.feature-nav-btn [data-feature-nav-title] {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.feature-nav-btn:hover,
.feature-nav-btn:focus-visible {
  --btn-bg: transparent;
  --btn-fg: var(--color-accent);
  --btn-border: var(--color-accent);
  background-color: transparent;
  border-color: var(--color-accent);
  color: var(--color-accent);
}

.feature-trigger {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  align-self: flex-start;
  margin-top: 0.5rem;
  font-size: 0.875rem;
  font-weight: 600;
  /* deliberately quiet: a dim "More detail" cue that only lights up when the
     card is hovered, so eight of them do not shout over the content */
  color: color-mix(in oklab, var(--color-base-content) 60%, transparent);
  cursor: pointer;
  outline: none;
  transition: color 0.2s;
}

.feature-card:hover .feature-trigger,
.feature-trigger:focus-visible {
  color: var(--color-primary);
}

.feature-trigger::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
}

.feature-trigger svg {
  transition: transform 0.2s;
}

.feature-card .feature-trigger[aria-expanded="true"] {
  color: var(--color-accent);
}

.feature-trigger[aria-expanded="true"] svg {
  transform: rotate(180deg);
}

/* ---- the RatchTech FAB (home page) ------------------------------------
   Carried over from web_interface's admin console (.fab_duck there). daisy's
   .fab does the fixed bottom-right positioning; this is the button itself:
   round, the RT mark filling it, a light blue (primary) outline. Sits above
   the page content but below the sticky nav and the lightbox. */
.fab_duck {
  z-index: 40;
}

.fab_duck_button {
  --btn-bg: var(--rt-panel-surface);
  --btn-border: var(--color-primary);
  --btn-shadow: none;
  width: 4.5rem;
  height: 4.5rem;
  padding: 0;
  background-color: var(--rt-panel-surface);
  border: 3px solid var(--color-primary);
  box-shadow: none;
  overflow: hidden;
  transition: border-color 0.2s;
}

/* hover and keyboard focus: the outline turns pink, like the site's other
   hover states */
.fab_duck_button:hover,
.fab_duck_button:focus-visible {
  --btn-bg: var(--rt-panel-surface);
  --btn-border: var(--color-accent);
  --btn-shadow: none;
  background-color: var(--rt-panel-surface);
  border-color: var(--color-accent);
  box-shadow: none;
}

.fab_duck_icon {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

@media (width < 48rem) {
  .fab_duck_button {
    width: 3.5rem;
    height: 3.5rem;
    border-width: 2px;
  }
}

#duck_in_action {
  margin-top: 10vh;
  padding-bottom: 14vh;
}
/* Sticky navbar. It is a sibling of .header_block rather than a child, so its
   containing block is <body> and it holds the top of the viewport for the
   whole page - inside the header it would be released as soon as the header
   scrolled past. */
/* Already stacks above the duck watermark via this z-index, so it is
   deliberately left out of the z-index:1 group above - re-declaring
   position here would clash with that rule. */
#site-nav {
  position: sticky;
  top: 0;
  z-index: 50;
}

/* Outside the header it has no background of its own and page content would
   scroll straight through it. The blur sits on the inner bar rather than on
   #site-nav because backdrop-filter would make that the containing block for
   the drawer's position: fixed panel, breaking the mobile menu. */
#site-navbar {
  /* The sticky bar genuinely needs to obscure content scrolling underneath it,
     so unlike the other page surfaces this one keeps a fill. It sits above the
     duck watermark (z-index 50) rather than over it in the same layer, so the
     watermark is unaffected. */
  background-color: #070911e6;
  backdrop-filter: blur(10px);
}

/* Keeps hash links clear of the sticky bar, and keeps scrollIntoView({block:
   "center"}) centring on the visible area rather than the full viewport. */
html {
  scroll-padding-top: var(--hero-nav-h);
}

.header_block {
  background-color: transparent;
  /* the navbar no longer sits inside it, so the hero fills what is left */
  min-height: calc(100svh - var(--hero-nav-h));
}

/* #main_content sits on <main> and had no rule, so Services and Featured Work
   fell through to base-100 while the hero above them was dark. Same value as
   .header_block, so the dark field runs unbroken from the hero to the footer
   regardless of what shows through the alpha. */
#main_content {
  background-color: transparent;
}

/* The nav entry for whichever section is currently under the sticky bar. The
   resting state is text-base-content/70, so this needs the #site-nav id to
   outrank that utility on specificity rather than just following it. */
#site-nav a.nav-active {
  color: var(--color-primary);
  opacity: 1;
}

/* One surface for the footer, the About page panels and the service cards.
   The header sits on var(--rt-header-surface); this is that same near-black lifted slightly
   toward grey, so these blocks read as sitting on the page rather than being
   cut out of the same darkness. Paired with border-primary the way the header
   pairs its own background with border-b border-primary. */
.panel-surface {
  background-color: var(--rt-panel-surface);
}

/* Icon-only WhatsApp trigger: no brand-green fill, just the mark rendered in
   the nav's own colours so it sits quietly next to Contact Us rather than
   competing with it. Matches the resting/hover treatment the text nav links
   already use (text-base-content/70 -> full opacity, colour -> accent). */
.btn-whatsapp {
  /* daisy's .btn:hover also redefines --btn-bg/--btn-shadow/--btn-inset (a
     grey hover tint + shadow baked into any ghost button), which showed up
     as a colour flash before the icon settled on pink. Neutralising these
     custom properties too - not just background-color/box-shadow - stops
     that tint from ever being computed, so hover changes only the icon's
     colour. */
  --btn-bg: transparent;
  --btn-border: transparent;
  --btn-shadow: none;
  --btn-inset: none;
  background-color: transparent;
  border-color: transparent;
  box-shadow: none;
  color: var(--color-base-content);
  opacity: 0.7;
}

.btn-whatsapp:hover {
  --btn-bg: transparent;
  --btn-border: transparent;
  --btn-shadow: none;
  --btn-inset: none;
  background-color: transparent;
  border-color: transparent;
  box-shadow: none;
  color: var(--color-accent);
  opacity: 1;
}

/* Skip link - lets keyboard and screen-reader users jump straight past the
   nav to the page content (WCAG 2.4.1 Bypass Blocks). Visually hidden until
   focused, then pinned to the top-left so it is clearly visible. */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
}

.skip-link:focus {
  left: 0.5rem;
  top: 0.5rem;
  padding: 0.75rem 1.25rem;
  background-color: var(--color-primary);
  color: var(--color-primary-content);
  border-radius: var(--radius-field);
  font-weight: 600;
}

/* ------------------------------------------------------------
   Featured Work carousel - see the matching block in scripts.js.
   ------------------------------------------------------------ */

/* The outer carousel must move between projects ONLY via its buttons or its
   own timer. overflow:hidden keeps it scrollable programmatically (scrollTo
   still works) while removing it as a user-scrollable surface, so a finger
   swipe cannot jump projects. Snapping is off because the script positions it
   exactly; leaving snap on would fight the programmatic scroll. Note this is
   done with overflow rather than touch-action, because touch-action is
   intersected down the ancestor chain - blocking pan-x here would also kill
   the horizontal swipe on the photo carousels nested inside. */
#duck_in_action_carousel {
  overflow: hidden;
  scroll-snap-type: none;
}

/* Prev/next arrows. The slide's text and image sit inside the page width; on
   desktop the arrows sit outside it, centred in the left/right buffer, and
   shrink to fit when that buffer is narrow. Below 64rem the buffer is too
   thin to hold them, so they stay above the slide as before.
   The wrapper spans the slide, so it must not swallow clicks meant for the
   photo underneath - only the arrows themselves take pointer events. */
.work-arrows {
  pointer-events: none;
}

.work-arrow {
  pointer-events: auto;
}

@media (width >= 64rem) {
  #duck_in_action_carousel {
    --work-arrow-size: clamp(2rem, calc(var(--page-buffer-vw) - 0.5rem), 3rem);
  }

  #duck_in_action_carousel .work-arrows {
    left: calc(var(--page-buffer) / 2 - var(--work-arrow-size) / 2);
    right: calc(var(--page-buffer) / 2 - var(--work-arrow-size) / 2);
  }

  #duck_in_action_carousel .work-arrow {
    --size: var(--work-arrow-size);
  }
}

/* The arrows pop in when the section comes into view and tuck away when it
   leaves. scripts.js adds .work-arrows-armed once it is running (so without
   JS they are simply always visible) and toggles .is-in-view from the same
   IntersectionObserver that runs the carousel's timer. Keyboard focus inside
   the carousel shows them too. */
#duck_in_action_carousel .work-arrow {
  transition:
    opacity 0.25s ease,
    transform 0.45s cubic-bezier(0.34, 1.56, 0.64, 1),
    color 0.2s,
    background-color 0.2s;
}

#duck_in_action_carousel .work-arrow[data-dir="next"] {
  transition-delay: 0.08s;
}

/* Pink on hover and keyboard focus. daisy's .btn paints from these custom
   properties, so they are set alongside the plain ones. */
#duck_in_action_carousel .work-arrow:hover,
#duck_in_action_carousel .work-arrow:focus-visible {
  --btn-bg: var(--color-accent);
  --btn-fg: var(--color-accent-content);
  --btn-border: var(--color-accent);
  background-color: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-accent-content);
  transition-delay: 0s;
}

#duck_in_action_carousel.work-arrows-armed:not(.is-in-view):not(:focus-within)
  .work-arrow {
  opacity: 0;
  transform: scale(0.4);
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  #duck_in_action_carousel .work-arrow {
    transition: opacity 0.25s ease;
  }

  #duck_in_action_carousel.work-arrows-armed:not(.is-in-view):not(:focus-within)
    .work-arrow {
    transform: none;
  }
}

/* The photo carousels stay natively swipeable. overscroll-behavior-x:contain
   stops a swipe that runs past the last photo from chaining outward into the
   outer carousel and dragging the project along with it. */
[data-image-carousel] {
  overscroll-behavior-x: contain;
}

/* Photo indicator dots, injected by scripts.js only where a segment holds more
   than one photo. The button is a full 24px so it clears the 24x24 minimum
   touch target (WCAG 2.5.8); the visible dot is drawn by ::after, so the dot
   can stay small without shrinking the tap area. */
.carousel-dot {
  width: 1.5rem;
  height: 1.5rem;
  display: grid;
  place-items: center;
  cursor: pointer;
  background: none;
  border: 0;
  padding: 0;
}

.carousel-dot::after {
  content: "";
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 9999px;
  background-color: var(--color-base-content);
  opacity: 0.35;
  transition:
    opacity 0.2s,
    background-color 0.2s,
    transform 0.2s;
}

.carousel-dot:hover::after {
  opacity: 0.7;
}

.carousel-dot[aria-current="true"]::after {
  background-color: var(--color-accent);
  opacity: 1;
  transform: scale(1.25);
}

.carousel-dot:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
  border-radius: 9999px;
}

/* ------------------------------------------------------------
   Image lightbox (see the matching block in scripts.js).
   A native <dialog>, so Escape, focus trapping and the inert background come
   from the browser.
   ------------------------------------------------------------ */

/* The gap between the panel edge and the image is a single uniform padding,
   so it is identical on all four sides for every image whatever its aspect or
   native size. That means NO letterbox bars: the panel follows the image
   instead of the image sitting inside a fixed box.

   The images differ a lot natively (961x521, 1361x848, 1920x1080), so the
   image is driven to a common width and only clamped by height - which scales
   the small one UP rather than leaving it at native size. Their footprints
   then land within ~3% of each other while the border stays constant. */
#image-modal {
  /* fit-content, NOT auto: with inset:0 an absolutely positioned element and
     width:auto resolves to "stretch to the containing block", so the dialog
     filled the viewport and the image floated small inside it. fit-content
     shrink-wraps to the content while inset:0 + margin:auto still centres. */
  width: fit-content;
  height: fit-content;
  max-width: 94vw;
  max-height: 92dvh;
  padding: var(--rt-modal-gap);
  overflow: hidden;
  border: 1px solid var(--color-primary);
  border-radius: var(--radius-box);
  background-color: var(--rt-panel-surface);
  color: var(--color-base-content);

  /* A <dialog> is centred by the UA stylesheet's margin:auto, but that is
     fragile once width/height are set. Pin all four insets and centre. */
  inset: 0;
  margin: auto;
}

#image-modal::backdrop {
  background-color: rgb(3 5 9 / 0.8);
}

#image-modal img {
  display: block;
  margin-inline: auto;
  height: auto;

  /* The responsive box lives HERE, in CSS, as real max-* properties:
       - clamp() scales fluidly between a phone and a desktop with no fixed
         breakpoint and no magic pixel width
       - dvh, not vh, so mobile browser chrome collapsing does not push the
         modal off screen
       - rem bounds, so it respects the user's font size
     scripts.js reads these back with getComputedStyle (max-width/max-height
     resolve to px, unlike an unregistered custom property) and fits the
     image's aspect inside them. CSS decides how big; JS only decides the
     shape. */
  /* Subtract the panel's own padding, so image + border can never exceed the
     panel's max-width/max-height. A clamp() with a rem FLOOR would overflow a
     320px phone (the floor can exceed the viewport); min() cannot. */
  max-width: min(62rem, 94vw - 2 * var(--rt-modal-gap));
  max-height: min(
    calc(80dvh - 2 * var(--rt-modal-gap)),
    calc(92dvh - 2 * var(--rt-modal-gap) - 3.5rem)
  );

  border-radius: calc(var(--radius-box) - var(--rt-modal-gap) / 2);
}

/* Short/landscape phones: height is the scarce axis, so give the image more of
   it and let width follow the aspect ratio down. */
@media (orientation: landscape) and (height < 30rem) {
  #image-modal img {
    max-height: calc(80dvh - 2 * var(--rt-modal-gap));
  }
}

#image-modal-caption {
  margin: 0;
  padding: clamp(0.5rem, 1.6vw, 0.75rem) 0.25rem 0;
  font-size: clamp(0.8125rem, 0.75rem + 0.25vw, 0.875rem);
  line-height: 1.4;
  text-align: center;
  text-wrap: balance;
  color: color-mix(in oklab, var(--color-base-content) 75%, transparent);
}

#image-modal-close {
  position: absolute;
  /* offset from the image's corner, not the panel's, so it sits inside the
     picture and the uniform border around it stays clean */
  top: calc(var(--rt-modal-gap) + 0.5rem);
  right: calc(var(--rt-modal-gap) + 0.5rem);
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  border: 0;
  border-radius: 9999px;
  background-color: rgb(7 9 17 / 0.65);
  color: #ffffff;
  cursor: pointer;
}

#image-modal-close:hover {
  background-color: rgb(7 9 17 / 0.85);
  color: var(--color-accent);
}

#image-modal-close:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* ---------- Founder-photo doodle layer ----------------------------------
   Hovering a founder portrait swaps the cursor for a permanent marker and
   lets you scribble on their face. Each stroke is kept as points and fades
   out on its own 60s timeline once it is finished, so an old moustache is
   already going while a fresh pair of glasses is still solid.
   Drawing lives in setupDoodleSurfaces() in scripts.js, which adds
   .doodle-ready only on fine-pointer devices - on touch, a drag has to stay
   a scroll, so the whole feature stays off there.                          */
:root {
  --rt-doodle-ink: #fa85ea;  /* marker colour, matches the cursor's nib */
}

.doodle-surface {
  position: relative;
  display: block;
}

/* the marker cursor only appears once the JS has opted this surface in */
.doodle-surface.doodle-ready {
  cursor: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 32 32%22 width=%2232%22 height=%2232%22%3E %3Cg transform=%22rotate(45 16 16)%22%3E %3Crect x=%2212.5%22 y=%222%22 width=%227%22 height=%2214%22 rx=%221.6%22 fill=%22%231b1e24%22 stroke=%22%23f2f2f0%22 stroke-width=%221.1%22/%3E %3Crect x=%2212.5%22 y=%2215%22 width=%227%22 height=%223.2%22 fill=%22%23f2f2f0%22/%3E %3Cpath d=%22M13.6 18.2 h4.8 l-1.1 5.4 h-2.6 Z%22 fill=%22%23fa85ea%22 stroke=%22%231b1e24%22 stroke-width=%220.8%22 stroke-linejoin=%22round%22/%3E %3Cpath d=%22M14.7 23.6 h2.6 l-1.3 3.4 Z%22 fill=%22%23fa85ea%22/%3E %3C/g%3E %3C/svg%3E") 8 24, crosshair;
}

/* a native image-drag would steal the gesture mid-stroke, and on iOS a long
   press pops the save-image sheet over the top of whatever you are drawing */
.doodle-surface img {
  user-select: none;
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
}

/* A press on a photo draws, on any pointer. touch-action: none is what stops
   the browser turning a finger drag into a scroll - it only applies to the
   photos themselves, so the rest of the page scrolls exactly as normal. */
.doodle-surface {
  touch-action: none;
}

/* the wrapper owns the pointer handlers; the canvas is purely a display
   surface, and inherits the photo's corner radius so strokes clip to it */
.doodle-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* Confirmation toast, built by scripts.js on share so the markup stays out
   of all four pages. */
#doodle-toast {
  position: fixed;
  left: 50%;
  bottom: 1.5rem;
  z-index: 60;
  transform: translate(-50%, 0.75rem);
  padding: 0.6rem 1.1rem;
  border: 1px solid var(--color-primary);
  border-radius: 9999px;
  background-color: var(--rt-panel-surface);
  color: var(--color-base-content);
  font-size: 0.875rem;
  box-shadow: 0 8px 24px rgb(0 0 0 / 0.45);
  opacity: 0;
  pointer-events: none;
  transition: opacity 220ms ease, transform 220ms ease;
}

#doodle-toast.is-visible {
  opacity: 1;
  transform: translate(-50%, 0);
}

@media (prefers-reduced-motion: reduce) {
  #doodle-toast { transition: opacity 220ms ease; transform: translate(-50%, 0); }
}

/* Floating control bar: the touch-only draw toggle plus the share pill. Built
   by scripts.js so the markup stays out of all four pages. Sits above the
   toast so both can be on screen at once. */
#doodle-bar {
  position: fixed;
  left: 50%;
  bottom: 5rem;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  transform: translate(-50%, 0.75rem);
  opacity: 0;
  visibility: hidden;
  transition: opacity 220ms ease, transform 220ms ease, visibility 0s linear 220ms;
}

#doodle-bar.is-visible {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
  transition-delay: 0s;
}

/* share only earns its place once a photo actually has ink on it */
#doodle-share {
  display: none;
}

#doodle-share.is-visible {
  display: inline-flex;
}

.doodle-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.7rem 1.15rem;
  border: 1px solid var(--color-primary);
  border-radius: 9999px;
  background-color: var(--rt-panel-surface);
  color: var(--color-base-content);
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  box-shadow: 0 8px 24px rgb(0 0 0 / 0.45);
  transition: color 150ms ease, border-color 150ms ease, background-color 150ms ease;
}

/* comfortably past the 44px minimum touch target */
@media (any-pointer: coarse) {
  .doodle-pill {
    padding: 0.85rem 1.3rem;
  }
}

.doodle-pill:hover {
  color: var(--color-accent);
  border-color: var(--color-accent);
}

.doodle-pill:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  #doodle-bar {
    transition: opacity 220ms ease, visibility 0s linear 220ms;
    transform: translate(-50%, 0);
  }
}

/* Per-photo sponge. Sits just above its own portrait and only appears while
   that face actually has something on it, so each founder is cleaned
   independently. Absolutely positioned, so showing and hiding it never
   reflows the card. */
.doodle-sponge {
  position: absolute;
  left: 50%;
  /* --rt-sponge-lift is measured per card in scripts.js */
  bottom: calc(100% + var(--rt-sponge-lift, 0.4rem));
  z-index: 2;
  display: grid;
  place-items: center;
  padding: 0.3rem 0.5rem;
  border: 1px solid var(--color-primary);
  border-radius: 9999px;
  background-color: var(--rt-panel-surface);
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, 0.35rem) scale(var(--rt-sponge-scale, 1));
  transform-origin: bottom center;
  box-shadow: 0 6px 18px rgb(0 0 0 / 0.45);
  transition: opacity 200ms ease, transform 200ms ease, border-color 150ms ease,
    visibility 0s linear 200ms;
}

.doodle-sponge.is-visible {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0) scale(var(--rt-sponge-scale, 1));
  transition-delay: 0s;
}

.doodle-sponge:hover {
  border-color: var(--color-accent);
}

.doodle-sponge:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.doodle-sponge img {
  display: block;
  width: 1.75rem;
  height: auto;
  pointer-events: none;
}

/* the sponge is a control, not part of the drawing area */
.doodle-sponge {
  touch-action: auto;
}

@media (prefers-reduced-motion: reduce) {
  .doodle-sponge {
    transition: opacity 200ms ease, visibility 0s linear 200ms;
    transform: translate(-50%, 0) scale(var(--rt-sponge-scale, 1));
  }
}

/* Share menu: opens above the pill; items are plain buttons so keyboard and
   screen-reader users get real controls. Hidden until the pill is pressed. */
.doodle-menu {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 0.5rem);
  transform: translateX(-50%);
  display: none;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 11rem;
  padding: 0.4rem;
  border: 1px solid var(--color-primary);
  border-radius: 1rem;
  background-color: var(--rt-panel-surface);
  box-shadow: 0 8px 24px rgb(0 0 0 / 0.45);
}

.doodle-menu.is-open {
  display: flex;
}

.doodle-menu-item {
  padding: 0.55rem 0.9rem;
  border: 0;
  border-radius: 0.6rem;
  background: transparent;
  color: var(--color-base-content);
  font: inherit;
  font-size: 0.875rem;
  text-align: left;
  cursor: pointer;
}

.doodle-menu-item:hover,
.doodle-menu-item:focus-visible {
  background-color: rgb(255 255 255 / 0.06);
  color: var(--color-accent);
  outline: none;
}

.doodle-menu-item:focus-visible {
  box-shadow: inset 0 0 0 2px var(--color-primary);
}

