/* =============================================================================
   Components — informal, v0.1
   Three survivors from the smart-home sheet, rebuilt by hand rather than
   trusting get_design_context output (which had dead-branch ternaries and
   non-unique ids — see data/design-system.json > correctnessDefects).

   Naming: plain classes, BEM-ish, namespaced `ds-` so they can be dropped into
   an existing page without collisions. Swap for real components later.
   ========================================================================== */

/* --- Button ---------------------------------------------------------------
   Source: Figma 1:847 (Primary / Secondary / Tertiary, L/M/S/XS).
   v0.1 keeps Primary + Secondary at two sizes; XS was icon-only toolbar
   furniture and Tertiary is just a text link, handled below. */

.ds-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);

  font-family: inherit;
  font-size: var(--fs-body);
  font-weight: var(--fw-black);
  line-height: 1;
  text-align: center;
  text-decoration: none;

  padding: var(--s-3) var(--s-4);
  border: var(--bw-base) solid var(--c-fg);
  border-radius: var(--radius-pill);
  background: var(--c-fg);
  color: var(--c-bg);

  cursor: pointer;
  transition: box-shadow 120ms ease, transform 120ms ease, background-color 120ms ease;
}

.ds-btn--lg {
  padding: var(--s-4) var(--s-5);
}

/* Hover: the source used a soft blurred drop shadow here, which was the one
   non-brutalist element in the whole system. v0.1 replaces it with the same
   hard-offset idiom as the card/field, so the language stays consistent. */
.ds-btn:hover {
  box-shadow: var(--shadow-lift);
  transform: translate(2px, 2px);
}

/* Secondary: transparent fill, inverted emphasis. Source's visual read only —
   unlike the source, the label stays black in every state, because a greyed
   -out label on an interactive control reads as disabled. */
.ds-btn--secondary {
  background: transparent;
  color: var(--c-fg);
}

/* Small pill — the outlined pill used for header CTAs and nav.
   Hugs its label: the only vertical space is one step of --s on each side of the
   line box, so the height follows the text instead of a fixed number, and the
   pill's corner radius stays proportionally round at any label length. The
   mockup's "MORE"/"contact me" button was 58px tall, but that was drawn around
   a specific label in a fixed-width frame; sized this way the only thing shared
   with it is the shape and the hairline outline, which is what actually reads
   as the same control. */
.ds-btn--pill {
  padding-block: var(--s-2);
  padding-inline: var(--s-4);
  border-width: var(--bw-hairline);
  background: transparent;
  color: var(--c-fg);
  font-weight: var(--fw-bold);
  font-size: var(--fs-meta);
  letter-spacing: 0.02em;
  /* a pill that wraps to two lines stops reading as a pill */
  white-space: nowrap;
}

/* Disabled: colour swap, exactly as the source system did it. */
.ds-btn:disabled,
.ds-btn[aria-disabled="true"] {
  background: var(--c-disabled);
  border-color: var(--c-disabled);
  color: var(--c-bg);
  box-shadow: none;
  transform: none;
  cursor: not-allowed;
}

/* --- Link -----------------------------------------------------------------
   Source: Button/Tertiary (no border, no fill). Rendered as a proper anchor
   so it is keyboard- and screen-reader-native. */

.ds-link {
  color: inherit;
  text-decoration: underline;
  text-decoration-thickness: var(--bw-hairline);
  text-underline-offset: 0.2em;
  transition: text-decoration-thickness 120ms ease;
}

.ds-link:hover {
  text-decoration-thickness: var(--bw-base);
}

/* Standalone action link — reads as a control, not inline prose. */
.ds-link--action {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  font-weight: var(--fw-bold);
  text-decoration: none;
}

.ds-link--action:hover {
  text-decoration: underline;
  text-decoration-thickness: var(--bw-base);
  text-underline-offset: 0.2em;
}

/* --- Card -----------------------------------------------------------------
   Source: Figma 1:696. The shell carried over (white, hairline border, square);
   the contents did not — the source held "Kitchen / 13 devices". Rebuilt around
   the project schema a portfolio needs. */

.ds-card {
  display: flex;
  flex-direction: column;
  background: var(--c-bg);
  border: var(--bw-hairline) solid var(--c-fg);
  border-radius: var(--radius);
  overflow: clip;
  color: inherit;
  text-decoration: none;
  transition: box-shadow 120ms ease, transform 120ms ease;
}

a.ds-card:hover {
  box-shadow: var(--shadow);
  transform: translate(-2px, -2px);
}

.ds-card__media {
  aspect-ratio: 4 / 3;
  border-block-end: var(--bw-hairline) solid var(--c-fg);
  background: var(--c-disabled);
  overflow: clip;
}

.ds-card__media > img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.ds-card__body {
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
  padding: var(--s-4);
}

.ds-card__title {
  font-size: var(--fs-h2);
  font-weight: var(--fw-black);
  line-height: var(--lh-tight);
}

.ds-card__meta {
  font-size: var(--fs-meta);
  color: var(--c-muted);
}

/* --- Field ----------------------------------------------------------------
   Source: Figma 1:590 (9 variants). v0.1 keeps Default + Error; Click/Filled
   are native :focus and :not(:placeholder-shown) in real HTML. */

.ds-field {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
}

.ds-field__label {
  font-size: var(--fs-meta);
  font-weight: var(--fw-bold);
}

.ds-field__input {
  font-family: inherit;
  font-size: var(--fs-body);
  font-weight: var(--fw-bold);

  padding: var(--s-3) var(--s-4);
  min-height: 3rem;

  background: var(--c-bg);
  color: var(--c-fg);
  border: var(--bw-base) solid var(--c-fg);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

.ds-field__input::placeholder {
  color: var(--c-disabled);
  font-weight: var(--fw-regular);
}

/* Error: the source shipped an Input/E-mail/Error variant with no error colour
   defined at all. Rather than invent a red — which would break the monochrome
   rule — error is expressed as heavier border weight plus a message. */
.ds-field--error .ds-field__input {
  border-width: var(--bw-heavy);
}

.ds-field__error {
  font-size: var(--fs-meta);
  font-weight: var(--fw-bold);
}

/* --- Skip link ------------------------------------------------------------- */

.ds-skip {
  position: absolute;
  left: -9999px;
}

.ds-skip:focus {
  position: fixed;
  top: var(--s-3);
  left: var(--s-3);
  z-index: 10;
  padding: var(--s-2) var(--s-3);
  background: var(--c-fg);
  color: var(--c-bg);
  font-weight: var(--fw-bold);
  text-decoration: none;
}

/* =============================================================================
   Portfolio additions — carried from the mockup (Figma node 1:1731)
   These are the mockup's own devices, not inherited from the smart-home sheet.
   ========================================================================== */

/* --- Service row ----------------------------------------------------------
   The strongest part of the mockup: a huge numeral, an uppercase label, and a
   diagonal arrow, separated by full-bleed hairlines.

   In the mockup these are plain rows. Here the whole row is a <details> so the
   four services expand in place instead of implying four separate pages. */

.ds-service {
  border-block-start: var(--bw-hairline) solid var(--c-fg);
}

.ds-service:last-of-type {
  border-block-end: var(--bw-hairline) solid var(--c-fg);
}

.ds-service__summary {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--s-4);
  padding-block: var(--s-5);
  cursor: pointer;
  list-style: none;
}

/* Hide the native disclosure marker in every engine. */
.ds-service__summary::-webkit-details-marker {
  display: none;
}
.ds-service__summary::marker {
  content: "";
}

.ds-service__index {
  font-size: var(--fs-index);
  font-weight: var(--fw-regular);
  line-height: 0.9;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.ds-service__label {
  font-size: var(--fs-display);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  line-height: var(--lh-tight);
}

/* --- Expand affordance -----------------------------------------------------
   One arrow, two rotations. The glyph is never redrawn or rescaled: it is the
   mockup's diagonal at rest and the same diagonal turned 45deg when open.

   At rest the diagonal reads as "carry on down", the direction the list runs.
   Turned 45deg it lands on straight down, pointing at the copy the row just
   revealed. The sign is easy to get backwards -- the angle was measured, not
   reasoned: -135 lands UP, 135 lands LEFT, -45 lands RIGHT, 45 lands DOWN on
   this glyph. The old -45 pointed right, which implied a horizontal journey a
   vertical accordion never makes.

   The pivot is the middle of the shaft (20,20), not the arrowhead. Rotating
   about the head swung the arrow from one corner of the box to the other, so the
   two states read as different icons. About the shaft's midpoint it turns in
   place and keeps its footprint.

   Exactly one glyph is drawn per state: both are in the DOM, but the closed one
   is display:none when open and vice versa. No cross-fade -- a fade shows both
   arrows superimposed and blurred mid-swap, which is exactly the "two designs"
   artefact this replaces. <details> expands with no animation, so easing would
   only make the icon report the new state after the content arrived.

   overflow stays visible on the svg: mid-turn the stroke ends sweep out to the
   box edge, and clipping them there would shave the arrowhead. */
.ds-service__arrow {
  inline-size: 2.5rem;
  block-size: 2.5rem;
}

.ds-service__arrow-glyph {
  transform-box: view-box;
  transform-origin: 20px 20px; /* middle of the shaft, so the arrow turns in place */
}

.ds-service__arrow-glyph--open {
  display: none;
}

.ds-service[open] .ds-service__arrow-glyph--closed {
  display: none;
}

.ds-service[open] .ds-service__arrow-glyph--open {
  display: block;
  transform: rotate(45deg);
}

.ds-service__body {
  padding-block-end: var(--s-6);
  padding-inline-start: calc(var(--fs-index) + var(--s-4));
  max-width: 48ch;
}

@media (max-width: 47.99rem) {
  .ds-service__summary {
    grid-template-columns: auto 1fr;
    row-gap: var(--s-2);
  }
  .ds-service__arrow {
    grid-column: 2;
    justify-self: end;
    inline-size: 1.75rem;
    block-size: 1.75rem;
  }
  .ds-service__body {
    padding-inline-start: 0;
  }
}

/* --- Editorial rules ------------------------------------------------------
   The mockup's rhythm device: full-bleed hairlines that run edge to edge,
   deliberately breaking the content container. */

.ds-rule {
  border: 0;
  border-block-start: var(--bw-hairline) solid var(--c-fg);
  inline-size: 100vw;
  margin-inline-start: 50%;
  transform: translateX(-50%);
}

/* --- Band -----------------------------------------------------------------
   Full-bleed section band, used for the services list so its rules can run
   edge to edge while the content stays in the container. */

.ds-band {
  padding-block: var(--s-7);
}

.ds-band__inner {
  width: 100%;
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: var(--pad-page);
}

/* --- Case study -----------------------------------------------------------
   The mockup's featured work block: index + title, a large image, and a
   hairline footer carrying context. */

.ds-case {
  display: flex;
  flex-direction: column;
  gap: var(--s-5);
  padding-block: var(--s-7);
}

.ds-case__head {
  display: flex;
  align-items: baseline;
  gap: var(--s-4);
  flex-wrap: wrap;
}

.ds-case__index {
  font-size: var(--fs-h2);
  font-weight: var(--fw-regular);
  font-variant-numeric: tabular-nums;
}

.ds-case__title {
  font-size: var(--fs-display);
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
}

.ds-case__media {
  border: var(--bw-hairline) solid var(--c-fg);
  aspect-ratio: 16 / 9;
  max-block-size: 30rem;
  background: var(--c-disabled);
  overflow: clip;
}

.ds-case__media > img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

.ds-case__foot {
  display: flex;
  justify-content: space-between;
  gap: var(--s-4);
  flex-wrap: wrap;
  padding-block-start: var(--s-3);
  border-block-start: var(--bw-hairline) solid var(--c-fg);
  font-size: var(--fs-meta);
  color: var(--c-muted);
}

/* --- Hero -----------------------------------------------------------------
   The headline deliberately breaks out to the right, as in the mockup. */

.ds-hero__title {
  font-size: var(--fs-hero);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  line-height: 0.92;
  letter-spacing: -0.02em;
}

.ds-hero__media {
  aspect-ratio: 268 / 346;
  background: var(--c-disabled);
  overflow: clip;
  filter: grayscale(1);
}

.ds-hero__media > img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

/* --- Scroll cue -----------------------------------------------------------
   Purely decorative: it is not a link/control, so it contributes no tab stop
   and no focus ring. Do not add a href here. */

.ds-cue {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-2);
  font-size: var(--fs-meta);
  color: var(--c-muted);
}

.ds-cue svg {
  block-size: 3rem;
  animation: ds-cue 1.8s ease-in-out infinite;
}

@keyframes ds-cue {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(6px); }
}
